diff --git a/.github/workflows/dotnet-build-and-test.yml b/.github/workflows/dotnet-build-and-test.yml index 81a2546c8b60..351a24944280 100644 --- a/.github/workflows/dotnet-build-and-test.yml +++ b/.github/workflows/dotnet-build-and-test.yml @@ -25,11 +25,11 @@ jobs: fail-fast: false matrix: include: - - { dotnet: '6.0-jammy', os: 'ubuntu', configuration: Debug, integration-tests: true } + - { dotnet: '6.0-jammy', os: 'ubuntu', configuration: Debug } - { dotnet: '7.0-jammy', os: 'ubuntu', configuration: Release } - { dotnet: '8.0-preview-jammy', os: 'ubuntu', configuration: Release } - { dotnet: '6.0', os: 'windows', configuration: Release } - - { dotnet: '7.0', os: 'windows', configuration: Debug } + - { dotnet: '7.0', os: 'windows', configuration: Debug, integration-tests: true } - { dotnet: '8.0-preview', os: 'windows', configuration: Release } runs-on: ubuntu-latest diff --git a/.github/workflows/label-title-prefix.yml b/.github/workflows/label-title-prefix.yml index 99d8b6723e43..b0c01aad3fcd 100644 --- a/.github/workflows/label-title-prefix.yml +++ b/.github/workflows/label-title-prefix.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/github-script@v6 name: "Issue/PR: update title" with: - github-token: ${{ secrets.GH_ACTIONS_PR_WRITE }} + github-token: ${{ secrets.GITHUB_TOKEN }} script: | let prefixLabels = { "python": "Python", diff --git a/.github/workflows/python-integration-tests.yml b/.github/workflows/python-integration-tests.yml index 6cbf09b1dc1b..1a13270b1d9f 100644 --- a/.github/workflows/python-integration-tests.yml +++ b/.github/workflows/python-integration-tests.yml @@ -17,15 +17,36 @@ permissions: contents: read jobs: + paths-filter: + runs-on: ubuntu-latest + outputs: + pythonChanges: ${{ steps.filter.outputs.python}} + steps: + - uses: actions/checkout@v3 + - uses: dorny/paths-filter@v2 + id: filter + with: + filters: | + python: + - 'python/**' + # run only if 'python' files were changed + - name: python tests + if: steps.filter.outputs.python == 'true' + run: echo "Python file" + # run only if not 'python' files were changed + - name: not python tests + if: steps.filter.outputs.python != 'true' + run: echo "NOT python file" + python-merge-gate: - if: ${{ github.event_name == 'merge_group' }} + if: github.event_name != 'pull_request' && github.event_name != 'schedule' && needs.paths-filter.outputs.pythonChanges == 'true' runs-on: ${{ matrix.os }} strategy: max-parallel: 1 fail-fast: false matrix: python-version: ["3.11"] - os: [windows-latest, ubuntu-latest] + os: [ubuntu-latest] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} @@ -56,7 +77,7 @@ jobs: AzureOpenAIEmbedding__Label: azure-text-embedding-ada-002 AzureOpenAI__DeploymentName: ${{ vars.AZUREOPENAI__DEPLOYMENTNAME }} AzureOpenAIChat__DeploymentName: ${{ vars.AZUREOPENAI__CHAT__DEPLOYMENTNAME }} - AzureOpenAIEmbeddings__DeploymentName: ${{ vars.AZUREOPENAIEMBEDDING__DEPLOYMENTNAME }} + AzureOpenAIEmbeddings__DeploymentName: ${{ vars.AZUREOPENAIEMBEDDINGS__DEPLOYMENTNAME2 }} AzureOpenAI__Endpoint: ${{ secrets.AZUREOPENAI__ENDPOINT }} AzureOpenAIEmbeddings__Endpoint: ${{ secrets.AZUREOPENAI__ENDPOINT }} AzureOpenAI__ApiKey: ${{ secrets.AZUREOPENAI__APIKEY }} @@ -70,16 +91,10 @@ jobs: AZURE_COGNITIVE_SEARCH_ENDPOINT: ${{secrets.AZURE_COGNITIVE_SEARCH_ENDPOINT}} run: | cd python - poetry run pytest ./tests/integration/completions/test_azure_oai_chat_service.py -v - poetry run pytest ./tests/integration/completions/test_oai_chat_service.py -v - poetry run pytest ./tests/integration/completions/test_hf_local_text_completions.py -v - poetry run pytest ./tests/integration/connectors/memory/test_chroma.py -v - poetry run pytest ./tests/integration/connectors/memory/test_qdrant_memory_store.py -v - poetry run pytest ./tests/integration/planning -v - poetry run pytest ./tests/integration/embeddings -v + poetry run pytest ./tests/integration -v python-integration-tests: - if: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} + if: (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && needs.paths-filter.outputs.pythonChanges == 'true' runs-on: ${{ matrix.os }} strategy: max-parallel: 1 @@ -93,7 +108,7 @@ jobs: uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - + - name: Install dependencies with hnswlib native disabled if: matrix.os == 'macos-latest' && matrix.python-version == '3.11' run: | @@ -101,14 +116,14 @@ jobs: python -m pip install --upgrade pip setuptools wheel python -m pip install poetry pytest cd python && poetry install - + - name: Install dependencies with hnswlib native enabled if: matrix.os != 'macos-latest' || matrix.python-version != '3.11' run: | python -m pip install --upgrade pip setuptools wheel python -m pip install poetry pytest cd python && poetry install - + - name: Run Integration Tests id: run_tests shell: bash @@ -119,7 +134,7 @@ jobs: AzureOpenAIEmbedding__Label: azure-text-embedding-ada-002 AzureOpenAI__DeploymentName: ${{ vars.AZUREOPENAI__DEPLOYMENTNAME }} AzureOpenAIChat__DeploymentName: ${{ vars.AZUREOPENAI__CHAT__DEPLOYMENTNAME }} - AzureOpenAIEmbeddings__DeploymentName: ${{ vars.AZUREOPENAIEMBEDDING__DEPLOYMENTNAME }} + AzureOpenAIEmbeddings__DeploymentName: ${{ vars.AZUREOPENAIEMBEDDINGS__DEPLOYMENTNAME2 }} AzureOpenAI__Endpoint: ${{ secrets.AZUREOPENAI__ENDPOINT }} AzureOpenAIEmbeddings__Endpoint: ${{ secrets.AZUREOPENAI__ENDPOINT }} AzureOpenAI__ApiKey: ${{ secrets.AZUREOPENAI__APIKEY }} @@ -133,31 +148,70 @@ jobs: AZURE_COGNITIVE_SEARCH_ENDPOINT: ${{secrets.AZURE_COGNITIVE_SEARCH_ENDPOINT}} run: | cd python - echo "date=$(date +'%m/%d/%Y')" >> "$GITHUB_ENV" - EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) - echo "Test Output<<$EOF" >> "$GITHUB_OUTPUT" - echo "$(poetry run pytest ./tests/integration)" >> "$GITHUB_OUTPUT" - echo "$EOF" >> "$GITHUB_OUTPUT" - + poetry run pytest ./tests/integration -v + + # This final job is required to satisfy the merge queue. It must only run (or succeed) if no tests failed + python-integration-tests-check: + if: always() + runs-on: ubuntu-latest + strategy: + max-parallel: 1 + fail-fast: false + needs: [python-merge-gate, python-integration-tests] + steps: + - name: Get Date + shell: bash + run: | + echo "date=$(date +'%m/%d/%Y %H:%M:%S')" >> "$GITHUB_ENV" + + - name: Run Type is Daily + if: ${{ github.event_name == 'schedule' }} + shell: bash + run: | + echo "run_type=Daily" >> "$GITHUB_ENV" + + - name: Run Type is Manual + if: ${{ github.event_name == 'workflow_dispatch' }} + shell: bash + run: | + echo "run_type=Manual" >> "$GITHUB_ENV" + + - name: Run Type is ${{ github.event_name }} + if: ${{ github.event_name != 'schedule' && github.event_name != 'workflow_dispatch'}} + shell: bash + run: | + echo "run_type=${{ github.event_name }}" >> "$GITHUB_ENV" + + - name: Fail workflow if tests failed + id: check_tests_failed + if: contains(join(needs.*.result, ','), 'failure') + uses: actions/github-script@v6 + with: + script: core.setFailed('Integration Tests Failed!') + + - name: Fail workflow if tests cancelled + id: check_tests_cancelled + if: contains(join(needs.*.result, ','), 'cancelled') + uses: actions/github-script@v6 + with: + script: core.setFailed('Integration Tests Cancelled!') + - name: Microsoft Teams Notification uses: skitionek/notify-microsoft-teams@master - if: always() + if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' with: webhook_url: ${{ secrets.MSTEAMS_WEBHOOK }} - dry_run: False - needs: ${{ toJson(needs) }} + dry_run: ${{ env.run_type != 'Daily' && env.run_type != 'Manual'}} job: ${{ toJson(job) }} steps: ${{ toJson(steps) }} - overwrite: "{title: ` ${{ github.event_name }} ${{ steps.run_tests.outcome }}: ${{ env.date }} - ${{ matrix.python-version }} on ${{ matrix.os }}`, text: ` ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\n${{ toJson(steps.run_tests.outputs) }}`}" + overwrite: "{title: ` ${{ env.run_type }}: ${{ env.date }} `, text: ` ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`}" - # This final job is required to satisfy the merge queue. It must only run (or succeed) if no tests failed - python-integration-tests-check: - runs-on: ubuntu-latest - if: always() - needs: [python-merge-gate, python-integration-tests] - steps: - - name: Fail workflow if tests failed - if: contains(join(needs.*.result, ','), 'failed') - uses: actions/github-script@v6 - with: - script: core.setFailed('Integration Tests Failed!') + - name: Microsoft Teams Notification (Dry Run) + uses: skitionek/notify-microsoft-teams@master + if: github.ref != 'refs/heads/main' + with: + webhook_url: NONE + dry_run: ${{ env.run_type != 'Daily' && env.run_type != 'Manual'}} + job: ${{ toJson(job) }} + steps: ${{ toJson(steps) }} + overwrite: "{title: ` ${{ env.run_type }}: ${{ env.date }} `, text: ` ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`}" diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index bc3ecadf1947..9eb0de2b54c4 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -6,14 +6,14 @@ - + - + - + diff --git a/dotnet/README.md b/dotnet/README.md index 0deed5da6b35..22cbe95bf8f3 100644 --- a/dotnet/README.md +++ b/dotnet/README.md @@ -41,7 +41,7 @@ var prompt = @"{{$input}} One line TLDR with the fewest words."; -var summarize = kernel.CreateSemanticFunction(prompt); +var summarize = kernel.CreateSemanticFunction(prompt, maxTokens: 100); string text1 = @" 1st Law of Thermodynamics - Energy cannot be created or destroyed. @@ -80,8 +80,8 @@ string summarizePrompt = @"{{$input}} Give me a TLDR with the fewest words."; -var translator = kernel.CreateSemanticFunction(translationPrompt); -var summarize = kernel.CreateSemanticFunction(summarizePrompt); +var translator = kernel.CreateSemanticFunction(translationPrompt, maxTokens: 200); +var summarize = kernel.CreateSemanticFunction(summarizePrompt, maxTokens: 100); string inputText = @" 1st Law of Thermodynamics - Energy cannot be created or destroyed. diff --git a/dotnet/nuget/nuget-package.props b/dotnet/nuget/nuget-package.props index 333f96ede956..fb825d9a886e 100644 --- a/dotnet/nuget/nuget-package.props +++ b/dotnet/nuget/nuget-package.props @@ -1,7 +1,7 @@ - 0.19 + 0.20 Debug;Release;Publish true diff --git a/dotnet/samples/ApplicationInsightsExample/Program.cs b/dotnet/samples/ApplicationInsightsExample/Program.cs index e90cea2e1aa8..1c52a8c95734 100644 --- a/dotnet/samples/ApplicationInsightsExample/Program.cs +++ b/dotnet/samples/ApplicationInsightsExample/Program.cs @@ -40,7 +40,7 @@ public static async Task Main() var serviceProvider = GetServiceProvider(); var telemetryClient = serviceProvider.GetRequiredService(); - var logger = serviceProvider.GetRequiredService>(); + var logger = serviceProvider.GetRequiredService(); using var meterListener = new MeterListener(); using var activityListener = new ActivityListener(); @@ -102,14 +102,14 @@ private static void ConfigureApplicationInsightsTelemetry(ServiceCollection serv }); } - private static IKernel GetKernel(ILogger logger) + private static IKernel GetKernel(ILoggerFactory loggerFactory) { var folder = RepoFiles.SampleSkillsPath(); var bingConnector = new BingConnector(Env.Var("Bing__ApiKey")); var webSearchEngineSkill = new WebSearchEngineSkill(bingConnector); var kernel = new KernelBuilder() - .WithLogger(logger) + .WithLoggerFactory(loggerFactory) .WithAzureChatCompletionService( Env.Var("AzureOpenAI__ChatDeploymentName"), Env.Var("AzureOpenAI__Endpoint"), @@ -127,24 +127,24 @@ private static IKernel GetKernel(ILogger logger) private static ISequentialPlanner GetSequentialPlanner( IKernel kernel, - ILogger logger, + ILoggerFactory loggerFactory, int maxTokens = 1024) { var plannerConfig = new SequentialPlannerConfig { MaxTokens = maxTokens }; - return new SequentialPlanner(kernel, plannerConfig).WithInstrumentation(logger); + return new SequentialPlanner(kernel, plannerConfig).WithInstrumentation(loggerFactory); } private static IActionPlanner GetActionPlanner( IKernel kernel, - ILogger logger) + ILoggerFactory loggerFactory) { - return new ActionPlanner(kernel).WithInstrumentation(logger); + return new ActionPlanner(kernel).WithInstrumentation(loggerFactory); } private static IStepwisePlanner GetStepwisePlanner( IKernel kernel, - ILogger logger, + ILoggerFactory loggerFactory, int minIterationTimeMs = 1500, int maxTokens = 2000) { @@ -154,7 +154,7 @@ private static IStepwisePlanner GetStepwisePlanner( MaxTokens = maxTokens }; - return new StepwisePlanner(kernel, plannerConfig).WithInstrumentation(logger); + return new StepwisePlanner(kernel, plannerConfig).WithInstrumentation(loggerFactory); } /// diff --git a/dotnet/samples/KernelSyntaxExamples/Example02_Pipeline.cs b/dotnet/samples/KernelSyntaxExamples/Example02_Pipeline.cs index 819f84656fd8..99e25470250c 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example02_Pipeline.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example02_Pipeline.cs @@ -11,13 +11,13 @@ // ReSharper disable once InconsistentNaming public static class Example02_Pipeline { - private static readonly ILogger s_logger = ConsoleLogger.Logger; + private static readonly ILoggerFactory s_loggerFactory = ConsoleLogger.LoggerFactory; public static async Task RunAsync() { Console.WriteLine("======== Pipeline ========"); - IKernel kernel = new KernelBuilder().WithLogger(s_logger).Build(); + IKernel kernel = new KernelBuilder().WithLoggerFactory(s_loggerFactory).Build(); // Load native skill var text = kernel.ImportSkill(new TextSkill()); diff --git a/dotnet/samples/KernelSyntaxExamples/Example03_Variables.cs b/dotnet/samples/KernelSyntaxExamples/Example03_Variables.cs index 5d312892f49c..e69381202cf7 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example03_Variables.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example03_Variables.cs @@ -12,13 +12,13 @@ // ReSharper disable once InconsistentNaming public static class Example03_Variables { - private static readonly ILogger s_logger = ConsoleLogger.Logger; + private static readonly ILoggerFactory s_loggerFactory = ConsoleLogger.LoggerFactory; public static async Task RunAsync() { Console.WriteLine("======== Variables ========"); - IKernel kernel = new KernelBuilder().WithLogger(s_logger).Build(); + IKernel kernel = new KernelBuilder().WithLoggerFactory(s_loggerFactory).Build(); var text = kernel.ImportSkill(new StaticTextSkill(), "text"); var variables = new ContextVariables("Today is: "); diff --git a/dotnet/samples/KernelSyntaxExamples/Example04_CombineLLMPromptsAndNativeCode.cs b/dotnet/samples/KernelSyntaxExamples/Example04_CombineLLMPromptsAndNativeCode.cs index 2a73603452ca..a22f134b7733 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example04_CombineLLMPromptsAndNativeCode.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example04_CombineLLMPromptsAndNativeCode.cs @@ -23,7 +23,7 @@ public static async Task RunAsync() } IKernel kernel = new KernelBuilder() - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithOpenAIChatCompletionService(TestConfiguration.OpenAI.ChatModelId, openAIApiKey) .Build(); diff --git a/dotnet/samples/KernelSyntaxExamples/Example05_InlineFunctionDefinition.cs b/dotnet/samples/KernelSyntaxExamples/Example05_InlineFunctionDefinition.cs index 605c04a53d6b..68be390b0fe5 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example05_InlineFunctionDefinition.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example05_InlineFunctionDefinition.cs @@ -28,7 +28,7 @@ public static async Task RunAsync() */ IKernel kernel = new KernelBuilder() - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithOpenAIChatCompletionService( modelId: openAIModelId, apiKey: openAIApiKey) diff --git a/dotnet/samples/KernelSyntaxExamples/Example06_TemplateLanguage.cs b/dotnet/samples/KernelSyntaxExamples/Example06_TemplateLanguage.cs index 9eb50eda777f..0cdb28645b45 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example06_TemplateLanguage.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example06_TemplateLanguage.cs @@ -28,7 +28,7 @@ public static async Task RunAsync() } IKernel kernel = Kernel.Builder - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithOpenAIChatCompletionService( modelId: openAIModelId, apiKey: openAIApiKey) diff --git a/dotnet/samples/KernelSyntaxExamples/Example07_BingAndGoogleSkills.cs b/dotnet/samples/KernelSyntaxExamples/Example07_BingAndGoogleSkills.cs index 05306fa98047..745298d6327b 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example07_BingAndGoogleSkills.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example07_BingAndGoogleSkills.cs @@ -30,7 +30,7 @@ public static async Task RunAsync() } IKernel kernel = new KernelBuilder() - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithOpenAIChatCompletionService( modelId: openAIModelId, apiKey: openAIApiKey) diff --git a/dotnet/samples/KernelSyntaxExamples/Example08_RetryHandler.cs b/dotnet/samples/KernelSyntaxExamples/Example08_RetryHandler.cs index fe8cb526ff42..9e5bf8aa1f68 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example08_RetryHandler.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example08_RetryHandler.cs @@ -35,7 +35,7 @@ public static async Task RunAsync() private static async Task RunRetryHandlerConfigAsync(HttpRetryConfig? httpConfig = null) { - var kernelBuilder = Kernel.Builder.WithLogger(InfoLogger.Logger); + var kernelBuilder = Kernel.Builder.WithLoggerFactory(InfoLogger.LoggerFactory); if (httpConfig != null) { kernelBuilder = kernelBuilder.Configure(c => c.SetDefaultHttpRetryConfig(httpConfig)); @@ -57,7 +57,7 @@ private static async Task RunRetryHandlerConfigAsync(HttpRetryConfig? httpConfig private static IKernel InitializeKernel() { var kernel = Kernel.Builder - .WithLogger(InfoLogger.Logger) + .WithLoggerFactory(InfoLogger.LoggerFactory) // OpenAI settings - you can set the OpenAI.ApiKey to an invalid value to see the retry policy in play .WithOpenAIChatCompletionService(TestConfiguration.OpenAI.ChatModelId, "BAD_KEY") .Build(); @@ -73,7 +73,7 @@ private static async Task RunRetryPolicyAsync(IKernel kernel, IDelegatingHandler private static async Task RunRetryPolicyBuilderAsync(Type retryHandlerFactoryType) { - var kernel = Kernel.Builder.WithLogger(InfoLogger.Logger) + var kernel = Kernel.Builder.WithLoggerFactory(InfoLogger.LoggerFactory) .WithRetryHandlerFactory((Activator.CreateInstance(retryHandlerFactoryType) as IDelegatingHandlerFactory)!) // OpenAI settings - you can set the OpenAI.ApiKey to an invalid value to see the retry policy in play .WithOpenAIChatCompletionService(TestConfiguration.OpenAI.ChatModelId, "BAD_KEY") @@ -103,13 +103,13 @@ private static async Task ImportAndExecuteSkillAsync(IKernel kernel) private static class InfoLogger { - internal static ILogger Logger => LogFactory.CreateLogger(); - private static ILoggerFactory LogFactory => s_loggerFactory.Value; + internal static ILogger Logger => LoggerFactory.CreateLogger(); + internal static ILoggerFactory LoggerFactory => s_loggerFactory.Value; private static readonly Lazy s_loggerFactory = new(LogBuilder); private static ILoggerFactory LogBuilder() { - return LoggerFactory.Create(builder => + return Microsoft.Extensions.Logging.LoggerFactory.Create(builder => { builder.SetMinimumLevel(LogLevel.Information); builder.AddFilter("Microsoft", LogLevel.Information); diff --git a/dotnet/samples/KernelSyntaxExamples/Example09_FunctionTypes.cs b/dotnet/samples/KernelSyntaxExamples/Example09_FunctionTypes.cs index c409a9228bae..77a6b0630458 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example09_FunctionTypes.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example09_FunctionTypes.cs @@ -16,10 +16,10 @@ public static async Task RunAsync() { Console.WriteLine("======== Native function types ========"); - var fakeContext = new SKContext(logger: ConsoleLogger.Logger); + var fakeContext = new SKContext(loggerFactory: ConsoleLogger.LoggerFactory); var kernel = Kernel.Builder - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithOpenAIChatCompletionService(TestConfiguration.OpenAI.ChatModelId, TestConfiguration.OpenAI.ApiKey) .Build(); diff --git a/dotnet/samples/KernelSyntaxExamples/Example11_WebSearchQueries.cs b/dotnet/samples/KernelSyntaxExamples/Example11_WebSearchQueries.cs index f34d21e362d1..4833fcf8ec36 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example11_WebSearchQueries.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example11_WebSearchQueries.cs @@ -13,7 +13,7 @@ public static async Task RunAsync() { Console.WriteLine("======== WebSearchQueries ========"); - IKernel kernel = Kernel.Builder.WithLogger(ConsoleLogger.Logger).Build(); + IKernel kernel = Kernel.Builder.WithLoggerFactory(ConsoleLogger.LoggerFactory).Build(); // Load native skills var skill = new SearchUrlSkill(); diff --git a/dotnet/samples/KernelSyntaxExamples/Example12_SequentialPlanner.cs b/dotnet/samples/KernelSyntaxExamples/Example12_SequentialPlanner.cs index f689034ff16e..b5350c5c8a1b 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example12_SequentialPlanner.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example12_SequentialPlanner.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Threading.Tasks; using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; using Microsoft.SemanticKernel.Planning; using Microsoft.SemanticKernel.Planning.Sequential; @@ -17,7 +18,7 @@ internal static class Example12_SequentialPlanner public static async Task RunAsync() { await PoetrySamplesAsync(); - await EmailSamplesAsync(); + await EmailSamplesWithRecallAsync(); await BookSamplesAsync(); await MemorySampleAsync(); await PlanNotPossibleSampleAsync(); @@ -36,7 +37,7 @@ private static async Task PlanNotPossibleSampleAsync() { await planner.CreatePlanAsync("Write a poem about John Doe, then translate it into Italian."); } - catch (PlanningException e) + catch (SKException e) { Console.WriteLine(e.Message); // Create plan error: Not possible to create plan for goal with available functions. @@ -68,7 +69,7 @@ private static async Task PoetrySamplesAsync() { Console.WriteLine("======== Sequential Planner - Create and Execute Poetry Plan ========"); var kernel = new KernelBuilder() - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithAzureChatCompletionService( TestConfiguration.AzureOpenAI.ChatDeploymentName, TestConfiguration.AzureOpenAI.Endpoint, @@ -100,7 +101,7 @@ private static async Task PoetrySamplesAsync() Console.WriteLine(result.Result); } - private static async Task EmailSamplesAsync() + private static async Task EmailSamplesWithRecallAsync() { Console.WriteLine("======== Sequential Planner - Create and Execute Email Plan ========"); var kernel = InitializeKernelAndPlanner(out var planner, 512); @@ -126,6 +127,9 @@ private static async Task EmailSamplesAsync() Console.WriteLine("Original plan:"); Console.WriteLine(plan.ToPlanWithGoalString()); + // Serialize plan before execution for saving to memory on success. + var originalPlan = plan.ToJson(); + var input = "Once upon a time, in a faraway kingdom, there lived a kind and just king named Arjun. " + "He ruled over his kingdom with fairness and compassion, earning him the love and admiration of his people. " + @@ -139,6 +143,51 @@ private static async Task EmailSamplesAsync() "They ruled the kingdom together, ruling with fairness and compassion, just as Arjun had done before. They lived " + "happily ever after, with the people of the kingdom remembering Mira as the brave young woman who saved them from the dragon."; await ExecutePlanAsync(kernel, plan, input, 5); + + Console.WriteLine("======== Sequential Planner - Find and Execute Saved Plan ========"); + + // Save the plan for future use + var semanticMemory = GetMemory(); + await semanticMemory.SaveInformationAsync( + "plans", + id: Guid.NewGuid().ToString(), + text: plan.Description, // This is the goal used to create the plan + description: originalPlan); + + var goal = "Write summary in french and e-mail John Doe"; + + Console.WriteLine($"Goal: {goal}"); + Console.WriteLine("Searching for saved plan..."); + + Plan? restoredPlan = null; + var memories = semanticMemory.SearchAsync("plans", goal, limit: 1, minRelevanceScore: 0.5); + await foreach (MemoryQueryResult memory in memories) + { + Console.WriteLine($"Restored plan (relevance={memory.Relevance}):"); + + // Deseriliaze the plan from the description + restoredPlan = Plan.FromJson(memory.Metadata.Description, kernel.CreateNewContext()); + + Console.WriteLine(restoredPlan.ToPlanWithGoalString()); + Console.WriteLine(); + + break; + } + + if (restoredPlan is not null) + { + var newInput = + "Far in the future, on a planet lightyears away, 15 year old Remy lives a normal life. He goes to school, " + + "hangs out with his friends, and tries to avoid trouble. But when he stumbles across a secret that threatens to destroy " + + "everything he knows, he's forced to go on the run. With the help of a mysterious girl named Eve, he must evade the ruthless " + + "agents of the Galactic Federation, and uncover the truth about his past. But the more he learns, the more he realizes that " + + "he's not just an ordinary boy."; + + var result = await kernel.RunAsync(newInput, restoredPlan); + + Console.WriteLine("Result:"); + Console.WriteLine(result.Result); + } } private static async Task BookSamplesAsync() @@ -177,20 +226,7 @@ private static async Task MemorySampleAsync() { Console.WriteLine("======== Sequential Planner - Create and Execute Plan using Memory ========"); - // IMPORTANT: Register an embedding generation service and a memory store. The Planner will - // use these to generate and store embeddings for the function descriptions. - var kernel = new KernelBuilder() - .WithLogger(ConsoleLogger.Logger) - .WithAzureChatCompletionService( - TestConfiguration.AzureOpenAI.ChatDeploymentName, - TestConfiguration.AzureOpenAI.Endpoint, - TestConfiguration.AzureOpenAI.ApiKey) - .WithAzureTextEmbeddingGenerationService( - TestConfiguration.AzureOpenAIEmbeddings.DeploymentName, - TestConfiguration.AzureOpenAIEmbeddings.Endpoint, - TestConfiguration.AzureOpenAIEmbeddings.ApiKey) - .WithMemoryStorage(new VolatileMemoryStore()) - .Build(); + var kernel = InitializeKernelWithMemory(); string folder = RepoFiles.SampleSkillsPath(); kernel.ImportSemanticSkillFromDirectory(folder, @@ -224,7 +260,7 @@ private static async Task MemorySampleAsync() private static IKernel InitializeKernelAndPlanner(out SequentialPlanner planner, int maxTokens = 1024) { var kernel = new KernelBuilder() - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithAzureChatCompletionService( TestConfiguration.AzureOpenAI.ChatDeploymentName, TestConfiguration.AzureOpenAI.Endpoint, @@ -236,6 +272,41 @@ private static IKernel InitializeKernelAndPlanner(out SequentialPlanner planner, return kernel; } + private static IKernel InitializeKernelWithMemory() + { + // IMPORTANT: Register an embedding generation service and a memory store. The Planner will + // use these to generate and store embeddings for the function descriptions. + var kernel = new KernelBuilder() + .WithLoggerFactory(ConsoleLogger.LoggerFactory) + .WithAzureChatCompletionService( + TestConfiguration.AzureOpenAI.ChatDeploymentName, + TestConfiguration.AzureOpenAI.Endpoint, + TestConfiguration.AzureOpenAI.ApiKey) + .WithAzureTextEmbeddingGenerationService( + TestConfiguration.AzureOpenAIEmbeddings.DeploymentName, + TestConfiguration.AzureOpenAIEmbeddings.Endpoint, + TestConfiguration.AzureOpenAIEmbeddings.ApiKey) + .WithMemoryStorage(new VolatileMemoryStore()) + .Build(); + + return kernel; + } + + private static ISemanticTextMemory GetMemory(IKernel? kernel = null) + { + if (kernel is not null) + { + return kernel.Memory; + } + var memoryStorage = new VolatileMemoryStore(); + var textEmbeddingGenerator = new Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextEmbedding.AzureTextEmbeddingGeneration( + modelId: TestConfiguration.AzureOpenAIEmbeddings.DeploymentName, + endpoint: TestConfiguration.AzureOpenAIEmbeddings.Endpoint, + apiKey: TestConfiguration.AzureOpenAIEmbeddings.ApiKey); + var memory = new SemanticTextMemory(memoryStorage, textEmbeddingGenerator); + return memory; + } + private static async Task ExecutePlanAsync( IKernel kernel, Plan plan, @@ -272,7 +343,7 @@ private static async Task ExecutePlanAsync( Console.WriteLine(plan.State.ToString()); } } - catch (KernelException e) + catch (SKException e) { Console.WriteLine("Step - Execution failed:"); Console.WriteLine(e.Message); diff --git a/dotnet/samples/KernelSyntaxExamples/Example13_ConversationSummarySkill.cs b/dotnet/samples/KernelSyntaxExamples/Example13_ConversationSummarySkill.cs index f3413d7b0b65..dff91754e1d6 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example13_ConversationSummarySkill.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example13_ConversationSummarySkill.cs @@ -179,7 +179,7 @@ private static async Task GetConversationTopicsAsync() private static IKernel InitializeKernel() { IKernel kernel = Kernel.Builder - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithAzureChatCompletionService( TestConfiguration.AzureOpenAI.ChatDeploymentName, TestConfiguration.AzureOpenAI.Endpoint, diff --git a/dotnet/samples/KernelSyntaxExamples/Example14_SemanticMemory.cs b/dotnet/samples/KernelSyntaxExamples/Example14_SemanticMemory.cs index 68060e8927e8..b06989806f96 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example14_SemanticMemory.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example14_SemanticMemory.cs @@ -35,7 +35,7 @@ public static async Task RunAsync() */ var kernelWithACS = Kernel.Builder - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithOpenAITextEmbeddingGenerationService("text-embedding-ada-002", TestConfiguration.OpenAI.ApiKey) .WithMemoryStorage(new AzureCognitiveSearchMemoryStore(TestConfiguration.ACS.Endpoint, TestConfiguration.ACS.ApiKey)) .Build(); @@ -56,7 +56,7 @@ public static async Task RunAsync() */ var kernelWithCustomDb = Kernel.Builder - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithOpenAITextEmbeddingGenerationService("ada", "text-embedding-ada-002", TestConfiguration.OpenAI.ApiKey) .WithMemoryStorage(new VolatileMemoryStore()) .Build(); diff --git a/dotnet/samples/KernelSyntaxExamples/Example15_MemorySkill.cs b/dotnet/samples/KernelSyntaxExamples/Example15_MemorySkill.cs index 5b8e3181d28e..c4c21823631c 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example15_MemorySkill.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example15_MemorySkill.cs @@ -14,10 +14,10 @@ public static class Example15_MemorySkill public static async Task RunAsync() { - var logger = ConsoleLogger.Logger; + var loggerFactory = ConsoleLogger.LoggerFactory; var kernel = Kernel.Builder - .WithLogger(logger) + .WithLoggerFactory(loggerFactory) .WithOpenAIChatCompletionService(TestConfiguration.OpenAI.ChatModelId, TestConfiguration.OpenAI.ApiKey) .WithOpenAITextEmbeddingGenerationService(TestConfiguration.OpenAI.EmbeddingModelId, TestConfiguration.OpenAI.ApiKey) .WithMemoryStorage(new VolatileMemoryStore()) @@ -50,7 +50,7 @@ public static async Task RunAsync() // ========= Test memory remember ========= Console.WriteLine("========= Example: Recalling a Memory ========="); - var answer = await memorySkill.RetrieveAsync(MemoryCollectionName, "info1", logger: logger); + var answer = await memorySkill.RetrieveAsync(MemoryCollectionName, "info1", loggerFactory); Console.WriteLine("Memory associated with 'info1': {0}", answer); /* Output: @@ -60,11 +60,11 @@ public static async Task RunAsync() // ========= Test memory recall ========= Console.WriteLine("========= Example: Recalling an Idea ========="); - answer = await memorySkill.RecallAsync("where did I grow up?", MemoryCollectionName, relevance: null, limit: 2, logger: logger); + answer = await memorySkill.RecallAsync("where did I grow up?", MemoryCollectionName, relevance: null, limit: 2, loggerFactory); Console.WriteLine("Ask: where did I grow up?"); Console.WriteLine("Answer:\n{0}", answer); - answer = await memorySkill.RecallAsync("where do I live?", MemoryCollectionName, relevance: null, limit: 2, logger: logger); + answer = await memorySkill.RecallAsync("where do I live?", MemoryCollectionName, relevance: null, limit: 2, loggerFactory); Console.WriteLine("Ask: where do I live?"); Console.WriteLine("Answer:\n{0}", answer); @@ -134,7 +134,7 @@ Tell me a bit about myself My name is Andrea and my family is from New York. I work as a tourist operator. */ - await memorySkill.RemoveAsync(MemoryCollectionName, "info1", logger: logger); + await memorySkill.RemoveAsync(MemoryCollectionName, "info1", loggerFactory); result = await kernel.RunAsync(aboutMeOracle, new("Tell me a bit about myself")); diff --git a/dotnet/samples/KernelSyntaxExamples/Example16_CustomLLM.cs b/dotnet/samples/KernelSyntaxExamples/Example16_CustomLLM.cs index 7d06e6ed9225..fd74e70a2d67 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example16_CustomLLM.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example16_CustomLLM.cs @@ -99,11 +99,11 @@ private static async Task CustomTextCompletionWithSKFunctionAsync() Console.WriteLine("======== Custom LLM - Text Completion - SKFunction ========"); IKernel kernel = new KernelBuilder() - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) // Add your text completion service as a singleton instance .WithAIService("myService1", new MyTextCompletionService()) // Add your text completion service as a factory method - .WithAIService("myService2", (_) => new MyTextCompletionService()) + .WithAIService("myService2", (log, config) => new MyTextCompletionService()) .Build(); const string FunctionDefinition = "Does the text contain grammar errors (Y/N)? Text: {{$input}}"; @@ -134,7 +134,7 @@ private static async Task CustomTextCompletionStreamAsync() { Console.WriteLine("======== Custom LLM - Text Completion - Raw Streaming ========"); - IKernel kernel = new KernelBuilder().WithLogger(ConsoleLogger.Logger).Build(); + IKernel kernel = new KernelBuilder().WithLoggerFactory(ConsoleLogger.LoggerFactory).Build(); ITextCompletion textCompletion = new MyTextCompletionService(); var prompt = "Write one paragraph why AI is awesome"; diff --git a/dotnet/samples/KernelSyntaxExamples/Example18_DallE.cs b/dotnet/samples/KernelSyntaxExamples/Example18_DallE.cs index eb6d0276c835..26bf8c1196f2 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example18_DallE.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example18_DallE.cs @@ -26,7 +26,7 @@ private static async Task OpenAIDallEAsync() Console.WriteLine("======== OpenAI Dall-E 2 Image Generation ========"); IKernel kernel = new KernelBuilder() - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) // Add your image generation service .WithOpenAIImageGenerationService(TestConfiguration.OpenAI.ApiKey) // Add your chat completion service @@ -95,7 +95,7 @@ public static async Task AzureOpenAIDallEAsync() Console.WriteLine("========Azure OpenAI Dall-E 2 Image Generation ========"); IKernel kernel = new KernelBuilder() - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) // Add your image generation service .WithAzureOpenAIImageGenerationService(TestConfiguration.AzureOpenAI.Endpoint, TestConfiguration.AzureOpenAI.ApiKey) // Add your chat completion service diff --git a/dotnet/samples/KernelSyntaxExamples/Example19_Qdrant.cs b/dotnet/samples/KernelSyntaxExamples/Example19_Qdrant.cs index c9a00b003dfc..8b14e8a7ffc9 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example19_Qdrant.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example19_Qdrant.cs @@ -14,9 +14,9 @@ public static class Example19_Qdrant public static async Task RunAsync() { - QdrantMemoryStore memoryStore = new(TestConfiguration.Qdrant.Endpoint, 1536, ConsoleLogger.Logger); + QdrantMemoryStore memoryStore = new(TestConfiguration.Qdrant.Endpoint, 1536, ConsoleLogger.LoggerFactory); IKernel kernel = Kernel.Builder - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithOpenAIChatCompletionService(TestConfiguration.OpenAI.ChatModelId, TestConfiguration.OpenAI.ApiKey) .WithOpenAITextEmbeddingGenerationService(TestConfiguration.OpenAI.EmbeddingModelId, TestConfiguration.OpenAI.ApiKey) .WithMemoryStorage(memoryStore) diff --git a/dotnet/samples/KernelSyntaxExamples/Example20_HuggingFace.cs b/dotnet/samples/KernelSyntaxExamples/Example20_HuggingFace.cs index 118bce59520f..8ea3f9f196a6 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example20_HuggingFace.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example20_HuggingFace.cs @@ -17,7 +17,7 @@ public static async Task RunAsync() Console.WriteLine("======== HuggingFace text completion AI ========"); IKernel kernel = new KernelBuilder() - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithHuggingFaceTextCompletionService( model: TestConfiguration.HuggingFace.ApiKey, apiKey: TestConfiguration.HuggingFace.ApiKey) diff --git a/dotnet/samples/KernelSyntaxExamples/Example21_ChatGPTPlugins.cs b/dotnet/samples/KernelSyntaxExamples/Example21_ChatGPTPlugins.cs index 96ab0af24498..88fd99e8f978 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example21_ChatGPTPlugins.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example21_ChatGPTPlugins.cs @@ -18,7 +18,7 @@ public static async Task RunAsync() private static async Task RunChatGptPluginAsync() { - var kernel = new KernelBuilder().WithLogger(ConsoleLogger.Logger).Build(); + var kernel = new KernelBuilder().WithLoggerFactory(ConsoleLogger.LoggerFactory).Build(); using HttpClient httpClient = new(); //Import a ChatGPT plugin via URI @@ -36,7 +36,7 @@ private static async Task RunChatGptPluginAsync() //--------------- Example of using Klarna ChatGPT plugin ------------------------ - //var kernel = new KernelBuilder().WithLogger(ConsoleLogger.Logger).Build(); + //var kernel = new KernelBuilder().WithLogger(ConsoleLogger.LoggerFactory).Build(); //var skill = await kernel.ImportAIPluginAsync("Klarna", new Uri("https://www.klarna.com/.well-known/ai-plugin.json"), new OpenApiSkillExecutionParameters(httpClient)); diff --git a/dotnet/samples/KernelSyntaxExamples/Example22_OpenApiSkill_AzureKeyVault.cs b/dotnet/samples/KernelSyntaxExamples/Example22_OpenApiSkill_AzureKeyVault.cs index 2e402a813ebe..7094d19fa24d 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example22_OpenApiSkill_AzureKeyVault.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example22_OpenApiSkill_AzureKeyVault.cs @@ -35,7 +35,7 @@ public static async Task GetSecretFromAzureKeyVaultWithRetryAsync(InteractiveMsa var retryConfig = new HttpRetryConfig() { MaxRetryCount = 3, UseExponentialBackoff = true }; var kernel = new KernelBuilder() - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .Configure(c => c.SetDefaultHttpRetryConfig(retryConfig)) .Build(); @@ -64,7 +64,7 @@ public static async Task GetSecretFromAzureKeyVaultWithRetryAsync(InteractiveMsa public static async Task AddSecretToAzureKeyVaultAsync(InteractiveMsalAuthenticationProvider authenticationProvider) { - var kernel = new KernelBuilder().WithLogger(ConsoleLogger.Logger).Build(); + var kernel = new KernelBuilder().WithLoggerFactory(ConsoleLogger.LoggerFactory).Build(); var type = typeof(SkillResourceNames); var resourceName = $"{SkillResourceNames.AzureKeyVault}.openapi.json"; diff --git a/dotnet/samples/KernelSyntaxExamples/Example23_OpenApiSkill_Github.cs b/dotnet/samples/KernelSyntaxExamples/Example23_OpenApiSkill_Github.cs index b6ddb3684929..53c1d26da90f 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example23_OpenApiSkill_Github.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example23_OpenApiSkill_Github.cs @@ -31,7 +31,7 @@ public static async Task RunAsync() public static async Task ListPullRequestsFromGitHubAsync(BearerAuthenticationProvider authenticationProvider) { - var kernel = new KernelBuilder().WithLogger(ConsoleLogger.Logger).Build(); + var kernel = new KernelBuilder().WithLoggerFactory(ConsoleLogger.LoggerFactory).Build(); var skill = await kernel.ImportAIPluginAsync( "GitHubSkill", @@ -63,7 +63,7 @@ public static async Task ListPullRequestsFromGitHubAsync(BearerAuthentic public static async Task GetPullRequestFromGitHubAsync(BearerAuthenticationProvider authenticationProvider, string pullNumber) { - var kernel = new KernelBuilder().WithLogger(ConsoleLogger.Logger).Build(); + var kernel = new KernelBuilder().WithLoggerFactory(ConsoleLogger.LoggerFactory).Build(); var skill = await kernel.ImportAIPluginAsync( "GitHubSkill", diff --git a/dotnet/samples/KernelSyntaxExamples/Example24_OpenApiSkill_Jira.cs b/dotnet/samples/KernelSyntaxExamples/Example24_OpenApiSkill_Jira.cs index e00e47e02c27..a49025bbcafa 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example24_OpenApiSkill_Jira.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example24_OpenApiSkill_Jira.cs @@ -22,7 +22,7 @@ public static class Example24_OpenApiSkill_Jira { public static async Task RunAsync() { - var kernel = new KernelBuilder().WithLogger(ConsoleLogger.Logger).Build(); + var kernel = new KernelBuilder().WithLoggerFactory(ConsoleLogger.LoggerFactory).Build(); var contextVariables = new ContextVariables(); // Change to a jira instance you have access to with your authentication credentials diff --git a/dotnet/samples/KernelSyntaxExamples/Example25_ReadOnlyMemoryStore.cs b/dotnet/samples/KernelSyntaxExamples/Example25_ReadOnlyMemoryStore.cs index b27aa9b91e00..42d4e04fd0ed 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example25_ReadOnlyMemoryStore.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example25_ReadOnlyMemoryStore.cs @@ -4,10 +4,10 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.AI.Embeddings.VectorOperations; using Microsoft.SemanticKernel.Memory; using Microsoft.SemanticKernel.Memory.Collections; @@ -30,20 +30,20 @@ public static async Task RunAsync() { var store = new ReadOnlyMemoryStore(s_jsonVectorEntries); - var embedding = new Embedding(new float[] { 22, 4, 6 }); + var embedding = new ReadOnlyMemory(new float[] { 22, 4, 6 }); Console.WriteLine("Reading data from custom read-only memory store"); var memoryRecord = await store.GetAsync("collection", "key3"); if (memoryRecord != null) { - Console.WriteLine("ID = {0}, Embedding = {1}", memoryRecord.Metadata.Id, string.Join(", ", memoryRecord.Embedding.Vector)); + Console.WriteLine("ID = {0}, Embedding = {1}", memoryRecord.Metadata.Id, string.Join(", ", MemoryMarshal.ToEnumerable(memoryRecord.Embedding))); } - Console.WriteLine("Getting most similar vector to {0}", string.Join(", ", embedding.Vector)); + Console.WriteLine("Getting most similar vector to {0}", string.Join(", ", MemoryMarshal.ToEnumerable(embedding))); var result = await store.GetNearestMatchAsync("collection", embedding, 0.0); if (result.HasValue) { - Console.WriteLine("Embedding = {0}, Similarity = {1}", string.Join(", ", result.Value.Item1.Embedding.Vector), result.Value.Item2); + Console.WriteLine("Embedding = {0}, Similarity = {1}", string.Join(", ", MemoryMarshal.ToEnumerable(result.Value.Item1.Embedding)), result.Value.Item2); } } @@ -105,7 +105,7 @@ public IAsyncEnumerable GetCollectionsAsync(CancellationToken cancellati throw new System.NotImplementedException(); } - public async Task<(MemoryRecord, double)?> GetNearestMatchAsync(string collectionName, Embedding embedding, double minRelevanceScore = 0, + public async Task<(MemoryRecord, double)?> GetNearestMatchAsync(string collectionName, ReadOnlyMemory embedding, double minRelevanceScore = 0, bool withEmbedding = false, CancellationToken cancellationToken = default) { // Note: with this simple implementation, the MemoryRecord will always contain the embedding. @@ -123,7 +123,7 @@ public IAsyncEnumerable GetCollectionsAsync(CancellationToken cancellati return default; } - public async IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesAsync(string collectionName, Embedding embedding, int limit, + public async IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesAsync(string collectionName, ReadOnlyMemory embedding, int limit, double minRelevanceScore = 0, bool withEmbeddings = false, [EnumeratorCancellation] CancellationToken cancellationToken = default) { // Note: with this simple implementation, the MemoryRecord will always contain the embedding. @@ -132,16 +132,16 @@ public IAsyncEnumerable GetCollectionsAsync(CancellationToken cancellati yield break; } - if (embedding.Count != this._vectorSize) + if (embedding.Length != this._vectorSize) { - throw new Exception($"Embedding vector size {embedding.Count} does not match expected size of {this._vectorSize}"); + throw new Exception($"Embedding vector size {embedding.Length} does not match expected size of {this._vectorSize}"); } TopNCollection embeddings = new(limit); foreach (var item in this._memoryRecords) { - double similarity = embedding.AsReadOnlySpan().CosineSimilarity(item.Embedding.AsReadOnlySpan()); + double similarity = embedding.Span.CosineSimilarity(item.Embedding.Span); if (similarity >= minRelevanceScore) { embeddings.Add(new(item, similarity)); diff --git a/dotnet/samples/KernelSyntaxExamples/Example26_AADAuth.cs b/dotnet/samples/KernelSyntaxExamples/Example26_AADAuth.cs index 367b866de772..5b6ef8967886 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example26_AADAuth.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example26_AADAuth.cs @@ -40,7 +40,7 @@ public static async Task RunAsync() }; IKernel kernel = new KernelBuilder() - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) // Add Azure chat completion service using DefaultAzureCredential AAD auth .WithAzureChatCompletionService( TestConfiguration.AzureOpenAI.ChatDeploymentName, diff --git a/dotnet/samples/KernelSyntaxExamples/Example27_SemanticFunctionsUsingChatGPT.cs b/dotnet/samples/KernelSyntaxExamples/Example27_SemanticFunctionsUsingChatGPT.cs index 7342428cc3bf..ba4ee1b068a7 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example27_SemanticFunctionsUsingChatGPT.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example27_SemanticFunctionsUsingChatGPT.cs @@ -17,7 +17,7 @@ public static async Task RunAsync() Console.WriteLine("======== Using Chat GPT model for text completion ========"); IKernel kernel = new KernelBuilder() - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithAzureChatCompletionService(TestConfiguration.AzureOpenAI.ChatDeploymentName, "https://....openai.azure.com/", "...API KEY...") .Build(); diff --git a/dotnet/samples/KernelSyntaxExamples/Example28_ActionPlanner.cs b/dotnet/samples/KernelSyntaxExamples/Example28_ActionPlanner.cs index e38dc4eee761..55d3a7bd9450 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example28_ActionPlanner.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example28_ActionPlanner.cs @@ -14,7 +14,7 @@ public static async Task RunAsync() { Console.WriteLine("======== Action Planner ========"); var kernel = new KernelBuilder() - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithOpenAIChatCompletionService(TestConfiguration.OpenAI.ChatModelId, TestConfiguration.OpenAI.ApiKey) .Build(); diff --git a/dotnet/samples/KernelSyntaxExamples/Example30_ChatWithPrompts.cs b/dotnet/samples/KernelSyntaxExamples/Example30_ChatWithPrompts.cs index 22a28b0f2227..f94e0bc491ad 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example30_ChatWithPrompts.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example30_ChatWithPrompts.cs @@ -63,7 +63,7 @@ public static async Task RunAsync() var userPromptTemplate = EmbeddedResource.Read("30-user-prompt.txt"); IKernel kernel = new KernelBuilder() - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithOpenAIChatCompletionService(TestConfiguration.OpenAI.ChatModelId, TestConfiguration.OpenAI.ApiKey, serviceId: "chat") .Build(); diff --git a/dotnet/samples/KernelSyntaxExamples/Example31_CustomPlanner.cs b/dotnet/samples/KernelSyntaxExamples/Example31_CustomPlanner.cs index c0d732b7064d..e254177f7173 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example31_CustomPlanner.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example31_CustomPlanner.cs @@ -127,7 +127,7 @@ private static IDictionary LoadQASkill(IKernel kernel) private static IKernel InitializeKernel() { return new KernelBuilder() - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithAzureChatCompletionService( TestConfiguration.AzureOpenAI.ChatDeploymentName, TestConfiguration.AzureOpenAI.Endpoint, diff --git a/dotnet/samples/KernelSyntaxExamples/Example35_GrpcSkills.cs b/dotnet/samples/KernelSyntaxExamples/Example35_GrpcSkills.cs index 7e51a9e067db..ab4d261b5f35 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example35_GrpcSkills.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example35_GrpcSkills.cs @@ -16,7 +16,7 @@ public static class Example35_GrpcSkills { public static async Task RunAsync() { - var kernel = new KernelBuilder().WithLogger(ConsoleLogger.Logger).Build(); + var kernel = new KernelBuilder().WithLoggerFactory(ConsoleLogger.LoggerFactory).Build(); // Import a gRPC skill using one of the following Kernel extension methods // kernel.RegisterGrpcSkill diff --git a/dotnet/samples/KernelSyntaxExamples/Example38_Pinecone.cs b/dotnet/samples/KernelSyntaxExamples/Example38_Pinecone.cs index 772cccb2451e..a774bfdbed28 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example38_Pinecone.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example38_Pinecone.cs @@ -28,7 +28,7 @@ public static async Task RunAsync() PineconeMemoryStore memoryStore = new(pineconeEnvironment, apiKey); IKernel kernel = Kernel.Builder - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithOpenAIChatCompletionService(TestConfiguration.OpenAI.ChatModelId, TestConfiguration.OpenAI.ApiKey) .WithOpenAITextEmbeddingGenerationService(TestConfiguration.OpenAI.EmbeddingModelId, TestConfiguration.OpenAI.ApiKey) .WithMemoryStorage(memoryStore) diff --git a/dotnet/samples/KernelSyntaxExamples/Example39_Postgres.cs b/dotnet/samples/KernelSyntaxExamples/Example39_Postgres.cs index 2061e59565f2..cef12f1a3345 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example39_Postgres.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example39_Postgres.cs @@ -23,7 +23,7 @@ public static async Task RunAsync() PostgresMemoryStore memoryStore = new(dataSource, vectorSize: 1536, schema: "public"); IKernel kernel = Kernel.Builder - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithOpenAIChatCompletionService( modelId: TestConfiguration.OpenAI.ChatModelId, apiKey: TestConfiguration.OpenAI.ApiKey) diff --git a/dotnet/samples/KernelSyntaxExamples/Example40_DIContainer.cs b/dotnet/samples/KernelSyntaxExamples/Example40_DIContainer.cs index 5c00b31da9df..7873b01ccca7 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example40_DIContainer.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example40_DIContainer.cs @@ -42,7 +42,7 @@ private static async Task UseKernelInDIPowerAppAsync() collection.AddTransient((serviceProvider) => { return Kernel.Builder - .WithLogger(serviceProvider.GetRequiredService()) + .WithLoggerFactory(serviceProvider.GetRequiredService()) .WithOpenAIChatCompletionService(TestConfiguration.OpenAI.ChatModelId, TestConfiguration.OpenAI.ApiKey) .Build(); }); diff --git a/dotnet/samples/KernelSyntaxExamples/Example42_KernelBuilder.cs b/dotnet/samples/KernelSyntaxExamples/Example42_KernelBuilder.cs index 95b0b4efc353..2fd857857991 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example42_KernelBuilder.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example42_KernelBuilder.cs @@ -68,19 +68,19 @@ public static Task RunAsync() // a correct dependency injection. // Manually setup all the dependencies used internally by the kernel - var logger = NullLogger.Instance; + var loggerFactory = NullLoggerFactory.Instance; var memoryStorage = new VolatileMemoryStore(); var textEmbeddingGenerator = new AzureTextEmbeddingGeneration( modelId: azureOpenAIEmbeddingDeployment, endpoint: azureOpenAIEndpoint, apiKey: azureOpenAIKey, - logger: logger); + loggerFactory: loggerFactory); using var memory = new SemanticTextMemory(memoryStorage, textEmbeddingGenerator); var skills = new SkillCollection(); - var templateEngine = new PromptTemplateEngine(logger); + var templateEngine = new PromptTemplateEngine(loggerFactory); var kernelConfig = new KernelConfig(); - using var httpHandler = new DefaultHttpRetryHandler(new HttpRetryConfig(), logger); + using var httpHandler = new DefaultHttpRetryHandler(new HttpRetryConfig(), loggerFactory); using var httpClient = new HttpClient(httpHandler); var aiServices = new AIServiceCollection(); ITextCompletion Factory() => new AzureTextCompletion( @@ -88,12 +88,12 @@ public static Task RunAsync() endpoint: azureOpenAIEndpoint, apiKey: azureOpenAIKey, httpClient, - logger); + loggerFactory); aiServices.SetService("foo", Factory); IAIServiceProvider aiServiceProvider = aiServices.Build(); // Create kernel manually injecting all the dependencies - using var kernel3 = new Kernel(skills, aiServiceProvider, templateEngine, memory, kernelConfig, logger); + using var kernel3 = new Kernel(skills, aiServiceProvider, templateEngine, memory, kernelConfig, loggerFactory); // ========================================================================================================== // The kernel builder purpose is to simplify this process, automating how dependencies @@ -101,7 +101,7 @@ public static Task RunAsync() // Example: how to use a custom memory and configure Azure OpenAI var kernel4 = Kernel.Builder - .WithLogger(NullLogger.Instance) + .WithLoggerFactory(NullLoggerFactory.Instance) .WithMemory(memory) .WithAzureChatCompletionService( deploymentName: azureOpenAIChatCompletionDeployment, @@ -111,7 +111,7 @@ public static Task RunAsync() // Example: how to use a custom memory storage var kernel6 = Kernel.Builder - .WithLogger(NullLogger.Instance) + .WithLoggerFactory(NullLoggerFactory.Instance) .WithMemoryStorage(memoryStorage) // Custom memory storage .WithAzureChatCompletionService( deploymentName: azureOpenAIChatCompletionDeployment, @@ -164,9 +164,9 @@ public static Task RunAsync() // Example of a basic custom retry handler public class RetryThreeTimesFactory : IDelegatingHandlerFactory { - public DelegatingHandler Create(ILogger? logger) + public DelegatingHandler Create(ILoggerFactory? loggerFactory) { - return new RetryThreeTimes(logger); + return new RetryThreeTimes(loggerFactory); } } @@ -174,9 +174,11 @@ public class RetryThreeTimes : DelegatingHandler { private readonly AsyncRetryPolicy _policy; - public RetryThreeTimes(ILogger? logger = null) + public RetryThreeTimes(ILoggerFactory? loggerFactory = null) { - this._policy = GetPolicy(logger ?? NullLogger.Instance); + this._policy = GetPolicy(loggerFactory is not null ? + loggerFactory.CreateLogger(nameof(RetryThreeTimes)) : + NullLogger.Instance); } protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) diff --git a/dotnet/samples/KernelSyntaxExamples/Example46_Weaviate.cs b/dotnet/samples/KernelSyntaxExamples/Example46_Weaviate.cs index cf7978796d09..13a0fcf96720 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example46_Weaviate.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example46_Weaviate.cs @@ -16,9 +16,9 @@ public static async Task RunAsync() { string endpoint = TestConfiguration.Weaviate.Endpoint; string apiKey = TestConfiguration.Weaviate.ApiKey; - WeaviateMemoryStore memoryStore = new(endpoint, apiKey, ConsoleLogger.Logger); + WeaviateMemoryStore memoryStore = new(endpoint, apiKey, ConsoleLogger.LoggerFactory); IKernel kernel = Kernel.Builder - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithOpenAIChatCompletionService( modelId: TestConfiguration.OpenAI.ChatModelId, apiKey: TestConfiguration.OpenAI.ApiKey) diff --git a/dotnet/samples/KernelSyntaxExamples/Example47_Redis.cs b/dotnet/samples/KernelSyntaxExamples/Example47_Redis.cs index d48642f2b37e..dd8260d681bd 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example47_Redis.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example47_Redis.cs @@ -6,7 +6,6 @@ using Microsoft.SemanticKernel.Connectors.Memory.Redis; using Microsoft.SemanticKernel.Memory; using RepoUtils; -using StackExchange.Redis; // ReSharper disable once InconsistentNaming public static class Example47_Redis @@ -15,12 +14,10 @@ public static class Example47_Redis public static async Task RunAsync() { - string configuration = TestConfiguration.Redis.Configuration; - await using ConnectionMultiplexer connectionMultiplexer = await ConnectionMultiplexer.ConnectAsync(configuration); - IDatabase database = connectionMultiplexer.GetDatabase(); - RedisMemoryStore memoryStore = new(database, vectorSize: 1536); + using RedisMemoryStore memoryStore = new(TestConfiguration.Redis.Configuration, vectorSize: 1536); + IKernel kernel = Kernel.Builder - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithOpenAIChatCompletionService( modelId: TestConfiguration.OpenAI.ChatModelId, apiKey: TestConfiguration.OpenAI.ApiKey) diff --git a/dotnet/samples/KernelSyntaxExamples/Example48_GroundednessChecks.cs b/dotnet/samples/KernelSyntaxExamples/Example48_GroundednessChecks.cs index 89fb0a045069..12fc120f87f9 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example48_GroundednessChecks.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example48_GroundednessChecks.cs @@ -58,7 +58,7 @@ public static async Task GroundednessCheckingSkill() { Console.WriteLine("======== Groundedness Checks ========"); var kernel = new KernelBuilder() - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithAzureChatCompletionService( TestConfiguration.AzureOpenAI.ChatDeploymentName, TestConfiguration.AzureOpenAI.Endpoint, @@ -123,7 +123,7 @@ which are not grounded in the original. Console.WriteLine("======== Planning - Groundedness Checks ========"); var kernel = new KernelBuilder() - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithAzureChatCompletionService( TestConfiguration.AzureOpenAI.ChatDeploymentName, TestConfiguration.AzureOpenAI.Endpoint, diff --git a/dotnet/samples/KernelSyntaxExamples/Example50_Chroma.cs b/dotnet/samples/KernelSyntaxExamples/Example50_Chroma.cs index aec862250f68..f921329ac93c 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example50_Chroma.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example50_Chroma.cs @@ -19,7 +19,7 @@ public static async Task RunAsync() var memoryStore = new ChromaMemoryStore(endpoint); IKernel kernel = Kernel.Builder - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithOpenAIChatCompletionService( modelId: TestConfiguration.OpenAI.ChatModelId, apiKey: TestConfiguration.OpenAI.ApiKey) diff --git a/dotnet/samples/KernelSyntaxExamples/Example51_StepwisePlanner.cs b/dotnet/samples/KernelSyntaxExamples/Example51_StepwisePlanner.cs index a5db26df7cc2..f531620026e1 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example51_StepwisePlanner.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example51_StepwisePlanner.cs @@ -86,7 +86,7 @@ private static IKernel GetKernel() setAsDefault: true); var kernel = builder - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .Configure(c => c.SetDefaultHttpRetryConfig(new HttpRetryConfig { MaxRetryCount = 3, diff --git a/dotnet/samples/KernelSyntaxExamples/Example52_ApimAuth.cs b/dotnet/samples/KernelSyntaxExamples/Example52_ApimAuth.cs index b23346f7af6b..c80f114b9eb1 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example52_ApimAuth.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example52_ApimAuth.cs @@ -60,9 +60,9 @@ public static async Task RunAsync() }); var kernel = Kernel.Builder - .WithLogger(loggerFactory.CreateLogger()) - .WithAIService(TestConfiguration.AzureOpenAI.ChatDeploymentName, (parameters) => - new AzureChatCompletion(TestConfiguration.AzureOpenAI.ChatDeploymentName, openAIClient, parameters.Logger)) + .WithLoggerFactory(loggerFactory) + .WithAIService(TestConfiguration.AzureOpenAI.ChatDeploymentName, (loggerFactory, config) => + new AzureChatCompletion(TestConfiguration.AzureOpenAI.ChatDeploymentName, openAIClient, loggerFactory)) .Build(); // Load semantic skill defined with prompt templates diff --git a/dotnet/samples/KernelSyntaxExamples/Example53_Kusto.cs b/dotnet/samples/KernelSyntaxExamples/Example53_Kusto.cs index c133c69b2e73..eaaa4befe833 100644 --- a/dotnet/samples/KernelSyntaxExamples/Example53_Kusto.cs +++ b/dotnet/samples/KernelSyntaxExamples/Example53_Kusto.cs @@ -18,7 +18,7 @@ public static async Task RunAsync() using KustoMemoryStore memoryStore = new(connectionString, "MyDatabase"); IKernel kernel = Kernel.Builder - .WithLogger(ConsoleLogger.Logger) + .WithLoggerFactory(ConsoleLogger.LoggerFactory) .WithOpenAITextCompletionService( modelId: TestConfiguration.OpenAI.ModelId, apiKey: TestConfiguration.OpenAI.ApiKey) diff --git a/dotnet/samples/KernelSyntaxExamples/Example54_AzureChatCompletionWithData.cs b/dotnet/samples/KernelSyntaxExamples/Example54_AzureChatCompletionWithData.cs new file mode 100644 index 000000000000..7703a225d3e6 --- /dev/null +++ b/dotnet/samples/KernelSyntaxExamples/Example54_AzureChatCompletionWithData.cs @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Threading.Tasks; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.AI.ChatCompletion; +using Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletionWithData; + +/** + * This example shows how to use Azure OpenAI Chat Completion with data. + * More information: + */ +// ReSharper disable once InconsistentNaming +public static class Example54_AzureChatCompletionWithData +{ + public static async Task RunAsync() + { + // Uploaded content in Azure Blob Storage in .txt file: + + // Emily and David, two passionate scientists, met during a research expedition to Antarctica. + // Bonded by their love for the natural world and shared curiosity, + // they uncovered a groundbreaking phenomenon in glaciology that could + // potentially reshape our understanding of climate change. + + await ExampleWithChatCompletion(); + await ExampleWithKernel(); + } + + private static async Task ExampleWithChatCompletion() + { + Console.WriteLine("=== Example with Chat Completion ==="); + + var chatCompletion = new AzureChatCompletionWithData(GetCompletionWithDataConfig()); + var chatHistory = chatCompletion.CreateNewChat(); + + // First question without previous context based on uploaded content. + chatHistory.AddUserMessage("How did Emily and David meet?"); + + // Chat Completion example + string reply = await chatCompletion.GenerateMessageAsync(chatHistory); + + // Output: Emily and David, both passionate scientists, met during a research expedition to Antarctica. + Console.WriteLine(reply); + Console.WriteLine(); + + // Second question based on uploaded content. + chatHistory.AddUserMessage("What are Emily and David studying?"); + + // Chat Completion Streaming example + await foreach (var result in chatCompletion.GetStreamingChatCompletionsAsync(chatHistory)) + { + await foreach (var message in result.GetStreamingChatMessageAsync()) + { + // Output: + // They are passionate scientists who study glaciology, + // a branch of geology that deals with the study of ice and its effects. + Console.Write(message.Content); + } + } + + Console.WriteLine(Environment.NewLine); + } + + private static async Task ExampleWithKernel() + { + Console.WriteLine("=== Example with Kernel ==="); + + var completionWithDataConfig = GetCompletionWithDataConfig(); + + IKernel kernel = new KernelBuilder() + .WithAzureChatCompletionService(config: completionWithDataConfig) + .Build(); + + var semanticFunction = kernel.CreateSemanticFunction("Question: {{$input}}"); + + // First question without previous context based on uploaded content. + var result = await kernel.RunAsync("How did Emily and David meet?", semanticFunction); + + // Output: Emily and David, both passionate scientists, met during a research expedition to Antarctica. + Console.WriteLine(result); + Console.WriteLine(); + + // Second question based on uploaded content. + result = await kernel.RunAsync("What are Emily and David studying?", semanticFunction); + + // Output: + // They are passionate scientists who study glaciology, + // a branch of geology that deals with the study of ice and its effects. + Console.WriteLine(result); + + Console.WriteLine(Environment.NewLine); + } + + /// + /// Initializes a new instance of the class. + /// + private static AzureChatCompletionWithDataConfig GetCompletionWithDataConfig() + { + return new AzureChatCompletionWithDataConfig + { + CompletionModelId = TestConfiguration.AzureOpenAI.ChatDeploymentName, + CompletionEndpoint = TestConfiguration.AzureOpenAI.Endpoint, + CompletionApiKey = TestConfiguration.AzureOpenAI.ApiKey, + DataSourceEndpoint = TestConfiguration.ACS.Endpoint, + DataSourceApiKey = TestConfiguration.ACS.ApiKey, + DataSourceIndex = TestConfiguration.ACS.IndexName + }; + } +} diff --git a/dotnet/samples/KernelSyntaxExamples/Example55_TextChunker.cs b/dotnet/samples/KernelSyntaxExamples/Example55_TextChunker.cs new file mode 100644 index 000000000000..5c11cbe2feaa --- /dev/null +++ b/dotnet/samples/KernelSyntaxExamples/Example55_TextChunker.cs @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.SemanticKernel.Connectors.AI.OpenAI.Tokenizers; +using Microsoft.SemanticKernel.Text; + +// ReSharper disable once InconsistentNaming +public static class Example55_TextChunker +{ + private const string text = @"The city of Venice, located in the northeastern part of Italy, +is renowned for its unique geographical features. Built on more than 100 small islands in a lagoon in the +Adriatic Sea, it has no roads, just canals including the Grand Canal thoroughfare lined with Renaissance and +Gothic palaces. The central square, Piazza San Marco, contains St. Mark's Basilica, which is tiled with Byzantine +mosaics, and the Campanile bell tower offering views of the city's red roofs. + +The Amazon Rainforest, also known as Amazonia, is a moist broadleaf tropical rainforest in the Amazon biome that +covers most of the Amazon basin of South America. This basin encompasses 7 million square kilometers, of which +5.5 million square kilometers are covered by the rainforest. This region includes territory belonging to nine nations +and 3.4 million square kilometers of uncontacted tribes. The Amazon represents over half of the planet's remaining +rainforests and comprises the largest and most biodiverse tract of tropical rainforest in the world. + +The Great Barrier Reef is the world's largest coral reef system composed of over 2,900 individual reefs and 900 islands +stretching for over 2,300 kilometers over an area of approximately 344,400 square kilometers. The reef is located in the +Coral Sea, off the coast of Queensland, Australia. The Great Barrier Reef can be seen from outer space and is the world's +biggest single structure made by living organisms. This reef structure is composed of and built by billions of tiny organisms, +known as coral polyps."; + + public static Task RunAsync() + { + RunExample(); + RunExampleWithCustomTokenCounter(); + + return Task.CompletedTask; + } + + private static void RunExample() + { + Console.WriteLine("=== Text chunking ==="); + + var lines = TextChunker.SplitPlainTextLines(text, 40); + var paragraphs = TextChunker.SplitPlainTextParagraphs(lines, 120); + + WriteParagraphsToConsole(paragraphs); + } + + private static void RunExampleWithCustomTokenCounter() + { + Console.WriteLine("=== Text chunking with a custom token counter ==="); + + var lines = TextChunker.SplitPlainTextLines(text, 40, TokenCounter); + var paragraphs = TextChunker.SplitPlainTextParagraphs(lines, 120, tokenCounter: TokenCounter); + + WriteParagraphsToConsole(paragraphs); + } + + private static void WriteParagraphsToConsole(List paragraphs) + { + for (var i = 0; i < paragraphs.Count; i++) + { + Console.WriteLine(paragraphs[i]); + + if (i < paragraphs.Count - 1) + { + Console.WriteLine("------------------------"); + } + } + } + + private static int TokenCounter(string input) + { + var tokens = GPT3Tokenizer.Encode(input); + + return tokens.Count; + } +} diff --git a/dotnet/samples/KernelSyntaxExamples/Program.cs b/dotnet/samples/KernelSyntaxExamples/Program.cs index b0db3feae29f..8a37f9620b6a 100644 --- a/dotnet/samples/KernelSyntaxExamples/Program.cs +++ b/dotnet/samples/KernelSyntaxExamples/Program.cs @@ -73,6 +73,8 @@ public static async Task Main() await Example51_StepwisePlanner.RunAsync().SafeWaitAsync(cancelToken); await Example52_ApimAuth.RunAsync().SafeWaitAsync(cancelToken); await Example53_Kusto.RunAsync().SafeWaitAsync(cancelToken); + await Example54_AzureChatCompletionWithData.RunAsync().SafeWaitAsync(cancelToken); + await Example55_TextChunker.RunAsync().SafeWaitAsync(cancelToken); } private static void LoadUserSecrets() diff --git a/dotnet/samples/KernelSyntaxExamples/Reliability/RetryThreeTimesWithBackoff.cs b/dotnet/samples/KernelSyntaxExamples/Reliability/RetryThreeTimesWithBackoff.cs index ca287872b55a..318f998d439c 100644 --- a/dotnet/samples/KernelSyntaxExamples/Reliability/RetryThreeTimesWithBackoff.cs +++ b/dotnet/samples/KernelSyntaxExamples/Reliability/RetryThreeTimesWithBackoff.cs @@ -16,9 +16,9 @@ namespace Reliability; /// public class RetryThreeTimesWithBackoffFactory : IDelegatingHandlerFactory { - public DelegatingHandler Create(ILogger? logger) + public DelegatingHandler Create(ILoggerFactory? loggerFactory) { - return new RetryThreeTimesWithBackoff(logger); + return new RetryThreeTimesWithBackoff(loggerFactory); } } @@ -29,9 +29,9 @@ public class RetryThreeTimesWithBackoff : DelegatingHandler { private readonly AsyncRetryPolicy _policy; - public RetryThreeTimesWithBackoff(ILogger? logger) + public RetryThreeTimesWithBackoff(ILoggerFactory? loggerFactory) { - this._policy = GetPolicy(logger); + this._policy = GetPolicy(loggerFactory); } protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) @@ -43,7 +43,7 @@ protected override async Task SendAsync(HttpRequestMessage }); } - private static AsyncRetryPolicy GetPolicy(ILogger? logger) + private static AsyncRetryPolicy GetPolicy(ILoggerFactory? logger) { // Handle 429 and 401 errors // Typically 401 would not be something we retry but for demonstration @@ -57,7 +57,7 @@ private static AsyncRetryPolicy GetPolicy(ILogger? logger) TimeSpan.FromSeconds(4), TimeSpan.FromSeconds(8) }, - (outcome, timespan, retryCount, _) => logger?.LogWarning( + (outcome, timespan, retryCount, _) => logger?.CreateLogger(nameof(RetryThreeTimesWithBackoff)).LogWarning( "Error executing action [attempt {0} of 3], pausing {1}ms. Outcome: {2}", retryCount, timespan.TotalMilliseconds, diff --git a/dotnet/samples/KernelSyntaxExamples/Reliability/RetryThreeTimesWithRetryAfterBackoff.cs b/dotnet/samples/KernelSyntaxExamples/Reliability/RetryThreeTimesWithRetryAfterBackoff.cs index d26fb8f49ca1..ff46910c778b 100644 --- a/dotnet/samples/KernelSyntaxExamples/Reliability/RetryThreeTimesWithRetryAfterBackoff.cs +++ b/dotnet/samples/KernelSyntaxExamples/Reliability/RetryThreeTimesWithRetryAfterBackoff.cs @@ -16,9 +16,9 @@ namespace Reliability; /// public class RetryThreeTimesWithRetryAfterBackoffFactory : IDelegatingHandlerFactory { - public DelegatingHandler Create(ILogger? logger) + public DelegatingHandler Create(ILoggerFactory? loggerFactory) { - return new RetryThreeTimesWithRetryAfterBackoff(logger); + return new RetryThreeTimesWithRetryAfterBackoff(loggerFactory); } } @@ -29,9 +29,9 @@ public class RetryThreeTimesWithRetryAfterBackoff : DelegatingHandler { private readonly AsyncRetryPolicy _policy; - public RetryThreeTimesWithRetryAfterBackoff(ILogger? logger) + public RetryThreeTimesWithRetryAfterBackoff(ILoggerFactory? loggerFactory) { - this._policy = GetPolicy(logger); + this._policy = GetPolicy(loggerFactory); } protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) @@ -43,7 +43,7 @@ protected override async Task SendAsync(HttpRequestMessage }); } - private static AsyncRetryPolicy GetPolicy(ILogger? logger) + private static AsyncRetryPolicy GetPolicy(ILoggerFactory? loggerFactory) { // Handle 429 and 401 errors // Typically 401 would not be something we retry but for demonstration @@ -61,7 +61,7 @@ private static AsyncRetryPolicy GetPolicy(ILogger? logger) }, (outcome, timespan, retryCount, _) => { - logger?.LogWarning( + loggerFactory?.CreateLogger(nameof(RetryThreeTimesWithRetryAfterBackoff)).LogWarning( "Error executing action [attempt {0} of 3], pausing {1}ms. Outcome: {2}", retryCount, timespan.TotalMilliseconds, diff --git a/dotnet/samples/KernelSyntaxExamples/RepoUtils/ConsoleLogger.cs b/dotnet/samples/KernelSyntaxExamples/RepoUtils/ConsoleLogger.cs index 68f3b6073723..2ab9067ca8dd 100644 --- a/dotnet/samples/KernelSyntaxExamples/RepoUtils/ConsoleLogger.cs +++ b/dotnet/samples/KernelSyntaxExamples/RepoUtils/ConsoleLogger.cs @@ -10,15 +10,15 @@ namespace RepoUtils; /// internal static class ConsoleLogger { - internal static ILogger Logger => LogFactory.CreateLogger(); + internal static ILogger Logger => LoggerFactory.CreateLogger(); - private static ILoggerFactory LogFactory => s_loggerFactory.Value; + internal static ILoggerFactory LoggerFactory => s_loggerFactory.Value; private static readonly Lazy s_loggerFactory = new(LogBuilder); private static ILoggerFactory LogBuilder() { - return LoggerFactory.Create(builder => + return Microsoft.Extensions.Logging.LoggerFactory.Create(builder => { builder.SetMinimumLevel(LogLevel.Warning); diff --git a/dotnet/samples/KernelSyntaxExamples/TestConfiguration.cs b/dotnet/samples/KernelSyntaxExamples/TestConfiguration.cs index 7b41017cafec..f6f9a426d79d 100644 --- a/dotnet/samples/KernelSyntaxExamples/TestConfiguration.cs +++ b/dotnet/samples/KernelSyntaxExamples/TestConfiguration.cs @@ -83,6 +83,7 @@ public class ACSConfig { public string Endpoint { get; set; } public string ApiKey { get; set; } + public string IndexName { get; set; } } public class QdrantConfig diff --git a/dotnet/src/Connectors/Connectors.AI.HuggingFace/HuggingFaceKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.AI.HuggingFace/HuggingFaceKernelBuilderExtensions.cs index 8867c71c0af8..713dea9c9793 100644 --- a/dotnet/src/Connectors/Connectors.AI.HuggingFace/HuggingFaceKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.AI.HuggingFace/HuggingFaceKernelBuilderExtensions.cs @@ -36,11 +36,11 @@ public static KernelBuilder WithHuggingFaceTextCompletionService(this KernelBuil bool setAsDefault = false, HttpClient? httpClient = null) { - builder.WithAIService(serviceId, (parameters) => + builder.WithAIService(serviceId, (loggerFactory, config) => new HuggingFaceTextCompletion( model, apiKey, - HttpClientProvider.GetHttpClient(parameters.Config, httpClient, parameters.Logger), + HttpClientProvider.GetHttpClient(config, httpClient, loggerFactory), endpoint), setAsDefault); @@ -62,10 +62,10 @@ public static KernelBuilder WithHuggingFaceTextEmbeddingGenerationService(this K string? serviceId = null, bool setAsDefault = false) { - builder.WithAIService(serviceId, (parameters) => + builder.WithAIService(serviceId, (loggerFactory, config) => new HuggingFaceTextEmbeddingGeneration( model, - HttpClientProvider.GetHttpClient(parameters.Config, httpClient: null, parameters.Logger), + HttpClientProvider.GetHttpClient(config, httpClient: null, loggerFactory), endpoint), setAsDefault); @@ -89,10 +89,10 @@ public static KernelBuilder WithHuggingFaceTextEmbeddingGenerationService(this K string? serviceId = null, bool setAsDefault = false) { - builder.WithAIService(serviceId, (parameters) => + builder.WithAIService(serviceId, (loggerFactory, config) => new HuggingFaceTextEmbeddingGeneration( model, - HttpClientProvider.GetHttpClient(parameters.Config, httpClient, parameters.Logger), + HttpClientProvider.GetHttpClient(config, httpClient, loggerFactory), endpoint), setAsDefault); diff --git a/dotnet/src/Connectors/Connectors.AI.HuggingFace/TextEmbedding/HuggingFaceTextEmbeddingGeneration.cs b/dotnet/src/Connectors/Connectors.AI.HuggingFace/TextEmbedding/HuggingFaceTextEmbeddingGeneration.cs index b38085c70b9e..aa8a6e0c7ade 100644 --- a/dotnet/src/Connectors/Connectors.AI.HuggingFace/TextEmbedding/HuggingFaceTextEmbeddingGeneration.cs +++ b/dotnet/src/Connectors/Connectors.AI.HuggingFace/TextEmbedding/HuggingFaceTextEmbeddingGeneration.cs @@ -81,7 +81,7 @@ public HuggingFaceTextEmbeddingGeneration(string model, HttpClient httpClient, s } /// - public async Task>> GenerateEmbeddingsAsync(IList data, CancellationToken cancellationToken = default) + public async Task>> GenerateEmbeddingsAsync(IList data, CancellationToken cancellationToken = default) { return await this.ExecuteEmbeddingRequestAsync(data, cancellationToken).ConfigureAwait(false); } @@ -95,7 +95,7 @@ public async Task>> GenerateEmbeddingsAsync(IList /// The to monitor for cancellation requests. The default is . /// List of generated embeddings. /// Exception when backend didn't respond with generated embeddings. - private async Task>> ExecuteEmbeddingRequestAsync(IList data, CancellationToken cancellationToken) + private async Task>> ExecuteEmbeddingRequestAsync(IList data, CancellationToken cancellationToken) { try { @@ -113,7 +113,7 @@ private async Task>> ExecuteEmbeddingRequestAsync(IList(body); - return embeddingResponse?.Embeddings?.Select(l => new Embedding(l.Embedding!, transferOwnership: true)).ToList()!; + return embeddingResponse?.Embeddings?.Select(l => l.Embedding).ToList()!; } catch (Exception e) when (e is not AIException && !e.IsCriticalException()) { diff --git a/dotnet/src/Connectors/Connectors.AI.HuggingFace/TextEmbedding/TextEmbeddingResponse.cs b/dotnet/src/Connectors/Connectors.AI.HuggingFace/TextEmbedding/TextEmbeddingResponse.cs index 781959e59522..6924ee86d107 100644 --- a/dotnet/src/Connectors/Connectors.AI.HuggingFace/TextEmbedding/TextEmbeddingResponse.cs +++ b/dotnet/src/Connectors/Connectors.AI.HuggingFace/TextEmbedding/TextEmbeddingResponse.cs @@ -1,7 +1,9 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Text.Json.Serialization; +using Microsoft.SemanticKernel.Text; namespace Microsoft.SemanticKernel.Connectors.AI.HuggingFace.TextEmbedding; @@ -16,7 +18,8 @@ public sealed class TextEmbeddingResponse public sealed class EmbeddingVector { [JsonPropertyName("embedding")] - public IList? Embedding { get; set; } + [JsonConverter(typeof(ReadOnlyMemoryConverter))] + public ReadOnlyMemory Embedding { get; set; } } /// diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/AzureOpenAIClientBase.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/AzureOpenAIClientBase.cs index dc369655229a..91321a61d0e4 100644 --- a/dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/AzureOpenAIClientBase.cs +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/AzureOpenAIClientBase.cs @@ -26,13 +26,13 @@ public abstract class AzureOpenAIClientBase : ClientBase /// Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart /// Azure OpenAI API key, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart /// Custom for HTTP requests. - /// Application logger + /// The to use for logging. If null, no logging will be performed. private protected AzureOpenAIClientBase( string modelId, string endpoint, string apiKey, HttpClient? httpClient = null, - ILogger? logger = null) : base(logger) + ILoggerFactory? loggerFactory = null) : base(loggerFactory) { Verify.NotNullOrWhiteSpace(modelId); Verify.NotNullOrWhiteSpace(endpoint); @@ -56,13 +56,13 @@ private protected AzureOpenAIClientBase( /// Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart /// Token credential, e.g. DefaultAzureCredential, ManagedIdentityCredential, EnvironmentCredential, etc. /// Custom for HTTP requests. - /// Application logger + /// The to use for logging. If null, no logging will be performed. private protected AzureOpenAIClientBase( string modelId, string endpoint, TokenCredential credential, HttpClient? httpClient = null, - ILogger? logger = null) : base(logger) + ILoggerFactory? loggerFactory = null) : base(loggerFactory) { Verify.NotNullOrWhiteSpace(modelId); Verify.NotNullOrWhiteSpace(endpoint); @@ -85,11 +85,11 @@ private protected AzureOpenAIClientBase( /// /// Azure OpenAI model ID or deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource /// Custom . - /// Application logger + /// The to use for logging. If null, no logging will be performed. private protected AzureOpenAIClientBase( string modelId, OpenAIClient openAIClient, - ILogger? logger = null) : base(logger) + ILoggerFactory? loggerFactory = null) : base(loggerFactory) { Verify.NotNullOrWhiteSpace(modelId); Verify.NotNull(openAIClient); diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/ClientBase.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/ClientBase.cs index b9545640210a..7db116a7b8f4 100644 --- a/dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/ClientBase.cs +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/ClientBase.cs @@ -12,7 +12,6 @@ using Microsoft.Extensions.Logging.Abstractions; using Microsoft.SemanticKernel.AI; using Microsoft.SemanticKernel.AI.ChatCompletion; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.AI.TextCompletion; using Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletion; using Microsoft.SemanticKernel.Diagnostics; @@ -27,9 +26,9 @@ public abstract class ClientBase private const int MaxResultsPerPrompt = 128; // Prevent external inheritors - private protected ClientBase(ILogger? logger = null) + private protected ClientBase(ILoggerFactory? loggerFactory = null) { - this.Logger = logger ?? NullLogger.Instance; + this.Logger = loggerFactory is not null ? loggerFactory.CreateLogger(this.GetType().Name) : NullLogger.Instance; } /// @@ -67,16 +66,16 @@ private protected async Task> InternalGetTextResultsA Response? response = await RunRequestAsync?>( () => this.Client.GetCompletionsAsync(this.ModelId, options, cancellationToken)).ConfigureAwait(false); - if (response == null) + if (response is null) { - throw new OpenAIInvalidResponseException(null, "Text completions null response"); + throw new SKException("Text completions null response"); } var responseData = response.Value; if (responseData.Choices.Count == 0) { - throw new OpenAIInvalidResponseException(responseData, "Text completions not found"); + throw new SKException("Text completions not found"); } return responseData.Choices.Select(choice => new TextResult(responseData, choice)).ToList(); @@ -115,11 +114,11 @@ private protected async IAsyncEnumerable InternalGetTextStr /// List of strings to generate embeddings for /// The to monitor for cancellation requests. The default is . /// List of embeddings - private protected async Task>> InternalGetEmbeddingsAsync( + private protected async Task>> InternalGetEmbeddingsAsync( IList data, CancellationToken cancellationToken = default) { - var result = new List>(); + var result = new List>(data.Count); foreach (string text in data) { var options = new EmbeddingsOptions(text); @@ -127,19 +126,17 @@ private protected async Task>> InternalGetEmbeddingsAsync Response? response = await RunRequestAsync?>( () => this.Client.GetEmbeddingsAsync(this.ModelId, options, cancellationToken)).ConfigureAwait(false); - if (response == null) + if (response is null) { - throw new OpenAIInvalidResponseException(null, "Text embedding null response"); + throw new SKException("Text embedding null response"); } if (response.Value.Data.Count == 0) { - throw new OpenAIInvalidResponseException(response.Value, "Text embedding not found"); + throw new SKException("Text embedding not found"); } - EmbeddingItem x = response.Value.Data[0]; - - result.Add(new Embedding(x.Embedding, transferOwnership: true)); + result.Add(response.Value.Data[0].Embedding.ToArray()); } return result; @@ -166,14 +163,14 @@ private protected async Task> InternalGetChatResultsA Response? response = await RunRequestAsync?>( () => this.Client.GetChatCompletionsAsync(this.ModelId, chatOptions, cancellationToken)).ConfigureAwait(false); - if (response == null) + if (response is null) { - throw new OpenAIInvalidResponseException(null, "Chat completions null response"); + throw new SKException("Chat completions null response"); } if (response.Value.Choices.Count == 0) { - throw new OpenAIInvalidResponseException(response.Value, "Chat completions not found"); + throw new SKException("Chat completions not found"); } return response.Value.Choices.Select(chatChoice => new ChatResult(response.Value, chatChoice)).ToList(); @@ -203,7 +200,7 @@ private protected async IAsyncEnumerable InternalGetChatSt if (response is null) { - throw new OpenAIInvalidResponseException(null, "Chat completions null response"); + throw new SKException("Chat completions null response"); } using StreamingChatCompletions streamingChatCompletions = response.Value; diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/OpenAIClientBase.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/OpenAIClientBase.cs index ce9e81df65b6..89e686a495f9 100644 --- a/dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/OpenAIClientBase.cs +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/OpenAIClientBase.cs @@ -24,13 +24,13 @@ public abstract class OpenAIClientBase : ClientBase /// OpenAI API Key /// OpenAI Organization Id (usually optional) /// Custom for HTTP requests. - /// Application logger + /// The to use for logging. If null, no logging will be performed. private protected OpenAIClientBase( string modelId, string apiKey, string? organization = null, HttpClient? httpClient = null, - ILogger? logger = null) : base(logger) + ILoggerFactory? loggerFactory = null) : base(loggerFactory) { Verify.NotNullOrWhiteSpace(modelId); Verify.NotNullOrWhiteSpace(apiKey); @@ -58,11 +58,11 @@ private protected OpenAIClientBase( /// /// Azure OpenAI model ID or deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource /// Custom . - /// Application logger + /// The to use for logging. If null, no logging will be performed. private protected OpenAIClientBase( string modelId, OpenAIClient openAIClient, - ILogger? logger = null) : base(logger) + ILoggerFactory? loggerFactory = null) : base(loggerFactory) { Verify.NotNullOrWhiteSpace(modelId); Verify.NotNull(openAIClient); diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/OpenAIInvalidResponseException{T}.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/OpenAIInvalidResponseException{T}.cs deleted file mode 100644 index 3b2c5042853a..000000000000 --- a/dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/OpenAIInvalidResponseException{T}.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using Microsoft.SemanticKernel.AI; - -#pragma warning disable RCS1194 // Implement exception constructors. - -namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.AzureSdk; - -internal sealed class OpenAIInvalidResponseException : AIException -{ - public T? ResponseData { get; } - - public OpenAIInvalidResponseException(T? responseData, string? message = null) : base(ErrorCodes.InvalidResponseContent, message) - { - this.ResponseData = responseData; - } -} diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/SKChatMessage.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/SKChatMessage.cs index 8ee6a8daf327..f8966e0aafb8 100644 --- a/dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/SKChatMessage.cs +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/AzureSdk/SKChatMessage.cs @@ -10,11 +10,21 @@ namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.AzureSdk; public class SKChatMessage : ChatMessageBase { /// - /// Create a new instance of a chat message + /// Initializes a new instance of the class. /// /// OpenAI SDK chat message representation public SKChatMessage(Azure.AI.OpenAI.ChatMessage message) : base(new AuthorRole(message.Role.ToString()), message.Content) { } + + /// + /// Initializes a new instance of the class. + /// + /// Role of the author of the message. + /// Content of the message. + public SKChatMessage(string role, string content) + : base(new AuthorRole(role), content) + { + } } diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletion/AzureChatCompletion.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletion/AzureChatCompletion.cs index 27838f2a8f49..36f97f0f8659 100644 --- a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletion/AzureChatCompletion.cs +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletion/AzureChatCompletion.cs @@ -26,13 +26,13 @@ public sealed class AzureChatCompletion : AzureOpenAIClientBase, IChatCompletion /// Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart /// Azure OpenAI API key, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart /// Custom for HTTP requests. - /// Application logger + /// The to use for logging. If null, no logging will be performed. public AzureChatCompletion( string modelId, string endpoint, string apiKey, HttpClient? httpClient = null, - ILogger? logger = null) : base(modelId, endpoint, apiKey, httpClient, logger) + ILoggerFactory? loggerFactory = null) : base(modelId, endpoint, apiKey, httpClient, loggerFactory) { } @@ -43,13 +43,13 @@ public AzureChatCompletion( /// Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart /// Token credentials, e.g. DefaultAzureCredential, ManagedIdentityCredential, EnvironmentCredential, etc. /// Custom for HTTP requests. - /// Application logger + /// The to use for logging. If null, no logging will be performed. public AzureChatCompletion( string modelId, string endpoint, TokenCredential credentials, HttpClient? httpClient = null, - ILogger? logger = null) : base(modelId, endpoint, credentials, httpClient, logger) + ILoggerFactory? loggerFactory = null) : base(modelId, endpoint, credentials, httpClient, loggerFactory) { } @@ -58,11 +58,11 @@ public AzureChatCompletion( /// /// Azure OpenAI model ID or deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource /// Custom . - /// Application logger + /// The to use for logging. If null, no logging will be performed. public AzureChatCompletion( string modelId, OpenAIClient openAIClient, - ILogger? logger = null) : base(modelId, openAIClient, logger) + ILoggerFactory? loggerFactory = null) : base(modelId, openAIClient, loggerFactory) { } diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletion/OpenAIChatCompletion.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletion/OpenAIChatCompletion.cs index ed733c61efcf..f79f8ea715ae 100644 --- a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletion/OpenAIChatCompletion.cs +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletion/OpenAIChatCompletion.cs @@ -25,13 +25,13 @@ public sealed class OpenAIChatCompletion : OpenAIClientBase, IChatCompletion, IT /// OpenAI API Key /// OpenAI Organization Id (usually optional) /// Custom for HTTP requests. - /// Application logger + /// The to use for logging. If null, no logging will be performed. public OpenAIChatCompletion( string modelId, string apiKey, string? organization = null, HttpClient? httpClient = null, - ILogger? logger = null) : base(modelId, apiKey, organization, httpClient, logger) + ILoggerFactory? loggerFactory = null) : base(modelId, apiKey, organization, httpClient, loggerFactory) { } @@ -40,11 +40,11 @@ public OpenAIChatCompletion( /// /// Model name /// Custom for HTTP requests. - /// Application logger + /// The to use for logging. If null, no logging will be performed. public OpenAIChatCompletion( - string modelId, - OpenAIClient openAIClient, - ILogger? logger = null) : base(modelId, openAIClient, logger) + string modelId, + OpenAIClient openAIClient, + ILoggerFactory? loggerFactory = null) : base(modelId, openAIClient, loggerFactory) { } diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/AzureChatCompletionWithData.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/AzureChatCompletionWithData.cs new file mode 100644 index 000000000000..a3fe8fe46996 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/AzureChatCompletionWithData.cs @@ -0,0 +1,347 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.SemanticKernel.AI; +using Microsoft.SemanticKernel.AI.ChatCompletion; +using Microsoft.SemanticKernel.AI.TextCompletion; +using Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletion; +using Microsoft.SemanticKernel.Diagnostics; +using Microsoft.SemanticKernel.Text; + +namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletionWithData; + +/// +/// Azure OpenAI Chat Completion with data client. +/// More information: +/// +public sealed class AzureChatCompletionWithData : IChatCompletion, ITextCompletion +{ + /// + /// Initializes a new instance of the class. + /// + /// Instance of class with completion configuration. + /// Custom for HTTP requests. + /// Instance of to use for logging. + public AzureChatCompletionWithData( + AzureChatCompletionWithDataConfig config, + HttpClient? httpClient = null, + ILoggerFactory? loggerFactory = null) + { + this.ValidateConfig(config); + + this._config = config; + + this._httpClient = httpClient ?? new HttpClient(NonDisposableHttpClientHandler.Instance, disposeHandler: false); + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(this.GetType().Name) : NullLogger.Instance; + } + + /// + public ChatHistory CreateNewChat(string? instructions = null) + { + return new OpenAIChatHistory(instructions); + } + + /// + public async Task> GetChatCompletionsAsync( + ChatHistory chat, + ChatRequestSettings? requestSettings = null, + CancellationToken cancellationToken = default) + { + Verify.NotNull(chat); + + requestSettings ??= new(); + + ValidateMaxTokens(requestSettings.MaxTokens); + + return await this.ExecuteCompletionRequestAsync(chat, requestSettings, cancellationToken).ConfigureAwait(false); + } + + /// + public IAsyncEnumerable GetStreamingChatCompletionsAsync( + ChatHistory chat, + ChatRequestSettings? requestSettings = null, + CancellationToken cancellationToken = default) + { + Verify.NotNull(chat); + + requestSettings ??= new(); + + ValidateMaxTokens(requestSettings.MaxTokens); + + return this.ExecuteCompletionStreamingRequestAsync(chat, requestSettings, cancellationToken); + } + + /// + public async Task> GetCompletionsAsync( + string text, + CompleteRequestSettings requestSettings, + CancellationToken cancellationToken = default) + { + requestSettings ??= new(); + + var chat = this.PrepareChatHistory(text, requestSettings); + var chatRequestSettings = this.PrepareChatRequestSettings(requestSettings); + + return (await this.GetChatCompletionsAsync(chat, chatRequestSettings, cancellationToken).ConfigureAwait(false)) + .OfType() + .ToList(); + } + + /// + public async IAsyncEnumerable GetStreamingCompletionsAsync( + string text, + CompleteRequestSettings requestSettings, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + requestSettings ??= new(); + + var chat = this.PrepareChatHistory(text, requestSettings); + var chatRequestSettings = this.PrepareChatRequestSettings(requestSettings); + + await foreach (var result in this.GetStreamingChatCompletionsAsync(chat, chatRequestSettings, cancellationToken)) + { + yield return (ITextStreamingResult)result; + } + } + + #region private ================================================================================ + + private const string DefaultApiVersion = "2023-06-01-preview"; + + private readonly AzureChatCompletionWithDataConfig _config; + + private readonly HttpClient _httpClient; + private readonly ILogger _logger; + + private void ValidateConfig(AzureChatCompletionWithDataConfig config) + { + Verify.NotNull(config); + + Verify.NotNullOrWhiteSpace(config.CompletionModelId); + Verify.NotNullOrWhiteSpace(config.CompletionEndpoint); + Verify.NotNullOrWhiteSpace(config.CompletionApiKey); + Verify.NotNullOrWhiteSpace(config.DataSourceEndpoint); + Verify.NotNullOrWhiteSpace(config.DataSourceApiKey); + Verify.NotNullOrWhiteSpace(config.DataSourceIndex); + } + + private static void ValidateMaxTokens(int? maxTokens) + { + if (maxTokens.HasValue && maxTokens < 1) + { + throw new AIException( + AIException.ErrorCodes.InvalidRequest, + $"MaxTokens {maxTokens} is not valid, the value must be greater than zero"); + } + } + + private async Task> ExecuteCompletionRequestAsync( + ChatHistory chat, + ChatRequestSettings requestSettings, + CancellationToken cancellationToken = default) + { + using var request = this.GetRequest(chat, requestSettings, isStreamEnabled: false); + using var response = await this.SendRequestAsync(request, cancellationToken).ConfigureAwait(false); + + this.EnsureSuccessStatusCode(response); + + var body = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + + var chatWithDataResponse = this.DeserializeResponse(body); + + return chatWithDataResponse.Choices.Select(choice => new ChatWithDataResult(chatWithDataResponse, choice)).ToList(); + } + + private async IAsyncEnumerable ExecuteCompletionStreamingRequestAsync( + ChatHistory chat, + ChatRequestSettings requestSettings, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + using var request = this.GetRequest(chat, requestSettings, isStreamEnabled: true); + using var response = await this.SendRequestAsync(request, cancellationToken).ConfigureAwait(false); + + this.EnsureSuccessStatusCode(response); + + await foreach (var result in this.GetStreamingResultsAsync(response)) + { + yield return result; + } + } + + private async Task SendRequestAsync( + HttpRequestMessage request, + CancellationToken cancellationToken = default) + { + request.Headers.Add("User-Agent", Telemetry.HttpUserAgent); + request.Headers.Add("Api-Key", this._config.CompletionApiKey); + + return await this._httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); + } + + private void EnsureSuccessStatusCode(HttpResponseMessage response) + { + try + { + response.EnsureSuccessStatusCode(); + } + catch (HttpRequestException ex) + { + this._logger.LogError( + "Error occurred on chat completion with data request execution: {ExceptionMessage}", ex.Message); + + throw new AIException( + AIException.ErrorCodes.UnknownError, + $"Error occurred on chat completion with data request execution: {ex.Message}", ex); + } + } + + private async IAsyncEnumerable GetStreamingResultsAsync(HttpResponseMessage response) + { + const string ServerEventPayloadPrefix = "data:"; + + using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + using var reader = new StreamReader(stream); + + while (!reader.EndOfStream) + { + var body = await reader.ReadLineAsync().ConfigureAwait(false); + + if (string.IsNullOrWhiteSpace(body)) + { + continue; + } + + if (body.StartsWith(ServerEventPayloadPrefix, StringComparison.Ordinal)) + { + body = body.Substring(ServerEventPayloadPrefix.Length); + } + + var chatWithDataResponse = this.DeserializeResponse(body); + + foreach (var choice in chatWithDataResponse.Choices) + { + yield return new ChatWithDataStreamingResult(chatWithDataResponse, choice); + } + } + } + + private T DeserializeResponse(string body) + { + var response = Json.Deserialize(body); + + if (response is null) + { + const string errorMessage = "Error occurred on chat completion with data response deserialization"; + + this._logger.LogError(errorMessage); + + throw new AIException( + AIException.ErrorCodes.InvalidResponseContent, + errorMessage); + } + + return response; + } + + private HttpRequestMessage GetRequest( + ChatHistory chat, + ChatRequestSettings requestSettings, + bool isStreamEnabled) + { + var payload = new ChatWithDataRequest + { + Temperature = requestSettings.Temperature, + TopP = requestSettings.TopP, + IsStreamEnabled = isStreamEnabled, + StopSequences = requestSettings.StopSequences, + MaxTokens = requestSettings.MaxTokens, + PresencePenalty = requestSettings.PresencePenalty, + FrequencyPenalty = requestSettings.FrequencyPenalty, + TokenSelectionBiases = requestSettings.TokenSelectionBiases, + DataSources = this.GetDataSources(), + Messages = this.GetMessages(chat) + }; + + return HttpRequest.CreatePostRequest(this.GetRequestUri(), payload); + } + + private List GetDataSources() + { + return new List + { + new ChatWithDataSource + { + Parameters = new ChatWithDataSourceParameters + { + Endpoint = this._config.DataSourceEndpoint, + ApiKey = this._config.DataSourceApiKey, + IndexName = this._config.DataSourceIndex + } + } + }; + } + + private List GetMessages(ChatHistory chat) + { + return chat + .Select(message => new ChatWithDataMessage + { + Role = message.Role.Label, + Content = message.Content + }) + .ToList(); + } + + private ChatHistory PrepareChatHistory(string text, CompleteRequestSettings requestSettings) + { + var chat = this.CreateNewChat(requestSettings.ChatSystemPrompt); + + chat.AddUserMessage(text); + + return chat; + } + + private ChatRequestSettings PrepareChatRequestSettings(CompleteRequestSettings requestSettings) + { + return new ChatRequestSettings + { + MaxTokens = requestSettings.MaxTokens, + Temperature = requestSettings.Temperature, + TopP = requestSettings.TopP, + PresencePenalty = requestSettings.PresencePenalty, + FrequencyPenalty = requestSettings.FrequencyPenalty, + StopSequences = requestSettings.StopSequences, + }; + } + + private string GetRequestUri() + { + const string EndpointUriFormat = "{0}/openai/deployments/{1}/extensions/chat/completions?api-version={2}"; + + var apiVersion = this._config.CompletionApiVersion; + + if (string.IsNullOrWhiteSpace(apiVersion)) + { + apiVersion = DefaultApiVersion; + } + + return string.Format( + CultureInfo.InvariantCulture, + EndpointUriFormat, + this._config.CompletionEndpoint.TrimEnd('/'), + this._config.CompletionModelId, + apiVersion); + } + + #endregion +} diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/AzureChatCompletionWithDataConfig.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/AzureChatCompletionWithDataConfig.cs new file mode 100644 index 000000000000..dc4d58bb503c --- /dev/null +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/AzureChatCompletionWithDataConfig.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft. All rights reserved. + +namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletionWithData; + +/// +/// Required configuration for Azure OpenAI chat completion with data. +/// More information: +/// +public class AzureChatCompletionWithDataConfig +{ + /// + /// Azure OpenAI model ID or deployment name, see + /// + public string CompletionModelId { get; set; } = string.Empty; + + /// + /// Azure OpenAI deployment URL, see + /// + public string CompletionEndpoint { get; set; } = string.Empty; + + /// + /// Azure OpenAI API key, see + /// + public string CompletionApiKey { get; set; } = string.Empty; + + /// + /// Azure OpenAI Completion API version (e.g. 2023-06-01-preview) + /// + public string CompletionApiVersion { get; set; } = string.Empty; + + /// + /// Data source endpoint URL. + /// For Azure Cognitive Search, see + /// + public string DataSourceEndpoint { get; set; } = string.Empty; + + /// + /// Data source API key. + /// For Azure Cognitive Search keys, see + /// + public string DataSourceApiKey { get; set; } = string.Empty; + + /// + /// Data source index name. + /// For Azure Cognitive Search indexes, see + /// + public string DataSourceIndex { get; set; } = string.Empty; +} diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataChoice.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataChoice.cs new file mode 100644 index 000000000000..842d236d0586 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataChoice.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletionWithData; + +[Serializable] +[SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Used for JSON deserialization")] +internal sealed class ChatWithDataChoice +{ + [JsonPropertyName("messages")] + public IList Messages { get; set; } = Array.Empty(); +} diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataMessage.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataMessage.cs new file mode 100644 index 000000000000..533d87d30cee --- /dev/null +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataMessage.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Text.Json.Serialization; + +namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletionWithData; + +[Serializable] +internal sealed class ChatWithDataMessage +{ + [JsonPropertyName("role")] + public string Role { get; set; } = string.Empty; + + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; +} diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataModelResult.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataModelResult.cs new file mode 100644 index 000000000000..11f21eae3d06 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataModelResult.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; + +namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletionWithData; + +/// +/// Represents result of a chat completion with data. +/// +public class ChatWithDataModelResult +{ + /// + /// A unique identifier associated with chat completion with data response. + /// + public string Id { get; } + + /// + /// The first timestamp associated with generation activity for chat completion with data response, + /// represented as seconds since the beginning of the Unix epoch of 00:00 on 1 Jan 1970. + /// + public DateTimeOffset Created { get; } + + /// + /// Initializes a new instance of the class. + /// + /// A unique identifier associated with chat completion with data response. + /// The first timestamp associated with generation activity for chat completion with data response. + public ChatWithDataModelResult(string id, DateTimeOffset created) + { + this.Id = id; + this.Created = created; + } +} diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataRequest.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataRequest.cs new file mode 100644 index 000000000000..ab57062b3c3b --- /dev/null +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataRequest.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletionWithData; + +[Serializable] +internal sealed class ChatWithDataRequest +{ + [JsonPropertyName("temperature")] + public double Temperature { get; set; } = 0; + + [JsonPropertyName("top_p")] + public double TopP { get; set; } = 0; + + [JsonPropertyName("stream")] + public bool IsStreamEnabled { get; set; } + + [JsonPropertyName("stop")] + public IList StopSequences { get; set; } = Array.Empty(); + + [JsonPropertyName("max_tokens")] + public int? MaxTokens { get; set; } + + [JsonPropertyName("presence_penalty")] + public double PresencePenalty { get; set; } = 0; + + [JsonPropertyName("frequency_penalty")] + public double FrequencyPenalty { get; set; } = 0; + + [JsonPropertyName("logit_bias")] + public IDictionary TokenSelectionBiases { get; set; } = new Dictionary(); + + [JsonPropertyName("dataSources")] + public IList DataSources { get; set; } = Array.Empty(); + + [JsonPropertyName("messages")] + public IList Messages { get; set; } = Array.Empty(); +} diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataResponse.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataResponse.cs new file mode 100644 index 000000000000..62e45328cc2c --- /dev/null +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataResponse.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletionWithData; + +[Serializable] +[SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Used for JSON deserialization")] +internal sealed class ChatWithDataResponse +{ + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + [JsonPropertyName("created")] + public int Created { get; set; } = default; + + [JsonPropertyName("choices")] + public IList Choices { get; set; } = Array.Empty(); +} diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataResult.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataResult.cs new file mode 100644 index 000000000000..4a2c88be736a --- /dev/null +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataResult.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.SemanticKernel.AI.ChatCompletion; +using Microsoft.SemanticKernel.AI.TextCompletion; +using Microsoft.SemanticKernel.Connectors.AI.OpenAI.AzureSdk; +using Microsoft.SemanticKernel.Diagnostics; +using Microsoft.SemanticKernel.Orchestration; + +namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletionWithData; + +internal sealed class ChatWithDataResult : IChatResult, ITextResult +{ + public ModelResult ModelResult { get; } + + public ChatWithDataResult(ChatWithDataResponse response, ChatWithDataChoice choice) + { + Verify.NotNull(response); + Verify.NotNull(choice); + + this.ModelResult = new(new ChatWithDataModelResult(response.Id, DateTimeOffset.FromUnixTimeSeconds(response.Created))); + + this._choice = choice; + } + + public Task GetChatMessageAsync(CancellationToken cancellationToken = default) + { + var message = this._choice.Messages + .FirstOrDefault(message => message.Role.Equals(AuthorRole.Assistant.Label, StringComparison.Ordinal)); + + return Task.FromResult(new SKChatMessage(message.Role, message.Content)); + } + + public async Task GetCompletionAsync(CancellationToken cancellationToken = default) + { + var message = await this.GetChatMessageAsync(cancellationToken).ConfigureAwait(false); + + return message.Content; + } + + #region private ================================================================================ + + private readonly ChatWithDataChoice _choice; + + #endregion +} diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataSource.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataSource.cs new file mode 100644 index 000000000000..3877e5a21ad1 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataSource.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Text.Json.Serialization; + +namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletionWithData; + +[Serializable] +internal sealed class ChatWithDataSource +{ + [JsonPropertyName("type")] + public string Type { get; set; } = ChatWithDataSourceType.AzureCognitiveSearch.ToString(); + + [JsonPropertyName("parameters")] + public ChatWithDataSourceParameters Parameters { get; set; } = new ChatWithDataSourceParameters(); +} diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataSourceParameters.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataSourceParameters.cs new file mode 100644 index 000000000000..e0e5cb0de81d --- /dev/null +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataSourceParameters.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Text.Json.Serialization; + +namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletionWithData; + +[Serializable] +internal sealed class ChatWithDataSourceParameters +{ + [JsonPropertyName("endpoint")] + public string Endpoint { get; set; } = string.Empty; + + [JsonPropertyName("key")] + public string ApiKey { get; set; } = string.Empty; + + [JsonPropertyName("indexName")] + public string IndexName { get; set; } = string.Empty; +} diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataSourceType.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataSourceType.cs new file mode 100644 index 000000000000..4aadf06e149f --- /dev/null +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataSourceType.cs @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft. All rights reserved. + +namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletionWithData; + +internal enum ChatWithDataSourceType +{ + AzureCognitiveSearch +} diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataStreamingChoice.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataStreamingChoice.cs new file mode 100644 index 000000000000..1718e386279a --- /dev/null +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataStreamingChoice.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletionWithData; + +[Serializable] +[SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Used for JSON deserialization")] +internal sealed class ChatWithDataStreamingChoice +{ + [JsonPropertyName("messages")] + public IList Messages { get; set; } = Array.Empty(); +} diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataStreamingDelta.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataStreamingDelta.cs new file mode 100644 index 000000000000..1096ed22c4a5 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataStreamingDelta.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Text.Json.Serialization; + +namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletionWithData; + +[Serializable] +internal sealed class ChatWithDataStreamingDelta +{ + [JsonPropertyName("role")] + public string? Role { get; set; } + + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; +} diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataStreamingMessage.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataStreamingMessage.cs new file mode 100644 index 000000000000..80c1f258e2ca --- /dev/null +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataStreamingMessage.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletionWithData; + +[Serializable] +[SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Used for JSON deserialization")] +internal sealed class ChatWithDataStreamingMessage +{ + [JsonPropertyName("delta")] + public ChatWithDataStreamingDelta Delta { get; set; } = new(); + + [JsonPropertyName("end_turn")] + public bool EndTurn { get; set; } +} diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataStreamingResponse.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataStreamingResponse.cs new file mode 100644 index 000000000000..ce0c8d0e637c --- /dev/null +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataStreamingResponse.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletionWithData; + +[Serializable] +[SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Used for JSON deserialization")] +internal sealed class ChatWithDataStreamingResponse +{ + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + [JsonPropertyName("created")] + public int Created { get; set; } = default; + + [JsonPropertyName("choices")] + public IList Choices { get; set; } = Array.Empty(); +} diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataStreamingResult.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataStreamingResult.cs new file mode 100644 index 000000000000..6c5198528e86 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ChatCompletionWithData/ChatWithDataStreamingResult.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.SemanticKernel.AI.ChatCompletion; +using Microsoft.SemanticKernel.AI.TextCompletion; +using Microsoft.SemanticKernel.Connectors.AI.OpenAI.AzureSdk; +using Microsoft.SemanticKernel.Diagnostics; +using Microsoft.SemanticKernel.Orchestration; + +namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletionWithData; + +internal sealed class ChatWithDataStreamingResult : IChatStreamingResult, ITextStreamingResult +{ + public ModelResult ModelResult { get; } + + public ChatWithDataStreamingResult(ChatWithDataStreamingResponse response, ChatWithDataStreamingChoice choice) + { + Verify.NotNull(response); + Verify.NotNull(choice); + + this.ModelResult = new(new ChatWithDataModelResult(response.Id, DateTimeOffset.FromUnixTimeSeconds(response.Created))); + + this._choice = choice; + } + + public async Task GetChatMessageAsync(CancellationToken cancellationToken = default) + { + var message = this._choice.Messages.FirstOrDefault(this.IsValidMessage); + + var result = new SKChatMessage(AuthorRole.Assistant.Label, message?.Delta?.Content ?? string.Empty); + + return await Task.FromResult(result).ConfigureAwait(false); + } + + public async IAsyncEnumerable GetStreamingChatMessageAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) + { + var message = await this.GetChatMessageAsync(cancellationToken).ConfigureAwait(false); + + if (!string.IsNullOrWhiteSpace(message.Content)) + { + yield return message; + } + } + + public async IAsyncEnumerable GetCompletionStreamingAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) + { + await foreach (var result in this.GetStreamingChatMessageAsync(cancellationToken)) + { + yield return result.Content; + } + } + + public async Task GetCompletionAsync(CancellationToken cancellationToken = default) + { + var message = await this.GetChatMessageAsync(cancellationToken).ConfigureAwait(false); + + return message.Content; + } + + #region private ================================================================================ + + private readonly ChatWithDataStreamingChoice _choice; + + private bool IsValidMessage(ChatWithDataStreamingMessage message) + { + return !message.EndTurn && + (message.Delta.Role is null || !message.Delta.Role.Equals(AuthorRole.Tool.Label, StringComparison.Ordinal)); + } + + #endregion +} diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/CustomClient/OpenAIClientBase.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/CustomClient/OpenAIClientBase.cs index 4edbee4f42af..107dc6879070 100644 --- a/dotnet/src/Connectors/Connectors.AI.OpenAI/CustomClient/OpenAIClientBase.cs +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/CustomClient/OpenAIClientBase.cs @@ -12,7 +12,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.SemanticKernel.AI; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Connectors.AI.OpenAI.ImageGeneration; using Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextEmbedding; using Microsoft.SemanticKernel.Diagnostics; @@ -27,11 +26,11 @@ public abstract class OpenAIClientBase /// Initializes a new instance of the class. /// /// The HttpClient used for making HTTP requests. - /// The ILogger used for logging. If null, a NullLogger instance will be used. - private protected OpenAIClientBase(HttpClient? httpClient, ILogger? logger = null) + /// The ILoggerFactory used to create a logger for logging. If null, no logging will be performed. + private protected OpenAIClientBase(HttpClient? httpClient, ILoggerFactory? loggerFactory = null) { this._httpClient = httpClient ?? new HttpClient(NonDisposableHttpClientHandler.Instance, disposeHandler: false); - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(this.GetType().Name) : NullLogger.Instance; } /// Adds headers to use for OpenAI HTTP requests. @@ -48,7 +47,7 @@ private protected virtual void AddRequestHeaders(HttpRequestMessage request) /// The to monitor for cancellation requests. The default is . /// List of text embeddings /// AIException thrown during the request. - private protected async Task>> ExecuteTextEmbeddingRequestAsync( + private protected async Task>> ExecuteTextEmbeddingRequestAsync( string url, string requestBody, CancellationToken cancellationToken = default) @@ -61,7 +60,7 @@ private protected async Task>> ExecuteTextEmbeddingReques "Embeddings not found"); } - return result.Embeddings.Select(e => new Embedding(e.Values, transferOwnership: true)).ToList(); + return result.Embeddings.Select(e => e.Values).ToList(); } /// diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ImageGeneration/AzureOpenAIImageGeneration.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ImageGeneration/AzureOpenAIImageGeneration.cs index 910ae36dbb99..a2bb898c804f 100644 --- a/dotnet/src/Connectors/Connectors.AI.OpenAI/ImageGeneration/AzureOpenAIImageGeneration.cs +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ImageGeneration/AzureOpenAIImageGeneration.cs @@ -56,10 +56,10 @@ public class AzureOpenAIImageGeneration : OpenAIClientBase, IImageGeneration /// Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart /// Azure OpenAI API key, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart /// Custom for HTTP requests. - /// Application logger + /// The ILoggerFactory used to create a logger for logging. If null, no logging will be performed. /// Maximum number of attempts to retrieve the image generation operation result. /// Azure OpenAI Endpoint ApiVersion - public AzureOpenAIImageGeneration(string endpoint, string apiKey, HttpClient? httpClient = null, ILogger? logger = null, int maxRetryCount = 5, string apiVersion = "2023-06-01-preview") : base(httpClient, logger) + public AzureOpenAIImageGeneration(string endpoint, string apiKey, HttpClient? httpClient = null, ILoggerFactory? loggerFactory = null, int maxRetryCount = 5, string apiVersion = "2023-06-01-preview") : base(httpClient, loggerFactory) { Verify.NotNullOrWhiteSpace(endpoint); Verify.NotNullOrWhiteSpace(apiKey); @@ -77,10 +77,10 @@ public AzureOpenAIImageGeneration(string endpoint, string apiKey, HttpClient? ht /// Azure OpenAI API key, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart /// Custom for HTTP requests. /// Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart - /// Application logger + /// The ILoggerFactory used to create a logger for logging. If null, no logging will be performed. /// Maximum number of attempts to retrieve the image generation operation result. /// Azure OpenAI Endpoint ApiVersion - public AzureOpenAIImageGeneration(string apiKey, HttpClient httpClient, string? endpoint = null, ILogger? logger = null, int maxRetryCount = 5, string apiVersion = "2023-06-01-preview") : base(httpClient, logger) + public AzureOpenAIImageGeneration(string apiKey, HttpClient httpClient, string? endpoint = null, ILoggerFactory? loggerFactory = null, int maxRetryCount = 5, string apiVersion = "2023-06-01-preview") : base(httpClient, loggerFactory) { Verify.NotNull(httpClient); Verify.NotNullOrWhiteSpace(apiKey); @@ -107,14 +107,14 @@ public async Task GenerateImageAsync(string description, int width, int var operationId = await this.StartImageGenerationAsync(description, width, height, cancellationToken).ConfigureAwait(false); var result = await this.GetImageGenerationResultAsync(operationId, cancellationToken).ConfigureAwait(false); - if (result.Result == null) + if (result.Result is null) { - throw new AzureSdk.OpenAIInvalidResponseException(null, "Azure Image Generation null response"); + throw new SKException("Azure Image Generation null response"); } if (result.Result.Images.Count == 0) { - throw new AzureSdk.OpenAIInvalidResponseException(result, "Azure Image Generation result not found"); + throw new SKException("Azure Image Generation result not found"); } return result.Result.Images.First().Url; @@ -184,7 +184,7 @@ private async Task GetImageGenerationResultAsync(s } else if (this.IsFailedOrCancelled(result.Status)) { - throw new AzureSdk.OpenAIInvalidResponseException(result, $"Azure OpenAI image generation {result.Status}"); + throw new SKException($"Azure OpenAI image generation {result.Status}"); } if (response.Headers.TryGetValues("retry-after", out var afterValues) && long.TryParse(afterValues.FirstOrDefault(), out var after)) diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/ImageGeneration/OpenAIImageGeneration.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/ImageGeneration/OpenAIImageGeneration.cs index 2be866699aaf..3697749d85ba 100644 --- a/dotnet/src/Connectors/Connectors.AI.OpenAI/ImageGeneration/OpenAIImageGeneration.cs +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/ImageGeneration/OpenAIImageGeneration.cs @@ -36,13 +36,13 @@ public class OpenAIImageGeneration : OpenAIClientBase, IImageGeneration /// OpenAI API key, see https://platform.openai.com/account/api-keys /// OpenAI organization id. This is usually optional unless your account belongs to multiple organizations. /// Custom for HTTP requests. - /// Application logger + /// The to use for logging. If null, no logging will be performed. public OpenAIImageGeneration( string apiKey, string? organization = null, HttpClient? httpClient = null, - ILogger? logger = null - ) : base(httpClient, logger) + ILoggerFactory? loggerFactory = null + ) : base(httpClient, loggerFactory) { Verify.NotNullOrWhiteSpace(apiKey); this._authorizationHeaderValue = $"Bearer {apiKey}"; diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/OpenAIKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/OpenAIKernelBuilderExtensions.cs index 4fb9417a51c5..828669796c3d 100644 --- a/dotnet/src/Connectors/Connectors.AI.OpenAI/OpenAIKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/OpenAIKernelBuilderExtensions.cs @@ -12,6 +12,7 @@ using Microsoft.SemanticKernel.AI.ImageGeneration; using Microsoft.SemanticKernel.AI.TextCompletion; using Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletion; +using Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletionWithData; using Microsoft.SemanticKernel.Connectors.AI.OpenAI.ImageGeneration; using Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextCompletion; using Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextEmbedding; @@ -49,10 +50,10 @@ public static KernelBuilder WithAzureTextCompletionService(this KernelBuilder bu bool setAsDefault = false, HttpClient? httpClient = null) { - builder.WithAIService(serviceId, (parameters) => + builder.WithAIService(serviceId, (loggerFactory, config) => { - var client = CreateAzureOpenAIClient(parameters.Logger, parameters.Config, deploymentName, endpoint, new AzureKeyCredential(apiKey), httpClient); - return new AzureTextCompletion(deploymentName, client, parameters.Logger); + var client = CreateAzureOpenAIClient(loggerFactory, config, deploymentName, endpoint, new AzureKeyCredential(apiKey), httpClient); + return new AzureTextCompletion(deploymentName, client, loggerFactory); }, setAsDefault); return builder; @@ -78,10 +79,10 @@ public static KernelBuilder WithAzureTextCompletionService(this KernelBuilder bu bool setAsDefault = false, HttpClient? httpClient = null) { - builder.WithAIService(serviceId, (parameters) => + builder.WithAIService(serviceId, (loggerFactory, config) => { - var client = CreateAzureOpenAIClient(parameters.Logger, parameters.Config, deploymentName, endpoint, credentials, httpClient); - return new AzureTextCompletion(deploymentName, client, parameters.Logger); + var client = CreateAzureOpenAIClient(loggerFactory, config, deploymentName, endpoint, credentials, httpClient); + return new AzureTextCompletion(deploymentName, client, loggerFactory); }, setAsDefault); return builder; @@ -103,11 +104,11 @@ public static KernelBuilder WithAzureTextCompletionService(this KernelBuilder bu string? serviceId = null, bool setAsDefault = false) { - builder.WithAIService(serviceId, (parameters) => + builder.WithAIService(serviceId, (loggerFactory, config) => new AzureTextCompletion( deploymentName, openAIClient, - parameters.Logger), + loggerFactory), setAsDefault); return builder; @@ -133,13 +134,13 @@ public static KernelBuilder WithOpenAITextCompletionService(this KernelBuilder b bool setAsDefault = false, HttpClient? httpClient = null) { - builder.WithAIService(serviceId, (parameters) => + builder.WithAIService(serviceId, (loggerFactory, config) => new OpenAITextCompletion( modelId, apiKey, orgId, - HttpClientProvider.GetHttpClient(parameters.Config, httpClient, parameters.Logger), - parameters.Logger), + HttpClientProvider.GetHttpClient(config, httpClient, loggerFactory), + loggerFactory), setAsDefault); return builder; } @@ -168,13 +169,13 @@ public static KernelBuilder WithAzureTextEmbeddingGenerationService(this KernelB bool setAsDefault = false, HttpClient? httpClient = null) { - builder.WithAIService(serviceId, (parameters) => + builder.WithAIService(serviceId, (loggerFactory, config) => new AzureTextEmbeddingGeneration( deploymentName, endpoint, apiKey, - HttpClientProvider.GetHttpClient(parameters.Config, httpClient, parameters.Logger), - parameters.Logger), + HttpClientProvider.GetHttpClient(config, httpClient, loggerFactory), + loggerFactory), setAsDefault); return builder; } @@ -199,13 +200,13 @@ public static KernelBuilder WithAzureTextEmbeddingGenerationService(this KernelB bool setAsDefault = false, HttpClient? httpClient = null) { - builder.WithAIService(serviceId, (parameters) => + builder.WithAIService(serviceId, (loggerFactory, config) => new AzureTextEmbeddingGeneration( deploymentName, endpoint, credential, - HttpClientProvider.GetHttpClient(parameters.Config, httpClient, parameters.Logger), - parameters.Logger), + HttpClientProvider.GetHttpClient(config, httpClient, loggerFactory), + loggerFactory), setAsDefault); return builder; } @@ -230,13 +231,13 @@ public static KernelBuilder WithOpenAITextEmbeddingGenerationService(this Kernel bool setAsDefault = false, HttpClient? httpClient = null) { - builder.WithAIService(serviceId, (parameters) => + builder.WithAIService(serviceId, (loggerFactory, config) => new OpenAITextEmbeddingGeneration( modelId, apiKey, orgId, - HttpClientProvider.GetHttpClient(parameters.Config, httpClient, parameters.Logger), - parameters.Logger), + HttpClientProvider.GetHttpClient(config, httpClient, loggerFactory), + loggerFactory), setAsDefault); return builder; } @@ -267,11 +268,11 @@ public static KernelBuilder WithAzureChatCompletionService(this KernelBuilder bu bool setAsDefault = false, HttpClient? httpClient = null) { - AzureChatCompletion Factory((ILogger Logger, KernelConfig Config) parameters) + AzureChatCompletion Factory(ILoggerFactory loggerFactory, KernelConfig config) { - OpenAIClient client = CreateAzureOpenAIClient(parameters.Logger, parameters.Config, deploymentName, endpoint, new AzureKeyCredential(apiKey), httpClient); + OpenAIClient client = CreateAzureOpenAIClient(loggerFactory, config, deploymentName, endpoint, new AzureKeyCredential(apiKey), httpClient); - return new(deploymentName, client, parameters.Logger); + return new(deploymentName, client, loggerFactory); }; builder.WithAIService(serviceId, Factory, setAsDefault); @@ -307,11 +308,11 @@ public static KernelBuilder WithAzureChatCompletionService(this KernelBuilder bu bool setAsDefault = false, HttpClient? httpClient = null) { - AzureChatCompletion Factory((ILogger Logger, KernelConfig Config) parameters) + AzureChatCompletion Factory(ILoggerFactory loggerFactory, KernelConfig config) { - OpenAIClient client = CreateAzureOpenAIClient(parameters.Logger, parameters.Config, deploymentName, endpoint, credentials, httpClient); + OpenAIClient client = CreateAzureOpenAIClient(loggerFactory, config, deploymentName, endpoint, credentials, httpClient); - return new(deploymentName, client, parameters.Logger); + return new(deploymentName, client, loggerFactory); }; builder.WithAIService(serviceId, Factory, setAsDefault); @@ -325,6 +326,39 @@ AzureChatCompletion Factory((ILogger Logger, KernelConfig Config) parameters) return builder; } + /// + /// Adds the Azure OpenAI chat completion with data service to the list. + /// More information: + /// + /// The instance. + /// Required configuration for Azure OpenAI chat completion with data. + /// Whether to use the service also for text completion, if supported. + /// A local identifier for the given AI service. + /// Whether the service should be the default for its type. + /// Custom for HTTP requests. + /// Self instance + public static KernelBuilder WithAzureChatCompletionService(this KernelBuilder builder, + AzureChatCompletionWithDataConfig config, + bool alsoAsTextCompletion = true, + string? serviceId = null, + bool setAsDefault = false, + HttpClient? httpClient = null) + { + AzureChatCompletionWithData Factory(ILoggerFactory loggerFactory, KernelConfig kernelConfig) => new( + config, + HttpClientProvider.GetHttpClient(kernelConfig, httpClient, loggerFactory), + loggerFactory); + + builder.WithAIService(serviceId, Factory, setAsDefault); + + if (alsoAsTextCompletion && typeof(ITextCompletion).IsAssignableFrom(typeof(AzureChatCompletionWithData))) + { + builder.WithAIService(serviceId, Factory, setAsDefault); + } + + return builder; + } + /// /// Adds the OpenAI ChatGPT completion service to the list. /// See https://platform.openai.com/docs for service details. @@ -347,12 +381,12 @@ public static KernelBuilder WithOpenAIChatCompletionService(this KernelBuilder b bool setAsDefault = false, HttpClient? httpClient = null) { - OpenAIChatCompletion Factory((ILogger Logger, KernelConfig Config) parameters) => new( + OpenAIChatCompletion Factory(ILoggerFactory loggerFactory, KernelConfig config) => new( modelId, apiKey, orgId, - HttpClientProvider.GetHttpClient(parameters.Config, httpClient, parameters.Logger), - parameters.Logger); + HttpClientProvider.GetHttpClient(config, httpClient, loggerFactory), + loggerFactory); builder.WithAIService(serviceId, Factory, setAsDefault); @@ -383,9 +417,9 @@ public static KernelBuilder WithAzureChatCompletionService(this KernelBuilder bu string? serviceId = null, bool setAsDefault = false) { - AzureChatCompletion Factory((ILogger Logger, KernelConfig Config) parameters) + AzureChatCompletion Factory(ILoggerFactory loggerFactory, KernelConfig config) { - return new(deploymentName, openAIClient, parameters.Logger); + return new(deploymentName, openAIClient, loggerFactory); }; builder.WithAIService(serviceId, Factory, setAsDefault); @@ -417,9 +451,9 @@ public static KernelBuilder WithOpenAIChatCompletionService(this KernelBuilder b string? serviceId = null, bool setAsDefault = false) { - OpenAIChatCompletion Factory((ILogger Logger, KernelConfig Config) parameters) + OpenAIChatCompletion Factory(ILoggerFactory loggerFactory, KernelConfig config) { - return new(deploymentName, openAIClient, parameters.Logger); + return new(deploymentName, openAIClient, loggerFactory); }; builder.WithAIService(serviceId, Factory, setAsDefault); @@ -454,12 +488,12 @@ public static KernelBuilder WithOpenAIImageGenerationService(this KernelBuilder bool setAsDefault = false, HttpClient? httpClient = null) { - builder.WithAIService(serviceId, ((ILogger Logger, KernelConfig Config) parameters) => + builder.WithAIService(serviceId, (ILoggerFactory loggerFactory, KernelConfig config) => new OpenAIImageGeneration( apiKey, orgId, - HttpClientProvider.GetHttpClient(parameters.Config, httpClient, parameters.Logger), - parameters.Logger), + HttpClientProvider.GetHttpClient(config, httpClient, loggerFactory), + loggerFactory), setAsDefault); return builder; @@ -484,12 +518,12 @@ public static KernelBuilder WithAzureOpenAIImageGenerationService(this KernelBui HttpClient? httpClient = null, int maxRetryCount = 5) { - builder.WithAIService(serviceId, ((ILogger Logger, KernelConfig Config) parameters) => + builder.WithAIService(serviceId, (ILoggerFactory loggerFactory, KernelConfig config) => new AzureOpenAIImageGeneration( endpoint, apiKey, - HttpClientProvider.GetHttpClient(parameters.Config, httpClient, parameters.Logger), - parameters.Logger, + HttpClientProvider.GetHttpClient(config, httpClient, loggerFactory), + loggerFactory, maxRetryCount), setAsDefault); @@ -498,25 +532,25 @@ public static KernelBuilder WithAzureOpenAIImageGenerationService(this KernelBui #endregion - private static OpenAIClient CreateAzureOpenAIClient(ILogger logger, KernelConfig config, string deploymentName, string endpoint, AzureKeyCredential credentials, HttpClient? httpClient) + private static OpenAIClient CreateAzureOpenAIClient(ILoggerFactory loggerFactory, KernelConfig config, string deploymentName, string endpoint, AzureKeyCredential credentials, HttpClient? httpClient) { - OpenAIClientOptions options = CreateOpenAIClientOptions(logger, config, httpClient); + OpenAIClientOptions options = CreateOpenAIClientOptions(loggerFactory, config, httpClient); return new(new Uri(endpoint), credentials, options); } - private static OpenAIClient CreateAzureOpenAIClient(ILogger logger, KernelConfig config, string deploymentName, string endpoint, TokenCredential credentials, HttpClient? httpClient) + private static OpenAIClient CreateAzureOpenAIClient(ILoggerFactory loggerFactory, KernelConfig config, string deploymentName, string endpoint, TokenCredential credentials, HttpClient? httpClient) { - OpenAIClientOptions options = CreateOpenAIClientOptions(logger, config, httpClient); + OpenAIClientOptions options = CreateOpenAIClientOptions(loggerFactory, config, httpClient); return new(new Uri(endpoint), credentials, options); } - private static OpenAIClientOptions CreateOpenAIClientOptions(ILogger logger, KernelConfig config, HttpClient? httpClient) + private static OpenAIClientOptions CreateOpenAIClientOptions(ILoggerFactory loggerFactory, KernelConfig config, HttpClient? httpClient) { OpenAIClientOptions options = new(); #pragma warning disable CA2000 // Dispose objects before losing scope - options.Transport = new HttpClientTransport(HttpClientProvider.GetHttpClient(config, httpClient, logger)); + options.Transport = new HttpClientTransport(HttpClientProvider.GetHttpClient(config, httpClient, loggerFactory)); #pragma warning restore CA2000 // Dispose objects before losing scope if (config.HttpHandlerFactory is DefaultHttpRetryHandlerFactory factory && factory.Config is not null) diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/TextCompletion/AzureTextCompletion.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/TextCompletion/AzureTextCompletion.cs index 26350604742c..b7d1eded2518 100644 --- a/dotnet/src/Connectors/Connectors.AI.OpenAI/TextCompletion/AzureTextCompletion.cs +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/TextCompletion/AzureTextCompletion.cs @@ -25,13 +25,13 @@ public sealed class AzureTextCompletion : AzureOpenAIClientBase, ITextCompletion /// Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart /// Azure OpenAI API key, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart /// Custom for HTTP requests. - /// Application logger + /// The to use for logging. If null, no logging will be performed. public AzureTextCompletion( string modelId, string endpoint, string apiKey, HttpClient? httpClient = null, - ILogger? logger = null) : base(modelId, endpoint, apiKey, httpClient, logger) + ILoggerFactory? loggerFactory = null) : base(modelId, endpoint, apiKey, httpClient, loggerFactory) { } @@ -42,13 +42,13 @@ public AzureTextCompletion( /// Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart /// Token credentials, e.g. DefaultAzureCredential, ManagedIdentityCredential, EnvironmentCredential, etc. /// Custom for HTTP requests. - /// Application logger + /// The to use for logging. If null, no logging will be performed. public AzureTextCompletion( string modelId, string endpoint, TokenCredential credential, HttpClient? httpClient = null, - ILogger? logger = null) : base(modelId, endpoint, credential, httpClient, logger) + ILoggerFactory? loggerFactory = null) : base(modelId, endpoint, credential, httpClient, loggerFactory) { } @@ -57,11 +57,11 @@ public AzureTextCompletion( /// /// Azure OpenAI model ID or deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource /// Custom . - /// Application logger + /// The to use for logging. If null, no logging will be performed. public AzureTextCompletion( string modelId, OpenAIClient openAIClient, - ILogger? logger = null) : base(modelId, openAIClient, logger) + ILoggerFactory? loggerFactory = null) : base(modelId, openAIClient, loggerFactory) { } diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/TextCompletion/OpenAITextCompletion.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/TextCompletion/OpenAITextCompletion.cs index 0c9bcc0f28e4..86b6b4b53849 100644 --- a/dotnet/src/Connectors/Connectors.AI.OpenAI/TextCompletion/OpenAITextCompletion.cs +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/TextCompletion/OpenAITextCompletion.cs @@ -23,14 +23,14 @@ public sealed class OpenAITextCompletion : OpenAIClientBase, ITextCompletion /// OpenAI API Key /// OpenAI Organization Id (usually optional) /// Custom for HTTP requests. - /// Application logger + /// The to use for logging. If null, no logging will be performed. public OpenAITextCompletion( string modelId, string apiKey, string? organization = null, HttpClient? httpClient = null, - ILogger? logger = null - ) : base(modelId, apiKey, organization, httpClient, logger) + ILoggerFactory? loggerFactory = null + ) : base(modelId, apiKey, organization, httpClient, loggerFactory) { } diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/AzureTextEmbeddingGeneration.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/AzureTextEmbeddingGeneration.cs index 3f7b50959610..ceed977b10a0 100644 --- a/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/AzureTextEmbeddingGeneration.cs +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/AzureTextEmbeddingGeneration.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Net.Http; using System.Threading; @@ -23,13 +24,13 @@ public sealed class AzureTextEmbeddingGeneration : AzureOpenAIClientBase, ITextE /// Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart /// Azure OpenAI API key, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart /// Custom for HTTP requests. - /// Application logger + /// The to use for logging. If null, no logging will be performed. public AzureTextEmbeddingGeneration( string modelId, string endpoint, string apiKey, HttpClient? httpClient = null, - ILogger? logger = null) : base(modelId, endpoint, apiKey, httpClient, logger) + ILoggerFactory? loggerFactory = null) : base(modelId, endpoint, apiKey, httpClient, loggerFactory) { } @@ -40,13 +41,13 @@ public AzureTextEmbeddingGeneration( /// Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart /// Token credentials, e.g. DefaultAzureCredential, ManagedIdentityCredential, EnvironmentCredential, etc. /// Custom for HTTP requests. - /// Application logger + /// The to use for logging. If null, no logging will be performed. public AzureTextEmbeddingGeneration( string modelId, string endpoint, TokenCredential credential, HttpClient? httpClient = null, - ILogger? logger = null) : base(modelId, endpoint, credential, httpClient, logger) + ILoggerFactory? loggerFactory = null) : base(modelId, endpoint, credential, httpClient, loggerFactory) { } @@ -56,7 +57,7 @@ public AzureTextEmbeddingGeneration( /// List of strings to generate embeddings for /// The to monitor for cancellation requests. The default is . /// List of embeddings - public Task>> GenerateEmbeddingsAsync( + public Task>> GenerateEmbeddingsAsync( IList data, CancellationToken cancellationToken = default) { diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/OpenAITextEmbeddingGeneration.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/OpenAITextEmbeddingGeneration.cs index 4f41aae08b9f..1d19b0b546d5 100644 --- a/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/OpenAITextEmbeddingGeneration.cs +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/OpenAITextEmbeddingGeneration.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Net.Http; using System.Threading; @@ -22,14 +23,14 @@ public sealed class OpenAITextEmbeddingGeneration : OpenAIClientBase, ITextEmbed /// OpenAI API Key /// OpenAI Organization Id (usually optional) /// Custom for HTTP requests. - /// Application logger + /// The to use for logging. If null, no logging will be performed. public OpenAITextEmbeddingGeneration( string modelId, string apiKey, string? organization = null, HttpClient? httpClient = null, - ILogger? logger = null - ) : base(modelId, apiKey, organization, httpClient, logger) + ILoggerFactory? loggerFactory = null + ) : base(modelId, apiKey, organization, httpClient, loggerFactory) { } @@ -39,7 +40,7 @@ public OpenAITextEmbeddingGeneration( /// List of strings to generate embeddings for /// The to monitor for cancellation requests. The default is . /// List of embeddings - public Task>> GenerateEmbeddingsAsync( + public Task>> GenerateEmbeddingsAsync( IList data, CancellationToken cancellationToken = default) { diff --git a/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/TextEmbeddingResponse.cs b/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/TextEmbeddingResponse.cs index 0e6db886925f..1dcdca9841e0 100644 --- a/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/TextEmbeddingResponse.cs +++ b/dotnet/src/Connectors/Connectors.AI.OpenAI/TextEmbedding/TextEmbeddingResponse.cs @@ -1,7 +1,9 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Text.Json.Serialization; +using Microsoft.SemanticKernel.Text; namespace Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextEmbedding; @@ -19,7 +21,8 @@ public sealed class EmbeddingResponseIndex /// The embedding vector /// [JsonPropertyName("embedding")] - public IList Values { get; set; } = new List(); + [JsonConverter(typeof(ReadOnlyMemoryConverter))] + public ReadOnlyMemory Values { get; set; } /// /// Index of the embedding vector diff --git a/dotnet/src/Connectors/Connectors.Memory.AzureCognitiveSearch/AzureCognitiveSearchMemoryRecord.cs b/dotnet/src/Connectors/Connectors.Memory.AzureCognitiveSearch/AzureCognitiveSearchMemoryRecord.cs index 40ece7daea7f..73cd2386ef49 100644 --- a/dotnet/src/Connectors/Connectors.Memory.AzureCognitiveSearch/AzureCognitiveSearchMemoryRecord.cs +++ b/dotnet/src/Connectors/Connectors.Memory.AzureCognitiveSearch/AzureCognitiveSearchMemoryRecord.cs @@ -1,12 +1,10 @@ // Copyright (c) Microsoft. All rights reserved. using System; -using System.Collections.Generic; -using System.Linq; using System.Text; using System.Text.Json.Serialization; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Memory; +using Microsoft.SemanticKernel.Text; namespace Microsoft.SemanticKernel.Connectors.Memory.AzureCognitiveSearch; @@ -41,7 +39,8 @@ public class AzureCognitiveSearchMemoryRecord /// Content embedding /// [JsonPropertyName(EmbeddingField)] - public List Embedding { get; set; } = Array.Empty().ToList(); + [JsonConverter(typeof(ReadOnlyMemoryConverter))] + public ReadOnlyMemory Embedding { get; set; } /// /// Optional description of the content, e.g. a title. This can be useful when @@ -87,13 +86,13 @@ public AzureCognitiveSearchMemoryRecord( string text, string externalSourceName, bool isReference, - Embedding embedding, + ReadOnlyMemory embedding, string? description = null, string? additionalMetadata = null) { this.Id = EncodeId(id); this.IsReference = isReference; - this.Embedding = embedding.Vector.ToList(); + this.Embedding = embedding; this.Text = text; this.ExternalSourceName = externalSourceName; this.Description = description; @@ -128,7 +127,7 @@ public MemoryRecord ToMemoryRecord(bool withEmbeddings = true) { return new MemoryRecord( metadata: this.ToMemoryRecordMetadata(), - embedding: new Embedding(withEmbeddings ? this.Embedding : Array.Empty()), + embedding: withEmbeddings ? this.Embedding : default, key: this.Id); } diff --git a/dotnet/src/Connectors/Connectors.Memory.AzureCognitiveSearch/AzureCognitiveSearchMemoryStore.cs b/dotnet/src/Connectors/Connectors.Memory.AzureCognitiveSearch/AzureCognitiveSearchMemoryStore.cs index 76a3c74a42dd..a74308ce44c2 100644 --- a/dotnet/src/Connectors/Connectors.Memory.AzureCognitiveSearch/AzureCognitiveSearchMemoryStore.cs +++ b/dotnet/src/Connectors/Connectors.Memory.AzureCognitiveSearch/AzureCognitiveSearchMemoryStore.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -14,7 +15,6 @@ using Azure.Search.Documents.Indexes; using Azure.Search.Documents.Indexes.Models; using Azure.Search.Documents.Models; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; @@ -134,7 +134,7 @@ public async IAsyncEnumerable GetBatchAsync( /// public async Task<(MemoryRecord, double)?> GetNearestMatchAsync( string collectionName, - Embedding embedding, + ReadOnlyMemory embedding, double minRelevanceScore = 0, bool withEmbedding = false, CancellationToken cancellationToken = default) @@ -147,7 +147,7 @@ public async IAsyncEnumerable GetBatchAsync( /// public async IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesAsync( string collectionName, - Embedding embedding, + ReadOnlyMemory embedding, int limit, double minRelevanceScore = 0, bool withEmbeddings = false, @@ -161,7 +161,7 @@ public async IAsyncEnumerable GetBatchAsync( { KNearestNeighborsCount = limit, Fields = AzureCognitiveSearchMemoryRecord.EmbeddingField, - Value = embedding.Vector.ToList() + Value = MemoryMarshal.TryGetArray(embedding, out var array) && array.Count == embedding.Length ? array.Array! : embedding.ToArray(), }; SearchOptions options = new() { Vector = vectorQuery }; @@ -305,7 +305,7 @@ private async Task> UpsertBatchAsync( if (records.Count < 1) { return keys; } - var embeddingSize = records[0].Embedding.Count; + var embeddingSize = records[0].Embedding.Length; var client = this.GetSearchClient(indexName); diff --git a/dotnet/src/Connectors/Connectors.Memory.Chroma/ChromaClient.cs b/dotnet/src/Connectors/Connectors.Memory.Chroma/ChromaClient.cs index f431a41bbabf..41771ec181de 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Chroma/ChromaClient.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Chroma/ChromaClient.cs @@ -28,12 +28,12 @@ public class ChromaClient : IChromaClient /// Initializes a new instance of the class. /// /// Chroma server endpoint URL. - /// Optional logger instance. - public ChromaClient(string endpoint, ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public ChromaClient(string endpoint, ILoggerFactory? loggerFactory = null) { this._httpClient = new HttpClient(NonDisposableHttpClientHandler.Instance, disposeHandler: false); this._endpoint = endpoint; - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(ChromaClient)) : NullLogger.Instance; } /// @@ -41,9 +41,9 @@ public ChromaClient(string endpoint, ILogger? logger = null) /// /// The instance used for making HTTP requests. /// Chroma server endpoint URL. - /// Optional logger instance. + /// The to use for logging. If null, no logging will be performed. /// Occurs when doesn't have base address and endpoint parameter is not provided. - public ChromaClient(HttpClient httpClient, string? endpoint = null, ILogger? logger = null) + public ChromaClient(HttpClient httpClient, string? endpoint = null, ILoggerFactory? loggerFactory = null) { if (string.IsNullOrEmpty(httpClient.BaseAddress?.AbsoluteUri) && string.IsNullOrEmpty(endpoint)) { @@ -52,7 +52,7 @@ public ChromaClient(HttpClient httpClient, string? endpoint = null, ILogger? log this._httpClient = httpClient; this._endpoint = endpoint; - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(ChromaClient)) : NullLogger.Instance; } /// @@ -107,7 +107,7 @@ public async IAsyncEnumerable ListCollectionsAsync([EnumeratorCancellati } /// - public async Task UpsertEmbeddingsAsync(string collectionId, string[] ids, float[][] embeddings, object[]? metadatas = null, CancellationToken cancellationToken = default) + public async Task UpsertEmbeddingsAsync(string collectionId, string[] ids, ReadOnlyMemory[] embeddings, object[]? metadatas = null, CancellationToken cancellationToken = default) { this._logger.LogDebug("Upserting embeddings to collection with id: {0}", collectionId); @@ -141,7 +141,7 @@ public async Task DeleteEmbeddingsAsync(string collectionId, string[] ids, Cance } /// - public async Task QueryEmbeddingsAsync(string collectionId, float[][] queryEmbeddings, int nResults, string[]? include = null, CancellationToken cancellationToken = default) + public async Task QueryEmbeddingsAsync(string collectionId, ReadOnlyMemory[] queryEmbeddings, int nResults, string[]? include = null, CancellationToken cancellationToken = default) { this._logger.LogDebug("Query embeddings in collection with id: {0}", collectionId); diff --git a/dotnet/src/Connectors/Connectors.Memory.Chroma/ChromaKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.Chroma/ChromaKernelBuilderExtensions.cs index f020d6c1905e..f232916d2e93 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Chroma/ChromaKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Chroma/ChromaKernelBuilderExtensions.cs @@ -20,12 +20,12 @@ public static class ChromaKernelBuilderExtensions /// Self instance. public static KernelBuilder WithChromaMemoryStore(this KernelBuilder builder, string endpoint) { - builder.WithMemoryStorage((parameters) => + builder.WithMemoryStorage((loggerFactory, config) => { return new ChromaMemoryStore( - HttpClientProvider.GetHttpClient(parameters.Config, null, parameters.Logger), + HttpClientProvider.GetHttpClient(config, null, loggerFactory), endpoint, - parameters.Logger); + loggerFactory); }); return builder; @@ -42,12 +42,12 @@ public static KernelBuilder WithChromaMemoryStore(this KernelBuilder builder, HttpClient httpClient, string? endpoint = null) { - builder.WithMemoryStorage((parameters) => + builder.WithMemoryStorage((loggerFactory, config) => { return new ChromaMemoryStore( - HttpClientProvider.GetHttpClient(parameters.Config, httpClient, parameters.Logger), + HttpClientProvider.GetHttpClient(config, httpClient, loggerFactory), endpoint, - parameters.Logger); + loggerFactory); }); return builder; diff --git a/dotnet/src/Connectors/Connectors.Memory.Chroma/ChromaMemoryStore.cs b/dotnet/src/Connectors/Connectors.Memory.Chroma/ChromaMemoryStore.cs index f68850e52b73..f83703453c65 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Chroma/ChromaMemoryStore.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Chroma/ChromaMemoryStore.cs @@ -11,10 +11,10 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Connectors.Memory.Chroma.Http.ApiSchema; using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; +using Microsoft.SemanticKernel.Text; namespace Microsoft.SemanticKernel.Connectors.Memory.Chroma; @@ -27,9 +27,9 @@ public class ChromaMemoryStore : IMemoryStore /// Initializes a new instance of the class. /// /// Chroma server endpoint URL. - /// Optional logger instance. - public ChromaMemoryStore(string endpoint, ILogger? logger = null) - : this(new ChromaClient(endpoint, logger), logger) + /// The to use for logging. If null, no logging will be performed. + public ChromaMemoryStore(string endpoint, ILoggerFactory? loggerFactory = null) + : this(new ChromaClient(endpoint, loggerFactory), loggerFactory) { } @@ -38,9 +38,9 @@ public ChromaMemoryStore(string endpoint, ILogger? logger = null) /// /// The instance used for making HTTP requests. /// Chroma server endpoint URL. - /// Optional logger instance. - public ChromaMemoryStore(HttpClient httpClient, string? endpoint = null, ILogger? logger = null) - : this(new ChromaClient(httpClient, endpoint, logger), logger) + /// The to use for logging. If null, no logging will be performed. + public ChromaMemoryStore(HttpClient httpClient, string? endpoint = null, ILoggerFactory? loggerFactory = null) + : this(new ChromaClient(httpClient, endpoint, loggerFactory), loggerFactory) { } @@ -48,11 +48,11 @@ public ChromaMemoryStore(HttpClient httpClient, string? endpoint = null, ILogger /// Initializes a new instance of the class. /// /// Instance of implementation. - /// Optional logger instance. - public ChromaMemoryStore(IChromaClient client, ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public ChromaMemoryStore(IChromaClient client, ILoggerFactory? loggerFactory = null) { this._chromaClient = client; - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(ChromaMemoryStore)) : NullLogger.Instance; } /// @@ -124,7 +124,7 @@ public IAsyncEnumerable GetCollectionsAsync(CancellationToken cancellati } /// - public async Task<(MemoryRecord, double)?> GetNearestMatchAsync(string collectionName, Embedding embedding, double minRelevanceScore = 0, bool withEmbedding = false, CancellationToken cancellationToken = default) + public async Task<(MemoryRecord, double)?> GetNearestMatchAsync(string collectionName, ReadOnlyMemory embedding, double minRelevanceScore = 0, bool withEmbedding = false, CancellationToken cancellationToken = default) { var results = this.GetNearestMatchesAsync( collectionName, @@ -140,13 +140,13 @@ public IAsyncEnumerable GetCollectionsAsync(CancellationToken cancellati } /// - public async IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesAsync(string collectionName, Embedding embedding, int limit, double minRelevanceScore = 0, bool withEmbeddings = false, [EnumeratorCancellation] CancellationToken cancellationToken = default) + public async IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesAsync(string collectionName, ReadOnlyMemory embedding, int limit, double minRelevanceScore = 0, bool withEmbeddings = false, [EnumeratorCancellation] CancellationToken cancellationToken = default) { Verify.NotNullOrWhiteSpace(collectionName); var collection = await this.GetCollectionOrThrowAsync(collectionName, cancellationToken).ConfigureAwait(false); - var queryEmbeddings = new float[][] { embedding.Vector.ToArray() }; + var queryEmbeddings = new[] { embedding }; var nResults = limit; var include = this.GetEmbeddingIncludeTypes(withEmbeddings: withEmbeddings, withDistances: true); @@ -204,13 +204,13 @@ public async IAsyncEnumerable UpsertBatchAsync(string collectionName, IE var recordsLength = recordsArray.Length; var ids = new string[recordsLength]; - var embeddings = new float[recordsLength][]; + var embeddings = new ReadOnlyMemory[recordsLength]; var metadatas = new object[recordsLength]; for (var i = 0; i < recordsLength; i++) { ids[i] = recordsArray[i].Metadata.Id; - embeddings[i] = recordsArray[i].Embedding.Vector.ToArray(); + embeddings[i] = recordsArray[i].Embedding; metadatas[i] = recordsArray[i].Metadata; } @@ -232,9 +232,13 @@ public async IAsyncEnumerable UpsertBatchAsync(string collectionName, IE private readonly IChromaClient _chromaClient; private readonly List _defaultEmbeddingIncludeTypes = new() { IncludeMetadatas }; - private readonly JsonSerializerOptions _jsonSerializerOptions = new() + internal static readonly JsonSerializerOptions s_jsonSerializerOptions = new() { - Converters = { new ChromaBooleanConverter() } + Converters = + { + new ChromaBooleanConverter(), + new ReadOnlyMemoryConverter(), + } }; private async Task GetCollectionOrThrowAsync(string collectionName, CancellationToken cancellationToken) @@ -307,16 +311,16 @@ private MemoryRecord GetMemoryRecordFromModel(List>? private MemoryRecordMetadata GetMetadataForMemoryRecord(List>? metadatas, int recordIndex) { - var serializedMetadata = metadatas != null ? JsonSerializer.Serialize(metadatas[recordIndex]) : string.Empty; + var serializedMetadata = metadatas != null ? JsonSerializer.Serialize(metadatas[recordIndex], s_jsonSerializerOptions) : string.Empty; return - JsonSerializer.Deserialize(serializedMetadata, this._jsonSerializerOptions) ?? + JsonSerializer.Deserialize(serializedMetadata, ChromaMemoryStore.s_jsonSerializerOptions) ?? throw new SKException("Unable to deserialize memory record metadata."); } - private Embedding GetEmbeddingForMemoryRecord(List? embeddings, int recordIndex) + private ReadOnlyMemory GetEmbeddingForMemoryRecord(List? embeddings, int recordIndex) { - return embeddings != null ? new Embedding(embeddings[recordIndex]) : Embedding.Empty; + return embeddings != null ? embeddings[recordIndex] : ReadOnlyMemory.Empty; } private double GetSimilarityScore(List? distances, int recordIndex) diff --git a/dotnet/src/Connectors/Connectors.Memory.Chroma/Http/ApiSchema/Internal/QueryEmbeddingsRequest.cs b/dotnet/src/Connectors/Connectors.Memory.Chroma/Http/ApiSchema/Internal/QueryEmbeddingsRequest.cs index 47c8c5486916..d350d6a14a8f 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Chroma/Http/ApiSchema/Internal/QueryEmbeddingsRequest.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Chroma/Http/ApiSchema/Internal/QueryEmbeddingsRequest.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Net.Http; using System.Text.Json.Serialization; @@ -11,7 +12,7 @@ internal sealed class QueryEmbeddingsRequest public string CollectionId { get; set; } [JsonPropertyName("query_embeddings")] - public float[][] QueryEmbeddings { get; set; } + public ReadOnlyMemory[] QueryEmbeddings { get; set; } [JsonPropertyName("n_results")] public int NResults { get; set; } @@ -19,7 +20,7 @@ internal sealed class QueryEmbeddingsRequest [JsonPropertyName("include")] public string[]? Include { get; set; } - public static QueryEmbeddingsRequest Create(string collectionId, float[][] queryEmbeddings, int nResults, string[]? include = null) + public static QueryEmbeddingsRequest Create(string collectionId, ReadOnlyMemory[] queryEmbeddings, int nResults, string[]? include = null) { return new QueryEmbeddingsRequest(collectionId, queryEmbeddings, nResults, include); } @@ -31,7 +32,7 @@ public HttpRequestMessage Build() #region private ================================================================================ - private QueryEmbeddingsRequest(string collectionId, float[][] queryEmbeddings, int nResults, string[]? include = null) + private QueryEmbeddingsRequest(string collectionId, ReadOnlyMemory[] queryEmbeddings, int nResults, string[]? include = null) { this.CollectionId = collectionId; this.QueryEmbeddings = queryEmbeddings; diff --git a/dotnet/src/Connectors/Connectors.Memory.Chroma/Http/ApiSchema/Internal/UpsertEmbeddingsRequest.cs b/dotnet/src/Connectors/Connectors.Memory.Chroma/Http/ApiSchema/Internal/UpsertEmbeddingsRequest.cs index bda2ca179c50..354c808f7bd0 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Chroma/Http/ApiSchema/Internal/UpsertEmbeddingsRequest.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Chroma/Http/ApiSchema/Internal/UpsertEmbeddingsRequest.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Net.Http; using System.Text.Json.Serialization; @@ -14,12 +15,12 @@ internal sealed class UpsertEmbeddingsRequest public string[] Ids { get; set; } [JsonPropertyName("embeddings")] - public float[][] Embeddings { get; set; } + public ReadOnlyMemory[] Embeddings { get; set; } [JsonPropertyName("metadatas")] public object[]? Metadatas { get; set; } - public static UpsertEmbeddingsRequest Create(string collectionId, string[] ids, float[][] embeddings, object[]? metadatas = null) + public static UpsertEmbeddingsRequest Create(string collectionId, string[] ids, ReadOnlyMemory[] embeddings, object[]? metadatas = null) { return new UpsertEmbeddingsRequest(collectionId, ids, embeddings, metadatas); } @@ -31,7 +32,7 @@ public HttpRequestMessage Build() #region private ================================================================================ - private UpsertEmbeddingsRequest(string collectionId, string[] ids, float[][] embeddings, object[]? metadatas = null) + private UpsertEmbeddingsRequest(string collectionId, string[] ids, ReadOnlyMemory[] embeddings, object[]? metadatas = null) { this.CollectionId = collectionId; this.Ids = ids; diff --git a/dotnet/src/Connectors/Connectors.Memory.Chroma/IChromaClient.cs b/dotnet/src/Connectors/Connectors.Memory.Chroma/IChromaClient.cs index 9260bde408f3..e650c0b1388d 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Chroma/IChromaClient.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Chroma/IChromaClient.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -49,7 +50,7 @@ public interface IChromaClient /// Array of embedding vectors. /// Array of embedding metadatas. /// The to monitor for cancellation requests. The default is . - Task UpsertEmbeddingsAsync(string collectionId, string[] ids, float[][] embeddings, object[]? metadatas = null, CancellationToken cancellationToken = default); + Task UpsertEmbeddingsAsync(string collectionId, string[] ids, ReadOnlyMemory[] embeddings, object[]? metadatas = null, CancellationToken cancellationToken = default); /// /// Returns embeddings from specified collection. @@ -78,5 +79,5 @@ public interface IChromaClient /// Array of entities to include in response (e.g. "embeddings", "metadatas" "documents"). For more information see: https://github.com/chroma-core/chroma/blob/main/chromadb/api/types.py /// The to monitor for cancellation requests. The default is . /// Instance of model. - Task QueryEmbeddingsAsync(string collectionId, float[][] queryEmbeddings, int nResults, string[]? include = null, CancellationToken cancellationToken = default); + Task QueryEmbeddingsAsync(string collectionId, ReadOnlyMemory[] queryEmbeddings, int nResults, string[]? include = null, CancellationToken cancellationToken = default); } diff --git a/dotnet/src/Connectors/Connectors.Memory.DuckDB/DuckDBMemoryStore.cs b/dotnet/src/Connectors/Connectors.Memory.DuckDB/DuckDBMemoryStore.cs index 3e79b72d1be9..27a6630666f4 100644 --- a/dotnet/src/Connectors/Connectors.Memory.DuckDB/DuckDBMemoryStore.cs +++ b/dotnet/src/Connectors/Connectors.Memory.DuckDB/DuckDBMemoryStore.cs @@ -9,10 +9,10 @@ using System.Threading; using System.Threading.Tasks; using DuckDB.NET.Data; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.AI.Embeddings.VectorOperations; using Microsoft.SemanticKernel.Memory; using Microsoft.SemanticKernel.Memory.Collections; +using Microsoft.SemanticKernel.Text; namespace Microsoft.SemanticKernel.Connectors.Memory.DuckDB; @@ -141,7 +141,7 @@ public async Task RemoveBatchAsync(string collectionName, IEnumerable ke /// public async IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesAsync( string collectionName, - Embedding embedding, + ReadOnlyMemory embedding, int limit, double minRelevanceScore = 0, bool withEmbeddings = false, @@ -160,11 +160,11 @@ public async Task RemoveBatchAsync(string collectionName, IEnumerable ke if (record != null) { double similarity = embedding - .AsReadOnlySpan() - .CosineSimilarity(record.Embedding.AsReadOnlySpan()); + .Span + .CosineSimilarity(record.Embedding.Span); if (similarity >= minRelevanceScore) { - var entry = withEmbeddings ? record : MemoryRecord.FromMetadata(record.Metadata, Embedding.Empty, record.Key, record.Timestamp); + var entry = withEmbeddings ? record : MemoryRecord.FromMetadata(record.Metadata, ReadOnlyMemory.Empty, record.Key, record.Timestamp); embeddings.Add(new(entry, similarity)); } } @@ -179,7 +179,7 @@ public async Task RemoveBatchAsync(string collectionName, IEnumerable ke } /// - public async Task<(MemoryRecord, double)?> GetNearestMatchAsync(string collectionName, Embedding embedding, double minRelevanceScore = 0, bool withEmbedding = false, + public async Task<(MemoryRecord, double)?> GetNearestMatchAsync(string collectionName, ReadOnlyMemory embedding, double minRelevanceScore = 0, bool withEmbedding = false, CancellationToken cancellationToken = default) { return await this.GetNearestMatchesAsync( @@ -285,7 +285,7 @@ private async IAsyncEnumerable GetAllAsync(string collectionName, await foreach (DatabaseEntry dbEntry in this._dbConnector.ReadAllAsync(this._dbConnection, collectionName, cancellationToken)) { var dbEntryEmbeddingString = dbEntry.EmbeddingString; - Embedding? vector = JsonSerializer.Deserialize>(dbEntryEmbeddingString); + ReadOnlyMemory vector = JsonSerializer.Deserialize>(dbEntryEmbeddingString, s_jsonSerializerOptions); var record = MemoryRecord.FromJsonMetadata(dbEntry.MetadataString, vector, dbEntry.Key, ParseTimestamp(dbEntry.Timestamp)); @@ -301,7 +301,7 @@ await this._dbConnector.UpdateOrInsertAsync(conn: connection, collection: collectionName, key: record.Key, metadata: record.GetSerializedMetadata(), - embedding: JsonSerializer.Serialize(record.Embedding), + embedding: JsonSerializer.Serialize(record.Embedding, s_jsonSerializerOptions), timestamp: ToTimestampString(record.Timestamp), cancellationToken: cancellationToken).ConfigureAwait(false); @@ -322,17 +322,26 @@ await this._dbConnector.UpdateOrInsertAsync(conn: connection, { return MemoryRecord.FromJsonMetadata( json: entry.Value.MetadataString, - JsonSerializer.Deserialize>(entry.Value.EmbeddingString), + JsonSerializer.Deserialize>(entry.Value.EmbeddingString, s_jsonSerializerOptions), entry.Value.Key, ParseTimestamp(entry.Value.Timestamp)); } return MemoryRecord.FromJsonMetadata( json: entry.Value.MetadataString, - Embedding.Empty, + ReadOnlyMemory.Empty, entry.Value.Key, ParseTimestamp(entry.Value.Timestamp)); } + private static readonly JsonSerializerOptions s_jsonSerializerOptions = CreateSerializerOptions(); + + private static JsonSerializerOptions CreateSerializerOptions() + { + var jso = new JsonSerializerOptions(); + jso.Converters.Add(new ReadOnlyMemoryConverter()); + return jso; + } + #endregion } diff --git a/dotnet/src/Connectors/Connectors.Memory.Kusto/KustoMemoryRecord.cs b/dotnet/src/Connectors/Connectors.Memory.Kusto/KustoMemoryRecord.cs index 74fb8e9897b2..a7e8783f06f1 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Kusto/KustoMemoryRecord.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Kusto/KustoMemoryRecord.cs @@ -1,9 +1,10 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using System.Text.Json.Serialization; using Kusto.Cloud.Platform.Utils; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Memory; +using Microsoft.SemanticKernel.Text; namespace Microsoft.SemanticKernel.Connectors.Memory.Kusto; @@ -25,7 +26,8 @@ public sealed class KustoMemoryRecord /// /// Source content embedding. /// - public Embedding Embedding { get; set; } + [JsonConverter(typeof(ReadOnlyMemoryConverter))] + public ReadOnlyMemory Embedding { get; set; } /// /// Optional timestamp. @@ -45,7 +47,7 @@ public KustoMemoryRecord(MemoryRecord record) : this(record.Key, record.Metadata /// Metadata associated with memory entity. /// Source content embedding. /// Optional timestamp. - public KustoMemoryRecord(string key, MemoryRecordMetadata metadata, Embedding embedding, DateTimeOffset? timestamp = null) + public KustoMemoryRecord(string key, MemoryRecordMetadata metadata, ReadOnlyMemory embedding, DateTimeOffset? timestamp = null) { this.Key = key; this.Metadata = metadata; diff --git a/dotnet/src/Connectors/Connectors.Memory.Kusto/KustoMemoryStore.cs b/dotnet/src/Connectors/Connectors.Memory.Kusto/KustoMemoryStore.cs index b5d9e7a95a8f..c22cf0663efd 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Kusto/KustoMemoryStore.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Kusto/KustoMemoryStore.cs @@ -11,7 +11,6 @@ using Kusto.Data; using Kusto.Data.Common; using Kusto.Data.Net.Client; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; @@ -160,7 +159,7 @@ public async IAsyncEnumerable GetCollectionsAsync([EnumeratorCancellatio /// public async Task<(MemoryRecord, double)?> GetNearestMatchAsync( string collectionName, - Embedding embedding, + ReadOnlyMemory embedding, double minRelevanceScore = 0, bool withEmbedding = false, CancellationToken cancellationToken = default) @@ -172,7 +171,7 @@ public async IAsyncEnumerable GetCollectionsAsync([EnumeratorCancellatio /// public async IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesAsync( string collectionName, - Embedding embedding, + ReadOnlyMemory embedding, int limit, double minRelevanceScore = 0, bool withEmbeddings = false, diff --git a/dotnet/src/Connectors/Connectors.Memory.Kusto/KustoSerializer.cs b/dotnet/src/Connectors/Connectors.Memory.Kusto/KustoSerializer.cs index d4d5e3d73883..34a859a08c8a 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Kusto/KustoSerializer.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Kusto/KustoSerializer.cs @@ -5,6 +5,7 @@ using System.Text.Json; using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Memory; +using Microsoft.SemanticKernel.Text; namespace Microsoft.SemanticKernel.Connectors.Memory.Kusto; @@ -17,30 +18,20 @@ public static class KustoSerializer /// Returns serialized string from instance. /// /// Instance of for serialization. - public static string SerializeEmbedding(Embedding embedding) + public static string SerializeEmbedding(ReadOnlyMemory embedding) { - return JsonSerializer.Serialize(embedding.Vector); + return JsonSerializer.Serialize(embedding, s_jsonSerializerOptions); } /// /// Returns deserialized instance of from serialized embedding. /// /// Serialized embedding. - public static Embedding DeserializeEmbedding(string? embedding) + public static ReadOnlyMemory DeserializeEmbedding(string? embedding) { - if (string.IsNullOrEmpty(embedding)) - { - return default; - } - - float[]? floatArray = JsonSerializer.Deserialize(embedding!); - - if (floatArray == null) - { - return default; - } - - return new Embedding(floatArray); + return string.IsNullOrEmpty(embedding) ? + default : + JsonSerializer.Deserialize>(embedding!, s_jsonSerializerOptions); } /// @@ -103,5 +94,14 @@ public static string SerializeDateTimeOffset(DateTimeOffset? dateTimeOffset) private const string TimestampFormat = "yyyy-MM-ddTHH:mm:ssZ"; + private static readonly JsonSerializerOptions s_jsonSerializerOptions = CreateSerializerOptions(); + + private static JsonSerializerOptions CreateSerializerOptions() + { + var jso = new JsonSerializerOptions(); + jso.Converters.Add(new ReadOnlyMemoryConverter()); + return jso; + } + #endregion } diff --git a/dotnet/src/Connectors/Connectors.Memory.Pinecone/Http/ApiSchema/QueryRequest.cs b/dotnet/src/Connectors/Connectors.Memory.Pinecone/Http/ApiSchema/QueryRequest.cs index bceee402a30d..446dac99d767 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Pinecone/Http/ApiSchema/QueryRequest.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Pinecone/Http/ApiSchema/QueryRequest.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Net.Http; using System.Text.Json.Serialization; @@ -36,7 +37,7 @@ internal sealed class QueryRequest /// Vector dense data. This should be the same length as the dimension of the index being queried. /// [JsonPropertyName("vector")] - public IEnumerable? Vector { get; set; } + public ReadOnlyMemory Vector { get; set; } /// /// The unique ID of a vector @@ -106,7 +107,7 @@ public HttpRequestMessage Build() /// Initializes a new instance of the class. /// [JsonConstructor] - private QueryRequest(IEnumerable? values = null) + private QueryRequest(ReadOnlyMemory values) { this.Vector = values; } diff --git a/dotnet/src/Connectors/Connectors.Memory.Pinecone/Http/ApiSchema/UpdateVectorRequest.cs b/dotnet/src/Connectors/Connectors.Memory.Pinecone/Http/ApiSchema/UpdateVectorRequest.cs index c4eda878cd8e..008fb11594a6 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Pinecone/Http/ApiSchema/UpdateVectorRequest.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Pinecone/Http/ApiSchema/UpdateVectorRequest.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Net.Http; using System.Text.Json.Serialization; @@ -25,7 +26,7 @@ internal sealed class UpdateVectorRequest /// Vector dense data. This should be the same length as the dimension of the index being queried. /// [JsonPropertyName("values")] - public IEnumerable? Values { get; set; } + public ReadOnlyMemory Values { get; set; } /// /// The sparse vector data @@ -77,7 +78,7 @@ public UpdateVectorRequest UpdateSparseValues(SparseVectorData? sparseValues) return this; } - public UpdateVectorRequest UpdateValues(IEnumerable? values) + public UpdateVectorRequest UpdateValues(ReadOnlyMemory values) { this.Values = values; return this; @@ -99,7 +100,7 @@ public HttpRequestMessage Build() /// Initializes a new instance of the class. /// [JsonConstructor] - private UpdateVectorRequest(string id, IEnumerable? values = default) + private UpdateVectorRequest(string id, ReadOnlyMemory values = default) { this.Id = id; this.Values = values; diff --git a/dotnet/src/Connectors/Connectors.Memory.Pinecone/IPineconeClient.cs b/dotnet/src/Connectors/Connectors.Memory.Pinecone/IPineconeClient.cs index ea8e7eeca6fb..48fc3753bdaa 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Pinecone/IPineconeClient.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Pinecone/IPineconeClient.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -59,7 +60,7 @@ public interface IPineconeClient /// Cancellation token. IAsyncEnumerable<(PineconeDocument, double)> GetMostRelevantAsync( string indexName, - IEnumerable vector, + ReadOnlyMemory vector, double threshold, int topK, bool includeValues, diff --git a/dotnet/src/Connectors/Connectors.Memory.Pinecone/IPineconeMemoryStore.cs b/dotnet/src/Connectors/Connectors.Memory.Pinecone/IPineconeMemoryStore.cs index 53ccaac38697..16c88be093b0 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Pinecone/IPineconeMemoryStore.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Pinecone/IPineconeMemoryStore.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -222,7 +223,7 @@ Task RemoveWithFilterAsync( /// IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesWithFilterAsync( string indexName, - Embedding embedding, + ReadOnlyMemory embedding, int limit, Dictionary filter, double minRelevanceScore = 0.0, @@ -244,7 +245,7 @@ Task RemoveWithFilterAsync( IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesFromNamespaceAsync( string indexName, string indexNamespace, - Embedding embedding, + ReadOnlyMemory embedding, int limit, double minRelevanceScore = 0.0, bool withEmbeddings = false, @@ -263,7 +264,7 @@ Task RemoveWithFilterAsync( Task<(MemoryRecord, double)?> GetNearestMatchFromNamespaceAsync( string indexName, string indexNamespace, - Embedding embedding, + ReadOnlyMemory embedding, double minRelevanceScore = 0.0, bool withEmbedding = false, CancellationToken cancellationToken = default); diff --git a/dotnet/src/Connectors/Connectors.Memory.Pinecone/Model/Query.cs b/dotnet/src/Connectors/Connectors.Memory.Pinecone/Model/Query.cs index cb2345a5b7e2..bd549502b180 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Pinecone/Model/Query.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Pinecone/Model/Query.cs @@ -1,7 +1,9 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Text.Json.Serialization; +using Microsoft.SemanticKernel.Text; namespace Microsoft.SemanticKernel.Connectors.Memory.Pinecone.Model; @@ -28,7 +30,8 @@ public sealed class Query /// /// Vector dense data. This should be the same length as the dimension of the index being queried. /// - public IEnumerable? Vector { get; set; } + [JsonConverter(typeof(ReadOnlyMemoryConverter))] + public ReadOnlyMemory Vector { get; set; } /// /// The unique ID of a vector @@ -56,7 +59,7 @@ public static Query Create(int topK) /// Sets vector for instance. /// /// Vector dense data. This should be the same length as the dimension of the index being queried. - public Query WithVector(IEnumerable? vector) + public Query WithVector(ReadOnlyMemory vector) { this.Vector = vector; return this; diff --git a/dotnet/src/Connectors/Connectors.Memory.Pinecone/Model/SparseVectorData.cs b/dotnet/src/Connectors/Connectors.Memory.Pinecone/Model/SparseVectorData.cs index 4623cbf7da92..728ec4e96243 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Pinecone/Model/SparseVectorData.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Pinecone/Model/SparseVectorData.cs @@ -1,7 +1,9 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Text.Json.Serialization; +using Microsoft.SemanticKernel.Text; namespace Microsoft.SemanticKernel.Connectors.Memory.Pinecone.Model; @@ -22,9 +24,10 @@ public class SparseVectorData /// /// The corresponding values of the sparse data, which must be the same length as the indices. [JsonPropertyName("values")] - public IEnumerable Values { get; set; } + [JsonConverter(typeof(ReadOnlyMemoryConverter))] + public ReadOnlyMemory Values { get; set; } - public static SparseVectorData CreateSparseVectorData(List indices, List values) + public static SparseVectorData CreateSparseVectorData(List indices, ReadOnlyMemory values) { return new SparseVectorData(indices, values); } @@ -35,7 +38,7 @@ public static SparseVectorData CreateSparseVectorData(List indices, ListThe indices of the sparse data. (required). /// The corresponding values of the sparse data, which must be the same length as the indices. (required). [JsonConstructor] - public SparseVectorData(List indices, List values) + public SparseVectorData(List indices, ReadOnlyMemory values) { this.Indices = indices; this.Values = values; diff --git a/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeClient.cs b/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeClient.cs index 19540869f5de..9d4f19eb70f6 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeClient.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeClient.cs @@ -28,14 +28,14 @@ public sealed class PineconeClient : IPineconeClient /// /// The environment for Pinecone. /// The API key for accessing Pinecone services. - /// An optional logger instance for logging. + /// The to use for logging. If null, no logging will be performed. /// An optional HttpClient instance for making HTTP requests. - public PineconeClient(string pineconeEnvironment, string apiKey, ILogger? logger = null, HttpClient? httpClient = null) + public PineconeClient(string pineconeEnvironment, string apiKey, ILoggerFactory? loggerFactory = null, HttpClient? httpClient = null) { this._pineconeEnvironment = pineconeEnvironment; this._authHeader = new KeyValuePair("Api-Key", apiKey); this._jsonSerializerOptions = PineconeUtils.DefaultSerializerOptions; - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(PineconeClient)) : NullLogger.Instance; this._httpClient = httpClient ?? new HttpClient(NonDisposableHttpClientHandler.Instance, disposeHandler: false); this._indexHostMapping = new ConcurrentDictionary(); } @@ -146,7 +146,7 @@ public PineconeClient(string pineconeEnvironment, string apiKey, ILogger? logger /// public async IAsyncEnumerable<(PineconeDocument, double)> GetMostRelevantAsync( string indexName, - IEnumerable vector, + ReadOnlyMemory vector, double threshold, int topK, bool includeValues, diff --git a/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeDocument.cs b/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeDocument.cs index a602e5768921..397c5389a926 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeDocument.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeDocument.cs @@ -7,6 +7,7 @@ using System.Text.Json.Serialization; using Microsoft.SemanticKernel.Connectors.Memory.Pinecone.Http.ApiSchema; using Microsoft.SemanticKernel.Connectors.Memory.Pinecone.Model; +using Microsoft.SemanticKernel.Text; namespace Microsoft.SemanticKernel.Connectors.Memory.Pinecone; @@ -26,7 +27,8 @@ public class PineconeDocument /// Vector dense data. This should be the same length as the dimension of the index being queried. /// [JsonPropertyName("values")] - public IEnumerable Values { get; set; } + [JsonConverter(typeof(ReadOnlyMemoryConverter))] + public ReadOnlyMemory Values { get; set; } /// /// The metadata associated with the document @@ -91,14 +93,14 @@ public class PineconeDocument /// [JsonConstructor] public PineconeDocument( - IEnumerable? values = null, + ReadOnlyMemory values = default, string? id = default, Dictionary? metadata = null, SparseVectorData? sparseValues = null, float? score = null) { this.Id = id ?? Guid.NewGuid().ToString(); - this.Values = values ?? Array.Empty(); + this.Values = values; this.Metadata = metadata ?? new Dictionary(); this.SparseValues = sparseValues; this.Score = score; @@ -109,7 +111,7 @@ public PineconeDocument( /// /// The unique ID of a vector. /// Vector dense data. This should be the same length as the dimension of the index being queried. - public static PineconeDocument Create(string? id = default, IEnumerable? values = default) + public static PineconeDocument Create(string? id = default, ReadOnlyMemory values = default) { return new PineconeDocument(values, id); } @@ -152,11 +154,20 @@ public string GetSerializedMetadata() .Where(x => !propertiesToSkip.Contains(x.Key)) .ToDictionary(x => x.Key, x => x.Value); - return JsonSerializer.Serialize(distinctMetadata); + return JsonSerializer.Serialize(distinctMetadata, s_jsonSerializerOptions); } internal UpdateVectorRequest ToUpdateRequest() { return UpdateVectorRequest.FromPineconeDocument(this); } + + private static readonly JsonSerializerOptions s_jsonSerializerOptions = CreateSerializerOptions(); + + private static JsonSerializerOptions CreateSerializerOptions() + { + var jso = new JsonSerializerOptions(); + jso.Converters.Add(new ReadOnlyMemoryConverter()); + return jso; + } } diff --git a/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeDocumentExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeDocumentExtensions.cs index 3c4341b615f3..0962fd43ac44 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeDocumentExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeDocumentExtensions.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Globalization; using System.Text.Json; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Memory; namespace Microsoft.SemanticKernel.Connectors.Memory.Pinecone; @@ -50,7 +49,7 @@ public static PineconeDocument ToPineconeDocument(this MemoryRecord memoryRecord } return PineconeDocument - .Create(key, memoryRecord.Embedding.Vector) + .Create(key, memoryRecord.Embedding) .WithMetadata(metadata); } @@ -70,7 +69,7 @@ public static MemoryRecord ToMemoryRecord(this PineconeDocument pineconeDocument /// Instance of . internal static MemoryRecord ToMemoryRecord(this PineconeDocument pineconeDocument, bool transferVectorOwnership) { - Embedding embedding = new(pineconeDocument.Values, transferVectorOwnership); + ReadOnlyMemory embedding = pineconeDocument.Values; string additionalMetadataJson = pineconeDocument.GetSerializedMetadata(); diff --git a/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeKernelBuilderExtensions.cs index 000864986eea..3833e28bdbbe 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeKernelBuilderExtensions.cs @@ -25,15 +25,15 @@ public static KernelBuilder WithPineconeMemoryStore(this KernelBuilder builder, string apiKey, HttpClient? httpClient = null) { - builder.WithMemoryStorage((parameters) => + builder.WithMemoryStorage((loggerFactory, config) => { var client = new PineconeClient( environment, apiKey, - parameters.Logger, - HttpClientProvider.GetHttpClient(parameters.Config, httpClient, parameters.Logger)); + loggerFactory, + HttpClientProvider.GetHttpClient(config, httpClient, loggerFactory)); - return new PineconeMemoryStore(client, parameters.Logger); + return new PineconeMemoryStore(client, loggerFactory); }); return builder; diff --git a/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeMemoryStore.cs b/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeMemoryStore.cs index 1ee47a89318e..5100f306a988 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeMemoryStore.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeMemoryStore.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; @@ -8,7 +9,6 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Connectors.Memory.Pinecone.Model; using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; @@ -31,13 +31,13 @@ public class PineconeMemoryStore : IPineconeMemoryStore /// Initializes a new instance of the class. /// /// Instance of Pinecone client which implements interface. - /// Instance of logger. + /// The to use for logging. If null, no logging will be performed. public PineconeMemoryStore( IPineconeClient pineconeClient, - ILogger? logger = null) + ILoggerFactory? loggerFactory = null) { this._pineconeClient = pineconeClient; - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(PineconeMemoryStore)) : NullLogger.Instance; } /// @@ -45,14 +45,14 @@ public PineconeMemoryStore( /// /// Pinecone project environment, see https://docs.pinecone.io/docs/projects#project-environment. /// Pinecone API key. - /// Instance of logger. + /// The to use for logging. If null, no logging will be performed. public PineconeMemoryStore( string pineconeEnvironment, string apiKey, - ILogger? logger = null) + ILoggerFactory? loggerFactory = null) { - this._pineconeClient = new PineconeClient(pineconeEnvironment, apiKey, logger); - this._logger = logger ?? NullLogger.Instance; + this._pineconeClient = new PineconeClient(pineconeEnvironment, apiKey, loggerFactory); + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(PineconeMemoryStore)) : NullLogger.Instance; } /// @@ -509,7 +509,7 @@ public async Task RemoveWithDocumentIdBatchAsync( /// public IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesAsync( string collectionName, - Embedding embedding, + ReadOnlyMemory embedding, int limit, double minRelevanceScore = 0, bool withEmbeddings = false, @@ -529,7 +529,7 @@ public async Task RemoveWithDocumentIdBatchAsync( public async IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesFromNamespaceAsync( string indexName, string indexNamespace, - Embedding embedding, + ReadOnlyMemory embedding, int limit, double minRelevanceScore = 0, bool withEmbeddings = false, @@ -537,7 +537,7 @@ public async Task RemoveWithDocumentIdBatchAsync( { IAsyncEnumerable<(PineconeDocument, double)> results = this._pineconeClient.GetMostRelevantAsync( indexName, - embedding.Vector, + embedding, minRelevanceScore, limit, withEmbeddings, @@ -560,7 +560,7 @@ public async Task RemoveWithDocumentIdBatchAsync( /// public async Task<(MemoryRecord, double)?> GetNearestMatchAsync( string collectionName, - Embedding embedding, + ReadOnlyMemory embedding, double minRelevanceScore = 0, bool withEmbedding = false, CancellationToken cancellationToken = default) @@ -578,7 +578,7 @@ public async Task RemoveWithDocumentIdBatchAsync( public async Task<(MemoryRecord, double)?> GetNearestMatchFromNamespaceAsync( string indexName, string indexNamespace, - Embedding embedding, + ReadOnlyMemory embedding, double minRelevanceScore = 0, bool withEmbedding = false, CancellationToken cancellationToken = default) @@ -600,7 +600,7 @@ public async Task RemoveWithDocumentIdBatchAsync( /// public async IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesWithFilterAsync( string indexName, - Embedding embedding, + ReadOnlyMemory embedding, int limit, Dictionary filter, double minRelevanceScore = 0D, @@ -610,7 +610,7 @@ public async Task RemoveWithDocumentIdBatchAsync( { IAsyncEnumerable<(PineconeDocument, double)> results = this._pineconeClient.GetMostRelevantAsync( indexName, - embedding.Vector, + embedding, minRelevanceScore, limit, withEmbeddings, diff --git a/dotnet/src/Connectors/Connectors.Memory.Postgres/PostgresKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.Postgres/PostgresKernelBuilderExtensions.cs index 4baa6728c2df..29bdff8aa983 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Postgres/PostgresKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Postgres/PostgresKernelBuilderExtensions.cs @@ -25,7 +25,7 @@ public static KernelBuilder WithPostgresMemoryStore(this KernelBuilder builder, int vectorSize, string schema = PostgresMemoryStore.DefaultSchema) { - builder.WithMemoryStorage((parameters) => + builder.WithMemoryStorage((loggerFactory, config) => { return new PostgresMemoryStore(dataSource, vectorSize, schema); }); @@ -41,7 +41,7 @@ public static KernelBuilder WithPostgresMemoryStore(this KernelBuilder builder, /// Self instance public static KernelBuilder WithPostgresMemoryStore(this KernelBuilder builder, IPostgresDbClient postgresDbClient) { - builder.WithMemoryStorage((parameters) => + builder.WithMemoryStorage((loggerFactory, config) => { return new PostgresMemoryStore(postgresDbClient); }); diff --git a/dotnet/src/Connectors/Connectors.Memory.Postgres/PostgresMemoryStore.cs b/dotnet/src/Connectors/Connectors.Memory.Postgres/PostgresMemoryStore.cs index b95ac91f8fb1..6c35f32ff4b2 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Postgres/PostgresMemoryStore.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Postgres/PostgresMemoryStore.cs @@ -1,11 +1,12 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; using Npgsql; @@ -135,7 +136,7 @@ public async Task RemoveBatchAsync(string collectionName, IEnumerable ke /// public async IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesAsync( string collectionName, - Embedding embedding, + ReadOnlyMemory embedding, int limit, double minRelevanceScore = 0, bool withEmbeddings = false, @@ -150,7 +151,7 @@ public async Task RemoveBatchAsync(string collectionName, IEnumerable ke IAsyncEnumerable<(PostgresMemoryEntry, double)> results = this._postgresDbClient.GetNearestMatchesAsync( tableName: collectionName, - embedding: new Vector(embedding.Vector.ToArray()), + embedding: new Vector(GetOrCreateArray(embedding)), limit: limit, minRelevanceScore: minRelevanceScore, withEmbeddings: withEmbeddings, @@ -163,7 +164,7 @@ public async Task RemoveBatchAsync(string collectionName, IEnumerable ke } /// - public async Task<(MemoryRecord, double)?> GetNearestMatchAsync(string collectionName, Embedding embedding, double minRelevanceScore = 0, bool withEmbedding = false, + public async Task<(MemoryRecord, double)?> GetNearestMatchAsync(string collectionName, ReadOnlyMemory embedding, double minRelevanceScore = 0, bool withEmbedding = false, CancellationToken cancellationToken = default) { return await this.GetNearestMatchesAsync( @@ -187,7 +188,7 @@ await this._postgresDbClient.UpsertAsync( tableName: collectionName, key: record.Key, metadata: record.GetSerializedMetadata(), - embedding: new Vector(record.Embedding.Vector.ToArray()), + embedding: new Vector(GetOrCreateArray(record.Embedding)), timestamp: record.Timestamp?.UtcDateTime, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -198,10 +199,16 @@ private MemoryRecord GetMemoryRecordFromEntry(PostgresMemoryEntry entry) { return MemoryRecord.FromJsonMetadata( json: entry.MetadataString, - embedding: entry.Embedding != null ? new Embedding(entry.Embedding!.ToArray()) : Embedding.Empty, + embedding: entry.Embedding?.ToArray() ?? ReadOnlyMemory.Empty, key: entry.Key, timestamp: entry.Timestamp?.ToLocalTime()); } + private static float[] GetOrCreateArray(ReadOnlyMemory memory) => + MemoryMarshal.TryGetArray(memory, out ArraySegment array) && + array.Count == array.Array!.Length ? + array.Array : + memory.ToArray(); + #endregion } diff --git a/dotnet/src/Connectors/Connectors.Memory.Qdrant/Http/ApiSchema/GetVectorsResponse.cs b/dotnet/src/Connectors/Connectors.Memory.Qdrant/Http/ApiSchema/GetVectorsResponse.cs index 9acfea54abc4..49813b86e1f6 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Qdrant/Http/ApiSchema/GetVectorsResponse.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Qdrant/Http/ApiSchema/GetVectorsResponse.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Text.Json.Serialization; @@ -19,10 +20,10 @@ internal sealed class Record [JsonPropertyName("vector")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public IEnumerable? Vector { get; set; } + public ReadOnlyMemory? Vector { get; set; } [JsonConstructor] - public Record(string id, Dictionary? payload, IEnumerable? vector) + public Record(string id, Dictionary? payload, ReadOnlyMemory? vector) { this.Id = id; this.Payload = payload; diff --git a/dotnet/src/Connectors/Connectors.Memory.Qdrant/Http/ApiSchema/SearchVectorsRequest.cs b/dotnet/src/Connectors/Connectors.Memory.Qdrant/Http/ApiSchema/SearchVectorsRequest.cs index 65003e619b2e..1800c3aca6c9 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Qdrant/Http/ApiSchema/SearchVectorsRequest.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Qdrant/Http/ApiSchema/SearchVectorsRequest.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Net.Http; using System.Text.Json.Serialization; @@ -10,7 +11,7 @@ namespace Microsoft.SemanticKernel.Connectors.Memory.Qdrant.Http.ApiSchema; internal sealed class SearchVectorsRequest : IValidatable { [JsonPropertyName("vector")] - public IEnumerable StartingVector { get; set; } = System.Array.Empty(); + public ReadOnlyMemory StartingVector { get; set; } [JsonPropertyName("filter")] public Filter Filters { get; set; } @@ -40,7 +41,7 @@ public static SearchVectorsRequest Create(string collectionName, int vectorSize) return new SearchVectorsRequest(collectionName).SimilarTo(new float[vectorSize]); } - public SearchVectorsRequest SimilarTo(IEnumerable vector) + public SearchVectorsRequest SimilarTo(ReadOnlyMemory vector) { this.StartingVector = vector; return this; diff --git a/dotnet/src/Connectors/Connectors.Memory.Qdrant/Http/ApiSchema/SearchVectorsResponse.cs b/dotnet/src/Connectors/Connectors.Memory.Qdrant/Http/ApiSchema/SearchVectorsResponse.cs index 8f15ba2b1e4e..4d2b7bd9ef30 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Qdrant/Http/ApiSchema/SearchVectorsResponse.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Qdrant/Http/ApiSchema/SearchVectorsResponse.cs @@ -1,7 +1,9 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Text.Json.Serialization; +using Microsoft.SemanticKernel.Text; namespace Microsoft.SemanticKernel.Connectors.Memory.Qdrant.Http.ApiSchema; @@ -24,10 +26,11 @@ internal sealed class ScoredPoint public Dictionary Payload { get; set; } [JsonPropertyName("vector")] - public IEnumerable? Vector { get; } + [JsonConverter(typeof(ReadOnlyMemoryConverter))] + public ReadOnlyMemory Vector { get; } [JsonConstructor] - public ScoredPoint(string id, double? score, Dictionary payload, IEnumerable vector) + public ScoredPoint(string id, double? score, Dictionary payload, ReadOnlyMemory vector) { this.Id = id; this.Score = score; diff --git a/dotnet/src/Connectors/Connectors.Memory.Qdrant/Http/ApiSchema/UpsertVectorRequest.cs b/dotnet/src/Connectors/Connectors.Memory.Qdrant/Http/ApiSchema/UpsertVectorRequest.cs index a62d84346f9b..b258caee498e 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Qdrant/Http/ApiSchema/UpsertVectorRequest.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Qdrant/Http/ApiSchema/UpsertVectorRequest.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Net.Http; using System.Text.Json.Serialization; @@ -50,7 +51,7 @@ internal sealed class BatchRequest public IList Ids { get; set; } [JsonPropertyName("vectors")] - public IList> Vectors { get; set; } + public IList> Vectors { get; set; } [JsonPropertyName("payloads")] public IList> Payloads { get; set; } @@ -58,7 +59,7 @@ internal sealed class BatchRequest internal BatchRequest() { this.Ids = new List(); - this.Vectors = new List>(); + this.Vectors = new List>(); this.Payloads = new List>(); } } diff --git a/dotnet/src/Connectors/Connectors.Memory.Qdrant/IQdrantVectorDbClient.cs b/dotnet/src/Connectors/Connectors.Memory.Qdrant/IQdrantVectorDbClient.cs index f426f4dd5b3b..8bd6e89275e0 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Qdrant/IQdrantVectorDbClient.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Qdrant/IQdrantVectorDbClient.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -68,7 +69,7 @@ public IAsyncEnumerable GetVectorsByIdAsync(string collectio /// The to monitor for cancellation requests. The default is . public IAsyncEnumerable<(QdrantVectorRecord, double)> FindNearestInCollectionAsync( string collectionName, - IEnumerable target, + ReadOnlyMemory target, double threshold, int top = 1, bool withVectors = false, diff --git a/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantKernelBuilderExtensions.cs index 2e6e6e3a3934..02efbb3cba40 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantKernelBuilderExtensions.cs @@ -23,15 +23,15 @@ public static KernelBuilder WithQdrantMemoryStore(this KernelBuilder builder, string endpoint, int vectorSize) { - builder.WithMemoryStorage((parameters) => + builder.WithMemoryStorage((loggerFactory, config) => { var client = new QdrantVectorDbClient( - HttpClientProvider.GetHttpClient(parameters.Config, null, parameters.Logger), + HttpClientProvider.GetHttpClient(config, null, loggerFactory), vectorSize, endpoint, - parameters.Logger); + loggerFactory); - return new QdrantMemoryStore(client, parameters.Logger); + return new QdrantMemoryStore(client, loggerFactory); }); return builder; @@ -50,15 +50,15 @@ public static KernelBuilder WithQdrantMemoryStore(this KernelBuilder builder, int vectorSize, string? endpoint = null) { - builder.WithMemoryStorage((parameters) => + builder.WithMemoryStorage((loggerFactory, config) => { var client = new QdrantVectorDbClient( - HttpClientProvider.GetHttpClient(parameters.Config, httpClient, parameters.Logger), + HttpClientProvider.GetHttpClient(config, httpClient, loggerFactory), vectorSize, endpoint, - parameters.Logger); + loggerFactory); - return new QdrantMemoryStore(client, parameters.Logger); + return new QdrantMemoryStore(client, loggerFactory); }); return builder; diff --git a/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantMemoryStore.cs b/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantMemoryStore.cs index 77b6610fce4b..414a855069f5 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantMemoryStore.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantMemoryStore.cs @@ -8,7 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Microsoft.SemanticKernel.AI.Embeddings; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; @@ -25,18 +25,18 @@ public class QdrantMemoryStore : IMemoryStore /// /// The Qdrant Vector Database memory store logger. /// - private readonly ILogger? _logger; + private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The Qdrant Vector Database endpoint. /// The size of the vectors used. - /// Optional logger instance. - public QdrantMemoryStore(string endpoint, int vectorSize, ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public QdrantMemoryStore(string endpoint, int vectorSize, ILoggerFactory? loggerFactory = null) { - this._qdrantClient = new QdrantVectorDbClient(endpoint, vectorSize, logger); - this._logger = logger; + this._qdrantClient = new QdrantVectorDbClient(endpoint, vectorSize, loggerFactory); + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(QdrantMemoryStore)) : NullLogger.Instance; } /// @@ -45,22 +45,22 @@ public QdrantMemoryStore(string endpoint, int vectorSize, ILogger? logger = null /// The instance used for making HTTP requests. /// The size of the vectors used in the Qdrant Vector Database. /// The optional endpoint URL for the Qdrant Vector Database. If not specified, the base address of the HTTP client is used. - /// Optional logger instance. - public QdrantMemoryStore(HttpClient httpClient, int vectorSize, string? endpoint = null, ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public QdrantMemoryStore(HttpClient httpClient, int vectorSize, string? endpoint = null, ILoggerFactory? loggerFactory = null) { - this._qdrantClient = new QdrantVectorDbClient(httpClient, vectorSize, endpoint, logger); - this._logger = logger; + this._qdrantClient = new QdrantVectorDbClient(httpClient, vectorSize, endpoint, loggerFactory); + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(QdrantMemoryStore)) : NullLogger.Instance; } /// /// Initializes a new instance of the class. /// /// The Qdrant Db client for interacting with Qdrant Vector Database. - /// Optional logger instance. - public QdrantMemoryStore(IQdrantVectorDbClient client, ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public QdrantMemoryStore(IQdrantVectorDbClient client, ILoggerFactory? loggerFactory = null) { this._qdrantClient = client; - this._logger = logger; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(QdrantMemoryStore)) : NullLogger.Instance; } /// @@ -153,7 +153,7 @@ await this._qdrantClient.UpsertVectorsAsync( return MemoryRecord.FromJsonMetadata( json: vectorData.GetSerializedPayload(), - embedding: new Embedding(vectorData.Embedding, transferOwnership: true), + embedding: vectorData.Embedding, key: vectorData.PointId); } catch (HttpRequestException ex) @@ -199,7 +199,7 @@ public async IAsyncEnumerable GetBatchAsync(string collectionName, return MemoryRecord.FromJsonMetadata( json: vectorData.GetSerializedPayload(), - embedding: new Embedding(vectorData.Embedding, transferOwnership: true)); + embedding: vectorData.Embedding); } catch (HttpRequestException ex) { @@ -228,7 +228,7 @@ public async IAsyncEnumerable GetWithPointIdBatchAsync( { yield return MemoryRecord.FromJsonMetadata( json: vectorData.GetSerializedPayload(), - embedding: new Embedding(vectorData.Embedding, transferOwnership: true), + embedding: vectorData.Embedding, key: vectorData.PointId); } } @@ -293,7 +293,7 @@ public async Task RemoveWithPointIdBatchAsync(string collectionName, IEnumerable /// public async IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesAsync( string collectionName, - Embedding embedding, + ReadOnlyMemory embedding, int limit, double minRelevanceScore = 0, bool withEmbeddings = false, @@ -302,7 +302,7 @@ public async Task RemoveWithPointIdBatchAsync(string collectionName, IEnumerable IAsyncEnumerator<(QdrantVectorRecord, double)> enumerator = this._qdrantClient .FindNearestInCollectionAsync( collectionName: collectionName, - target: embedding.Vector, + target: embedding, threshold: minRelevanceScore, top: limit, withVectors: withEmbeddings, @@ -328,7 +328,7 @@ public async Task RemoveWithPointIdBatchAsync(string collectionName, IEnumerable } catch (HttpRequestException ex) when (ex.Message.Contains("404")) { - this._logger?.LogWarning("NotFound when calling {0}::FindNearestInCollectionAsync - the collection '{1}' may not exist yet", + this._logger.LogWarning("NotFound when calling {0}::FindNearestInCollectionAsync - the collection '{1}' may not exist yet", nameof(QdrantMemoryStore), collectionName); hasResult = false; } @@ -338,7 +338,7 @@ public async Task RemoveWithPointIdBatchAsync(string collectionName, IEnumerable yield return ( MemoryRecord.FromJsonMetadata( json: result.Value.Item1.GetSerializedPayload(), - embedding: new Embedding(result.Value.Item1.Embedding, transferOwnership: true)), + embedding: result.Value.Item1.Embedding), result.Value.Item2); } } while (hasResult); @@ -347,7 +347,7 @@ public async Task RemoveWithPointIdBatchAsync(string collectionName, IEnumerable /// public async Task<(MemoryRecord, double)?> GetNearestMatchAsync( string collectionName, - Embedding embedding, + ReadOnlyMemory embedding, double minRelevanceScore = 0, bool withEmbedding = false, CancellationToken cancellationToken = default) @@ -408,7 +408,7 @@ private async Task ConvertFromMemoryRecordAsync( var vectorData = QdrantVectorRecord.FromJsonMetadata( pointId: pointId, - embedding: record.Embedding.Vector, + embedding: record.Embedding, json: record.GetSerializedMetadata()); if (vectorData == null) diff --git a/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantVectorDbClient.cs b/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantVectorDbClient.cs index bbd70d7cbef3..d16a72791051 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantVectorDbClient.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantVectorDbClient.cs @@ -30,16 +30,16 @@ public sealed class QdrantVectorDbClient : IQdrantVectorDbClient /// /// The Qdrant Vector Database endpoint. /// The size of the vectors used in the Qdrant Vector Database. - /// Optional logger instance. + /// The to use for logging. If null, no logging will be performed. public QdrantVectorDbClient( string endpoint, int vectorSize, - ILogger? logger = null) + ILoggerFactory? loggerFactory = null) { this._vectorSize = vectorSize; this._httpClient = new HttpClient(NonDisposableHttpClientHandler.Instance, disposeHandler: false); this._httpClient.BaseAddress = SanitizeEndpoint(endpoint); - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(QdrantVectorDbClient)) : NullLogger.Instance; } /// @@ -48,12 +48,12 @@ public QdrantVectorDbClient( /// The instance used for making HTTP requests. /// The size of the vectors used in the Qdrant Vector Database. /// The optional endpoint URL for the Qdrant Vector Database. If not specified, the base address of the HTTP client is used. - /// Optional logger instance. + /// The to use for logging. If null, no logging will be performed. public QdrantVectorDbClient( HttpClient httpClient, int vectorSize, string? endpoint = null, - ILogger? logger = null) + ILoggerFactory? loggerFactory = null) { if (string.IsNullOrEmpty(httpClient.BaseAddress?.AbsoluteUri) && string.IsNullOrEmpty(endpoint)) { @@ -65,7 +65,7 @@ public QdrantVectorDbClient( this._httpClient = httpClient; this._vectorSize = vectorSize; this._endpointOverride = string.IsNullOrEmpty(endpoint) ? null : SanitizeEndpoint(endpoint!); - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(QdrantVectorDbClient)) : NullLogger.Instance; } /// @@ -112,7 +112,7 @@ public async IAsyncEnumerable GetVectorsByIdAsync(string col { yield return new QdrantVectorRecord( pointId: record.Id, - embedding: record.Vector ?? Array.Empty(), + embedding: record.Vector ?? default, record.Payload, tags: null); } @@ -159,7 +159,7 @@ public async IAsyncEnumerable GetVectorsByIdAsync(string col var record = new QdrantVectorRecord( pointId: point.Id, - embedding: point.Vector ?? Array.Empty(), + embedding: point.Vector, payload: point.Payload, tags: null); this._logger.LogDebug("Vector found}"); @@ -272,7 +272,7 @@ public async Task UpsertVectorsAsync(string collectionName, IEnumerable public async IAsyncEnumerable<(QdrantVectorRecord, double)> FindNearestInCollectionAsync( string collectionName, - IEnumerable target, + ReadOnlyMemory target, double threshold, int top = 1, bool withVectors = false, @@ -323,7 +323,7 @@ public async Task UpsertVectorsAsync(string collectionName, IEnumerable(), + embedding: v.Vector, payload: v.Payload); result.Add((record, v.Score ?? 0.0)); diff --git a/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantVectorRecord.cs b/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantVectorRecord.cs index a324feb89d8a..5dd1ad3a43eb 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantVectorRecord.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantVectorRecord.cs @@ -1,9 +1,11 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.SemanticKernel.Diagnostics; +using Microsoft.SemanticKernel.Text; namespace Microsoft.SemanticKernel.Connectors.Memory.Qdrant; @@ -22,7 +24,8 @@ public class QdrantVectorRecord /// The embedding data. /// [JsonPropertyName("embedding")] - public IEnumerable Embedding { get; } + [JsonConverter(typeof(ReadOnlyMemoryConverter))] + public ReadOnlyMemory Embedding { get; } /// /// The metadata. @@ -43,7 +46,7 @@ public class QdrantVectorRecord /// /// /// - public QdrantVectorRecord(string pointId, IEnumerable embedding, Dictionary payload, List? tags = null) + public QdrantVectorRecord(string pointId, ReadOnlyMemory embedding, Dictionary payload, List? tags = null) { this.PointId = pointId; this.Embedding = embedding; @@ -69,7 +72,7 @@ public string GetSerializedPayload() /// /// Vector record /// Qdrant exception - public static QdrantVectorRecord FromJsonMetadata(string pointId, IEnumerable embedding, string json, List? tags = null) + public static QdrantVectorRecord FromJsonMetadata(string pointId, ReadOnlyMemory embedding, string json, List? tags = null) { var payload = JsonSerializer.Deserialize>(json); if (payload != null) diff --git a/dotnet/src/Connectors/Connectors.Memory.Redis/RedisMemoryStore.cs b/dotnet/src/Connectors/Connectors.Memory.Redis/RedisMemoryStore.cs index dbd3f2b6e20f..69f6a98589fa 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Redis/RedisMemoryStore.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Redis/RedisMemoryStore.cs @@ -7,7 +7,6 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; using NRedisStack; @@ -15,6 +14,7 @@ using NRedisStack.Search; using NRedisStack.Search.Literals.Enums; using StackExchange.Redis; +using static NRedisStack.Search.Schema.VectorField; namespace Microsoft.SemanticKernel.Connectors.Memory.Redis; @@ -24,18 +24,65 @@ namespace Microsoft.SemanticKernel.Connectors.Memory.Redis; /// The embedded data is saved to the Redis server database specified in the constructor. /// Similarity search capability is provided through the RediSearch module. Use RediSearch's "Index" to implement "Collection". /// -public sealed class RedisMemoryStore : IMemoryStore +public class RedisMemoryStore : IMemoryStore, IDisposable { /// /// Create a new instance of semantic memory using Redis. /// - /// The database of the redis server. - /// Embedding vector size - public RedisMemoryStore(IDatabase database, int vectorSize) + /// The database of the Redis server. + /// Embedding vector size, defaults to 1536 + /// Indexing algorithm for vectors, defaults to "HNSW" + /// Metric for measuring vector distances, defaults to "COSINE" + /// Query dialect, must be 2 or greater for vector similarity searching, defaults to 2 + public RedisMemoryStore( + IDatabase database, + int vectorSize = DefaultVectorSize, + VectorAlgo vectorIndexAlgorithm = DefaultIndexAlgorithm, + VectorDistanceMetric vectorDistanceMetric = DefaultDistanceMetric, + int queryDialect = DefaultQueryDialect) { + if (vectorSize <= 0) + { + throw new ArgumentException( + $"Invalid vector size: {vectorSize}. Vector size must be a positive integer.", nameof(vectorSize)); + } + this._database = database; this._vectorSize = vectorSize; this._ft = database.FT(); + this._vectorIndexAlgorithm = vectorIndexAlgorithm; + this._vectorDistanceMetric = vectorDistanceMetric.ToString(); + this._queryDialect = queryDialect; + } + + /// + /// Create a new instance of semantic memory using Redis. + /// + /// Provide connection URL to a Redis instance + /// Embedding vector size, defaults to 1536 + /// Indexing algorithm for vectors, defaults to "HNSW" + /// Metric for measuring vector distances, defaults to "COSINE" + /// Query dialect, must be 2 or greater for vector similarity searching, defaults to 2 + public RedisMemoryStore( + string connectionString, + int vectorSize = DefaultVectorSize, + VectorAlgo vectorIndexAlgorithm = DefaultIndexAlgorithm, + VectorDistanceMetric vectorDistanceMetric = DefaultDistanceMetric, + int queryDialect = DefaultQueryDialect) + { + if (vectorSize <= 0) + { + throw new ArgumentException( + $"Invalid vector size: {vectorSize}. Vector size must be a positive integer.", nameof(vectorSize)); + } + + this._connection = ConnectionMultiplexer.Connect(connectionString); + this._database = this._connection.GetDatabase(); + this._vectorSize = vectorSize; + this._ft = this._database.FT(); + this._vectorIndexAlgorithm = vectorIndexAlgorithm; + this._vectorDistanceMetric = vectorDistanceMetric.ToString(); + this._queryDialect = queryDialect; } /// @@ -55,10 +102,10 @@ public async Task CreateCollectionAsync(string collectionName, CancellationToken .AddTextField("key") .AddTextField("metadata") .AddNumericField("timestamp") - .AddVectorField("embedding", VECTOR_INDEX_ALGORITHM, new Dictionary { - {"TYPE", VECTOR_TYPE}, + .AddVectorField("embedding", this._vectorIndexAlgorithm, new Dictionary { + {"TYPE", DefaultVectorType}, {"DIM", this._vectorSize}, - {"DISTANCE_METRIC", VECTOR_DISTANCE_METRIC}, + {"DISTANCE_METRIC", this._vectorDistanceMetric}, }); await this._ft.CreateAsync(collectionName, ftCreateParams, schema).ConfigureAwait(false); @@ -72,7 +119,7 @@ public async Task DoesCollectionExistAsync(string collectionName, Cancella await this._ft.InfoAsync(collectionName).ConfigureAwait(false); return true; } - catch (RedisServerException ex) when (ex.Message == MESSAGE_WHEN_INDEX_DOES_NOT_EXIST) + catch (RedisServerException ex) when (ex.Message.Equals(IndexDoesNotExistErrorMessage, StringComparison.Ordinal)) { return false; } @@ -113,7 +160,7 @@ public async Task UpsertAsync(string collectionName, MemoryRecord record await this._database.HashSetAsync(GetRedisKey(collectionName, record.Key), new[] { new HashEntry("key", record.Key), new HashEntry("metadata", record.GetSerializedMetadata()), - new HashEntry("embedding", MemoryMarshal.Cast(record.Embedding.AsReadOnlySpan()).ToArray()), + new HashEntry("embedding", this.ConvertEmbeddingToBytes(record.Embedding)), new HashEntry("timestamp", ToTimestampLong(record.Timestamp)) }, flags: CommandFlags.None).ConfigureAwait(false); @@ -144,7 +191,7 @@ public async Task RemoveBatchAsync(string collectionName, IEnumerable ke /// public async IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesAsync( string collectionName, - Embedding embedding, + ReadOnlyMemory embedding, int limit, double minRelevanceScore = 0, bool withEmbeddings = false, @@ -156,11 +203,11 @@ public async Task RemoveBatchAsync(string collectionName, IEnumerable ke } var query = new Query($"*=>[KNN {limit} @embedding $embedding AS vector_score]") - .AddParam("embedding", MemoryMarshal.Cast(embedding.AsReadOnlySpan()).ToArray()) + .AddParam("embedding", this.ConvertEmbeddingToBytes(embedding)) .SetSortBy("vector_score") .ReturnFields("key", "metadata", "embedding", "timestamp", "vector_score") .Limit(0, limit) - .Dialect(QUERY_DIALECT); + .Dialect(this._queryDialect); var results = await this._ft.SearchAsync(collectionName, query).ConfigureAwait(false); @@ -172,11 +219,11 @@ public async Task RemoveBatchAsync(string collectionName, IEnumerable ke yield break; } - Embedding convertedEmbedding = withEmbeddings && document["embedding"].HasValue + ReadOnlyMemory convertedEmbedding = withEmbeddings && document["embedding"].HasValue ? - new Embedding(MemoryMarshal.Cast((byte[])document["embedding"]!).ToArray()) + MemoryMarshal.Cast((byte[])document["embedding"]!).ToArray() : - Embedding.Empty; + ReadOnlyMemory.Empty; yield return (MemoryRecord.FromJsonMetadata( json: document["metadata"]!, @@ -187,7 +234,7 @@ public async Task RemoveBatchAsync(string collectionName, IEnumerable ke } /// - public async Task<(MemoryRecord, double)?> GetNearestMatchAsync(string collectionName, Embedding embedding, double minRelevanceScore = 0, bool withEmbedding = false, + public async Task<(MemoryRecord, double)?> GetNearestMatchAsync(string collectionName, ReadOnlyMemory embedding, double minRelevanceScore = 0, bool withEmbedding = false, CancellationToken cancellationToken = default) { return await this.GetNearestMatchesAsync( @@ -199,43 +246,63 @@ public async Task RemoveBatchAsync(string collectionName, IEnumerable ke cancellationToken: cancellationToken).FirstOrDefaultAsync(cancellationToken: cancellationToken).ConfigureAwait(false); } - #region constants ================================================================================ + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + this._connection?.Dispose(); + } + } + + #region private ================================================================================ /// - /// Vector similarity index algorithm. The default value is "HNSW". - /// + /// Vector similarity index algorithm. Supported algorithms are {FLAT, HNSW}. The default value is "HNSW". + /// /// - internal const Schema.VectorField.VectorAlgo VECTOR_INDEX_ALGORITHM = Schema.VectorField.VectorAlgo.HNSW; + private const VectorAlgo DefaultIndexAlgorithm = VectorAlgo.HNSW; /// - /// Vector type. Supported types are FLOAT32 and FLOAT64. The default value is "FLOAT32". + /// Vector type. Available values are {FLOAT32, FLOAT64}. + /// Value "FLOAT32" is used by default based on type. /// - internal const string VECTOR_TYPE = "FLOAT32"; + private const string DefaultVectorType = "FLOAT32"; /// - /// Supported distance metric, one of {L2, IP, COSINE}. The default value is "COSINE". + /// Supported distance metrics are {L2, IP, COSINE}. The default value is "COSINE". /// - internal const string VECTOR_DISTANCE_METRIC = "COSINE"; + private const VectorDistanceMetric DefaultDistanceMetric = VectorDistanceMetric.COSINE; /// /// Query dialect. To use a vector similarity query, specify DIALECT 2 or higher. The default value is "2". - /// + /// /// - internal const int QUERY_DIALECT = 2; + private const int DefaultQueryDialect = 2; /// - /// Message when index does not exist. - /// + /// Embedding vector size. /// - internal const string MESSAGE_WHEN_INDEX_DOES_NOT_EXIST = "Unknown Index name"; - - #endregion + private const int DefaultVectorSize = 1536; - #region private ================================================================================ + /// + /// Message when index does not exist. + /// + /// + private const string IndexDoesNotExistErrorMessage = "Unknown Index name"; private readonly IDatabase _database; private readonly int _vectorSize; private readonly SearchCommands _ft; + private readonly ConnectionMultiplexer? _connection; + private readonly Schema.VectorField.VectorAlgo _vectorIndexAlgorithm; + private readonly string _vectorDistanceMetric; + private readonly int _queryDialect; private static long ToTimestampLong(DateTimeOffset? timestamp) { @@ -272,14 +339,14 @@ private static RedisKey GetRedisKey(string collectionName, string key) RedisValue embedding = hashEntries.FirstOrDefault(x => x.Name == "embedding").Value; return MemoryRecord.FromJsonMetadata( json: hashEntries.FirstOrDefault(x => x.Name == "metadata").Value!, - embedding: embedding.HasValue ? new Embedding(MemoryMarshal.Cast((byte[])embedding!).ToArray()) : Embedding.Empty, + embedding: embedding.HasValue ? MemoryMarshal.Cast((byte[])embedding!).ToArray() : ReadOnlyMemory.Empty, key: hashEntries.FirstOrDefault(x => x.Name == "key").Value, timestamp: ParseTimestamp((long?)hashEntries.FirstOrDefault(x => x.Name == "timestamp").Value)); } return MemoryRecord.FromJsonMetadata( json: hashEntries.FirstOrDefault(x => x.Name == "metadata").Value!, - embedding: Embedding.Empty, + embedding: ReadOnlyMemory.Empty, key: hashEntries.FirstOrDefault(x => x.Name == "key").Value, timestamp: ParseTimestamp((long?)hashEntries.FirstOrDefault(x => x.Name == "timestamp").Value)); } @@ -296,5 +363,10 @@ private double GetSimilarity(Document document) return 1 - vectorScore; } + private byte[] ConvertEmbeddingToBytes(ReadOnlyMemory embedding) + { + return MemoryMarshal.AsBytes(embedding.Span).ToArray(); + } + #endregion } diff --git a/dotnet/src/Connectors/Connectors.Memory.Redis/RedisVectorDistanceMetric.cs b/dotnet/src/Connectors/Connectors.Memory.Redis/RedisVectorDistanceMetric.cs new file mode 100644 index 000000000000..2d5ff71c900c --- /dev/null +++ b/dotnet/src/Connectors/Connectors.Memory.Redis/RedisVectorDistanceMetric.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft. All rights reserved. + +namespace Microsoft.SemanticKernel.Connectors.Memory.Redis; + +/// +/// Supported distance metrics are {L2, IP, COSINE}. The default value is "COSINE". +/// +/// +public enum VectorDistanceMetric +{ + /// + /// Euclidean distance between two vectors + /// + L2, + + /// + /// Inner product of two vectors + /// + IP, + + /// + /// Cosine distance of two vectors + /// + COSINE, +} diff --git a/dotnet/src/Connectors/Connectors.Memory.Sqlite/SqliteMemoryStore.cs b/dotnet/src/Connectors/Connectors.Memory.Sqlite/SqliteMemoryStore.cs index d0c001c04115..45cb433ee975 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Sqlite/SqliteMemoryStore.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Sqlite/SqliteMemoryStore.cs @@ -9,10 +9,10 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Data.Sqlite; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.AI.Embeddings.VectorOperations; using Microsoft.SemanticKernel.Memory; using Microsoft.SemanticKernel.Memory.Collections; +using Microsoft.SemanticKernel.Text; namespace Microsoft.SemanticKernel.Connectors.Memory.Sqlite; @@ -120,7 +120,7 @@ public async Task RemoveBatchAsync(string collectionName, IEnumerable ke /// public async IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesAsync( string collectionName, - Embedding embedding, + ReadOnlyMemory embedding, int limit, double minRelevanceScore = 0, bool withEmbeddings = false, @@ -139,11 +139,11 @@ public async Task RemoveBatchAsync(string collectionName, IEnumerable ke if (record != null) { double similarity = embedding - .AsReadOnlySpan() - .CosineSimilarity(record.Embedding.AsReadOnlySpan()); + .Span + .CosineSimilarity(record.Embedding.Span); if (similarity >= minRelevanceScore) { - var entry = withEmbeddings ? record : MemoryRecord.FromMetadata(record.Metadata, Embedding.Empty, record.Key, record.Timestamp); + var entry = withEmbeddings ? record : MemoryRecord.FromMetadata(record.Metadata, ReadOnlyMemory.Empty, record.Key, record.Timestamp); embeddings.Add(new(entry, similarity)); } } @@ -158,7 +158,7 @@ public async Task RemoveBatchAsync(string collectionName, IEnumerable ke } /// - public async Task<(MemoryRecord, double)?> GetNearestMatchAsync(string collectionName, Embedding embedding, double minRelevanceScore = 0, bool withEmbedding = false, + public async Task<(MemoryRecord, double)?> GetNearestMatchAsync(string collectionName, ReadOnlyMemory embedding, double minRelevanceScore = 0, bool withEmbedding = false, CancellationToken cancellationToken = default) { return await this.GetNearestMatchesAsync( @@ -236,7 +236,7 @@ private async IAsyncEnumerable GetAllAsync(string collectionName, await foreach (DatabaseEntry dbEntry in this._dbConnector.ReadAllAsync(this._dbConnection, collectionName, cancellationToken)) { - Embedding? vector = JsonSerializer.Deserialize>(dbEntry.EmbeddingString); + ReadOnlyMemory vector = JsonSerializer.Deserialize>(dbEntry.EmbeddingString, s_jsonSerializerOptions); var record = MemoryRecord.FromJsonMetadata(dbEntry.MetadataString, vector, dbEntry.Key, ParseTimestamp(dbEntry.Timestamp)); @@ -254,7 +254,7 @@ await this._dbConnector.UpdateAsync( collection: collectionName, key: record.Key, metadata: record.GetSerializedMetadata(), - embedding: JsonSerializer.Serialize(record.Embedding), + embedding: JsonSerializer.Serialize(record.Embedding, s_jsonSerializerOptions), timestamp: ToTimestampString(record.Timestamp), cancellationToken: cancellationToken).ConfigureAwait(false); @@ -264,7 +264,7 @@ await this._dbConnector.InsertOrIgnoreAsync( collection: collectionName, key: record.Key, metadata: record.GetSerializedMetadata(), - embedding: JsonSerializer.Serialize(record.Embedding), + embedding: JsonSerializer.Serialize(record.Embedding, s_jsonSerializerOptions), timestamp: ToTimestampString(record.Timestamp), cancellationToken: cancellationToken).ConfigureAwait(false); @@ -285,17 +285,26 @@ await this._dbConnector.InsertOrIgnoreAsync( { return MemoryRecord.FromJsonMetadata( json: entry.Value.MetadataString, - JsonSerializer.Deserialize>(entry.Value.EmbeddingString), + JsonSerializer.Deserialize>(entry.Value.EmbeddingString, s_jsonSerializerOptions), entry.Value.Key, ParseTimestamp(entry.Value.Timestamp)); } return MemoryRecord.FromJsonMetadata( json: entry.Value.MetadataString, - Embedding.Empty, + ReadOnlyMemory.Empty, entry.Value.Key, ParseTimestamp(entry.Value.Timestamp)); } + private static readonly JsonSerializerOptions s_jsonSerializerOptions = CreateSerializerOptions(); + + private static JsonSerializerOptions CreateSerializerOptions() + { + var jso = new JsonSerializerOptions(); + jso.Converters.Add(new ReadOnlyMemoryConverter()); + return jso; + } + #endregion } diff --git a/dotnet/src/Connectors/Connectors.Memory.Weaviate/Http/ApiSchema/BatchRequest.cs b/dotnet/src/Connectors/Connectors.Memory.Weaviate/Http/ApiSchema/BatchRequest.cs index 1bbab81403db..0d2c45943bdb 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Weaviate/Http/ApiSchema/BatchRequest.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Weaviate/Http/ApiSchema/BatchRequest.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. using System.Collections.Generic; -using System.Linq; using System.Net.Http; using Microsoft.SemanticKernel.Connectors.Memory.Weaviate.Model; using Microsoft.SemanticKernel.Memory; @@ -38,7 +37,7 @@ public void Add(MemoryRecord record) { Class = this._class, Id = record.Key, - Vector = record.Embedding.Vector.ToArray(), + Vector = record.Embedding, Properties = new() { { "sk_timestamp", record.Timestamp! }, diff --git a/dotnet/src/Connectors/Connectors.Memory.Weaviate/Http/ApiSchema/CreateGraphRequest.cs b/dotnet/src/Connectors/Connectors.Memory.Weaviate/Http/ApiSchema/CreateGraphRequest.cs index e30debedf287..d937eb78019d 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Weaviate/Http/ApiSchema/CreateGraphRequest.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Weaviate/Http/ApiSchema/CreateGraphRequest.cs @@ -1,7 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. -using System.Collections.Generic; +using System; using System.Net.Http; +using System.Runtime.InteropServices; namespace Microsoft.SemanticKernel.Connectors.Memory.Weaviate.Http.ApiSchema; @@ -10,7 +11,7 @@ internal sealed class CreateGraphRequest { #pragma warning disable CS8618 public string Class { get; set; } - public IEnumerable Vector { get; set; } + public ReadOnlyMemory Vector { get; set; } #pragma warning restore CS8618 public int Limit { get; set; } public bool WithVector { get; set; } @@ -19,7 +20,7 @@ internal sealed class CreateGraphRequest public HttpRequestMessage Build() { string payload = $"{{Get{{{this.Class}(" + - $"nearVector:{{vector:[{string.Join(",", this.Vector)}] " + + $"nearVector:{{vector:[{string.Join(",", MemoryMarshal.ToEnumerable(this.Vector))}] " + $"distance:{this.Distance}}} " + $"limit:{this.Limit}){{{(this.WithVector ? "_additional{vector}" : string.Empty)} " + "_additional{id distance} sk_timestamp sk_id sk_description sk_text sk_additional_metadata}}}"; diff --git a/dotnet/src/Connectors/Connectors.Memory.Weaviate/Http/HttpRequest.cs b/dotnet/src/Connectors/Connectors.Memory.Weaviate/Http/HttpRequest.cs index ec72b4d9580c..60a51cf482e1 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Weaviate/Http/HttpRequest.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Weaviate/Http/HttpRequest.cs @@ -4,16 +4,13 @@ using System.Text; using System.Text.Json; using System.Text.Json.Serialization; +using Microsoft.SemanticKernel.Text; namespace Microsoft.SemanticKernel.Connectors.Memory.Weaviate.Http; internal static class HttpRequest { - private static readonly JsonSerializerOptions s_jsonSerializerOptions = new() - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; + private static readonly JsonSerializerOptions s_jsonSerializerOptions = CreateSerializerOptions(); public static HttpRequestMessage CreateGetRequest(string url, object? payload = null) { @@ -46,4 +43,15 @@ public static HttpRequestMessage CreateDeleteRequest(string url) string strPayload = payload as string ?? JsonSerializer.Serialize(payload, s_jsonSerializerOptions); return new(strPayload, Encoding.UTF8, "application/json"); } + + private static JsonSerializerOptions CreateSerializerOptions() + { + var jso = new JsonSerializerOptions() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + jso.Converters.Add(new ReadOnlyMemoryConverter()); + return jso; + } } diff --git a/dotnet/src/Connectors/Connectors.Memory.Weaviate/Model/WeaviateObject.cs b/dotnet/src/Connectors/Connectors.Memory.Weaviate/Model/WeaviateObject.cs index 9462a8586e48..f3c60fb3a461 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Weaviate/Model/WeaviateObject.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Weaviate/Model/WeaviateObject.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; namespace Microsoft.SemanticKernel.Connectors.Memory.Weaviate.Model; @@ -9,5 +10,5 @@ internal class WeaviateObject public string? Id { get; set; } public string? Class { get; set; } public Dictionary? Properties { get; set; } - public float[]? Vector { get; set; } + public ReadOnlyMemory Vector { get; set; } } diff --git a/dotnet/src/Connectors/Connectors.Memory.Weaviate/WeaviateKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.Weaviate/WeaviateKernelBuilderExtensions.cs index a56ad06145d5..237454bfa9bd 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Weaviate/WeaviateKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Weaviate/WeaviateKernelBuilderExtensions.cs @@ -21,13 +21,13 @@ public static class WeaviateKernelBuilderExtensions /// Self instance public static KernelBuilder WithWeaviateMemoryStore(this KernelBuilder builder, string endpoint, string? apiKey) { - builder.WithMemoryStorage((parameters) => + builder.WithMemoryStorage((loggerFactory, config) => { return new WeaviateMemoryStore( - HttpClientProvider.GetHttpClient(parameters.Config, null, parameters.Logger), + HttpClientProvider.GetHttpClient(config, null, loggerFactory), apiKey, endpoint, - parameters.Logger); + loggerFactory); }); return builder; @@ -46,13 +46,13 @@ public static KernelBuilder WithWeaviateMemoryStore(this KernelBuilder builder, string? endpoint = null, string? apiKey = null) { - builder.WithMemoryStorage((parameters) => + builder.WithMemoryStorage((loggerFactory, config) => { return new WeaviateMemoryStore( - HttpClientProvider.GetHttpClient(parameters.Config, httpClient, parameters.Logger), + HttpClientProvider.GetHttpClient(config, httpClient, loggerFactory), apiKey, endpoint, - parameters.Logger); + loggerFactory); }); return builder; diff --git a/dotnet/src/Connectors/Connectors.Memory.Weaviate/WeaviateMemoryStore.cs b/dotnet/src/Connectors/Connectors.Memory.Weaviate/WeaviateMemoryStore.cs index 121447ec44c0..4dd08146af95 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Weaviate/WeaviateMemoryStore.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Weaviate/WeaviateMemoryStore.cs @@ -16,7 +16,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.SemanticKernel.AI; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Connectors.Memory.Weaviate.Http.ApiSchema; using Microsoft.SemanticKernel.Connectors.Memory.Weaviate.Model; using Microsoft.SemanticKernel.Diagnostics; @@ -60,14 +59,14 @@ public class WeaviateMemoryStore : IMemoryStore /// /// The Weaviate server endpoint URL. /// The API key for accessing Weaviate server. - /// Optional logger instance. - public WeaviateMemoryStore(string endpoint, string? apiKey = null, ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public WeaviateMemoryStore(string endpoint, string? apiKey = null, ILoggerFactory? loggerFactory = null) { Verify.NotNullOrWhiteSpace(endpoint); this._endpoint = new Uri(endpoint); this._apiKey = apiKey; - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(WeaviateMemoryStore)) : NullLogger.Instance; this._httpClient = new HttpClient(NonDisposableHttpClientHandler.Instance, disposeHandler: false); } @@ -77,8 +76,8 @@ public WeaviateMemoryStore(string endpoint, string? apiKey = null, ILogger? logg /// The instance used for making HTTP requests. /// The API key for accessing Weaviate server. /// The optional Weaviate server endpoint URL. If not specified, the base address of the HTTP client is used. - /// Optional logger instance. - public WeaviateMemoryStore(HttpClient httpClient, string? apiKey = null, string? endpoint = null, ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public WeaviateMemoryStore(HttpClient httpClient, string? apiKey = null, string? endpoint = null, ILoggerFactory? loggerFactory = null) { Verify.NotNull(httpClient); @@ -91,7 +90,7 @@ public WeaviateMemoryStore(HttpClient httpClient, string? apiKey = null, string? this._apiKey = apiKey; this._endpoint = string.IsNullOrEmpty(endpoint) ? null : new Uri(endpoint); - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(WeaviateMemoryStore)) : NullLogger.Instance; this._httpClient = httpClient; } @@ -311,7 +310,7 @@ public async IAsyncEnumerable UpsertBatchAsync(string collectionName, IE MemoryRecord record = new( key: weaviateObject.Id!, timestamp: timestamp, - embedding: new(weaviateObject.Vector ?? Array.Empty()), + embedding: weaviateObject.Vector, metadata: ToMetadata(weaviateObject)); this._logger.LogDebug("Vector found with key: {0}", key); @@ -377,7 +376,7 @@ public async Task RemoveBatchAsync(string collectionName, IEnumerable ke /// public async IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesAsync( string collectionName, - Embedding embedding, + ReadOnlyMemory embedding, int limit, double minRelevanceScore = 0, bool withEmbeddings = false, @@ -392,7 +391,7 @@ public async Task RemoveBatchAsync(string collectionName, IEnumerable ke using HttpRequestMessage request = new CreateGraphRequest { Class = className, - Vector = embedding.Vector, + Vector = embedding, Distance = minRelevanceScore, Limit = limit, WithVector = withEmbeddings @@ -435,11 +434,10 @@ public async Task RemoveBatchAsync(string collectionName, IEnumerable ke private static MemoryRecord DeserializeToMemoryRecord(JsonNode? json) { string id = json!["_additional"]!["id"]!.GetValue(); - Embedding vector = Embedding.Empty; - if (json["_additional"]!["vector"] != null) + ReadOnlyMemory vector = ReadOnlyMemory.Empty; + if (json["_additional"]!["vector"] is JsonArray jsonArray) { - IEnumerable floats = json["_additional"]!["vector"]!.AsArray().Select(a => a!.GetValue()); - vector = new(floats); + vector = jsonArray.Select(a => a!.GetValue()).ToArray(); } string text = json["sk_text"]!.GetValue(); @@ -464,7 +462,7 @@ private static MemoryRecord DeserializeToMemoryRecord(JsonNode? json) /// public async Task<(MemoryRecord, double)?> GetNearestMatchAsync( string collectionName, - Embedding embedding, + ReadOnlyMemory embedding, double minRelevanceScore = 0, bool withEmbedding = false, CancellationToken cancellationToken = default) diff --git a/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs index d29233ec5b2e..6161715bb5af 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/HuggingFace/TextEmbedding/HuggingFaceEmbeddingGenerationTests.cs @@ -129,7 +129,7 @@ public async Task ShouldHandleServiceResponseAsync() Assert.NotNull(embeddings); Assert.Equal(1, embeddings.Count); - Assert.Equal(8, embeddings.First().Count); + Assert.Equal(8, embeddings.First().Length); } public void Dispose() diff --git a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Chroma/ChromaMemoryStoreTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Chroma/ChromaMemoryStoreTests.cs index 51411c4bd86c..a05dba7f2858 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Chroma/ChromaMemoryStoreTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Chroma/ChromaMemoryStoreTests.cs @@ -7,7 +7,6 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Connectors.Memory.Chroma; using Microsoft.SemanticKernel.Connectors.Memory.Chroma.Http.ApiSchema; using Microsoft.SemanticKernel.Diagnostics; @@ -287,7 +286,7 @@ public void Dispose() private void AssertMemoryRecordEqual(MemoryRecord expectedRecord, MemoryRecord actualRecord) { Assert.Equal(expectedRecord.Key, actualRecord.Key); - Assert.Equal(expectedRecord.Embedding.Vector, actualRecord.Embedding.Vector); + Assert.True(expectedRecord.Embedding.Span.SequenceEqual(actualRecord.Embedding.Span)); Assert.Equal(expectedRecord.Metadata.Id, actualRecord.Metadata.Id); Assert.Equal(expectedRecord.Metadata.Text, actualRecord.Metadata.Text); Assert.Equal(expectedRecord.Metadata.Description, actualRecord.Metadata.Description); @@ -301,10 +300,10 @@ private HttpClient GetHttpClientStub() return new HttpClient(this._messageHandlerStub, false); } - private MemoryRecord GetRandomMemoryRecord(Embedding? embedding = null) + private MemoryRecord GetRandomMemoryRecord(ReadOnlyMemory? embedding = null) { var id = Guid.NewGuid().ToString(); - var memoryEmbedding = embedding ?? new Embedding(new[] { 1f, 3f, 5f }); + var memoryEmbedding = embedding ?? new[] { 1f, 3f, 5f }; return MemoryRecord.LocalRecord( id: id, @@ -326,7 +325,7 @@ private ChromaEmbeddingsModel GetEmbeddingsModelFromMemoryRecords(MemoryRecord[] var embeddingsModel = new ChromaEmbeddingsModel(); embeddingsModel.Ids.AddRange(memoryRecords.Select(l => l.Key)); - embeddingsModel.Embeddings.AddRange(memoryRecords.Select(l => l.Embedding.Vector.ToArray())); + embeddingsModel.Embeddings.AddRange(memoryRecords.Select(l => l.Embedding.ToArray())); embeddingsModel.Metadatas.AddRange(memoryRecords.Select(this.GetEmbeddingMetadataFromMemoryRecord)); return embeddingsModel; diff --git a/dotnet/src/Connectors/Connectors.UnitTests/Memory/DuckDB/DuckDBMemoryStoreTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/Memory/DuckDB/DuckDBMemoryStoreTests.cs index 8e809a5dd4e9..5db6d37d34cb 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/Memory/DuckDB/DuckDBMemoryStoreTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/Memory/DuckDB/DuckDBMemoryStoreTests.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Connectors.Memory.DuckDB; using Microsoft.SemanticKernel.Memory; using Xunit; @@ -32,7 +31,7 @@ private IEnumerable CreateBatchRecords(int numRecords) id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); records = records.Append(testRecord); } @@ -42,7 +41,7 @@ private IEnumerable CreateBatchRecords(int numRecords) externalId: "test" + i, sourceName: "sourceName" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); records = records.Append(testRecord); } @@ -139,7 +138,7 @@ public async Task ItCanInsertIntoNonExistentCollectionAsync() id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 }), + embedding: new float[] { 1, 2, 3 }, key: null, timestamp: null); @@ -151,7 +150,7 @@ public async Task ItCanInsertIntoNonExistentCollectionAsync() Assert.NotNull(actual); Assert.Equal(testRecord.Metadata.Id, key); Assert.Equal(testRecord.Metadata.Id, actual.Key); - Assert.Equal(testRecord.Embedding.Vector, actual.Embedding.Vector); + Assert.True(testRecord.Embedding.Span.SequenceEqual(actual.Embedding.Span)); Assert.Equal(testRecord.Metadata.Text, actual.Metadata.Text); Assert.Equal(testRecord.Metadata.Description, actual.Metadata.Description); Assert.Equal(testRecord.Metadata.ExternalSourceName, actual.Metadata.ExternalSourceName); @@ -167,7 +166,7 @@ public async Task GetAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync() id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 }), + embedding: new float[] { 1, 2, 3 }, key: null, timestamp: null); string collection = "test_collection" + this._collectionNum; @@ -182,8 +181,8 @@ public async Task GetAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync() // Assert Assert.NotNull(actualDefault); Assert.NotNull(actualWithEmbedding); - Assert.Empty(actualDefault.Embedding.Vector); - Assert.NotEmpty(actualWithEmbedding.Embedding.Vector); + Assert.True(actualDefault.Embedding.IsEmpty); + Assert.False(actualWithEmbedding.Embedding.IsEmpty); } [Fact] @@ -195,7 +194,7 @@ public async Task ItCanUpsertAndRetrieveARecordWithNoTimestampAsync() id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 }), + embedding: new float[] { 1, 2, 3 }, key: null, timestamp: null); string collection = "test_collection" + this._collectionNum; @@ -210,7 +209,7 @@ public async Task ItCanUpsertAndRetrieveARecordWithNoTimestampAsync() Assert.NotNull(actual); Assert.Equal(testRecord.Metadata.Id, key); Assert.Equal(testRecord.Metadata.Id, actual.Key); - Assert.Equal(testRecord.Embedding.Vector, actual.Embedding.Vector); + Assert.True(testRecord.Embedding.Span.SequenceEqual(actual.Embedding.Span)); Assert.Equal(testRecord.Metadata.Text, actual.Metadata.Text); Assert.Equal(testRecord.Metadata.Description, actual.Metadata.Description); Assert.Equal(testRecord.Metadata.ExternalSourceName, actual.Metadata.ExternalSourceName); @@ -226,7 +225,7 @@ public async Task ItCanUpsertAndRetrieveARecordWithTimestampAsync() id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 }), + embedding: new float[] { 1, 2, 3 }, key: null, timestamp: DateTimeOffset.UtcNow); string collection = "test_collection" + this._collectionNum; @@ -241,7 +240,7 @@ public async Task ItCanUpsertAndRetrieveARecordWithTimestampAsync() Assert.NotNull(actual); Assert.Equal(testRecord.Metadata.Id, key); Assert.Equal(testRecord.Metadata.Id, actual.Key); - Assert.Equal(testRecord.Embedding.Vector, actual.Embedding.Vector); + Assert.True(testRecord.Embedding.Span.SequenceEqual(actual.Embedding.Span)); Assert.Equal(testRecord.Metadata.Text, actual.Metadata.Text); Assert.Equal(testRecord.Metadata.Description, actual.Metadata.Description); Assert.Equal(testRecord.Metadata.ExternalSourceName, actual.Metadata.ExternalSourceName); @@ -258,12 +257,12 @@ public async Task UpsertReplacesExistingRecordWithSameIdAsync() id: commonId, text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); MemoryRecord testRecord2 = MemoryRecord.LocalRecord( id: commonId, text: "text2", description: "description2", - embedding: new Embedding(new float[] { 1, 2, 4 })); + embedding: new float[] { 1, 2, 4 }); string collection = "test_collection" + this._collectionNum; this._collectionNum++; @@ -277,8 +276,8 @@ public async Task UpsertReplacesExistingRecordWithSameIdAsync() Assert.NotNull(actual); Assert.Equal(testRecord.Metadata.Id, key); Assert.Equal(testRecord2.Metadata.Id, actual.Key); - Assert.NotEqual(testRecord.Embedding.Vector, actual.Embedding.Vector); - Assert.Equal(testRecord2.Embedding.Vector, actual.Embedding.Vector); + Assert.False(testRecord.Embedding.Span.SequenceEqual(actual.Embedding.Span)); + Assert.True(testRecord2.Embedding.Span.SequenceEqual(actual.Embedding.Span)); Assert.NotEqual(testRecord.Metadata.Text, actual.Metadata.Text); Assert.Equal(testRecord2.Metadata.Description, actual.Metadata.Description); } @@ -292,7 +291,7 @@ public async Task ExistingRecordCanBeRemovedAsync() id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); string collection = "test_collection" + this._collectionNum; this._collectionNum++; @@ -359,7 +358,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() { // Arrange using var db = await DuckDBMemoryStore.ConnectAsync(); - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + var compareEmbedding = new float[] { 1, 1, 1 }; int topN = 4; string collection = "test_collection" + this._collectionNum; this._collectionNum++; @@ -369,7 +368,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -377,7 +376,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -1, -1 })); + embedding: new float[] { -1, -1, -1 }); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -385,7 +384,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -393,7 +392,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -2, -3 })); + embedding: new float[] { -1, -2, -3 }); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -401,7 +400,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, -1, -2 })); + embedding: new float[] { 1, -1, -2 }); _ = await db.UpsertAsync(collection, testRecord); // Act @@ -422,7 +421,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( { // Arrange using var db = await DuckDBMemoryStore.ConnectAsync(); - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + var compareEmbedding = new float[] { 1, 1, 1 }; string collection = "test_collection" + this._collectionNum; this._collectionNum++; await db.CreateCollectionAsync(collection); @@ -431,7 +430,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -439,7 +438,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -1, -1 })); + embedding: new float[] { -1, -1, -1 }); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -447,7 +446,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -455,7 +454,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -2, -3 })); + embedding: new float[] { -1, -2, -3 }); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -463,7 +462,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, -1, -2 })); + embedding: new float[] { 1, -1, -2 }); _ = await db.UpsertAsync(collection, testRecord); // Act @@ -474,8 +473,8 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( // Assert Assert.NotNull(topNResultDefault); Assert.NotNull(topNResultWithEmbedding); - Assert.Empty(topNResultDefault.Value.Item1.Embedding.Vector); - Assert.NotEmpty(topNResultWithEmbedding.Value.Item1.Embedding.Vector); + Assert.True(topNResultDefault.Value.Item1.Embedding.IsEmpty); + Assert.False(topNResultWithEmbedding.Value.Item1.Embedding.IsEmpty); } [Fact] @@ -483,7 +482,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() { // Arrange using var db = await DuckDBMemoryStore.ConnectAsync(); - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + var compareEmbedding = new float[] { 1, 1, 1 }; string collection = "test_collection" + this._collectionNum; this._collectionNum++; await db.CreateCollectionAsync(collection); @@ -492,7 +491,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -500,7 +499,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -1, -1 })); + embedding: new float[] { -1, -1, -1 }); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -508,7 +507,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -516,7 +515,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -2, -3 })); + embedding: new float[] { -1, -2, -3 }); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -524,7 +523,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, -1, -2 })); + embedding: new float[] { 1, -1, -2 }); _ = await db.UpsertAsync(collection, testRecord); // Act @@ -542,7 +541,7 @@ public async Task GetNearestMatchesDifferentiatesIdenticalVectorsByKeyAsync() { // Arrange using var db = await DuckDBMemoryStore.ConnectAsync(); - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + var compareEmbedding = new float[] { 1, 1, 1 }; int topN = 4; string collection = "test_collection" + this._collectionNum; this._collectionNum++; @@ -554,7 +553,7 @@ public async Task GetNearestMatchesDifferentiatesIdenticalVectorsByKeyAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); _ = await db.UpsertAsync(collection, testRecord); } diff --git a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Kusto/KustoMemoryStoreTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Kusto/KustoMemoryStoreTests.cs index 2a3d6867eba1..57becd839788 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Kusto/KustoMemoryStoreTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Kusto/KustoMemoryStoreTests.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Kusto.Cloud.Platform.Utils; using Kusto.Data.Common; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Connectors.Memory.Kusto; using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; @@ -339,7 +338,7 @@ private void AssertMemoryRecordEqual(MemoryRecord expectedRecord, MemoryRecord a { Assert.Equal(expectedRecord.Key, actualRecord.Key); Assert.Equal(expectedRecord.Timestamp, actualRecord.Timestamp); - Assert.Equal(expectedRecord.Embedding.Vector, actualRecord.Embedding.Vector); + Assert.True(expectedRecord.Embedding.Span.SequenceEqual(actualRecord.Embedding.Span)); Assert.Equal(expectedRecord.Metadata.Id, actualRecord.Metadata.Id); Assert.Equal(expectedRecord.Metadata.Text, actualRecord.Metadata.Text); Assert.Equal(expectedRecord.Metadata.Description, actualRecord.Metadata.Description); @@ -348,10 +347,10 @@ private void AssertMemoryRecordEqual(MemoryRecord expectedRecord, MemoryRecord a Assert.Equal(expectedRecord.Metadata.ExternalSourceName, actualRecord.Metadata.ExternalSourceName); } - private MemoryRecord GetRandomMemoryRecord(Embedding? embedding = null) + private MemoryRecord GetRandomMemoryRecord(ReadOnlyMemory? embedding = null) { var id = Guid.NewGuid().ToString(); - var memoryEmbedding = embedding ?? new Embedding(new[] { 1f, 3f, 5f }); + var memoryEmbedding = embedding ?? new[] { 1f, 3f, 5f }; return MemoryRecord.LocalRecord( id: id, diff --git a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Pinecone/PineconeMemoryStoreTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Pinecone/PineconeMemoryStoreTests.cs index ddd3890e4713..aef930aa8bca 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Pinecone/PineconeMemoryStoreTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Pinecone/PineconeMemoryStoreTests.cs @@ -1,11 +1,11 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Connectors.Memory.Pinecone; using Microsoft.SemanticKernel.Connectors.Memory.Pinecone.Model; using Microsoft.SemanticKernel.Diagnostics; @@ -29,26 +29,26 @@ public class PineconeMemoryStoreTests private readonly string _description2 = "description2"; private readonly string _description3 = "description3"; - private readonly Embedding _embedding = new(new float[] { 1, 1, 1 }); - private readonly Embedding _embedding2 = new(new float[] { 2, 2, 2 }); - private readonly Embedding _embedding3 = new(new float[] { 3, 3, 3 }); + private readonly ReadOnlyMemory _embedding = new float[] { 1, 1, 1 }; + private readonly ReadOnlyMemory _embedding2 = new float[] { 2, 2, 2 }; + private readonly ReadOnlyMemory _embedding3 = new float[] { 3, 3, 3 }; private readonly Mock _mockPineconeClient; - private readonly Mock> _mockLogger = new(); + private readonly Mock _mockLoggerFactory = new(); private readonly PineconeMemoryStore _pineconeMemoryStore; public PineconeMemoryStoreTests() { this._mockPineconeClient = new Mock(); - this._pineconeMemoryStore = new PineconeMemoryStore(this._mockPineconeClient.Object, this._mockLogger.Object); + this._pineconeMemoryStore = new PineconeMemoryStore(this._mockPineconeClient.Object, this._mockLoggerFactory.Object); } [Fact] public void ConnectionCanBeInitialized() { // Arrange & Act - PineconeMemoryStore memoryStore = new(this._mockPineconeClient.Object, this._mockLogger.Object); + PineconeMemoryStore memoryStore = new(this._mockPineconeClient.Object, this._mockLoggerFactory.Object); // Assert Assert.NotNull(memoryStore); @@ -222,7 +222,7 @@ public async Task TestRemoveAsync() public async Task TestGetNearestMatchesAsync() { // Arrange - Embedding embedding = new(new float[] { 0.1f, 0.2f }); + ReadOnlyMemory embedding = new float[] { 0.1f, 0.2f }; List<(PineconeDocument, double)> queryResults = new() { @@ -233,20 +233,20 @@ public async Task TestGetNearestMatchesAsync() { { "document_Id", "value1" }, }, - Values = this._embedding.Vector + Values = this._embedding }, 0.9), new(new() { Id = this._id2, Metadata = new Dictionary { { "document_Id", "value2" } }, - Values = this._embedding2.Vector, + Values = this._embedding2, }, 0.5) }; this._mockPineconeClient .Setup>(x => x.GetMostRelevantAsync( It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), @@ -259,7 +259,7 @@ public async Task TestGetNearestMatchesAsync() // Act List<(MemoryRecord, double)> results = await this._pineconeMemoryStore.GetNearestMatchesAsync( "indexName", - new Embedding(new[] { 0.1f, 0.2f, 0.3f }), + new[] { 0.1f, 0.2f, 0.3f }, 2, 0.5, true).ToListAsync(); diff --git a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Postgres/PostgresMemoryStoreTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Postgres/PostgresMemoryStoreTests.cs index e358466ed960..b93abf1e1ef9 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Postgres/PostgresMemoryStoreTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Postgres/PostgresMemoryStoreTests.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Connectors.Memory.Postgres; using Microsoft.SemanticKernel.Memory; using Moq; @@ -272,7 +271,7 @@ public async Task ItCanRemoveBatchAsync() private void AssertMemoryRecordEqual(MemoryRecord expectedRecord, MemoryRecord actualRecord) { Assert.Equal(expectedRecord.Key, actualRecord.Key); - Assert.Equal(expectedRecord.Embedding.Vector, actualRecord.Embedding.Vector); + Assert.True(expectedRecord.Embedding.Span.SequenceEqual(actualRecord.Embedding.Span)); Assert.Equal(expectedRecord.Metadata.Id, actualRecord.Metadata.Id); Assert.Equal(expectedRecord.Metadata.Text, actualRecord.Metadata.Text); Assert.Equal(expectedRecord.Metadata.Description, actualRecord.Metadata.Description); @@ -281,10 +280,10 @@ private void AssertMemoryRecordEqual(MemoryRecord expectedRecord, MemoryRecord a Assert.Equal(expectedRecord.Metadata.ExternalSourceName, actualRecord.Metadata.ExternalSourceName); } - private MemoryRecord GetRandomMemoryRecord(Embedding? embedding = null) + private MemoryRecord GetRandomMemoryRecord(ReadOnlyMemory? embedding = null) { var id = Guid.NewGuid().ToString(); - var memoryEmbedding = embedding ?? new Embedding(new[] { 1f, 3f, 5f }); + var memoryEmbedding = embedding ?? new[] { 1f, 3f, 5f }; return MemoryRecord.LocalRecord( id: id, @@ -301,7 +300,7 @@ private PostgresMemoryEntry GetPostgresMemoryEntryFromMemoryRecord(MemoryRecord return new PostgresMemoryEntry() { Key = memoryRecord.Key, - Embedding = new Pgvector.Vector(memoryRecord.Embedding.Vector.ToArray()), + Embedding = new Pgvector.Vector(memoryRecord.Embedding.ToArray()), MetadataString = memoryRecord.GetSerializedMetadata(), Timestamp = memoryRecord.Timestamp?.UtcDateTime }; diff --git a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Qdrant/QdrantMemoryStoreTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Qdrant/QdrantMemoryStoreTests.cs index 02c0df59a483..c77363a3c91c 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Qdrant/QdrantMemoryStoreTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Qdrant/QdrantMemoryStoreTests.cs @@ -7,8 +7,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Microsoft.SemanticKernel.AI.Embeddings; -using Microsoft.SemanticKernel.Connectors.Memory.Pinecone; using Microsoft.SemanticKernel.Connectors.Memory.Qdrant; using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; @@ -31,10 +29,10 @@ public class QdrantMemoryStoreTests private readonly string _description = "description"; private readonly string _description2 = "description2"; private readonly string _description3 = "description3"; - private readonly Embedding _embedding = new(new float[] { 1, 1, 1 }); - private readonly Embedding _embedding2 = new(new float[] { 2, 2, 2 }); - private readonly Embedding _embedding3 = new(new float[] { 3, 3, 3 }); - private readonly Mock> _mockLogger = new(); + private readonly ReadOnlyMemory _embedding = new float[] { 1, 1, 1 }; + private readonly ReadOnlyMemory _embedding2 = new float[] { 2, 2, 2 }; + private readonly ReadOnlyMemory _embedding3 = new float[] { 3, 3, 3 }; + private readonly Mock _mockLogger = new(); [Fact] public async Task ItCreatesNewCollectionAsync() @@ -203,7 +201,7 @@ public async Task ItUpdatesExistingDataEntryBasedOnMetadataIdAsync() var qdrantVectorRecord = QdrantVectorRecord.FromJsonMetadata( key, - memoryRecord.Embedding.Vector, + memoryRecord.Embedding, memoryRecord.GetSerializedMetadata()); var mockQdrantClient = new Mock(); diff --git a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Qdrant/QdrantMemoryStoreTests2.cs b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Qdrant/QdrantMemoryStoreTests2.cs index 2b173851d5be..abd56fbcee3d 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Qdrant/QdrantMemoryStoreTests2.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Qdrant/QdrantMemoryStoreTests2.cs @@ -6,8 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Microsoft.SemanticKernel.AI.Embeddings; -using Microsoft.SemanticKernel.Connectors.Memory.Pinecone; using Microsoft.SemanticKernel.Connectors.Memory.Qdrant; using Microsoft.SemanticKernel.Memory; using Moq; @@ -29,10 +27,10 @@ public class QdrantMemoryStoreTests2 private readonly string _description = "description"; private readonly string _description2 = "description2"; private readonly string _description3 = "description3"; - private readonly Embedding _embedding = new(new float[] { 1, 1, 1 }); - private readonly Embedding _embedding2 = new(new float[] { 2, 2, 2 }); - private readonly Embedding _embedding3 = new(new float[] { 3, 3, 3 }); - private readonly Mock> _mockLogger = new(); + private readonly ReadOnlyMemory _embedding = new float[] { 1, 1, 1 }; + private readonly ReadOnlyMemory _embedding2 = new float[] { 2, 2, 2 }; + private readonly ReadOnlyMemory _embedding3 = new float[] { 3, 3, 3 }; + private readonly Mock _mockLogger = new(); [Fact] public async Task GetAsyncCallsDoNotRequestVectorsUnlessSpecifiedAsync() @@ -53,7 +51,7 @@ public async Task GetAsyncCallsDoNotRequestVectorsUnlessSpecifiedAsync() // this information will not be verified var qdrantVectorRecord = QdrantVectorRecord.FromJsonMetadata( guidString, - memoryRecord.Embedding.Vector, + memoryRecord.Embedding, memoryRecord.GetSerializedMetadata()); mockQdrantClient @@ -132,7 +130,7 @@ public async Task GetAsyncSearchesByMetadataIdReturnsMemoryRecordIfFoundAsync() var qdrantVectorRecord = QdrantVectorRecord.FromJsonMetadata( memoryRecord.Key, - memoryRecord.Embedding.Vector, + memoryRecord.Embedding, memoryRecord.GetSerializedMetadata()); var mockQdrantClient = new Mock(); @@ -154,7 +152,7 @@ public async Task GetAsyncSearchesByMetadataIdReturnsMemoryRecordIfFoundAsync() Assert.Equal(memoryRecord.Metadata.Description, getResult.Metadata.Description); Assert.Equal(memoryRecord.Metadata.ExternalSourceName, getResult.Metadata.ExternalSourceName); Assert.Equal(memoryRecord.Metadata.IsReference, getResult.Metadata.IsReference); - Assert.Equal(memoryRecord.Embedding.Vector, getResult.Embedding.Vector); + Assert.True(memoryRecord.Embedding.Span.SequenceEqual(getResult.Embedding.Span)); } [Fact] @@ -183,15 +181,15 @@ public async Task GetBatchAsyncSearchesByMetadataIdReturnsAllResultsIfAllFoundAs var qdrantVectorRecord = QdrantVectorRecord.FromJsonMetadata( key, - memoryRecord.Embedding.Vector, + memoryRecord.Embedding, memoryRecord.GetSerializedMetadata()); var qdrantVectorRecord2 = QdrantVectorRecord.FromJsonMetadata( key2, - memoryRecord2.Embedding.Vector, + memoryRecord2.Embedding, memoryRecord2.GetSerializedMetadata()); var qdrantVectorRecord3 = QdrantVectorRecord.FromJsonMetadata( key3, - memoryRecord3.Embedding.Vector, + memoryRecord3.Embedding, memoryRecord3.GetSerializedMetadata()); var mockQdrantClient = new Mock(); @@ -252,11 +250,11 @@ public async Task GetBatchAsyncSearchesByMetadataIdReturnsOnlyNonNullResultsAsyn var qdrantVectorRecord = QdrantVectorRecord.FromJsonMetadata( key, - memoryRecord.Embedding.Vector, + memoryRecord.Embedding, memoryRecord.GetSerializedMetadata()); var qdrantVectorRecord2 = QdrantVectorRecord.FromJsonMetadata( key2, - memoryRecord2.Embedding.Vector, + memoryRecord2.Embedding, memoryRecord2.GetSerializedMetadata()); var mockQdrantClient = new Mock(); @@ -366,7 +364,7 @@ public async Task GetByQdrantPointIdReturnsMemoryRecordIfFoundAsync() var qdrantVectorRecord = QdrantVectorRecord.FromJsonMetadata( memoryRecord.Key, - memoryRecord.Embedding.Vector, + memoryRecord.Embedding, memoryRecord.GetSerializedMetadata()); var mockQdrantClient = new Mock(); @@ -391,7 +389,7 @@ public async Task GetByQdrantPointIdReturnsMemoryRecordIfFoundAsync() Assert.Equal(memoryRecord.Metadata.Description, getResult.Metadata.Description); Assert.Equal(memoryRecord.Metadata.ExternalSourceName, getResult.Metadata.ExternalSourceName); Assert.Equal(memoryRecord.Metadata.IsReference, getResult.Metadata.IsReference); - Assert.Equal(memoryRecord.Embedding.Vector, getResult.Embedding.Vector); + Assert.True(memoryRecord.Embedding.Span.SequenceEqual(getResult.Embedding.Span)); } [Fact] @@ -420,15 +418,15 @@ public async Task GetBatchByQdrantPointIdsReturnsAllResultsIfFoundAsync() var qdrantVectorRecord = QdrantVectorRecord.FromJsonMetadata( key, - memoryRecord.Embedding.Vector, + memoryRecord.Embedding, memoryRecord.GetSerializedMetadata()); var qdrantVectorRecord2 = QdrantVectorRecord.FromJsonMetadata( key2, - memoryRecord2.Embedding.Vector, + memoryRecord2.Embedding, memoryRecord2.GetSerializedMetadata()); var qdrantVectorRecord3 = QdrantVectorRecord.FromJsonMetadata( key3, - memoryRecord3.Embedding.Vector, + memoryRecord3.Embedding, memoryRecord3.GetSerializedMetadata()); var mockQdrantClient = new Mock(); diff --git a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Qdrant/QdrantMemoryStoreTests3.cs b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Qdrant/QdrantMemoryStoreTests3.cs index 39a4982d75ff..9e0708c6015e 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Qdrant/QdrantMemoryStoreTests3.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Qdrant/QdrantMemoryStoreTests3.cs @@ -9,8 +9,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Microsoft.SemanticKernel.AI.Embeddings; -using Microsoft.SemanticKernel.Connectors.Memory.Pinecone; using Microsoft.SemanticKernel.Connectors.Memory.Qdrant; using Microsoft.SemanticKernel.Memory; using Moq; @@ -27,8 +25,8 @@ public class QdrantMemoryStoreTests3 private readonly string _id = "Id"; private readonly string _text = "text"; private readonly string _description = "description"; - private readonly Embedding _embedding = new(new float[] { 1, 1, 1 }); - private readonly Mock> _mockLogger = new(); + private readonly ReadOnlyMemory _embedding = new float[] { 1, 1, 1 }; + private readonly Mock _mockLoggerFactory = new(); [Fact] public async Task GetNearestMatchesAsyncCallsDoNotReturnVectorsUnlessSpecifiedAsync() @@ -38,7 +36,7 @@ public async Task GetNearestMatchesAsyncCallsDoNotReturnVectorsUnlessSpecifiedAs mockQdrantClient .Setup>(x => x.FindNearestInCollectionAsync( It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), @@ -46,7 +44,7 @@ public async Task GetNearestMatchesAsyncCallsDoNotReturnVectorsUnlessSpecifiedAs It.IsAny())) .Returns(AsyncEnumerable.Empty<(QdrantVectorRecord, double)>()); - var vectorStore = new QdrantMemoryStore(mockQdrantClient.Object, this._mockLogger.Object); + var vectorStore = new QdrantMemoryStore(mockQdrantClient.Object, this._mockLoggerFactory.Object); // Act _ = await vectorStore.GetNearestMatchAsync( @@ -73,7 +71,7 @@ public async Task GetNearestMatchesAsyncCallsDoNotReturnVectorsUnlessSpecifiedAs // Assert mockQdrantClient.Verify>(x => x.FindNearestInCollectionAsync( It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny(), 1, false, @@ -82,7 +80,7 @@ public async Task GetNearestMatchesAsyncCallsDoNotReturnVectorsUnlessSpecifiedAs Times.Once()); mockQdrantClient.Verify>(x => x.FindNearestInCollectionAsync( It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny(), 1, true, @@ -91,7 +89,7 @@ public async Task GetNearestMatchesAsyncCallsDoNotReturnVectorsUnlessSpecifiedAs Times.Once()); mockQdrantClient.Verify>(x => x.FindNearestInCollectionAsync( It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny(), 3, false, @@ -100,7 +98,7 @@ public async Task GetNearestMatchesAsyncCallsDoNotReturnVectorsUnlessSpecifiedAs Times.Once()); mockQdrantClient.Verify>(x => x.FindNearestInCollectionAsync( It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny(), 3, true, @@ -117,7 +115,7 @@ public async Task ItReturnsEmptyTupleIfNearestMatchNotFoundAsync() mockQdrantClient .Setup>(x => x.FindNearestInCollectionAsync( It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), @@ -125,7 +123,7 @@ public async Task ItReturnsEmptyTupleIfNearestMatchNotFoundAsync() It.IsAny())) .Returns(AsyncEnumerable.Empty<(QdrantVectorRecord, double)>()); - var vectorStore = new QdrantMemoryStore(mockQdrantClient.Object, this._mockLogger.Object); + var vectorStore = new QdrantMemoryStore(mockQdrantClient.Object, this._mockLoggerFactory.Object); // Act var similarityResult = await vectorStore.GetNearestMatchAsync( @@ -136,7 +134,7 @@ public async Task ItReturnsEmptyTupleIfNearestMatchNotFoundAsync() // Assert mockQdrantClient.Verify>(x => x.FindNearestInCollectionAsync( It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), @@ -162,14 +160,14 @@ public async Task ItWillReturnTheNearestMatchAsATupleAsync() var qdrantVectorRecord = QdrantVectorRecord.FromJsonMetadata( memoryRecord.Key, - memoryRecord.Embedding.Vector, + memoryRecord.Embedding, memoryRecord.GetSerializedMetadata()); var mockQdrantClient = new Mock(); mockQdrantClient .Setup>(x => x.FindNearestInCollectionAsync( It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), @@ -177,7 +175,7 @@ public async Task ItWillReturnTheNearestMatchAsATupleAsync() It.IsAny())) .Returns(new[] { (qdrantVectorRecord, 0.5) }.ToAsyncEnumerable()); - var vectorStore = new QdrantMemoryStore(mockQdrantClient.Object, this._mockLogger.Object); + var vectorStore = new QdrantMemoryStore(mockQdrantClient.Object, this._mockLoggerFactory.Object); // Act var similarityResult = await vectorStore.GetNearestMatchAsync( @@ -188,7 +186,7 @@ public async Task ItWillReturnTheNearestMatchAsATupleAsync() // Assert mockQdrantClient.Verify>(x => x.FindNearestInCollectionAsync( It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), @@ -199,7 +197,7 @@ public async Task ItWillReturnTheNearestMatchAsATupleAsync() Assert.Equal(this._id, similarityResult.Value.Item1.Metadata.Id); Assert.Equal(this._text, similarityResult.Value.Item1.Metadata.Text); Assert.Equal(this._description, similarityResult.Value.Item1.Metadata.Description); - Assert.Equal(this._embedding.Vector, similarityResult.Value.Item1.Embedding.Vector); + Assert.True(this._embedding.Span.SequenceEqual(similarityResult.Value.Item1.Embedding.Span)); Assert.Equal(0.5, similarityResult.Value.Item2); } @@ -211,7 +209,7 @@ public async Task ItReturnsEmptyListIfNearestMatchesNotFoundAsync() mockQdrantClient .Setup>(x => x.FindNearestInCollectionAsync( It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), @@ -219,7 +217,7 @@ public async Task ItReturnsEmptyListIfNearestMatchesNotFoundAsync() It.IsAny())) .Returns(AsyncEnumerable.Empty<(QdrantVectorRecord, double)>()); - var vectorStore = new QdrantMemoryStore(mockQdrantClient.Object, this._mockLogger.Object); + var vectorStore = new QdrantMemoryStore(mockQdrantClient.Object, this._mockLoggerFactory.Object); // Act var similarityResults = await vectorStore.GetNearestMatchesAsync( diff --git a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Redis/RedisMemoryStoreTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Redis/RedisMemoryStoreTests.cs index 1db3c06d0f9e..6e61599ea89a 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Redis/RedisMemoryStoreTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Redis/RedisMemoryStoreTests.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.AI.Embeddings.VectorOperations; using Microsoft.SemanticKernel.Connectors.Memory.Redis; using Microsoft.SemanticKernel.Diagnostics; @@ -36,14 +35,14 @@ public RedisMemoryStoreTests() public void ConnectionCanBeInitialized() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); } [Fact] public async Task ItCanCreateAndGetCollectionAsync() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); string collection = "test_collection"; this.MockCreateIndex(collection); @@ -60,7 +59,7 @@ public async Task ItCanCreateAndGetCollectionAsync() public async Task ItCanCheckIfCollectionExistsAsync() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); string collection = "my_collection"; this.MockCreateIndex(collection); @@ -76,7 +75,7 @@ public async Task ItCanCheckIfCollectionExistsAsync() public async Task CollectionsCanBeDeletedAsync() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); string collection = "test_collection"; this.MockCreateIndex(collection, () => { @@ -102,17 +101,17 @@ public async Task CollectionsCanBeDeletedAsync() public async Task ItCanInsertIntoNonExistentCollectionAsync() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); MemoryRecord testRecord = MemoryRecord.LocalRecord( id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 }), + embedding: new float[] { 1, 2, 3 }, key: null, timestamp: null); string collection = "random collection"; string redisKey = $"{collection}:{testRecord.Metadata.Id}"; - byte[] embedding = MemoryMarshal.Cast(testRecord.Embedding.AsReadOnlySpan()).ToArray(); + byte[] embedding = MemoryMarshal.Cast(testRecord.Embedding.Span).ToArray(); this._mockDatabase .Setup(x => x.HashSetAsync( It.Is(x => x == redisKey), @@ -142,7 +141,7 @@ public async Task ItCanInsertIntoNonExistentCollectionAsync() Assert.NotNull(actual); Assert.Equal(testRecord.Metadata.Id, key); Assert.Equal(testRecord.Metadata.Id, actual.Key); - Assert.Equal(testRecord.Embedding.Vector, actual.Embedding.Vector); + Assert.True(testRecord.Embedding.Span.SequenceEqual(actual.Embedding.Span)); Assert.Equal(testRecord.Metadata.Text, actual.Metadata.Text); Assert.Equal(testRecord.Metadata.Description, actual.Metadata.Description); Assert.Equal(testRecord.Metadata.ExternalSourceName, actual.Metadata.ExternalSourceName); @@ -153,12 +152,12 @@ public async Task ItCanInsertIntoNonExistentCollectionAsync() public async Task GetAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); MemoryRecord testRecord = MemoryRecord.LocalRecord( id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 }), + embedding: new float[] { 1, 2, 3 }, key: null, timestamp: null); string collection = "test_collection"; @@ -178,20 +177,20 @@ public async Task GetAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync() // Assert Assert.NotNull(actualDefault); Assert.NotNull(actualWithEmbedding); - Assert.Empty(actualDefault.Embedding.Vector); - Assert.NotEmpty(actualWithEmbedding.Embedding.Vector); + Assert.True(actualDefault.Embedding.IsEmpty); + Assert.False(actualWithEmbedding.Embedding.IsEmpty); } [Fact] public async Task ItCanUpsertAndRetrieveARecordWithNoTimestampAsync() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); MemoryRecord testRecord = MemoryRecord.LocalRecord( id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 }), + embedding: new float[] { 1, 2, 3 }, key: null, timestamp: null); string collection = "test_collection"; @@ -209,7 +208,7 @@ public async Task ItCanUpsertAndRetrieveARecordWithNoTimestampAsync() Assert.NotNull(actual); Assert.Equal(testRecord.Metadata.Id, key); Assert.Equal(testRecord.Metadata.Id, actual.Key); - Assert.Equal(testRecord.Embedding.Vector, actual.Embedding.Vector); + Assert.True(testRecord.Embedding.Span.SequenceEqual(actual.Embedding.Span)); Assert.Equal(testRecord.Metadata.Text, actual.Metadata.Text); Assert.Equal(testRecord.Metadata.Description, actual.Metadata.Description); Assert.Equal(testRecord.Metadata.ExternalSourceName, actual.Metadata.ExternalSourceName); @@ -220,12 +219,12 @@ public async Task ItCanUpsertAndRetrieveARecordWithNoTimestampAsync() public async Task ItCanUpsertAndRetrieveARecordWithTimestampAsync() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); MemoryRecord testRecord = MemoryRecord.LocalRecord( id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 }), + embedding: new float[] { 1, 2, 3 }, key: null, timestamp: DateTimeOffset.UtcNow); string collection = "test_collection"; @@ -243,7 +242,7 @@ public async Task ItCanUpsertAndRetrieveARecordWithTimestampAsync() Assert.NotNull(actual); Assert.Equal(testRecord.Metadata.Id, key); Assert.Equal(testRecord.Metadata.Id, actual.Key); - Assert.Equal(testRecord.Embedding.Vector, actual.Embedding.Vector); + Assert.True(testRecord.Embedding.Span.SequenceEqual(actual.Embedding.Span)); Assert.Equal(testRecord.Metadata.Text, actual.Metadata.Text); Assert.Equal(testRecord.Metadata.Description, actual.Metadata.Description); Assert.Equal(testRecord.Metadata.ExternalSourceName, actual.Metadata.ExternalSourceName); @@ -254,18 +253,18 @@ public async Task ItCanUpsertAndRetrieveARecordWithTimestampAsync() public async Task UpsertReplacesExistingRecordWithSameIdAsync() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); string commonId = "test"; MemoryRecord testRecord = MemoryRecord.LocalRecord( id: commonId, text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); MemoryRecord testRecord2 = MemoryRecord.LocalRecord( id: commonId, text: "text2", description: "description2", - embedding: new Embedding(new float[] { 1, 2, 4 })); + embedding: new float[] { 1, 2, 4 }); string collection = "test_collection"; this.MockCreateIndex(collection, () => { @@ -283,8 +282,8 @@ public async Task UpsertReplacesExistingRecordWithSameIdAsync() Assert.NotNull(actual); Assert.Equal(testRecord.Metadata.Id, key); Assert.Equal(testRecord2.Metadata.Id, actual.Key); - Assert.NotEqual(testRecord.Embedding.Vector, actual.Embedding.Vector); - Assert.Equal(testRecord2.Embedding.Vector, actual.Embedding.Vector); + Assert.False(testRecord.Embedding.Span.SequenceEqual(actual.Embedding.Span)); + Assert.True(testRecord2.Embedding.Span.SequenceEqual(actual.Embedding.Span)); Assert.NotEqual(testRecord.Metadata.Text, actual.Metadata.Text); Assert.Equal(testRecord2.Metadata.Description, actual.Metadata.Description); } @@ -293,12 +292,12 @@ public async Task UpsertReplacesExistingRecordWithSameIdAsync() public async Task ExistingRecordCanBeRemovedAsync() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); MemoryRecord testRecord = MemoryRecord.LocalRecord( id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); string collection = "test_collection"; this.MockCreateIndex(collection, () => { @@ -322,7 +321,7 @@ public async Task ExistingRecordCanBeRemovedAsync() public async Task RemovingNonExistingRecordDoesNothingAsync() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); string collection = "test_collection"; this.MockCreateIndex(collection, () => { @@ -342,7 +341,7 @@ public async Task RemovingNonExistingRecordDoesNothingAsync() public async Task ItCanListAllDatabaseCollectionsAsync() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); string[] testCollections = { "random_collection1", "random_collection2", "random_collection3" }; foreach (var collection in testCollections) { @@ -379,18 +378,18 @@ public async Task ItCanListAllDatabaseCollectionsAsync() public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + var compareEmbedding = new float[] { 1, 1, 1 }; string collection = "test_collection"; int topN = 4; double threshold = -1; var testEmbeddings = new[] { - new Embedding(new float[] { 1, 1, 1 }), - new Embedding(new float[] { -1, -1, -1 }), - new Embedding(new float[] { 1, 2, 3 }), - new Embedding(new float[] { -1, -2, -3 }), - new Embedding(new float[] { 1, -1, -2 }) + new float[] { 1, 1, 1 }, + new float[] { -1, -1, -1 }, + new float[] { 1, 2, 3 }, + new float[] { -1, -2, -3 }, + new float[] { 1, -1, -2 } }; var testRecords = new List(); for (int i = 0; i < testEmbeddings.Length; i++) @@ -437,18 +436,18 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + var compareEmbedding = new float[] { 1, 1, 1 }; string collection = "test_collection"; int topN = 1; double threshold = 0.75; var testEmbeddings = new[] { - new Embedding(new float[] { 1, 1, 1 }), - new Embedding(new float[] { -1, -1, -1 }), - new Embedding(new float[] { 1, 2, 3 }), - new Embedding(new float[] { -1, -2, -3 }), - new Embedding(new float[] { 1, -1, -2 }) + new float[] { 1, 1, 1 }, + new float[] { -1, -1, -1 }, + new float[] { 1, 2, 3 }, + new float[] { -1, -2, -3 }, + new float[] { 1, -1, -2 } }; var testRecords = new List(); for (int i = 0; i < testEmbeddings.Length; i++) @@ -489,26 +488,26 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( // Assert Assert.NotNull(topNResultDefault); Assert.NotNull(topNResultWithEmbedding); - Assert.Empty(topNResultDefault.Value.Item1.Embedding.Vector); - Assert.NotEmpty(topNResultWithEmbedding.Value.Item1.Embedding.Vector); + Assert.True(topNResultDefault.Value.Item1.Embedding.IsEmpty); + Assert.False(topNResultWithEmbedding.Value.Item1.Embedding.IsEmpty); } [Fact] public async Task GetNearestMatchAsyncReturnsExpectedAsync() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + var compareEmbedding = new float[] { 1, 1, 1 }; string collection = "test_collection"; int topN = 1; double threshold = 0.75; var testEmbeddings = new[] { - new Embedding(new float[] { 1, 1, 1 }), - new Embedding(new float[] { -1, -1, -1 }), - new Embedding(new float[] { 1, 2, 3 }), - new Embedding(new float[] { -1, -2, -3 }), - new Embedding(new float[] { 1, -1, -2 }) + new float[] { 1, 1, 1 }, + new float[] { -1, -1, -1 }, + new float[] { 1, 2, 3 }, + new float[] { -1, -2, -3 }, + new float[] { 1, -1, -2 } }; var testRecords = new List(); for (int i = 0; i < testEmbeddings.Length; i++) @@ -555,8 +554,8 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() public async Task GetNearestMatchesDifferentiatesIdenticalVectorsByKeyAsync() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + var compareEmbedding = new float[] { 1, 1, 1 }; int topN = 4; double threshold = 0.75; string collection = "test_collection"; @@ -567,7 +566,7 @@ public async Task GetNearestMatchesDifferentiatesIdenticalVectorsByKeyAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 }))); + embedding: new float[] { 1, 1, 1 })); } this.MockCreateIndex(collection, () => { @@ -611,7 +610,7 @@ public async Task GetNearestMatchesDifferentiatesIdenticalVectorsByKeyAsync() public async Task ItCanBatchUpsertRecordsAsync() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); int numRecords = 10; string collection = "test_collection"; IEnumerable records = this.CreateBatchRecords(numRecords); @@ -638,7 +637,7 @@ public async Task ItCanBatchUpsertRecordsAsync() public async Task ItCanBatchGetRecordsAsync() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); int numRecords = 10; string collection = "test_collection"; IEnumerable records = this.CreateBatchRecords(numRecords); @@ -665,7 +664,7 @@ public async Task ItCanBatchGetRecordsAsync() public async Task ItCanBatchRemoveRecordsAsync() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); int numRecords = 10; string collection = "test_collection"; IEnumerable records = this.CreateBatchRecords(numRecords); @@ -700,18 +699,18 @@ public async Task ItCanBatchRemoveRecordsAsync() public async Task GetNearestMatchAsyncThrowsExceptionOnInvalidVectorScoreAsync() { // Arrange - RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + using RedisMemoryStore store = new(this._mockDatabase.Object, vectorSize: 3); + var compareEmbedding = new float[] { 1, 1, 1 }; string collection = "test_collection"; int topN = 1; double threshold = 0.75; var testEmbeddings = new[] { - new Embedding(new float[] { 1, 1, 1 }), - new Embedding(new float[] { -1, -1, -1 }), - new Embedding(new float[] { 1, 2, 3 }), - new Embedding(new float[] { -1, -2, -3 }), - new Embedding(new float[] { 1, -1, -2 }) + new float[] { 1, 1, 1 }, + new float[] { -1, -1, -1 }, + new float[] { 1, 2, 3 }, + new float[] { -1, -2, -3 }, + new float[] { 1, -1, -2 } }; var testRecords = new List(); for (int i = 0; i < testEmbeddings.Length; i++) @@ -824,7 +823,7 @@ private void MockDropIndex(string collection, Action? callback = null) private void MockHashSet(string collection, MemoryRecord record, Action? callback = null) { string redisKey = $"{collection}:{record.Metadata.Id}"; - byte[] embedding = MemoryMarshal.Cast(record.Embedding.AsReadOnlySpan()).ToArray(); + byte[] embedding = MemoryMarshal.Cast(record.Embedding.Span).ToArray(); long timestamp = record.Timestamp?.ToUnixTimeMilliseconds() ?? -1; this._mockDatabase @@ -900,7 +899,7 @@ private void MockKeyDelete(string collection, IEnumerable keys, Action? }); } - private void MockSearch(string collection, Embedding compareEmbedding, int topN, double threshold, bool returnStringVectorScore = false) + private void MockSearch(string collection, ReadOnlyMemory compareEmbedding, int topN, double threshold, bool returnStringVectorScore = false) { TopNCollection embeddings = new(topN); @@ -909,8 +908,8 @@ private void MockSearch(string collection, Embedding compareEmbedding, in foreach (var record in records) { double similarity = compareEmbedding - .AsReadOnlySpan() - .CosineSimilarity(record.Embedding.AsReadOnlySpan()); + .Span + .CosineSimilarity(record.Embedding.Span); if (similarity >= threshold) { embeddings.Add(new(record, similarity)); @@ -927,7 +926,7 @@ private void MockSearch(string collection, Embedding compareEmbedding, in foreach (var item in embeddings) { long timestamp = item.Value.Timestamp?.ToUnixTimeMilliseconds() ?? -1; - byte[] embedding = MemoryMarshal.Cast(item.Value.Embedding.AsReadOnlySpan()).ToArray(); + byte[] embedding = MemoryMarshal.Cast(item.Value.Embedding.Span).ToArray(); redisResults.Add(RedisResult.Create($"{collection}:{item.Value.Metadata.Id}", ResultType.BulkString)); redisResults.Add(RedisResult.Create( new RedisResult[] @@ -966,7 +965,7 @@ private IEnumerable CreateBatchRecords(int numRecords) id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); records = records.Append(testRecord); } @@ -976,7 +975,7 @@ private IEnumerable CreateBatchRecords(int numRecords) externalId: "test" + i, sourceName: "sourceName" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); records = records.Append(testRecord); } diff --git a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Sqlite/SqliteMemoryStoreTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Sqlite/SqliteMemoryStoreTests.cs index c8bda27876ca..1ceb459a5c18 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Sqlite/SqliteMemoryStoreTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Sqlite/SqliteMemoryStoreTests.cs @@ -6,7 +6,6 @@ using System.IO; using System.Linq; using System.Threading.Tasks; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Connectors.Memory.Sqlite; using Microsoft.SemanticKernel.Memory; using Xunit; @@ -66,7 +65,7 @@ private IEnumerable CreateBatchRecords(int numRecords) id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); records = records.Append(testRecord); } @@ -76,7 +75,7 @@ private IEnumerable CreateBatchRecords(int numRecords) externalId: "test" + i, sourceName: "sourceName" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); records = records.Append(testRecord); } @@ -173,7 +172,7 @@ public async Task ItCanInsertIntoNonExistentCollectionAsync() id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 }), + embedding: new float[] { 1, 2, 3 }, key: null, timestamp: null); @@ -185,7 +184,7 @@ public async Task ItCanInsertIntoNonExistentCollectionAsync() Assert.NotNull(actual); Assert.Equal(testRecord.Metadata.Id, key); Assert.Equal(testRecord.Metadata.Id, actual.Key); - Assert.Equal(testRecord.Embedding.Vector, actual.Embedding.Vector); + Assert.True(testRecord.Embedding.Span.SequenceEqual(actual.Embedding.Span)); Assert.Equal(testRecord.Metadata.Text, actual.Metadata.Text); Assert.Equal(testRecord.Metadata.Description, actual.Metadata.Description); Assert.Equal(testRecord.Metadata.ExternalSourceName, actual.Metadata.ExternalSourceName); @@ -201,7 +200,7 @@ public async Task GetAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync() id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 }), + embedding: new float[] { 1, 2, 3 }, key: null, timestamp: null); string collection = "test_collection" + this._collectionNum; @@ -216,8 +215,8 @@ public async Task GetAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync() // Assert Assert.NotNull(actualDefault); Assert.NotNull(actualWithEmbedding); - Assert.Empty(actualDefault.Embedding.Vector); - Assert.NotEmpty(actualWithEmbedding.Embedding.Vector); + Assert.True(actualDefault.Embedding.IsEmpty); + Assert.False(actualWithEmbedding.Embedding.IsEmpty); } [Fact] @@ -229,7 +228,7 @@ public async Task ItCanUpsertAndRetrieveARecordWithNoTimestampAsync() id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 }), + embedding: new float[] { 1, 2, 3 }, key: null, timestamp: null); string collection = "test_collection" + this._collectionNum; @@ -244,7 +243,7 @@ public async Task ItCanUpsertAndRetrieveARecordWithNoTimestampAsync() Assert.NotNull(actual); Assert.Equal(testRecord.Metadata.Id, key); Assert.Equal(testRecord.Metadata.Id, actual.Key); - Assert.Equal(testRecord.Embedding.Vector, actual.Embedding.Vector); + Assert.True(testRecord.Embedding.Span.SequenceEqual(actual.Embedding.Span)); Assert.Equal(testRecord.Metadata.Text, actual.Metadata.Text); Assert.Equal(testRecord.Metadata.Description, actual.Metadata.Description); Assert.Equal(testRecord.Metadata.ExternalSourceName, actual.Metadata.ExternalSourceName); @@ -260,7 +259,7 @@ public async Task ItCanUpsertAndRetrieveARecordWithTimestampAsync() id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 }), + embedding: new float[] { 1, 2, 3 }, key: null, timestamp: DateTimeOffset.UtcNow); string collection = "test_collection" + this._collectionNum; @@ -275,7 +274,7 @@ public async Task ItCanUpsertAndRetrieveARecordWithTimestampAsync() Assert.NotNull(actual); Assert.Equal(testRecord.Metadata.Id, key); Assert.Equal(testRecord.Metadata.Id, actual.Key); - Assert.Equal(testRecord.Embedding.Vector, actual.Embedding.Vector); + Assert.True(testRecord.Embedding.Span.SequenceEqual(actual.Embedding.Span)); Assert.Equal(testRecord.Metadata.Text, actual.Metadata.Text); Assert.Equal(testRecord.Metadata.Description, actual.Metadata.Description); Assert.Equal(testRecord.Metadata.ExternalSourceName, actual.Metadata.ExternalSourceName); @@ -292,12 +291,12 @@ public async Task UpsertReplacesExistingRecordWithSameIdAsync() id: commonId, text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); MemoryRecord testRecord2 = MemoryRecord.LocalRecord( id: commonId, text: "text2", description: "description2", - embedding: new Embedding(new float[] { 1, 2, 4 })); + embedding: new float[] { 1, 2, 4 }); string collection = "test_collection" + this._collectionNum; this._collectionNum++; @@ -311,8 +310,8 @@ public async Task UpsertReplacesExistingRecordWithSameIdAsync() Assert.NotNull(actual); Assert.Equal(testRecord.Metadata.Id, key); Assert.Equal(testRecord2.Metadata.Id, actual.Key); - Assert.NotEqual(testRecord.Embedding.Vector, actual.Embedding.Vector); - Assert.Equal(testRecord2.Embedding.Vector, actual.Embedding.Vector); + Assert.False(testRecord.Embedding.Span.SequenceEqual(actual.Embedding.Span)); + Assert.True(testRecord2.Embedding.Span.SequenceEqual(actual.Embedding.Span)); Assert.NotEqual(testRecord.Metadata.Text, actual.Metadata.Text); Assert.Equal(testRecord2.Metadata.Description, actual.Metadata.Description); } @@ -326,7 +325,7 @@ public async Task ExistingRecordCanBeRemovedAsync() id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); string collection = "test_collection" + this._collectionNum; this._collectionNum++; @@ -393,7 +392,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() { // Arrange using SqliteMemoryStore db = await SqliteMemoryStore.ConnectAsync(DatabaseFile); - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + var compareEmbedding = new float[] { 1, 1, 1 }; int topN = 4; string collection = "test_collection" + this._collectionNum; this._collectionNum++; @@ -403,7 +402,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -411,7 +410,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -1, -1 })); + embedding: new ReadOnlyMemory(new float[] { -1, -1, -1 })); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -419,7 +418,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -427,7 +426,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -2, -3 })); + embedding: new ReadOnlyMemory(new float[] { -1, -2, -3 })); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -435,7 +434,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, -1, -2 })); + embedding: new ReadOnlyMemory(new float[] { 1, -1, -2 })); _ = await db.UpsertAsync(collection, testRecord); // Act @@ -456,7 +455,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( { // Arrange using SqliteMemoryStore db = await SqliteMemoryStore.ConnectAsync(DatabaseFile); - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + var compareEmbedding = new float[] { 1, 1, 1 }; string collection = "test_collection" + this._collectionNum; this._collectionNum++; await db.CreateCollectionAsync(collection); @@ -465,7 +464,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -473,7 +472,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -1, -1 })); + embedding: new ReadOnlyMemory(new float[] { -1, -1, -1 })); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -481,7 +480,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -489,7 +488,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -2, -3 })); + embedding: new ReadOnlyMemory(new float[] { -1, -2, -3 })); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -497,7 +496,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, -1, -2 })); + embedding: new ReadOnlyMemory(new float[] { 1, -1, -2 })); _ = await db.UpsertAsync(collection, testRecord); // Act @@ -508,8 +507,8 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( // Assert Assert.NotNull(topNResultDefault); Assert.NotNull(topNResultWithEmbedding); - Assert.Empty(topNResultDefault.Value.Item1.Embedding.Vector); - Assert.NotEmpty(topNResultWithEmbedding.Value.Item1.Embedding.Vector); + Assert.True(topNResultDefault.Value.Item1.Embedding.IsEmpty); + Assert.False(topNResultWithEmbedding.Value.Item1.Embedding.IsEmpty); } [Fact] @@ -517,7 +516,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() { // Arrange using SqliteMemoryStore db = await SqliteMemoryStore.ConnectAsync(DatabaseFile); - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + var compareEmbedding = new float[] { 1, 1, 1 }; string collection = "test_collection" + this._collectionNum; this._collectionNum++; await db.CreateCollectionAsync(collection); @@ -526,7 +525,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -534,7 +533,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -1, -1 })); + embedding: new ReadOnlyMemory(new float[] { -1, -1, -1 })); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -542,7 +541,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -550,7 +549,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -2, -3 })); + embedding: new ReadOnlyMemory(new float[] { -1, -2, -3 })); _ = await db.UpsertAsync(collection, testRecord); i++; @@ -558,7 +557,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, -1, -2 })); + embedding: new ReadOnlyMemory(new float[] { 1, -1, -2 })); _ = await db.UpsertAsync(collection, testRecord); // Act @@ -576,7 +575,7 @@ public async Task GetNearestMatchesDifferentiatesIdenticalVectorsByKeyAsync() { // Arrange using SqliteMemoryStore db = await SqliteMemoryStore.ConnectAsync(DatabaseFile); - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + var compareEmbedding = new float[] { 1, 1, 1 }; int topN = 4; string collection = "test_collection" + this._collectionNum; this._collectionNum++; @@ -588,7 +587,7 @@ public async Task GetNearestMatchesDifferentiatesIdenticalVectorsByKeyAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); _ = await db.UpsertAsync(collection, testRecord); } diff --git a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/AIServicesOpenAIExtensionsTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/AIServicesOpenAIExtensionsTests.cs index 36664cef1dd7..eb7a9799155e 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/AIServicesOpenAIExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/AIServicesOpenAIExtensionsTests.cs @@ -79,7 +79,7 @@ public void ItCanOverwriteServices() targetBuilder.WithAIService("one", new OpenAITextCompletion("model", "key")); targetBuilder.WithAIService("one", new OpenAITextCompletion("model", "key")); - targetBuilder.WithAIService("one", (_) => new OpenAITextCompletion("model", "key")); - targetBuilder.WithAIService("one", (_) => new OpenAITextCompletion("model", "key")); + targetBuilder.WithAIService("one", (loggerFactory, config) => new OpenAITextCompletion("model", "key")); + targetBuilder.WithAIService("one", (loggerFactory, config) => new OpenAITextCompletion("model", "key")); } } diff --git a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/ChatCompletionWithData/AzureChatCompletionWithDataTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/ChatCompletionWithData/AzureChatCompletionWithDataTests.cs new file mode 100644 index 000000000000..68e08497c648 --- /dev/null +++ b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/ChatCompletionWithData/AzureChatCompletionWithDataTests.cs @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Microsoft.SemanticKernel.AI.ChatCompletion; +using Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletionWithData; +using Xunit; + +namespace SemanticKernel.Connectors.UnitTests.OpenAI.ChatCompletionWithData; + +/// +/// Unit tests for +/// +public sealed class AzureChatCompletionWithDataTests : IDisposable +{ + private AzureChatCompletionWithDataConfig _config; + + private HttpMessageHandlerStub _messageHandlerStub; + private HttpClient _httpClient; + + public AzureChatCompletionWithDataTests() + { + this._config = this.GetConfig(); + + this._messageHandlerStub = new HttpMessageHandlerStub(); + this._httpClient = new HttpClient(this._messageHandlerStub, false); + } + + [Fact] + public async Task SpecifiedConfigurationShouldBeUsedAsync() + { + // Arrange + const string expectedUri = "https://fake-completion-endpoint/openai/deployments/fake-completion-model-id/extensions/chat/completions?api-version=fake-api-version"; + var chatCompletion = new AzureChatCompletionWithData(this._config, this._httpClient); + + // Act + await chatCompletion.GetChatCompletionsAsync(new ChatHistory()); + + // Assert + var actualUri = this._messageHandlerStub.RequestUri?.AbsoluteUri; + var actualRequestHeaderValues = this._messageHandlerStub.RequestHeaders!.GetValues("Api-Key"); + var actualRequestContent = Encoding.UTF8.GetString(this._messageHandlerStub.RequestContent!); + + Assert.Equal(expectedUri, actualUri); + + Assert.Contains("fake-completion-api-key", actualRequestHeaderValues); + Assert.Contains("https://fake-data-source-endpoint", actualRequestContent, StringComparison.OrdinalIgnoreCase); + Assert.Contains("fake-data-source-api-key", actualRequestContent, StringComparison.OrdinalIgnoreCase); + Assert.Contains("fake-data-source-index", actualRequestContent, StringComparison.OrdinalIgnoreCase); + } + + [Fact] + public async Task DefaultApiVersionShouldBeUsedAsync() + { + // Arrange + var config = this.GetConfig(); + config.CompletionApiVersion = string.Empty; + + var chatCompletion = new AzureChatCompletionWithData(config, this._httpClient); + + // Act + await chatCompletion.GetChatCompletionsAsync(new ChatHistory()); + + // Assert + var actualUri = this._messageHandlerStub.RequestUri?.AbsoluteUri; + + Assert.Contains("2023-06-01-preview", actualUri, StringComparison.OrdinalIgnoreCase); + } + + public void Dispose() + { + this._httpClient.Dispose(); + this._messageHandlerStub.Dispose(); + } + + private AzureChatCompletionWithDataConfig GetConfig() + { + return new AzureChatCompletionWithDataConfig + { + CompletionModelId = "fake-completion-model-id", + CompletionEndpoint = "https://fake-completion-endpoint", + CompletionApiKey = "fake-completion-api-key", + CompletionApiVersion = "fake-api-version", + DataSourceEndpoint = "https://fake-data-source-endpoint", + DataSourceApiKey = "fake-data-source-api-key", + DataSourceIndex = "fake-data-source-index" + }; + } +} diff --git a/dotnet/src/Connectors/Connectors.UnitTests/XunitLogger.cs b/dotnet/src/Connectors/Connectors.UnitTests/XunitLogger.cs index 1521dac75bed..6a6ad5c22bc6 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/XunitLogger.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/XunitLogger.cs @@ -9,7 +9,7 @@ namespace SemanticKernel.Connectors.UnitTests; /// /// A logger that writes to the Xunit test output /// -internal sealed class XunitLogger : ILogger, IDisposable +internal sealed class XunitLogger : ILogger, IDisposable { private readonly ITestOutputHelper _output; diff --git a/dotnet/src/Extensions/Extensions.UnitTests/Planning/ActionPlanner/ActionPlannerTests.cs b/dotnet/src/Extensions/Extensions.UnitTests/Planning/ActionPlanner/ActionPlannerTests.cs index 4a38ff333bca..d5bfef6e0e52 100644 --- a/dotnet/src/Extensions/Extensions.UnitTests/Planning/ActionPlanner/ActionPlannerTests.cs +++ b/dotnet/src/Extensions/Extensions.UnitTests/Planning/ActionPlanner/ActionPlannerTests.cs @@ -5,8 +5,8 @@ using System.Threading.Tasks; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.AI.TextCompletion; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Orchestration; -using Microsoft.SemanticKernel.Planning; using Microsoft.SemanticKernel.SemanticFunctions; using Microsoft.SemanticKernel.SkillDefinition; using Moq; @@ -78,7 +78,7 @@ public async Task InvalidJsonThrowsAsync() var planner = new Microsoft.SemanticKernel.Planning.ActionPlanner(kernel.Object); // Act & Assert - await Assert.ThrowsAsync(async () => await planner.CreatePlanAsync("goal")); + await Assert.ThrowsAsync(() => planner.CreatePlanAsync("goal")); } [Fact] @@ -94,7 +94,7 @@ public async Task MalformedJsonThrowsAsync() var planner = new Microsoft.SemanticKernel.Planning.ActionPlanner(kernel.Object); // Act & Assert - await Assert.ThrowsAsync(async () => await planner.CreatePlanAsync("goal")); + await Assert.ThrowsAsync(async () => await planner.CreatePlanAsync("goal")); } private Mock CreateMockKernelAndFunctionFlowWithTestString(string testPlanString, Mock? skills = null) diff --git a/dotnet/src/Extensions/Extensions.UnitTests/Planning/SequentialPlanner/SKContextExtensionsTests.cs b/dotnet/src/Extensions/Extensions.UnitTests/Planning/SequentialPlanner/SKContextExtensionsTests.cs index 79ad8405a4df..e4a69250fa4e 100644 --- a/dotnet/src/Extensions/Extensions.UnitTests/Planning/SequentialPlanner/SKContextExtensionsTests.cs +++ b/dotnet/src/Extensions/Extensions.UnitTests/Planning/SequentialPlanner/SKContextExtensionsTests.cs @@ -22,7 +22,7 @@ public async Task CanCallGetAvailableFunctionsWithNoFunctionsAsync() // Arrange var variables = new ContextVariables(); var skills = new SkillCollection(); - var logger = TestConsoleLogger.Log; + var loggerFactory = TestConsoleLogger.LoggerFactory; var cancellationToken = default(CancellationToken); // Arrange Mock Memory and Result @@ -43,7 +43,7 @@ public async Task CanCallGetAvailableFunctionsWithNoFunctionsAsync() .Returns(asyncEnumerable); // Arrange GetAvailableFunctionsAsync parameters - var context = new SKContext(variables, skills, logger); + var context = new SKContext(variables, skills, loggerFactory); var config = new SequentialPlannerConfig() { Memory = memory.Object }; var semanticQuery = "test"; @@ -62,7 +62,7 @@ public async Task CanCallGetAvailableFunctionsWithFunctionsAsync() { // Arrange var variables = new ContextVariables(); - var logger = TestConsoleLogger.Log; + var loggerFactory = TestConsoleLogger.LoggerFactory; var cancellationToken = default(CancellationToken); // Arrange FunctionView @@ -97,7 +97,7 @@ public async Task CanCallGetAvailableFunctionsWithFunctionsAsync() skills.Setup(x => x.GetFunctionsView(It.IsAny(), It.IsAny())).Returns(functionsView); // Arrange GetAvailableFunctionsAsync parameters - var context = new SKContext(variables, skills.Object, logger); + var context = new SKContext(variables, skills.Object, loggerFactory); var config = new SequentialPlannerConfig() { Memory = memory.Object }; var semanticQuery = "test"; @@ -127,7 +127,7 @@ public async Task CanCallGetAvailableFunctionsWithFunctionsWithRelevancyAsync() { // Arrange var variables = new ContextVariables(); - var logger = TestConsoleLogger.Log; + var loggerFactory = TestConsoleLogger.LoggerFactory; var cancellationToken = default(CancellationToken); // Arrange FunctionView @@ -162,7 +162,7 @@ public async Task CanCallGetAvailableFunctionsWithFunctionsWithRelevancyAsync() skills.Setup(x => x.GetFunctionsView(It.IsAny(), It.IsAny())).Returns(functionsView); // Arrange GetAvailableFunctionsAsync parameters - var context = new SKContext(variables, skills.Object, logger); + var context = new SKContext(variables, skills.Object, loggerFactory); var config = new SequentialPlannerConfig { RelevancyThreshold = 0.78, Memory = memory.Object }; var semanticQuery = "test"; @@ -193,7 +193,7 @@ public async Task CanCallGetAvailableFunctionsAsyncWithDefaultRelevancyAsync() // Arrange var variables = new ContextVariables(); var skills = new SkillCollection(); - var logger = TestConsoleLogger.Log; + var loggerFactory = TestConsoleLogger.LoggerFactory; var cancellationToken = default(CancellationToken); // Arrange Mock Memory and Result @@ -215,7 +215,7 @@ public async Task CanCallGetAvailableFunctionsAsyncWithDefaultRelevancyAsync() .Returns(asyncEnumerable); // Arrange GetAvailableFunctionsAsync parameters - var context = new SKContext(variables, skills, logger); + var context = new SKContext(variables, skills, loggerFactory); var config = new SequentialPlannerConfig { RelevancyThreshold = 0.78, Memory = memory.Object }; var semanticQuery = "test"; diff --git a/dotnet/src/Extensions/Extensions.UnitTests/Planning/SequentialPlanner/SequentialPlanParserTests.cs b/dotnet/src/Extensions/Extensions.UnitTests/Planning/SequentialPlanner/SequentialPlanParserTests.cs index c254d639b31c..f5546ed4c4ec 100644 --- a/dotnet/src/Extensions/Extensions.UnitTests/Planning/SequentialPlanner/SequentialPlanParserTests.cs +++ b/dotnet/src/Extensions/Extensions.UnitTests/Planning/SequentialPlanner/SequentialPlanParserTests.cs @@ -4,8 +4,8 @@ using System.Threading; using Microsoft.Extensions.Logging; using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Orchestration; -using Microsoft.SemanticKernel.Planning; using Microsoft.SemanticKernel.Planning.Sequential; using Microsoft.SemanticKernel.SemanticFunctions; using Microsoft.SemanticKernel.SkillDefinition; @@ -33,7 +33,7 @@ private Mock CreateKernelMock( var kernelMock = new Mock(); kernelMock.SetupGet(k => k.Skills).Returns(mockSkillCollection.Object); - kernelMock.SetupGet(k => k.Logger).Returns(mockLogger.Object); + kernelMock.SetupGet(k => k.LoggerFactory).Returns(new Mock().Object); return kernelMock; } @@ -42,7 +42,7 @@ private SKContext CreateSKContext( IKernel kernel, ContextVariables? variables = null) { - return new SKContext(variables, kernel.Skills, kernel.Logger); + return new SKContext(variables, kernel.Skills, kernel.LoggerFactory); } private static Mock CreateMockFunction(FunctionView functionView, string result = "") @@ -166,7 +166,7 @@ public void InvalidPlanExecutePlanReturnsInvalidResult() var planString = ""; // Act - Assert.Throws(() => planString.ToPlanFromXml(GoalText, SequentialPlanParser.GetSkillFunction(kernel.CreateNewContext()))); + Assert.Throws(() => planString.ToPlanFromXml(GoalText, SequentialPlanParser.GetSkillFunction(kernel.CreateNewContext()))); } // Test that contains a #text node in the plan @@ -237,7 +237,7 @@ public void CanCreatePlanWithInvalidFunctionNodes(string planText, bool allowMis } else { - Assert.Throws(() => planText.ToPlanFromXml(string.Empty, SequentialPlanParser.GetSkillFunction(kernel.CreateNewContext()), allowMissingFunctions)); + Assert.Throws(() => planText.ToPlanFromXml(string.Empty, SequentialPlanParser.GetSkillFunction(kernel.CreateNewContext()), allowMissingFunctions)); } } diff --git a/dotnet/src/Extensions/Extensions.UnitTests/Planning/SequentialPlanner/SequentialPlannerTests.cs b/dotnet/src/Extensions/Extensions.UnitTests/Planning/SequentialPlanner/SequentialPlannerTests.cs index 0b61b00a5b30..68197a5a90f7 100644 --- a/dotnet/src/Extensions/Extensions.UnitTests/Planning/SequentialPlanner/SequentialPlannerTests.cs +++ b/dotnet/src/Extensions/Extensions.UnitTests/Planning/SequentialPlanner/SequentialPlannerTests.cs @@ -7,8 +7,8 @@ using Microsoft.Extensions.Logging; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.AI.TextCompletion; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Orchestration; -using Microsoft.SemanticKernel.Planning; using Microsoft.SemanticKernel.SemanticFunctions; using Microsoft.SemanticKernel.SkillDefinition; using Moq; @@ -24,7 +24,7 @@ public async Task ItCanCreatePlanAsync(string goal) { // Arrange var kernel = new Mock(); - kernel.Setup(x => x.Logger).Returns(new Mock().Object); + kernel.Setup(x => x.LoggerFactory).Returns(new Mock().Object); var input = new List<(string name, string skillName, string description, bool isSemantic)>() { @@ -64,13 +64,13 @@ public async Task ItCanCreatePlanAsync(string goal) var context = new SKContext( new ContextVariables(), skills.Object, - new Mock().Object + new Mock().Object ); var returnContext = new SKContext( new ContextVariables(), skills.Object, - new Mock().Object + new Mock().Object ); var planString = @" @@ -140,7 +140,7 @@ public async Task EmptyGoalThrowsAsync() var planner = new Microsoft.SemanticKernel.Planning.SequentialPlanner(kernel.Object); // Act - await Assert.ThrowsAsync(async () => await planner.CreatePlanAsync("")); + await Assert.ThrowsAsync(async () => await planner.CreatePlanAsync("")); } [Fact] @@ -157,13 +157,13 @@ public async Task InvalidXMLThrowsAsync() var returnContext = new SKContext( new ContextVariables(planString), skills.Object, - new Mock().Object + new Mock().Object ); var context = new SKContext( new ContextVariables(), skills.Object, - new Mock().Object + new Mock().Object ); var mockFunctionFlowFunction = new Mock(); @@ -188,7 +188,7 @@ public async Task InvalidXMLThrowsAsync() var planner = new Microsoft.SemanticKernel.Planning.SequentialPlanner(kernel.Object); // Act - await Assert.ThrowsAsync(async () => await planner.CreatePlanAsync("goal")); + await Assert.ThrowsAsync(async () => await planner.CreatePlanAsync("goal")); } // Method to create Mock objects diff --git a/dotnet/src/Extensions/Extensions.UnitTests/Planning/StepwisePlanner/ParseResultTests.cs b/dotnet/src/Extensions/Extensions.UnitTests/Planning/StepwisePlanner/ParseResultTests.cs index 2e9712f24d56..ff918c0e52c8 100644 --- a/dotnet/src/Extensions/Extensions.UnitTests/Planning/StepwisePlanner/ParseResultTests.cs +++ b/dotnet/src/Extensions/Extensions.UnitTests/Planning/StepwisePlanner/ParseResultTests.cs @@ -23,7 +23,7 @@ public void WhenInputIsFinalAnswerReturnsFinalAnswer(string input, string expect { // Arrange var kernel = new Mock(); - kernel.Setup(x => x.Logger).Returns(new Mock().Object); + kernel.Setup(x => x.LoggerFactory).Returns(new Mock().Object); var planner = new Microsoft.SemanticKernel.Planning.StepwisePlanner(kernel.Object); @@ -50,7 +50,7 @@ public void ParseActionReturnsAction(string input, string expectedAction, params // Arrange var kernel = new Mock(); - kernel.Setup(x => x.Logger).Returns(new Mock().Object); + kernel.Setup(x => x.LoggerFactory).Returns(new Mock().Object); var planner = new Microsoft.SemanticKernel.Planning.StepwisePlanner(kernel.Object); diff --git a/dotnet/src/Extensions/Extensions.UnitTests/XunitHelpers/TestConsoleLogger.cs b/dotnet/src/Extensions/Extensions.UnitTests/XunitHelpers/TestConsoleLogger.cs index 3a375bed388c..8971cd44a83b 100644 --- a/dotnet/src/Extensions/Extensions.UnitTests/XunitHelpers/TestConsoleLogger.cs +++ b/dotnet/src/Extensions/Extensions.UnitTests/XunitHelpers/TestConsoleLogger.cs @@ -10,14 +10,14 @@ namespace SemanticKernel.Extensions.UnitTests.XunitHelpers; /// internal static class TestConsoleLogger { - internal static ILogger Log => LogFactory.CreateLogger(); + internal static ILogger Log => LoggerFactory.CreateLogger(); - private static ILoggerFactory LogFactory => s_loggerFactory.Value; + internal static ILoggerFactory LoggerFactory => s_loggerFactory.Value; private static readonly Lazy s_loggerFactory = new(LogBuilder); private static ILoggerFactory LogBuilder() { - return LoggerFactory.Create(builder => + return Microsoft.Extensions.Logging.LoggerFactory.Create(builder => { builder.SetMinimumLevel(LogLevel.Trace); // builder.AddFilter("Microsoft", LogLevel.Trace); diff --git a/dotnet/src/Extensions/Planning.ActionPlanner/ActionPlanner.cs b/dotnet/src/Extensions/Planning.ActionPlanner/ActionPlanner.cs index 89ca3218048d..8681f3ea22e7 100644 --- a/dotnet/src/Extensions/Planning.ActionPlanner/ActionPlanner.cs +++ b/dotnet/src/Extensions/Planning.ActionPlanner/ActionPlanner.cs @@ -54,15 +54,15 @@ public sealed class ActionPlanner : IActionPlanner /// /// The semantic kernel instance. /// Optional prompt override - /// Optional logger + /// The to use for logging. If null, no logging will be performed. public ActionPlanner( IKernel kernel, string? prompt = null, - ILogger? logger = null) + ILoggerFactory? loggerFactory = null) { Verify.NotNull(kernel); - this._logger = logger ?? new NullLogger(); + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(ActionPlanner)) : NullLogger.Instance; string promptTemplate = prompt ?? EmbeddedResource.Read("skprompt.txt"); @@ -83,7 +83,7 @@ public async Task CreatePlanAsync(string goal, CancellationToken cancellat { if (string.IsNullOrEmpty(goal)) { - throw new PlanningException(PlanningException.ErrorCodes.InvalidGoal, "The goal specified is empty"); + throw new SKException("The goal specified is empty"); } this._context.Variables.Update(goal); @@ -93,7 +93,7 @@ public async Task CreatePlanAsync(string goal, CancellationToken cancellat if (planData == null) { - throw new PlanningException(PlanningException.ErrorCodes.InvalidPlan, "The plan deserialized to a null object"); + throw new SKException("The plan deserialized to a null object"); } // Build and return plan @@ -245,13 +245,12 @@ No parameters. } catch (Exception e) { - throw new PlanningException(PlanningException.ErrorCodes.InvalidPlan, - "Plan parsing error, invalid JSON", e); + throw new SKException("Plan parsing error, invalid JSON", e); } } else { - throw new PlanningException(PlanningException.ErrorCodes.InvalidPlan, $"Failed to extract valid json string from planner result: '{plannerResult}'"); + throw new SKException($"Failed to extract valid json string from planner result: '{plannerResult}'"); } } diff --git a/dotnet/src/Extensions/Planning.ActionPlanner/ActionPlannerExtensions.cs b/dotnet/src/Extensions/Planning.ActionPlanner/ActionPlannerExtensions.cs index 618c1e647af1..62dccf52de34 100644 --- a/dotnet/src/Extensions/Planning.ActionPlanner/ActionPlannerExtensions.cs +++ b/dotnet/src/Extensions/Planning.ActionPlanner/ActionPlannerExtensions.cs @@ -13,9 +13,9 @@ public static class ActionPlannerExtensions /// Returns decorated instance of with enabled instrumentation. /// /// Instance of to decorate. - /// Optional logger. - public static IActionPlanner WithInstrumentation(this IActionPlanner planner, ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public static IActionPlanner WithInstrumentation(this IActionPlanner planner, ILoggerFactory? loggerFactory = null) { - return new InstrumentedActionPlanner(planner, logger); + return new InstrumentedActionPlanner(planner, loggerFactory); } } diff --git a/dotnet/src/Extensions/Planning.ActionPlanner/EmbeddedResource.cs b/dotnet/src/Extensions/Planning.ActionPlanner/EmbeddedResource.cs index ac8a49d5d285..912145e83c69 100644 --- a/dotnet/src/Extensions/Planning.ActionPlanner/EmbeddedResource.cs +++ b/dotnet/src/Extensions/Planning.ActionPlanner/EmbeddedResource.cs @@ -2,6 +2,7 @@ using System.IO; using System.Reflection; +using Microsoft.SemanticKernel.Diagnostics; namespace Microsoft.SemanticKernel.Planning.Action; @@ -12,10 +13,10 @@ internal static class EmbeddedResource internal static string Read(string name) { var assembly = typeof(EmbeddedResource).GetTypeInfo().Assembly; - if (assembly == null) { throw new PlanningException(PlanningException.ErrorCodes.InvalidConfiguration, $"[{s_namespace}] {name} assembly not found"); } + if (assembly == null) { throw new SKException($"[{s_namespace}] {name} assembly not found"); } using Stream? resource = assembly.GetManifestResourceStream($"{s_namespace}." + name); - if (resource == null) { throw new PlanningException(PlanningException.ErrorCodes.InvalidConfiguration, $"[{s_namespace}] {name} resource not found"); } + if (resource == null) { throw new SKException($"[{s_namespace}] {name} resource not found"); } using var reader = new StreamReader(resource); return reader.ReadToEnd(); diff --git a/dotnet/src/Extensions/Planning.ActionPlanner/IActionPlanner.cs b/dotnet/src/Extensions/Planning.ActionPlanner/IActionPlanner.cs index ce844303a7af..752d54d3665c 100644 --- a/dotnet/src/Extensions/Planning.ActionPlanner/IActionPlanner.cs +++ b/dotnet/src/Extensions/Planning.ActionPlanner/IActionPlanner.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; +using Microsoft.SemanticKernel.Diagnostics; namespace Microsoft.SemanticKernel.Planning.Action; @@ -16,6 +17,6 @@ public interface IActionPlanner /// The goal to create a plan for. /// The to monitor for cancellation requests. The default is . /// The plan. - /// Thrown when the plan cannot be created. + /// Thrown when the plan cannot be created. Task CreatePlanAsync(string goal, CancellationToken cancellationToken = default); } diff --git a/dotnet/src/Extensions/Planning.ActionPlanner/InstrumentedActionPlanner.cs b/dotnet/src/Extensions/Planning.ActionPlanner/InstrumentedActionPlanner.cs index e9fbcc264ddf..a28dfc277b7e 100644 --- a/dotnet/src/Extensions/Planning.ActionPlanner/InstrumentedActionPlanner.cs +++ b/dotnet/src/Extensions/Planning.ActionPlanner/InstrumentedActionPlanner.cs @@ -20,13 +20,13 @@ public class InstrumentedActionPlanner : IActionPlanner /// Initialize a new instance of the class. /// /// Instance of to decorate. - /// Optional logger. + /// The to use for logging. If null, no logging will be performed. public InstrumentedActionPlanner( IActionPlanner planner, - ILogger? logger = null) + ILoggerFactory? loggerFactory = null) { this._planner = planner; - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(InstrumentedActionPlanner)) : NullLogger.Instance; } /// diff --git a/dotnet/src/Extensions/Planning.SequentialPlanner/EmbeddedResource.cs b/dotnet/src/Extensions/Planning.SequentialPlanner/EmbeddedResource.cs index 54eb4b0187b6..608a42a48b9b 100644 --- a/dotnet/src/Extensions/Planning.SequentialPlanner/EmbeddedResource.cs +++ b/dotnet/src/Extensions/Planning.SequentialPlanner/EmbeddedResource.cs @@ -2,6 +2,7 @@ using System.IO; using System.Reflection; +using Microsoft.SemanticKernel.Diagnostics; namespace Microsoft.SemanticKernel.Planning.Sequential; @@ -12,10 +13,10 @@ internal sealed class EmbeddedResource internal static string Read(string name) { var assembly = typeof(EmbeddedResource).GetTypeInfo().Assembly; - if (assembly == null) { throw new PlanningException(PlanningException.ErrorCodes.InvalidConfiguration, $"[{s_namespace}] {name} assembly not found"); } + if (assembly == null) { throw new SKException($"[{s_namespace}] {name} assembly not found"); } using Stream? resource = assembly.GetManifestResourceStream($"{s_namespace}." + name); - if (resource == null) { throw new PlanningException(PlanningException.ErrorCodes.InvalidConfiguration, $"[{s_namespace}] {name} resource not found"); } + if (resource == null) { throw new SKException($"[{s_namespace}] {name} resource not found"); } using var reader = new StreamReader(resource); return reader.ReadToEnd(); diff --git a/dotnet/src/Extensions/Planning.SequentialPlanner/ISequentialPlanner.cs b/dotnet/src/Extensions/Planning.SequentialPlanner/ISequentialPlanner.cs index 7df5180c5cb1..2d8d0a0786ec 100644 --- a/dotnet/src/Extensions/Planning.SequentialPlanner/ISequentialPlanner.cs +++ b/dotnet/src/Extensions/Planning.SequentialPlanner/ISequentialPlanner.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; +using Microsoft.SemanticKernel.Diagnostics; namespace Microsoft.SemanticKernel.Planning.Sequential; @@ -16,6 +17,6 @@ public interface ISequentialPlanner /// The goal to create a plan for. /// The to monitor for cancellation requests. The default is . /// The plan. - /// Thrown when the plan cannot be created. + /// Thrown when the plan cannot be created. Task CreatePlanAsync(string goal, CancellationToken cancellationToken = default); } diff --git a/dotnet/src/Extensions/Planning.SequentialPlanner/InstrumentedSequentialPlanner.cs b/dotnet/src/Extensions/Planning.SequentialPlanner/InstrumentedSequentialPlanner.cs index 7a3c98f3c991..333d27cfccbc 100644 --- a/dotnet/src/Extensions/Planning.SequentialPlanner/InstrumentedSequentialPlanner.cs +++ b/dotnet/src/Extensions/Planning.SequentialPlanner/InstrumentedSequentialPlanner.cs @@ -20,13 +20,13 @@ public sealed class InstrumentedSequentialPlanner : ISequentialPlanner /// Initialize a new instance of the class. /// /// Instance of to decorate. - /// Optional logger. + /// The to use for logging. If null, no logging will be performed. public InstrumentedSequentialPlanner( ISequentialPlanner planner, - ILogger? logger = null) + ILoggerFactory? loggerFactory = null) { this._planner = planner; - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(InstrumentedSequentialPlanner)) : NullLogger.Instance; } /// diff --git a/dotnet/src/Extensions/Planning.SequentialPlanner/SKContextSequentialPlannerExtensions.cs b/dotnet/src/Extensions/Planning.SequentialPlanner/SKContextSequentialPlannerExtensions.cs index 5599fc655e8f..0bd16f39782e 100644 --- a/dotnet/src/Extensions/Planning.SequentialPlanner/SKContextSequentialPlannerExtensions.cs +++ b/dotnet/src/Extensions/Planning.SequentialPlanner/SKContextSequentialPlannerExtensions.cs @@ -111,14 +111,19 @@ private static async Task> GetRelevantFunctionsAsync( IAsyncEnumerable memories, CancellationToken cancellationToken = default) { + ILogger? logger = null; var relevantFunctions = new ConcurrentBag(); await foreach (var memoryEntry in memories.WithCancellation(cancellationToken)) { var function = availableFunctions.FirstOrDefault(x => x.ToFullyQualifiedName() == memoryEntry.Metadata.Id); if (function != null) { - context.Logger.LogDebug("Found relevant function. Relevance Score: {0}, Function: {1}", memoryEntry.Relevance, - function.ToFullyQualifiedName()); + logger ??= context.LoggerFactory.CreateLogger(nameof(SKContext)); + if (logger.IsEnabled(LogLevel.Debug)) + { + logger.LogDebug("Found relevant function. Relevance Score: {0}, Function: {1}", memoryEntry.Relevance, function.ToFullyQualifiedName()); + } + relevantFunctions.Add(function); } } diff --git a/dotnet/src/Extensions/Planning.SequentialPlanner/SequentialPlanParser.cs b/dotnet/src/Extensions/Planning.SequentialPlanner/SequentialPlanParser.cs index 8e40133f27c8..a72cc274bd2e 100644 --- a/dotnet/src/Extensions/Planning.SequentialPlanner/SequentialPlanParser.cs +++ b/dotnet/src/Extensions/Planning.SequentialPlanner/SequentialPlanParser.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Text.RegularExpressions; using System.Xml; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Orchestration; using Microsoft.SemanticKernel.SkillDefinition; @@ -68,7 +69,7 @@ internal static class SequentialPlanParser /// The callback to get a skill function. /// Whether to allow missing functions in the plan on creation. /// The plan. - /// Thrown when the plan xml is invalid. + /// Thrown when the plan xml is invalid. internal static Plan ToPlanFromXml(this string xmlString, string goal, Func getSkillFunction, bool allowMissingFunctions = false) { XmlDocument xmlDoc = new(); @@ -98,12 +99,12 @@ internal static Plan ToPlanFromXml(this string xmlString, string goal, Func CreatePlanAsync(string goal, CancellationToken cancellat { if (string.IsNullOrEmpty(goal)) { - throw new PlanningException(PlanningException.ErrorCodes.InvalidGoal, "The goal specified is empty"); + throw new SKException("The goal specified is empty"); } string relevantFunctionsManual = await this._context.GetFunctionsManualAsync(goal, this.Config, cancellationToken).ConfigureAwait(false); @@ -67,36 +66,20 @@ public async Task CreatePlanAsync(string goal, CancellationToken cancellat if (planResult.ErrorOccurred) { - throw new PlanningException(PlanningException.ErrorCodes.CreatePlanError, $"Error creating plan for goal: {planResult.LastException?.Message}", planResult.LastException); + throw new SKException($"Error creating plan for goal: {planResult.LastException?.Message}", planResult.LastException); } string planResultString = planResult.Result.Trim(); - try - { - var getSkillFunction = this.Config.GetSkillFunction ?? SequentialPlanParser.GetSkillFunction(this._context); - var plan = planResultString.ToPlanFromXml(goal, getSkillFunction, this.Config.AllowMissingFunctions); - - if (plan.Steps.Count == 0) - { - throw new PlanningException(PlanningException.ErrorCodes.CreatePlanError, $"Not possible to create plan for goal with available functions.\nGoal:{goal}\nFunctions:\n{relevantFunctionsManual}"); - } + var getSkillFunction = this.Config.GetSkillFunction ?? SequentialPlanParser.GetSkillFunction(this._context); + var plan = planResultString.ToPlanFromXml(goal, getSkillFunction, this.Config.AllowMissingFunctions); - return plan; - } - catch (PlanningException planException) when (planException.ErrorCode == PlanningException.ErrorCodes.CreatePlanError) - { - throw; - } - catch (PlanningException planException) when (planException.ErrorCode == PlanningException.ErrorCodes.InvalidPlan || - planException.ErrorCode == PlanningException.ErrorCodes.InvalidGoal) - { - throw new PlanningException(PlanningException.ErrorCodes.CreatePlanError, "Unable to create plan", planException); - } - catch (Exception e) + if (plan.Steps.Count == 0) { - throw new PlanningException(PlanningException.ErrorCodes.UnknownError, "Unknown error creating plan", e); + throw new SKException($"Not possible to create plan for goal with available functions.\nGoal:{goal}\nFunctions:\n{relevantFunctionsManual}"); } + + return plan; } private SequentialPlannerConfig Config { get; } diff --git a/dotnet/src/Extensions/Planning.SequentialPlanner/SequentialPlannerExtensions.cs b/dotnet/src/Extensions/Planning.SequentialPlanner/SequentialPlannerExtensions.cs index 4b212c618c8a..8bb087e2f1c2 100644 --- a/dotnet/src/Extensions/Planning.SequentialPlanner/SequentialPlannerExtensions.cs +++ b/dotnet/src/Extensions/Planning.SequentialPlanner/SequentialPlannerExtensions.cs @@ -13,9 +13,9 @@ public static class SequentialPlannerExtensions /// Returns decorated instance of with enabled instrumentation. /// /// Instance of to decorate. - /// Optional logger. - public static ISequentialPlanner WithInstrumentation(this ISequentialPlanner planner, ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public static ISequentialPlanner WithInstrumentation(this ISequentialPlanner planner, ILoggerFactory? loggerFactory = null) { - return new InstrumentedSequentialPlanner(planner, logger); + return new InstrumentedSequentialPlanner(planner, loggerFactory); } } diff --git a/dotnet/src/Extensions/Planning.StepwisePlanner/EmbeddedResource.cs b/dotnet/src/Extensions/Planning.StepwisePlanner/EmbeddedResource.cs index 9e8a711bdb9c..054ad2502966 100644 --- a/dotnet/src/Extensions/Planning.StepwisePlanner/EmbeddedResource.cs +++ b/dotnet/src/Extensions/Planning.StepwisePlanner/EmbeddedResource.cs @@ -2,6 +2,7 @@ using System.IO; using System.Reflection; +using Microsoft.SemanticKernel.Diagnostics; namespace Microsoft.SemanticKernel.Planning.Stepwise; @@ -12,10 +13,10 @@ internal static class EmbeddedResource internal static string Read(string name) { var assembly = typeof(EmbeddedResource).GetTypeInfo().Assembly; - if (assembly == null) { throw new PlanningException(PlanningException.ErrorCodes.InvalidConfiguration, $"[{s_namespace}] {name} assembly not found"); } + if (assembly is null) { throw new SKException($"[{s_namespace}] {name} assembly not found"); } using Stream? resource = assembly.GetManifestResourceStream($"{s_namespace}." + name); - if (resource == null) { throw new PlanningException(PlanningException.ErrorCodes.InvalidConfiguration, $"[{s_namespace}] {name} resource not found"); } + if (resource is null) { throw new SKException($"[{s_namespace}] {name} resource not found"); } using var reader = new StreamReader(resource); return reader.ReadToEnd(); diff --git a/dotnet/src/Extensions/Planning.StepwisePlanner/IStepwisePlanner.cs b/dotnet/src/Extensions/Planning.StepwisePlanner/IStepwisePlanner.cs index f63285cf3839..836b1bca45fd 100644 --- a/dotnet/src/Extensions/Planning.StepwisePlanner/IStepwisePlanner.cs +++ b/dotnet/src/Extensions/Planning.StepwisePlanner/IStepwisePlanner.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. +using Microsoft.SemanticKernel.Diagnostics; + namespace Microsoft.SemanticKernel.Planning.Stepwise; /// @@ -12,6 +14,6 @@ public interface IStepwisePlanner /// /// The goal to create a plan for. /// The plan. - /// Thrown when the plan cannot be created. + /// Thrown when the plan cannot be created. Plan CreatePlan(string goal); } diff --git a/dotnet/src/Extensions/Planning.StepwisePlanner/InstrumentedStepwisePlanner.cs b/dotnet/src/Extensions/Planning.StepwisePlanner/InstrumentedStepwisePlanner.cs index c47d64d6f7cd..f7327ac03c73 100644 --- a/dotnet/src/Extensions/Planning.StepwisePlanner/InstrumentedStepwisePlanner.cs +++ b/dotnet/src/Extensions/Planning.StepwisePlanner/InstrumentedStepwisePlanner.cs @@ -18,13 +18,13 @@ public class InstrumentedStepwisePlanner : IStepwisePlanner /// Initialize a new instance of the class. /// /// Instance of to decorate. - /// Optional logger. + /// The to use for logging. If null, no logging will be performed. public InstrumentedStepwisePlanner( IStepwisePlanner planner, - ILogger? logger = null) + ILoggerFactory? loggerFactory = null) { this._planner = planner; - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(InstrumentedStepwisePlanner)) : NullLogger.Instance; } /// diff --git a/dotnet/src/Extensions/Planning.StepwisePlanner/StepwisePlanner.cs b/dotnet/src/Extensions/Planning.StepwisePlanner/StepwisePlanner.cs index 4557d1a071c9..ea283ba9f19b 100644 --- a/dotnet/src/Extensions/Planning.StepwisePlanner/StepwisePlanner.cs +++ b/dotnet/src/Extensions/Planning.StepwisePlanner/StepwisePlanner.cs @@ -65,7 +65,7 @@ public StepwisePlanner( this._nativeFunctions = this._kernel.ImportSkill(this, RestrictedSkillName); this._context = this._kernel.CreateNewContext(); - this._logger = this._kernel.Logger; + this._logger = this._kernel.LoggerFactory.CreateLogger(nameof(StepwisePlanner)); } /// @@ -73,7 +73,7 @@ public Plan CreatePlan(string goal) { if (string.IsNullOrEmpty(goal)) { - throw new PlanningException(PlanningException.ErrorCodes.InvalidGoal, "The goal specified is empty"); + throw new SKException("The goal specified is empty"); } string functionDescriptions = this.GetFunctionDescriptions(); @@ -115,7 +115,7 @@ public async Task ExecutePlanAsync( if (llmResponse.ErrorOccurred) { - throw new PlanningException(PlanningException.ErrorCodes.UnknownError, $"Error occurred while executing stepwise plan: {llmResponse.LastException?.Message}", llmResponse.LastException); + throw new SKException($"Error occurred while executing stepwise plan: {llmResponse.LastException?.Message}", llmResponse.LastException); } string actionText = llmResponse.Result.Trim(); @@ -337,7 +337,7 @@ private async Task InvokeActionAsync(string actionName, Dictionary ToFullyQualifiedName(f) == actionName); if (targetFunction == null) { - throw new PlanningException(PlanningException.ErrorCodes.UnknownError, $"The function '{actionName}' was not found."); + throw new SKException($"The function '{actionName}' was not found."); } try diff --git a/dotnet/src/Extensions/Planning.StepwisePlanner/StepwisePlannerExtensions.cs b/dotnet/src/Extensions/Planning.StepwisePlanner/StepwisePlannerExtensions.cs index c982cb054b62..673443962709 100644 --- a/dotnet/src/Extensions/Planning.StepwisePlanner/StepwisePlannerExtensions.cs +++ b/dotnet/src/Extensions/Planning.StepwisePlanner/StepwisePlannerExtensions.cs @@ -13,9 +13,9 @@ public static class StepwisePlannerExtensions /// Returns decorated instance of with enabled instrumentation. /// /// Instance of to decorate. - /// Optional logger. - public static IStepwisePlanner WithInstrumentation(this IStepwisePlanner planner, ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public static IStepwisePlanner WithInstrumentation(this IStepwisePlanner planner, ILoggerFactory? loggerFactory = null) { - return new InstrumentedStepwisePlanner(planner, logger); + return new InstrumentedStepwisePlanner(planner, loggerFactory); } } diff --git a/dotnet/src/IntegrationTests/Connectors/Memory/Chroma/ChromaMemoryStoreTests.cs b/dotnet/src/IntegrationTests/Connectors/Memory/Chroma/ChromaMemoryStoreTests.cs index 6add7561e055..6f257b00c440 100644 --- a/dotnet/src/IntegrationTests/Connectors/Memory/Chroma/ChromaMemoryStoreTests.cs +++ b/dotnet/src/IntegrationTests/Connectors/Memory/Chroma/ChromaMemoryStoreTests.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Connectors.Memory.Chroma; using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; @@ -252,11 +251,11 @@ public async Task ItCanGetNearestMatchAsync() // Arrange var collectionName = this.GetRandomCollectionName(); - var expectedRecord1 = this.GetRandomMemoryRecord(embedding: new Embedding(new[] { 10f, 10f, 10f })); - var expectedRecord2 = this.GetRandomMemoryRecord(embedding: new Embedding(new[] { 5f, 5f, 5f })); - var expectedRecord3 = this.GetRandomMemoryRecord(embedding: new Embedding(new[] { 1f, 1f, 1f })); + var expectedRecord1 = this.GetRandomMemoryRecord(embedding: new[] { 10f, 10f, 10f }); + var expectedRecord2 = this.GetRandomMemoryRecord(embedding: new[] { 5f, 5f, 5f }); + var expectedRecord3 = this.GetRandomMemoryRecord(embedding: new[] { 1f, 1f, 1f }); - var searchEmbedding = new Embedding(new[] { 2f, 2f, 2f }); + var searchEmbedding = new[] { 2f, 2f, 2f }; var batch = new List { expectedRecord1, expectedRecord2, expectedRecord3 }; var keys = batch.Select(l => l.Key); @@ -283,11 +282,11 @@ public async Task ItCanGetNearestMatchesAsync() // Arrange var collectionName = this.GetRandomCollectionName(); - var expectedRecord1 = this.GetRandomMemoryRecord(embedding: new Embedding(new[] { 10f, 10f, 10f })); - var expectedRecord2 = this.GetRandomMemoryRecord(embedding: new Embedding(new[] { 5f, 5f, 5f })); - var expectedRecord3 = this.GetRandomMemoryRecord(embedding: new Embedding(new[] { 1f, 1f, 1f })); + var expectedRecord1 = this.GetRandomMemoryRecord(embedding: new[] { 10f, 10f, 10f }); + var expectedRecord2 = this.GetRandomMemoryRecord(embedding: new[] { 5f, 5f, 5f }); + var expectedRecord3 = this.GetRandomMemoryRecord(embedding: new[] { 1f, 1f, 1f }); - var searchEmbedding = new Embedding(new[] { 2f, 2f, 2f }); + var searchEmbedding = new[] { 2f, 2f, 2f }; var batch = new List { expectedRecord1, expectedRecord2, expectedRecord3 }; var keys = batch.Select(l => l.Key); @@ -320,7 +319,7 @@ public async Task ItReturnsNoMatchesFromEmptyCollection() { // Arrange var collectionName = this.GetRandomCollectionName(); - var searchEmbedding = new Embedding(new[] { 2f, 2f, 2f }); + var searchEmbedding = new[] { 2f, 2f, 2f }; await this._chromaMemoryStore.CreateCollectionAsync(collectionName); @@ -424,7 +423,7 @@ private void Dispose(bool disposing) private void AssertMemoryRecordEqual(MemoryRecord expectedRecord, MemoryRecord actualRecord) { Assert.Equal(expectedRecord.Key, actualRecord.Key); - Assert.Equal(expectedRecord.Embedding.Vector, actualRecord.Embedding.Vector); + Assert.True(expectedRecord.Embedding.Span.SequenceEqual(actualRecord.Embedding.Span)); Assert.Equal(expectedRecord.Metadata.Id, actualRecord.Metadata.Id); Assert.Equal(expectedRecord.Metadata.Text, actualRecord.Metadata.Text); Assert.Equal(expectedRecord.Metadata.Description, actualRecord.Metadata.Description); @@ -438,10 +437,10 @@ private string GetRandomCollectionName() return "sk-test-" + Guid.NewGuid(); } - private MemoryRecord GetRandomMemoryRecord(string? key = null, Embedding? embedding = null) + private MemoryRecord GetRandomMemoryRecord(string? key = null, ReadOnlyMemory? embedding = null) { var recordKey = key ?? Guid.NewGuid().ToString(); - var recordEmbedding = embedding ?? new Embedding(new[] { 1f, 3f, 5f }); + var recordEmbedding = embedding ?? new[] { 1f, 3f, 5f }; return MemoryRecord.LocalRecord( id: recordKey, @@ -452,9 +451,9 @@ private MemoryRecord GetRandomMemoryRecord(string? key = null, Embedding? key: recordKey); } - private MemoryRecord GetRandomMemoryRecord(MemoryRecordMetadata metadata, Embedding? embedding = null) + private MemoryRecord GetRandomMemoryRecord(MemoryRecordMetadata metadata, ReadOnlyMemory? embedding = null) { - var recordEmbedding = embedding ?? new Embedding(new[] { 1f, 3f, 5f }); + var recordEmbedding = embedding ?? new[] { 1f, 3f, 5f }; return MemoryRecord.FromMetadata( metadata: metadata, diff --git a/dotnet/src/IntegrationTests/Connectors/Memory/Postgres/PostgresMemoryStoreTests.cs b/dotnet/src/IntegrationTests/Connectors/Memory/Postgres/PostgresMemoryStoreTests.cs index 7fb957ec62e6..5bb8f6b3e590 100644 --- a/dotnet/src/IntegrationTests/Connectors/Memory/Postgres/PostgresMemoryStoreTests.cs +++ b/dotnet/src/IntegrationTests/Connectors/Memory/Postgres/PostgresMemoryStoreTests.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Configuration; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Connectors.Memory.Postgres; using Microsoft.SemanticKernel.Memory; using Npgsql; @@ -125,7 +124,7 @@ public async Task GetAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync() id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 }), + embedding: new float[] { 1, 2, 3 }, key: null, timestamp: null); string collection = "test_collection"; @@ -139,8 +138,8 @@ public async Task GetAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync() // Assert Assert.NotNull(actualDefault); Assert.NotNull(actualWithEmbedding); - Assert.Empty(actualDefault.Embedding.Vector); - Assert.NotEmpty(actualWithEmbedding.Embedding.Vector); + Assert.True(actualDefault.Embedding.IsEmpty); + Assert.False(actualWithEmbedding.Embedding.IsEmpty); } [Fact(Skip = SkipReason)] @@ -152,7 +151,7 @@ public async Task ItCanUpsertAndRetrieveARecordWithNoTimestampAsync() id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 }), + embedding: new ReadOnlyMemory(new float[] { 1, 2, 3 }), key: null, timestamp: null); string collection = "test_collection"; @@ -166,7 +165,7 @@ public async Task ItCanUpsertAndRetrieveARecordWithNoTimestampAsync() Assert.NotNull(actual); Assert.Equal(testRecord.Metadata.Id, key); Assert.Equal(testRecord.Metadata.Id, actual.Key); - Assert.Equal(testRecord.Embedding.Vector, actual.Embedding.Vector); + Assert.True(testRecord.Embedding.Span.SequenceEqual(actual.Embedding.Span)); Assert.Equal(testRecord.Metadata.Text, actual.Metadata.Text); Assert.Equal(testRecord.Metadata.Description, actual.Metadata.Description); Assert.Equal(testRecord.Metadata.ExternalSourceName, actual.Metadata.ExternalSourceName); @@ -182,7 +181,7 @@ public async Task ItCanUpsertAndRetrieveARecordWithTimestampAsync() id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 }), + embedding: new float[] { 1, 2, 3 }, key: null, timestamp: DateTimeOffset.FromUnixTimeMilliseconds(DateTimeOffset.UtcNow.ToUnixTimeMilliseconds())); string collection = "test_collection"; @@ -196,7 +195,7 @@ public async Task ItCanUpsertAndRetrieveARecordWithTimestampAsync() Assert.NotNull(actual); Assert.Equal(testRecord.Metadata.Id, key); Assert.Equal(testRecord.Metadata.Id, actual.Key); - Assert.Equal(testRecord.Embedding.Vector, actual.Embedding.Vector); + Assert.True(testRecord.Embedding.Span.SequenceEqual(actual.Embedding.Span)); Assert.Equal(testRecord.Metadata.Text, actual.Metadata.Text); Assert.Equal(testRecord.Metadata.Description, actual.Metadata.Description); Assert.Equal(testRecord.Metadata.ExternalSourceName, actual.Metadata.ExternalSourceName); @@ -214,12 +213,12 @@ public async Task UpsertReplacesExistingRecordWithSameIdAsync() id: commonId, text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); MemoryRecord testRecord2 = MemoryRecord.LocalRecord( id: commonId, text: "text2", description: "description2", - embedding: new Embedding(new float[] { 1, 2, 4 })); + embedding: new float[] { 1, 2, 4 }); string collection = "test_collection"; // Act @@ -232,8 +231,8 @@ public async Task UpsertReplacesExistingRecordWithSameIdAsync() Assert.NotNull(actual); Assert.Equal(testRecord.Metadata.Id, key); Assert.Equal(testRecord2.Metadata.Id, actual.Key); - Assert.NotEqual(testRecord.Embedding.Vector, actual.Embedding.Vector); - Assert.Equal(testRecord2.Embedding.Vector, actual.Embedding.Vector); + Assert.True(testRecord.Embedding.Span.SequenceEqual(actual.Embedding.Span)); + Assert.True(testRecord2.Embedding.Span.SequenceEqual(actual.Embedding.Span)); Assert.NotEqual(testRecord.Metadata.Text, actual.Metadata.Text); Assert.Equal(testRecord2.Metadata.Description, actual.Metadata.Description); } @@ -247,7 +246,7 @@ public async Task ExistingRecordCanBeRemovedAsync() id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); string collection = "test_collection"; // Act @@ -313,7 +312,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() { // Arrange PostgresMemoryStore memoryStore = this.CreateMemoryStore(); - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + var compareEmbedding = new float[] { 1, 1, 1 }; int topN = 4; string collection = "test_collection"; await memoryStore.CreateCollectionAsync(collection); @@ -322,7 +321,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); _ = await memoryStore.UpsertAsync(collection, testRecord); i++; @@ -330,7 +329,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -1, -1 })); + embedding: new float[] { -1, -1, -1 }); _ = await memoryStore.UpsertAsync(collection, testRecord); i++; @@ -338,7 +337,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); _ = await memoryStore.UpsertAsync(collection, testRecord); i++; @@ -346,7 +345,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -2, -3 })); + embedding: new float[] { -1, -2, -3 }); _ = await memoryStore.UpsertAsync(collection, testRecord); i++; @@ -354,7 +353,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, -1, -2 })); + embedding: new float[] { 1, -1, -2 }); _ = await memoryStore.UpsertAsync(collection, testRecord); // Act @@ -375,7 +374,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( { // Arrange PostgresMemoryStore memoryStore = this.CreateMemoryStore(); - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + var compareEmbedding = new float[] { 1, 1, 1 }; string collection = "test_collection"; await memoryStore.CreateCollectionAsync(collection); int i = 0; @@ -383,7 +382,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); _ = await memoryStore.UpsertAsync(collection, testRecord); i++; @@ -391,7 +390,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -1, -1 })); + embedding: new float[] { -1, -1, -1 }); _ = await memoryStore.UpsertAsync(collection, testRecord); i++; @@ -399,7 +398,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); _ = await memoryStore.UpsertAsync(collection, testRecord); i++; @@ -407,7 +406,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -2, -3 })); + embedding: new float[] { -1, -2, -3 }); _ = await memoryStore.UpsertAsync(collection, testRecord); i++; @@ -415,7 +414,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, -1, -2 })); + embedding: new float[] { 1, -1, -2 }); _ = await memoryStore.UpsertAsync(collection, testRecord); // Act @@ -426,8 +425,8 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( // Assert Assert.NotNull(topNResultDefault); Assert.NotNull(topNResultWithEmbedding); - Assert.Empty(topNResultDefault.Value.Item1.Embedding.Vector); - Assert.NotEmpty(topNResultWithEmbedding.Value.Item1.Embedding.Vector); + Assert.True(topNResultDefault.Value.Item1.Embedding.IsEmpty); + Assert.False(topNResultWithEmbedding.Value.Item1.Embedding.IsEmpty); } [Fact(Skip = SkipReason)] @@ -435,7 +434,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() { // Arrange PostgresMemoryStore memoryStore = this.CreateMemoryStore(); - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + var compareEmbedding = new float[] { 1, 1, 1 }; string collection = "test_collection"; await memoryStore.CreateCollectionAsync(collection); int i = 0; @@ -443,7 +442,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); _ = await memoryStore.UpsertAsync(collection, testRecord); i++; @@ -451,7 +450,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -1, -1 })); + embedding: new float[] { -1, -1, -1 }); _ = await memoryStore.UpsertAsync(collection, testRecord); i++; @@ -459,7 +458,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); _ = await memoryStore.UpsertAsync(collection, testRecord); i++; @@ -467,7 +466,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -2, -3 })); + embedding: new float[] { -1, -2, -3 }); _ = await memoryStore.UpsertAsync(collection, testRecord); i++; @@ -475,7 +474,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, -1, -2 })); + embedding: new float[] { 1, -1, -2 }); _ = await memoryStore.UpsertAsync(collection, testRecord); // Act @@ -493,7 +492,7 @@ public async Task GetNearestMatchesDifferentiatesIdenticalVectorsByKeyAsync() { // Arrange PostgresMemoryStore memoryStore = this.CreateMemoryStore(); - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + var compareEmbedding = new float[] { 1, 1, 1 }; int topN = 4; string collection = "test_collection"; await memoryStore.CreateCollectionAsync(collection); @@ -504,7 +503,7 @@ public async Task GetNearestMatchesDifferentiatesIdenticalVectorsByKeyAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); _ = await memoryStore.UpsertAsync(collection, testRecord); } @@ -682,7 +681,7 @@ private IEnumerable CreateBatchRecords(int numRecords) id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); records = records.Append(testRecord); } @@ -692,7 +691,7 @@ private IEnumerable CreateBatchRecords(int numRecords) externalId: "test" + i, sourceName: "sourceName" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); records = records.Append(testRecord); } diff --git a/dotnet/src/IntegrationTests/Connectors/OpenAI/AzureOpenAICompletionTests.cs b/dotnet/src/IntegrationTests/Connectors/OpenAI/AzureOpenAICompletionTests.cs index ab82f23cf5bc..be284740ba4b 100644 --- a/dotnet/src/IntegrationTests/Connectors/OpenAI/AzureOpenAICompletionTests.cs +++ b/dotnet/src/IntegrationTests/Connectors/OpenAI/AzureOpenAICompletionTests.cs @@ -48,7 +48,7 @@ public async Task AzureOpenAIChatNoHttpRetryPolicyTestShouldThrowAsync(string pr DefaultHttpRetryHandlerFactory defaultHttpRetryHandlerFactory = new(httpRetryConfig); var target = new KernelBuilder() - .WithLogger(this._logger) + .WithLoggerFactory(this._logger) .WithAzureChatCompletionService(configuration.ChatDeploymentName!, configuration.Endpoint, configuration.ApiKey) .WithRetryHandlerFactory(defaultHttpRetryHandlerFactory) .Build(); @@ -77,7 +77,7 @@ public async Task AzureOpenAIChatNoHttpRetryPolicyCustomClientShouldThrowAsync(s var openAIClient = new OpenAIClient(new Uri(configuration.Endpoint), new AzureKeyCredential(configuration.ApiKey), clientOptions); var target = new KernelBuilder() - .WithLogger(this._logger) + .WithLoggerFactory(this._logger) .WithAzureChatCompletionService(configuration.ChatDeploymentName!, openAIClient) .Build(); diff --git a/dotnet/src/IntegrationTests/Connectors/OpenAI/OpenAICompletionTests.cs b/dotnet/src/IntegrationTests/Connectors/OpenAI/OpenAICompletionTests.cs index 4766688730f9..e58219709b3f 100644 --- a/dotnet/src/IntegrationTests/Connectors/OpenAI/OpenAICompletionTests.cs +++ b/dotnet/src/IntegrationTests/Connectors/OpenAI/OpenAICompletionTests.cs @@ -47,7 +47,7 @@ public async Task OpenAITestAsync(string prompt, string expectedAnswerContains) Assert.NotNull(openAIConfiguration); IKernel target = Kernel.Builder - .WithLogger(this._logger) + .WithLoggerFactory(this._logger) .WithOpenAITextCompletionService( serviceId: openAIConfiguration.ServiceId, modelId: openAIConfiguration.ModelId, @@ -69,7 +69,7 @@ public async Task OpenAITestAsync(string prompt, string expectedAnswerContains) public async Task OpenAIChatAsTextTestAsync(string prompt, string expectedAnswerContains) { // Arrange - KernelBuilder builder = Kernel.Builder.WithLogger(this._logger); + KernelBuilder builder = Kernel.Builder.WithLoggerFactory(this._logger); this.ConfigureChatOpenAI(builder); @@ -88,7 +88,7 @@ public async Task OpenAIChatAsTextTestAsync(string prompt, string expectedAnswer public async Task CanUseOpenAiChatForTextCompletionAsync() { // Note: we use OpenAi Chat Completion and GPT 3.5 Turbo - KernelBuilder builder = Kernel.Builder.WithLogger(this._logger); + KernelBuilder builder = Kernel.Builder.WithLoggerFactory(this._logger); this.ConfigureChatOpenAI(builder); IKernel target = builder.Build(); @@ -114,7 +114,7 @@ public async Task AzureOpenAITestAsync(bool useChatModel, string prompt, string var azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get(); Assert.NotNull(azureOpenAIConfiguration); - var builder = Kernel.Builder.WithLogger(this._logger); + var builder = Kernel.Builder.WithLoggerFactory(this._logger); if (useChatModel) { @@ -152,7 +152,7 @@ public async Task OpenAIHttpRetryPolicyTestAsync(string prompt, string expectedO Assert.NotNull(openAIConfiguration); IKernel target = Kernel.Builder - .WithLogger(this._testOutputHelper) + .WithLoggerFactory(this._testOutputHelper) .Configure(c => c.SetDefaultHttpRetryConfig(retryConfig)) .WithOpenAITextCompletionService( serviceId: openAIConfiguration.ServiceId, @@ -179,7 +179,7 @@ public async Task AzureOpenAIHttpRetryPolicyTestAsync(string prompt, string expe var retryConfig = new HttpRetryConfig(); retryConfig.RetryableStatusCodes.Add(HttpStatusCode.Unauthorized); KernelBuilder builder = Kernel.Builder - .WithLogger(this._testOutputHelper) + .WithLoggerFactory(this._testOutputHelper) .Configure(c => c.SetDefaultHttpRetryConfig(retryConfig)); var azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get(); @@ -237,7 +237,7 @@ public async Task AzureOpenAIHttpInvalidKeyShouldReturnErrorDetailAsync() Assert.NotNull(azureOpenAIConfiguration); IKernel target = Kernel.Builder - .WithLogger(this._testOutputHelper) + .WithLoggerFactory(this._testOutputHelper) .WithAzureTextCompletionService( deploymentName: azureOpenAIConfiguration.DeploymentName, endpoint: azureOpenAIConfiguration.Endpoint, @@ -265,7 +265,7 @@ public async Task AzureOpenAIHttpExceededMaxTokensShouldReturnErrorDetailAsync() // Arrange IKernel target = Kernel.Builder - .WithLogger(this._testOutputHelper) + .WithLoggerFactory(this._testOutputHelper) .WithAzureTextCompletionService( deploymentName: azureOpenAIConfiguration.DeploymentName, endpoint: azureOpenAIConfiguration.Endpoint, @@ -295,7 +295,7 @@ public async Task CompletionWithDifferentLineEndingsAsync(string lineEnding, AIS const string ExpectedAnswerContains = "John"; - IKernel target = Kernel.Builder.WithLogger(this._logger).Build(); + IKernel target = Kernel.Builder.WithLoggerFactory(this._logger).Build(); this._serviceConfiguration[service](target); @@ -315,7 +315,7 @@ public async Task AzureOpenAIInvokePromptTestAsync() var azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get(); Assert.NotNull(azureOpenAIConfiguration); - var builder = Kernel.Builder.WithLogger(this._logger); + var builder = Kernel.Builder.WithLoggerFactory(this._logger); this.ConfigureAzureOpenAI(builder); IKernel target = builder.Build(); @@ -337,7 +337,7 @@ public async Task AzureOpenAIDefaultValueTestAsync() var azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get(); Assert.NotNull(azureOpenAIConfiguration); - var builder = Kernel.Builder.WithLogger(this._logger); + var builder = Kernel.Builder.WithLoggerFactory(this._logger); this.ConfigureAzureOpenAI(builder); IKernel target = builder.Build(); diff --git a/dotnet/src/IntegrationTests/Connectors/OpenAI/OpenAITextEmbeddingTests.cs b/dotnet/src/IntegrationTests/Connectors/OpenAI/OpenAITextEmbeddingTests.cs index a614db659f6d..83fb12cf4163 100644 --- a/dotnet/src/IntegrationTests/Connectors/OpenAI/OpenAITextEmbeddingTests.cs +++ b/dotnet/src/IntegrationTests/Connectors/OpenAI/OpenAITextEmbeddingTests.cs @@ -46,7 +46,7 @@ public async Task OpenAITestAsync(string testInputString) var batchResult = await embeddingGenerator.GenerateEmbeddingsAsync(new List { testInputString, testInputString, testInputString }); // Assert - Assert.Equal(AdaVectorLength, singleResult.Count); + Assert.Equal(AdaVectorLength, singleResult.Length); Assert.Equal(3, batchResult.Count); } @@ -67,7 +67,7 @@ public async Task AzureOpenAITestAsync(string testInputString) var batchResult = await embeddingGenerator.GenerateEmbeddingsAsync(new List { testInputString, testInputString, testInputString }); // Assert - Assert.Equal(AdaVectorLength, singleResult.Count); + Assert.Equal(AdaVectorLength, singleResult.Length); Assert.Equal(3, batchResult.Count); } diff --git a/dotnet/src/IntegrationTests/Connectors/Weaviate/WeaviateMemoryStoreTests.cs b/dotnet/src/IntegrationTests/Connectors/Weaviate/WeaviateMemoryStoreTests.cs index 0c899d3fbb6d..dbdc851eb902 100644 --- a/dotnet/src/IntegrationTests/Connectors/Weaviate/WeaviateMemoryStoreTests.cs +++ b/dotnet/src/IntegrationTests/Connectors/Weaviate/WeaviateMemoryStoreTests.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Connectors.Memory.Weaviate; using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; @@ -125,7 +124,7 @@ public async Task CrudOperationsAsync() var id = Guid.NewGuid().ToString(); var collectionName = "SK" + Guid.NewGuid(); var timestamp = new DateTimeOffset(2023, 1, 1, 1, 1, 1, new(0)); - var embedding = new Embedding(new[] { 1f, 1f, 1f }); + var embedding = new[] { 1f, 1f, 1f }; var memoryRecord = MemoryRecord.LocalRecord( id: id, @@ -148,7 +147,7 @@ public async Task CrudOperationsAsync() Assert.Equal(id, memoryRecordResultNoVector.Key); Assert.Equal(timestamp, memoryRecordResultNoVector.Timestamp); - Assert.Equal(Array.Empty(), memoryRecordResultNoVector.Embedding.Vector); + Assert.True(memoryRecordResultNoVector.Embedding.IsEmpty); Assert.True(memoryRecordResultNoVector.HasTimestamp); Assert.Equal(memoryRecordResultNoVector.Metadata.Id, memoryRecordResultNoVector.Metadata.Id); Assert.Equal(memoryRecordResultNoVector.Metadata.AdditionalMetadata, memoryRecordResultNoVector.Metadata.AdditionalMetadata); @@ -165,7 +164,7 @@ public async Task CrudOperationsAsync() Assert.Equal(id, memoryRecordResultWithVector.Key); Assert.Equal(timestamp, memoryRecordResultWithVector.Timestamp); - Assert.Equal(memoryRecord.Embedding.Vector, memoryRecordResultWithVector.Embedding.Vector); + Assert.True(memoryRecord.Embedding.Span.SequenceEqual(memoryRecordResultWithVector.Embedding.Span)); Assert.True(memoryRecordResultWithVector.HasTimestamp); Assert.Equal(memoryRecordResultNoVector.Metadata.Id, memoryRecordResultWithVector.Metadata.Id); Assert.Equal(memoryRecordResultNoVector.Metadata.AdditionalMetadata, memoryRecordResultWithVector.Metadata.AdditionalMetadata); @@ -189,15 +188,15 @@ public async Task BatchCrudOperationsAsync() var id1 = Guid.NewGuid().ToString(); var timestamp1 = new DateTimeOffset(2023, 1, 1, 1, 1, 1, new(0)); - var embedding1 = new Embedding(new[] { 1f, 1f, 1f }); + var embedding1 = new[] { 1f, 1f, 1f }; var id2 = Guid.NewGuid().ToString(); var timestamp2 = new DateTimeOffset(2023, 1, 1, 1, 1, 1, new(0)); - var embedding2 = new Embedding(new[] { 2f, 2f, 2f }); + var embedding2 = new[] { 2f, 2f, 2f }; var id3 = Guid.NewGuid().ToString(); var timestamp3 = new DateTimeOffset(2023, 1, 1, 1, 1, 1, new(0)); - var embedding3 = new Embedding(new[] { 3f, 3f, 3f }); + var embedding3 = new[] { 3f, 3f, 3f }; var memoryRecord1 = MemoryRecord.LocalRecord( id: id1, @@ -239,7 +238,7 @@ public async Task BatchCrudOperationsAsync() Assert.Equal(id3, first.Item1.Key); Assert.Equal(memoryRecord3.Timestamp, first.Item1.Timestamp); - Assert.Equal(memoryRecord3.Embedding.Vector, first.Item1.Embedding.Vector); + Assert.True(memoryRecord3.Embedding.Span.SequenceEqual(first.Item1.Embedding.Span)); Assert.True(first.Item1.HasTimestamp); Assert.Equal(memoryRecord3.Metadata.Id, first.Item1.Metadata.Id); Assert.Equal(memoryRecord3.Metadata.AdditionalMetadata, first.Item1.Metadata.AdditionalMetadata); @@ -250,7 +249,7 @@ public async Task BatchCrudOperationsAsync() Assert.Equal(id2, second.Item1.Key); Assert.Equal(memoryRecord2.Timestamp, second.Item1.Timestamp); - Assert.Equal(memoryRecord2.Embedding.Vector, second.Item1.Embedding.Vector); + Assert.True(memoryRecord2.Embedding.Span.SequenceEqual(second.Item1.Embedding.Span)); Assert.True(second.Item1.HasTimestamp); Assert.Equal(memoryRecord2.Metadata.Id, second.Item1.Metadata.Id); Assert.Equal(memoryRecord2.Metadata.AdditionalMetadata, second.Item1.Metadata.AdditionalMetadata); @@ -262,7 +261,7 @@ public async Task BatchCrudOperationsAsync() var closest = await this.weaviateMemoryStore.GetNearestMatchAsync(collectionName, embedding1, 0.8, true); Assert.Equal(id3, closest!.Value.Item1.Key); Assert.Equal(memoryRecord3.Timestamp, closest.Value.Item1.Timestamp); - Assert.Equal(memoryRecord3.Embedding.Vector, closest.Value.Item1.Embedding.Vector); + Assert.True(memoryRecord3.Embedding.Span.SequenceEqual(closest.Value.Item1.Embedding.Span)); Assert.True(closest.Value.Item1.HasTimestamp); Assert.Equal(memoryRecord3.Metadata.Id, closest.Value.Item1.Metadata.Id); Assert.Equal(memoryRecord3.Metadata.AdditionalMetadata, closest.Value.Item1.Metadata.AdditionalMetadata); diff --git a/dotnet/src/IntegrationTests/Planning/PlanTests.cs b/dotnet/src/IntegrationTests/Planning/PlanTests.cs index ff885ffea801..9635da176dd7 100644 --- a/dotnet/src/IntegrationTests/Planning/PlanTests.cs +++ b/dotnet/src/IntegrationTests/Planning/PlanTests.cs @@ -21,7 +21,7 @@ public sealed class PlanTests : IDisposable { public PlanTests(ITestOutputHelper output) { - this._logger = NullLogger.Instance; //new XunitLogger(output); + this._loggerFactory = NullLoggerFactory.Instance; this._testOutputHelper = new RedirectOutput(output); // Load configuration @@ -474,7 +474,7 @@ private IKernel InitializeKernel(bool useEmbeddings = false, bool useChatModel = AzureOpenAIConfiguration? azureOpenAIEmbeddingsConfiguration = this._configuration.GetSection("AzureOpenAIEmbeddings").Get(); Assert.NotNull(azureOpenAIEmbeddingsConfiguration); - var builder = Kernel.Builder.WithLogger(this._logger); + var builder = Kernel.Builder.WithLoggerFactory(this._loggerFactory); if (useChatModel) { @@ -510,7 +510,7 @@ private IKernel InitializeKernel(bool useEmbeddings = false, bool useChatModel = return kernel; } - private readonly ILogger _logger; + private readonly ILoggerFactory _loggerFactory; private readonly RedirectOutput _testOutputHelper; private readonly IConfigurationRoot _configuration; @@ -529,7 +529,7 @@ private void Dispose(bool disposing) { if (disposing) { - if (this._logger is IDisposable ld) + if (this._loggerFactory is IDisposable ld) { ld.Dispose(); } diff --git a/dotnet/src/IntegrationTests/Planning/SequentialPlanner/SequentialPlannerTests.cs b/dotnet/src/IntegrationTests/Planning/SequentialPlanner/SequentialPlannerTests.cs index 5f968f0ad88c..6a9668946897 100644 --- a/dotnet/src/IntegrationTests/Planning/SequentialPlanner/SequentialPlannerTests.cs +++ b/dotnet/src/IntegrationTests/Planning/SequentialPlanner/SequentialPlannerTests.cs @@ -19,7 +19,7 @@ public sealed class SequentialPlannerTests : IDisposable { public SequentialPlannerTests(ITestOutputHelper output) { - this._logger = NullLogger.Instance; //new XunitLogger(output); + this._logger = NullLoggerFactory.Instance; this._testOutputHelper = new RedirectOutput(output); // Load configuration @@ -111,7 +111,7 @@ private IKernel InitializeKernel(bool useEmbeddings = false, bool useChatModel = AzureOpenAIConfiguration? azureOpenAIEmbeddingsConfiguration = this._configuration.GetSection("AzureOpenAIEmbeddings").Get(); Assert.NotNull(azureOpenAIEmbeddingsConfiguration); - var builder = Kernel.Builder.WithLogger(this._logger); + var builder = Kernel.Builder.WithLoggerFactory(this._logger); if (useChatModel) { @@ -142,7 +142,7 @@ private IKernel InitializeKernel(bool useEmbeddings = false, bool useChatModel = return kernel; } - private readonly ILogger _logger; + private readonly ILoggerFactory _logger; private readonly RedirectOutput _testOutputHelper; private readonly IConfigurationRoot _configuration; diff --git a/dotnet/src/IntegrationTests/Planning/StepwisePlanner/StepwisePlannerTests.cs b/dotnet/src/IntegrationTests/Planning/StepwisePlanner/StepwisePlannerTests.cs index 7982ca2a42b1..a71de0cba89e 100644 --- a/dotnet/src/IntegrationTests/Planning/StepwisePlanner/StepwisePlannerTests.cs +++ b/dotnet/src/IntegrationTests/Planning/StepwisePlanner/StepwisePlannerTests.cs @@ -24,7 +24,7 @@ public sealed class StepwisePlannerTests : IDisposable public StepwisePlannerTests(ITestOutputHelper output) { - this._logger = NullLogger.Instance; //new XunitLogger(output); + this._loggerFactory = NullLoggerFactory.Instance; this._testOutputHelper = new RedirectOutput(output); // Load configuration @@ -104,7 +104,7 @@ private IKernel InitializeKernel(bool useEmbeddings = false, bool useChatModel = AzureOpenAIConfiguration? azureOpenAIEmbeddingsConfiguration = this._configuration.GetSection("AzureOpenAIEmbeddings").Get(); Assert.NotNull(azureOpenAIEmbeddingsConfiguration); - var builder = Kernel.Builder.WithLogger(this._logger); + var builder = Kernel.Builder.WithLoggerFactory(this._loggerFactory); if (useChatModel) { @@ -135,7 +135,7 @@ private IKernel InitializeKernel(bool useEmbeddings = false, bool useChatModel = return kernel; } - private readonly ILogger _logger; + private readonly ILoggerFactory _loggerFactory; private readonly RedirectOutput _testOutputHelper; private readonly IConfigurationRoot _configuration; @@ -154,7 +154,7 @@ private void Dispose(bool disposing) { if (disposing) { - if (this._logger is IDisposable ld) + if (this._loggerFactory is IDisposable ld) { ld.Dispose(); } diff --git a/dotnet/src/IntegrationTests/RedirectOutput.cs b/dotnet/src/IntegrationTests/RedirectOutput.cs index 64c374c57c78..9191a64d9e08 100644 --- a/dotnet/src/IntegrationTests/RedirectOutput.cs +++ b/dotnet/src/IntegrationTests/RedirectOutput.cs @@ -8,7 +8,7 @@ namespace SemanticKernel.IntegrationTests; -public class RedirectOutput : TextWriter, ILogger +public class RedirectOutput : TextWriter, ILogger, ILoggerFactory { private readonly ITestOutputHelper _output; private readonly StringBuilder _logs; @@ -48,4 +48,8 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except this._output?.WriteLine(message); this._logs.AppendLine(message); } + + public ILogger CreateLogger(string categoryName) => this; + + public void AddProvider(ILoggerProvider provider) => throw new NotSupportedException(); } diff --git a/dotnet/src/IntegrationTests/SkillDefinition/InlineFunctionsDefinitionExtensionsTests.cs b/dotnet/src/IntegrationTests/SkillDefinition/InlineFunctionsDefinitionExtensionsTests.cs index 1bfdfe9307df..56d223ad087b 100644 --- a/dotnet/src/IntegrationTests/SkillDefinition/InlineFunctionsDefinitionExtensionsTests.cs +++ b/dotnet/src/IntegrationTests/SkillDefinition/InlineFunctionsDefinitionExtensionsTests.cs @@ -27,7 +27,7 @@ public async Task ItSupportsFunctionCalls() { var builder = Kernel.Builder .WithAIService(null, new RedirectTextCompletion(), true) - .WithLogger(this._logger); + .WithLoggerFactory(this._logger); IKernel target = builder.Build(); var emailSkill = target.ImportSkill(new EmailSkillFake()); @@ -48,7 +48,7 @@ public async Task ItSupportsFunctionCallsWithInput() { var builder = Kernel.Builder .WithAIService(null, new RedirectTextCompletion(), true) - .WithLogger(this._logger); + .WithLoggerFactory(this._logger); IKernel target = builder.Build(); var emailSkill = target.ImportSkill(new EmailSkillFake()); diff --git a/dotnet/src/IntegrationTests/TemplateLanguage/PromptTemplateEngineTests.cs b/dotnet/src/IntegrationTests/TemplateLanguage/PromptTemplateEngineTests.cs index 00d7e55c57fc..ef2ff5b2b0e3 100644 --- a/dotnet/src/IntegrationTests/TemplateLanguage/PromptTemplateEngineTests.cs +++ b/dotnet/src/IntegrationTests/TemplateLanguage/PromptTemplateEngineTests.cs @@ -6,6 +6,7 @@ using System.IO; using System.Threading.Tasks; using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.SkillDefinition; using Microsoft.SemanticKernel.TemplateEngine; using Xunit; @@ -144,7 +145,7 @@ public async Task ItHandleEdgeCasesAsync(string template, string expectedResult) this._logger.WriteLine("expected: " + expectedResult); if (expectedResult.StartsWith("ERROR", StringComparison.OrdinalIgnoreCase)) { - await Assert.ThrowsAsync( + await Assert.ThrowsAsync( async () => await this._target.RenderAsync(template, kernel.CreateNewContext())); } else diff --git a/dotnet/src/IntegrationTests/WebSkill/WebSkillTests.cs b/dotnet/src/IntegrationTests/WebSkill/WebSkillTests.cs index 99e7094f16d9..912a086184db 100644 --- a/dotnet/src/IntegrationTests/WebSkill/WebSkillTests.cs +++ b/dotnet/src/IntegrationTests/WebSkill/WebSkillTests.cs @@ -43,7 +43,7 @@ public WebSkillTests(ITestOutputHelper output) public async Task BingSkillTestAsync(string prompt, string expectedAnswerContains) { // Arrange - IKernel kernel = Kernel.Builder.WithLogger(this._logger).Build(); + IKernel kernel = Kernel.Builder.WithLoggerFactory(this._logger).Build(); using XunitLogger connectorLogger = new(this._output); BingConnector connector = new(this._bingApiKey, connectorLogger); @@ -66,7 +66,7 @@ public async Task BingSkillTestAsync(string prompt, string expectedAnswerContain public async Task WebFileDownloadSkillFileTestAsync() { // Arrange - IKernel kernel = Kernel.Builder.WithLogger(this._logger).Build(); + IKernel kernel = Kernel.Builder.WithLoggerFactory(this._logger).Build(); using XunitLogger skillLogger = new(this._output); var skill = new WebFileDownloadSkill(skillLogger); var download = kernel.ImportSkill(skill, "WebFileDownload"); diff --git a/dotnet/src/IntegrationTests/XunitLogger.cs b/dotnet/src/IntegrationTests/XunitLogger.cs index 9118d31676a8..803ad87eda12 100644 --- a/dotnet/src/IntegrationTests/XunitLogger.cs +++ b/dotnet/src/IntegrationTests/XunitLogger.cs @@ -9,7 +9,7 @@ namespace SemanticKernel.IntegrationTests; /// /// A logger that writes to the Xunit test output /// -internal sealed class XunitLogger : ILogger, IDisposable +internal sealed class XunitLogger : ILoggerFactory, ILogger, IDisposable { private readonly ITestOutputHelper _output; @@ -37,4 +37,8 @@ public void Dispose() // This class is marked as disposable to support the BeginScope method. // However, there is no need to dispose anything. } + + public ILogger CreateLogger(string categoryName) => this; + + public void AddProvider(ILoggerProvider provider) => throw new NotSupportedException(); } diff --git a/dotnet/src/InternalUtilities/src/Diagnostics/Verify.cs b/dotnet/src/InternalUtilities/src/Diagnostics/Verify.cs index 79ad183bd094..c179ec620955 100644 --- a/dotnet/src/InternalUtilities/src/Diagnostics/Verify.cs +++ b/dotnet/src/InternalUtilities/src/Diagnostics/Verify.cs @@ -108,9 +108,7 @@ internal static void ParametersUniqueness(IList parameters) if (!seen.Add(p.Name)) { - throw new KernelException( - KernelException.ErrorCodes.InvalidFunctionDescription, - $"The function has two or more parameters with the same name '{p.Name}'"); + throw new SKException($"The function has two or more parameters with the same name '{p.Name}'"); } } } @@ -118,9 +116,7 @@ internal static void ParametersUniqueness(IList parameters) [DoesNotReturn] private static void ThrowInvalidName(string kind, string name) => - throw new KernelException( - KernelException.ErrorCodes.InvalidFunctionDescription, - $"A {kind} can contain only ASCII letters, digits, and underscores: '{name}' is not a valid name."); + throw new SKException($"A {kind} can contain only ASCII letters, digits, and underscores: '{name}' is not a valid name."); [DoesNotReturn] internal static void ThrowArgumentNullException(string? paramName) => diff --git a/dotnet/src/InternalUtilities/src/Http/HttpClientProvider.cs b/dotnet/src/InternalUtilities/src/Http/HttpClientProvider.cs index c62d22ee607c..d248878acae4 100644 --- a/dotnet/src/InternalUtilities/src/Http/HttpClientProvider.cs +++ b/dotnet/src/InternalUtilities/src/Http/HttpClientProvider.cs @@ -14,13 +14,13 @@ internal static class HttpClientProvider /// /// The kernel configuration. /// An optional pre-existing instance of HttpClient. - /// An optional logger. + /// The to use for logging. If null, no logging will be performed. /// An instance of HttpClient. - public static HttpClient GetHttpClient(KernelConfig config, HttpClient? httpClient, ILogger? logger) + public static HttpClient GetHttpClient(KernelConfig config, HttpClient? httpClient, ILoggerFactory? loggerFactory) { if (httpClient == null) { - var retryHandler = config.HttpHandlerFactory.Create(logger); + var retryHandler = config.HttpHandlerFactory.Create(loggerFactory); retryHandler.InnerHandler = NonDisposableHttpClientHandler.Instance; return new HttpClient(retryHandler, false); // We should refrain from disposing the underlying SK default HttpClient handler as it would impact other HTTP clients that utilize the same handler. } diff --git a/dotnet/src/InternalUtilities/src/Http/HttpRequest.cs b/dotnet/src/InternalUtilities/src/Http/HttpRequest.cs index 164baac1ffa6..206c6e18ae77 100644 --- a/dotnet/src/InternalUtilities/src/Http/HttpRequest.cs +++ b/dotnet/src/InternalUtilities/src/Http/HttpRequest.cs @@ -5,6 +5,7 @@ using System.Net.Http.Headers; using System.Text; using System.Text.Json; +using Microsoft.SemanticKernel.Text; namespace Microsoft.SemanticKernel; @@ -43,7 +44,7 @@ private static HttpRequestMessage CreateRequest(HttpMethod method, Uri url, obje { byte[] utf8Bytes = payload is string s ? Encoding.UTF8.GetBytes(s) : - JsonSerializer.SerializeToUtf8Bytes(payload); + JsonSerializer.SerializeToUtf8Bytes(payload, s_jsonSerializerOptions); content = new ByteArrayContent(utf8Bytes); content.Headers.ContentType = new MediaTypeHeaderValue("application/json") { CharSet = "utf-8" }; @@ -51,4 +52,13 @@ private static HttpRequestMessage CreateRequest(HttpMethod method, Uri url, obje return content; } + + private static readonly JsonSerializerOptions s_jsonSerializerOptions = CreateSerializerOptions(); + + private static JsonSerializerOptions CreateSerializerOptions() + { + var jso = new JsonSerializerOptions(); + jso.Converters.Add(new ReadOnlyMemoryConverter()); + return jso; + } } diff --git a/dotnet/src/InternalUtilities/src/Text/Json.cs b/dotnet/src/InternalUtilities/src/Text/Json.cs index 73ef89835f07..72815877849d 100644 --- a/dotnet/src/InternalUtilities/src/Text/Json.cs +++ b/dotnet/src/InternalUtilities/src/Text/Json.cs @@ -6,31 +6,31 @@ namespace Microsoft.SemanticKernel.Text; internal static class Json { - internal static string Serialize(object? o) - { - return JsonSerializer.Serialize(o, s_options); - } + internal static string Serialize(object? o) => JsonSerializer.Serialize(o, s_options); - internal static T? Deserialize(string json) - { - return JsonSerializer.Deserialize(json, s_options); - } + internal static T? Deserialize(string json) => JsonSerializer.Deserialize(json, s_options); - internal static string ToJson(this object o) - { - return JsonSerializer.Serialize(o, s_options); - } + internal static string ToJson(this object o) => JsonSerializer.Serialize(o, s_options); #region private ================================================================================ - private static readonly JsonSerializerOptions s_options = new() + private static readonly JsonSerializerOptions s_options = CreateOptions(); + + private static JsonSerializerOptions CreateOptions() { - WriteIndented = true, - MaxDepth = 20, - AllowTrailingCommas = true, - PropertyNameCaseInsensitive = true, - ReadCommentHandling = JsonCommentHandling.Skip, - }; + JsonSerializerOptions options = new() + { + WriteIndented = true, + MaxDepth = 20, + AllowTrailingCommas = true, + PropertyNameCaseInsensitive = true, + ReadCommentHandling = JsonCommentHandling.Skip, + }; + + options.Converters.Add(new ReadOnlyMemoryConverter()); + + return options; + } #endregion } diff --git a/dotnet/src/InternalUtilities/src/Text/ReadOnlyMemoryConverter.cs b/dotnet/src/InternalUtilities/src/Text/ReadOnlyMemoryConverter.cs new file mode 100644 index 000000000000..ecce02508e23 --- /dev/null +++ b/dotnet/src/InternalUtilities/src/Text/ReadOnlyMemoryConverter.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Microsoft.SemanticKernel.Text; + +// .NET 8 and the System.Text.Json v8.0.0 nuget package include built-in support for ReadOnlyMemory. +// This is a temporary workaround for .NET 6 and the System.Text.Json v6.0.0 nuget package. +// It should be removed once SK projects upgrade to System.Text.Json v8.0.0. + +/// Provides a converter for . +internal sealed class ReadOnlyMemoryConverter : JsonConverter> +{ + /// An instance of a converter for float[] that all operations delegate to. + private static readonly JsonConverter s_arrayConverter = (JsonConverter)new JsonSerializerOptions().GetConverter(typeof(float[])); + + public override ReadOnlyMemory Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => + s_arrayConverter.Read(ref reader, typeof(float[]), options).AsMemory(); + + public override void Write(Utf8JsonWriter writer, ReadOnlyMemory value, JsonSerializerOptions options) => + // This provides an efficient implementation when the ReadOnlyMemory represents the full length of an array. + // This is the common case for these projects, and thus the implementation doesn't spend more code on a complex + // implementation to efficiently handle slices or instances backed by MemoryManagers. + s_arrayConverter.Write( + writer, + MemoryMarshal.TryGetArray(value, out ArraySegment array) && array.Count == value.Length ? array.Array! : value.ToArray(), + options); +} diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/AuthorRole.cs b/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/AuthorRole.cs index af25d35fe857..26662cb221b2 100644 --- a/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/AuthorRole.cs +++ b/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/AuthorRole.cs @@ -15,15 +15,22 @@ namespace Microsoft.SemanticKernel.AI.ChatCompletion; /// The role that instructs or sets the behavior of the assistant. /// public static readonly AuthorRole System = new("system"); + /// /// The role that provides responses to system-instructed, user-prompted input. /// public static readonly AuthorRole Assistant = new("assistant"); + /// /// The role that provides input for chat completions. /// public static readonly AuthorRole User = new("user"); + /// + /// The role that provides additional information and references for chat completions. + /// + public static readonly AuthorRole Tool = new("tool"); + /// /// Gets the label associated with this AuthorRole. /// diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/ChatCompletionExtensions.cs b/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/ChatCompletionExtensions.cs index 66757c8c4a57..a1b4825e6717 100644 --- a/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/ChatCompletionExtensions.cs +++ b/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/ChatCompletionExtensions.cs @@ -30,6 +30,7 @@ public static async IAsyncEnumerable GenerateMessageStreamAsync( { yield return chatMessageStream.Content; } + yield break; } } diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/ChatCompletionServiceExtensions.cs b/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/ChatCompletionServiceExtensions.cs index f6edaf5a2ee0..a7b2f32ecf8a 100644 --- a/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/ChatCompletionServiceExtensions.cs +++ b/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/ChatCompletionServiceExtensions.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using Microsoft.SemanticKernel.AI.ChatCompletion; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Services; // Use base namespace for better discoverability and to avoid conflicts with other extensions. @@ -17,11 +18,11 @@ public static class ChatCompletionServiceExtensions /// The service provider. /// Optional identifier of the desired service. /// The completion service id matching the given id or the default. - /// Thrown when no suitable service is found. + /// Thrown when no suitable service is found. public static IChatCompletion GetChatCompletionService( this IAIServiceProvider services, string? serviceId = null) => services.GetService(serviceId) - ?? throw new KernelException(KernelException.ErrorCodes.ServiceNotFound, "Chat completion service not found"); + ?? throw new SKException("Chat completion service not found"); /// /// Returns true if a exist with the specified ID. diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/Embedding.cs b/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/Embedding.cs index 325e41e98bdf..c314d5257bb3 100644 --- a/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/Embedding.cs +++ b/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/Embedding.cs @@ -13,6 +13,7 @@ namespace Microsoft.SemanticKernel.AI.Embeddings; /// Represents a strongly typed vector of numeric data. /// /// +[Obsolete("This class is obsolete and will be removed in a future release. Please use ReadOnlyMemory instead.")] public readonly struct Embedding : IEquatable> where TEmbedding : unmanaged { @@ -196,6 +197,7 @@ public static explicit operator ReadOnlySpan(Embedding e /// /// Provides functionality related to . /// +[Obsolete("This class is obsolete and will be removed in a future release. Please use ReadOnlyMemory instead.")] public static class Embedding { /// diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/EmbeddingGenerationServiceExtensions.cs b/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/EmbeddingGenerationServiceExtensions.cs index a15e7678904b..4e7de976a509 100644 --- a/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/EmbeddingGenerationServiceExtensions.cs +++ b/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/EmbeddingGenerationServiceExtensions.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -21,7 +22,7 @@ public static class EmbeddingGenerationExtensions /// A value from which an will be generated. /// Cancellation token /// A list of structs representing the input . - public static async Task> GenerateEmbeddingAsync + public static async Task> GenerateEmbeddingAsync (this IEmbeddingGeneration generator, TValue value, CancellationToken cancellationToken = default) where TEmbedding : unmanaged { diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/IEmbeddingGeneration.cs b/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/IEmbeddingGeneration.cs index d28a25b9d6a5..a2fc94d18f38 100644 --- a/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/IEmbeddingGeneration.cs +++ b/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/IEmbeddingGeneration.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -21,5 +22,5 @@ public interface IEmbeddingGeneration : IAIService /// List of strings to generate embeddings for /// The to monitor for cancellation requests. The default is . /// List of embeddings - Task>> GenerateEmbeddingsAsync(IList data, CancellationToken cancellationToken = default); + Task>> GenerateEmbeddingsAsync(IList data, CancellationToken cancellationToken = default); } diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/TextEmbeddingServiceExtensions.cs b/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/TextEmbeddingServiceExtensions.cs index 613161f68002..fbd404ccc063 100644 --- a/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/TextEmbeddingServiceExtensions.cs +++ b/dotnet/src/SemanticKernel.Abstractions/AI/Embeddings/TextEmbeddingServiceExtensions.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using Microsoft.SemanticKernel.AI.Embeddings; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Services; // Use base namespace for better discoverability and to avoid conflicts with other extensions. @@ -17,12 +18,12 @@ public static class TextEmbeddingServiceExtensions /// The service provider. /// Optional identifier of the desired service. /// The embedding service matching the given id or the default service. - /// Thrown when no suitable service is found. + /// Thrown when no suitable service is found. public static ITextEmbeddingGeneration GetTextEmbeddingService( this IAIServiceProvider services, string? serviceId = null) => services.GetService(serviceId) - ?? throw new KernelException(KernelException.ErrorCodes.ServiceNotFound, "Text embedding service not available"); + ?? throw new SKException("Text embedding service not found"); /// /// Returns true if a exist with the specified ID. diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/ImageGeneration/ImageGenerationServiceExtensions.cs b/dotnet/src/SemanticKernel.Abstractions/AI/ImageGeneration/ImageGenerationServiceExtensions.cs index 17541b3e3aca..cc1f335caaa2 100644 --- a/dotnet/src/SemanticKernel.Abstractions/AI/ImageGeneration/ImageGenerationServiceExtensions.cs +++ b/dotnet/src/SemanticKernel.Abstractions/AI/ImageGeneration/ImageGenerationServiceExtensions.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using Microsoft.SemanticKernel.AI.ImageGeneration; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Services; // Use base namespace for better discoverability and to avoid conflicts with other extensions. @@ -17,11 +18,11 @@ public static class ImageGenerationServiceExtensions /// The service provider. /// Optional identifier of the desired service. /// The id matching the given id or the default. - /// Thrown when no suitable service is found. + /// Thrown when no suitable service is found. public static IImageGeneration GetImageGenerationService( this IAIServiceProvider services, string? serviceId = null) => services.GetService(serviceId) - ?? throw new KernelException(KernelException.ErrorCodes.ServiceNotFound, "Image generation service not found"); + ?? throw new SKException("Image generation service not found"); /// /// Returns true if a exist with the specified ID. diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/TextCompletion/TextCompletionExtensions.cs b/dotnet/src/SemanticKernel.Abstractions/AI/TextCompletion/TextCompletionExtensions.cs index 31d468bfe647..485b8d309d9e 100644 --- a/dotnet/src/SemanticKernel.Abstractions/AI/TextCompletion/TextCompletionExtensions.cs +++ b/dotnet/src/SemanticKernel.Abstractions/AI/TextCompletion/TextCompletionExtensions.cs @@ -58,4 +58,28 @@ public static async IAsyncEnumerable CompleteStreamAsync(this ITextCompl yield break; } } + + /// + /// Creates a completion for the prompt and settings. + /// + /// Target interface to extend. + /// The prompt to complete. + /// Request settings for the completion API. + /// The to monitor for cancellation requests. The default is . + /// Streaming content of the text generated by the remote model. + public static async IAsyncEnumerable CompleteStreamsAsync(this ITextCompletion textCompletion, + string text, + CompleteRequestSettings requestSettings, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + var completionResults = textCompletion.GetStreamingCompletionsAsync(text, requestSettings, cancellationToken); + + await foreach (var completionResult in completionResults) + { + await foreach (var word in completionResult.GetCompletionStreamingAsync(cancellationToken).ConfigureAwait(false)) + { + yield return word; + } + } + } } diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/TextCompletion/TextCompletionServiceExtensions.cs b/dotnet/src/SemanticKernel.Abstractions/AI/TextCompletion/TextCompletionServiceExtensions.cs index 5a0a9308af28..bf5816db1d22 100644 --- a/dotnet/src/SemanticKernel.Abstractions/AI/TextCompletion/TextCompletionServiceExtensions.cs +++ b/dotnet/src/SemanticKernel.Abstractions/AI/TextCompletion/TextCompletionServiceExtensions.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using Microsoft.SemanticKernel.AI.TextCompletion; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Services; // Use base namespace for better discoverability and to avoid conflicts with other extensions. @@ -17,11 +18,11 @@ public static class TextCompletionServiceExtensions /// The service provider. /// Optional identifier of the desired service. /// The text completion service id matching the given ID or the default. - /// Thrown when no suitable service is found. + /// Thrown when no suitable service is found. public static ITextCompletion GetTextCompletionServiceOrDefault( this IAIServiceProvider services, string? serviceId = null) => services.GetService(serviceId) - ?? throw new KernelException(KernelException.ErrorCodes.ServiceNotFound, "Text completion service not found"); + ?? throw new SKException("Text completion service not found"); /// /// Returns true if a exist with the specified ID. diff --git a/dotnet/src/SemanticKernel.Abstractions/IKernel.cs b/dotnet/src/SemanticKernel.Abstractions/IKernel.cs index 23297a3551b7..5e3693b75964 100644 --- a/dotnet/src/SemanticKernel.Abstractions/IKernel.cs +++ b/dotnet/src/SemanticKernel.Abstractions/IKernel.cs @@ -28,7 +28,7 @@ public interface IKernel /// /// App logger /// - ILogger Logger { get; } + ILoggerFactory LoggerFactory { get; } /// /// Semantic memory instance diff --git a/dotnet/src/SemanticKernel.Abstractions/KernelException.cs b/dotnet/src/SemanticKernel.Abstractions/KernelException.cs deleted file mode 100644 index 681fc4d81c71..000000000000 --- a/dotnet/src/SemanticKernel.Abstractions/KernelException.cs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using Microsoft.SemanticKernel.Diagnostics; - -namespace Microsoft.SemanticKernel; - -#pragma warning disable RCS1194 // Implement exception constructors - -/// -/// Exception thrown for errors related to kernel logic. -/// -public class KernelException : SKException -{ - /// - /// Initializes a new instance of the class with a provided error code and message. - /// - /// The error code. - /// The exception message. - public KernelException(ErrorCodes errorCode, string? message) - : this(errorCode, message, innerException: null) - { - } - - /// - /// Initializes a new instance of the class with a provided error code, message, and inner exception. - /// - /// The error code. - /// A string that describes the error. - /// The exception that is the cause of the current exception. - public KernelException(ErrorCodes errorCode, string? message = null, Exception? innerException = null) - : base(GetDefaultMessage(errorCode, message), innerException) - { - this.ErrorCode = errorCode; - } - - /// - /// Gets the error code for this exception. - /// - public ErrorCodes ErrorCode { get; } - - /// Translate the error code into a default message. - /// The error code. - /// Default error message if nothing available. - private static string GetDefaultMessage(ErrorCodes errorCode, string? defaultMessage) - { - string description = errorCode switch - { - ErrorCodes.InvalidFunctionDescription => "Invalid function description", - ErrorCodes.FunctionOverloadNotSupported => "Function overload not supported", - ErrorCodes.FunctionNotAvailable => "Function not available", - ErrorCodes.FunctionTypeNotSupported => "Function type not supported", - ErrorCodes.InvalidFunctionType => "Invalid function type", - ErrorCodes.InvalidServiceConfiguration => "Invalid service configuration", - ErrorCodes.ServiceNotFound => "Service not found", - ErrorCodes.SkillCollectionNotSet => "Skill collection not set", - ErrorCodes.FunctionInvokeError => "Function invoke error", - _ => $"Unknown error ({errorCode:G})", - }; - - return defaultMessage is not null ? $"{description}: {defaultMessage}" : description; - } - - /// - /// Semantic kernel error codes. - /// - public enum ErrorCodes - { - /// - /// Unknown error. - /// - UnknownError = -1, - - /// - /// Invalid function description. - /// - InvalidFunctionDescription, - - /// - /// Function overload not supported. - /// - FunctionOverloadNotSupported, - - /// - /// Function not available. - /// - FunctionNotAvailable, - - /// - /// Function type not supported. - /// - FunctionTypeNotSupported, - - /// - /// Invalid function type. - /// - InvalidFunctionType, - - /// - /// Invalid service configuration. - /// - InvalidServiceConfiguration, - - /// - /// Service not found. - /// - ServiceNotFound, - - /// - /// Skill collection not set. - /// - SkillCollectionNotSet, - - /// - /// Represents an error that occurs when invoking a function. - /// - FunctionInvokeError, - } -} diff --git a/dotnet/src/SemanticKernel.Abstractions/Memory/IMemoryStore.cs b/dotnet/src/SemanticKernel.Abstractions/Memory/IMemoryStore.cs index 997dba6a9d17..9bf72d31e8a1 100644 --- a/dotnet/src/SemanticKernel.Abstractions/Memory/IMemoryStore.cs +++ b/dotnet/src/SemanticKernel.Abstractions/Memory/IMemoryStore.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -111,7 +112,7 @@ public interface IMemoryStore /// A group of tuples where item1 is a and item2 is its similarity score as a . IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesAsync( string collectionName, - Embedding embedding, + ReadOnlyMemory embedding, int limit, double minRelevanceScore = 0.0, bool withEmbeddings = false, @@ -128,7 +129,7 @@ public interface IMemoryStore /// A tuple consisting of the and the similarity score as a . Null if no nearest match found. Task<(MemoryRecord, double)?> GetNearestMatchAsync( string collectionName, - Embedding embedding, + ReadOnlyMemory embedding, double minRelevanceScore = 0.0, bool withEmbedding = false, CancellationToken cancellationToken = default); diff --git a/dotnet/src/SemanticKernel.Abstractions/Memory/MemoryQueryResult.cs b/dotnet/src/SemanticKernel.Abstractions/Memory/MemoryQueryResult.cs index a68fac697df0..6be2e7a19e3b 100644 --- a/dotnet/src/SemanticKernel.Abstractions/Memory/MemoryQueryResult.cs +++ b/dotnet/src/SemanticKernel.Abstractions/Memory/MemoryQueryResult.cs @@ -1,7 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Text.Json.Serialization; -using Microsoft.SemanticKernel.AI.Embeddings; +using Microsoft.SemanticKernel.Text; namespace Microsoft.SemanticKernel.Memory; @@ -24,7 +25,8 @@ public class MemoryQueryResult /// /// Nullable embedding associated with the metadata returned for by a query. /// - public Embedding? Embedding { get; } + [JsonConverter(typeof(ReadOnlyMemoryConverter))] + public ReadOnlyMemory? Embedding { get; } /// /// Create a new instance of MemoryQueryResult @@ -39,7 +41,7 @@ public class MemoryQueryResult public MemoryQueryResult( MemoryRecordMetadata metadata, double relevance, - Embedding? embedding) + ReadOnlyMemory? embedding) { this.Metadata = metadata; this.Relevance = relevance; diff --git a/dotnet/src/SemanticKernel.Abstractions/Memory/MemoryRecord.cs b/dotnet/src/SemanticKernel.Abstractions/Memory/MemoryRecord.cs index ffc383e9663b..d87c7a876ed3 100644 --- a/dotnet/src/SemanticKernel.Abstractions/Memory/MemoryRecord.cs +++ b/dotnet/src/SemanticKernel.Abstractions/Memory/MemoryRecord.cs @@ -3,8 +3,8 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Diagnostics; +using Microsoft.SemanticKernel.Text; namespace Microsoft.SemanticKernel.Memory; @@ -17,7 +17,8 @@ public class MemoryRecord : DataEntryBase /// Source content embeddings. /// [JsonPropertyName("embedding")] - public Embedding Embedding { get; } + [JsonConverter(typeof(ReadOnlyMemoryConverter))] + public ReadOnlyMemory Embedding { get; } /// /// Metadata associated with a Semantic Kernel memory. @@ -31,7 +32,7 @@ public class MemoryRecord : DataEntryBase [JsonConstructor] public MemoryRecord( MemoryRecordMetadata metadata, - Embedding embedding, + ReadOnlyMemory embedding, string? key, DateTimeOffset? timestamp = null) : base(key, timestamp) { @@ -55,7 +56,7 @@ public static MemoryRecord ReferenceRecord( string externalId, string sourceName, string? description, - Embedding embedding, + ReadOnlyMemory embedding, string? additionalMetadata = null, string? key = null, DateTimeOffset? timestamp = null) @@ -91,7 +92,7 @@ public static MemoryRecord LocalRecord( string id, string text, string? description, - Embedding embedding, + ReadOnlyMemory embedding, string? additionalMetadata = null, string? key = null, DateTimeOffset? timestamp = null) @@ -124,13 +125,13 @@ public static MemoryRecord LocalRecord( /// public static MemoryRecord FromJsonMetadata( string json, - Embedding? embedding, + ReadOnlyMemory embedding, string? key = null, DateTimeOffset? timestamp = null) { var metadata = JsonSerializer.Deserialize(json); return metadata != null - ? new MemoryRecord(metadata, embedding ?? Embedding.Empty, key, timestamp) + ? new MemoryRecord(metadata, embedding, key, timestamp) : throw new SKException("Unable to create memory record from serialized metadata"); } @@ -144,11 +145,11 @@ public static MemoryRecord FromJsonMetadata( /// Memory record public static MemoryRecord FromMetadata( MemoryRecordMetadata metadata, - Embedding? embedding, + ReadOnlyMemory embedding, string? key = null, DateTimeOffset? timestamp = null) { - return new MemoryRecord(metadata, embedding ?? Embedding.Empty, key, timestamp); + return new MemoryRecord(metadata, embedding, key, timestamp); } /// diff --git a/dotnet/src/SemanticKernel.Abstractions/Orchestration/SKContext.cs b/dotnet/src/SemanticKernel.Abstractions/Orchestration/SKContext.cs index 39d9286e2a50..6b6138061e90 100644 --- a/dotnet/src/SemanticKernel.Abstractions/Orchestration/SKContext.cs +++ b/dotnet/src/SemanticKernel.Abstractions/Orchestration/SKContext.cs @@ -8,6 +8,7 @@ using System.Threading; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; using Microsoft.SemanticKernel.SkillDefinition; @@ -58,22 +59,22 @@ public CultureInfo Culture /// /// App logger /// - public ILogger Logger { get; } + public ILoggerFactory LoggerFactory { get; } /// /// Constructor for the context. /// /// Context variables to include in context. /// Skills to include in context. - /// Logger for operations in context. + /// The to use for logging. If null, no logging will be performed. public SKContext( ContextVariables? variables = null, IReadOnlySkillCollection? skills = null, - ILogger? logger = null) + ILoggerFactory? loggerFactory = null) { this.Variables = variables ?? new(); this.Skills = skills ?? NullReadOnlySkillCollection.Instance; - this.Logger = logger ?? NullLogger.Instance; + this.LoggerFactory = loggerFactory ?? NullLoggerFactory.Instance; this._culture = CultureInfo.CurrentCulture; } @@ -97,7 +98,7 @@ public SKContext Clone() return new SKContext( variables: this.Variables.Clone(), skills: this.Skills, - logger: this.Logger) + loggerFactory: this.LoggerFactory) { Culture = this.Culture, LastException = this.LastException @@ -159,7 +160,7 @@ public string this[string name] /// [Obsolete("Use SKContext.Logger instead. This will be removed in a future release.")] [EditorBrowsable(EditorBrowsableState.Never)] - public ILogger Log => this.Logger; + public ILogger Log => this.LoggerFactory.CreateLogger(); /// /// The token to monitor for cancellation requests. @@ -199,7 +200,7 @@ public ISemanticTextMemory Memory public SKContext Fail(string errorDescription, Exception? exception = null) { // Temporary workaround: if no exception is provided, create a new one. - this.LastException = exception ?? new KernelException(KernelException.ErrorCodes.UnknownError, errorDescription); + this.LastException = exception ?? new SKException(errorDescription); return this; } diff --git a/dotnet/src/SemanticKernel.Abstractions/Planning/PlanningException.cs b/dotnet/src/SemanticKernel.Abstractions/Planning/PlanningException.cs deleted file mode 100644 index d3c60f5332e7..000000000000 --- a/dotnet/src/SemanticKernel.Abstractions/Planning/PlanningException.cs +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using Microsoft.SemanticKernel.Diagnostics; - -namespace Microsoft.SemanticKernel.Planning; - -#pragma warning disable RCS1194 // Implement exception constructors - -/// -/// Exception thrown for errors related to planning. -/// -public class PlanningException : SKException -{ - /// - /// Initializes a new instance of the class with a provided error code and message. - /// - /// The error code. - /// The exception message. - public PlanningException(ErrorCodes errorCode, string? message) - : this(errorCode, message, innerException: null) - { - } - - /// - /// Initializes a new instance of the class with a provided error code, message, and inner exception. - /// - /// The error code. - /// A string that describes the error. - /// The exception that is the cause of the current exception. - public PlanningException(ErrorCodes errorCode, string? message = null, Exception? innerException = null) - : base(GetDefaultMessage(errorCode, message), innerException) - { - this.ErrorCode = errorCode; - } - - /// - /// Gets the error code for this exception. - /// - public ErrorCodes ErrorCode { get; } - - /// Translate the error code into a default message. - private static string GetDefaultMessage(ErrorCodes errorCode, string? message) - { - string description = errorCode switch - { - ErrorCodes.InvalidGoal => "Invalid goal", - ErrorCodes.InvalidPlan => "Invalid plan", - ErrorCodes.InvalidConfiguration => "Invalid configuration", - ErrorCodes.CreatePlanError => "Create plan error", - _ => $"Unknown error ({errorCode:G})", - }; - - return message is not null ? $"{description}: {message}" : description; - } - - /// - /// Error codes for . - /// - public enum ErrorCodes - { - /// - /// Unknown error. - /// - UnknownError = -1, - - /// - /// Invalid goal. - /// - InvalidGoal, - - /// - /// Invalid plan. - /// - InvalidPlan, - - /// - /// Invalid configuration. - /// - InvalidConfiguration, - - /// - /// Create plan error. - /// - CreatePlanError, - } -} diff --git a/dotnet/src/SemanticKernel.Abstractions/Reliability/DefaultHttpRetryHandler.cs b/dotnet/src/SemanticKernel.Abstractions/Reliability/DefaultHttpRetryHandler.cs index 109ec8c2839b..50e491ea948d 100644 --- a/dotnet/src/SemanticKernel.Abstractions/Reliability/DefaultHttpRetryHandler.cs +++ b/dotnet/src/SemanticKernel.Abstractions/Reliability/DefaultHttpRetryHandler.cs @@ -16,20 +16,20 @@ public sealed class DefaultHttpRetryHandler : DelegatingHandler /// Initializes a new instance of the class. /// /// The retry configuration. - /// The logger. - public DefaultHttpRetryHandler(HttpRetryConfig? config = null, ILogger? logger = null) - : this(config ?? new HttpRetryConfig(), logger, null, null) + /// The to use for logging. If null, no logging will be performed. + public DefaultHttpRetryHandler(HttpRetryConfig? config = null, ILoggerFactory? loggerFactory = null) + : this(config ?? new HttpRetryConfig(), loggerFactory, null, null) { } internal DefaultHttpRetryHandler( HttpRetryConfig config, - ILogger? logger = null, + ILoggerFactory? loggerFactory = null, IDelayProvider? delayProvider = null, ITimeProvider? timeProvider = null) { this._config = config; - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(DefaultHttpRetryHandler)) : NullLogger.Instance; this._delayProvider = delayProvider ?? new TaskDelayProvider(); this._timeProvider = timeProvider ?? new DefaultTimeProvider(); } diff --git a/dotnet/src/SemanticKernel.Abstractions/Reliability/DefaultHttpRetryHandlerFactory.cs b/dotnet/src/SemanticKernel.Abstractions/Reliability/DefaultHttpRetryHandlerFactory.cs index 586bbeacda60..d7358985e74c 100644 --- a/dotnet/src/SemanticKernel.Abstractions/Reliability/DefaultHttpRetryHandlerFactory.cs +++ b/dotnet/src/SemanticKernel.Abstractions/Reliability/DefaultHttpRetryHandlerFactory.cs @@ -12,9 +12,9 @@ public DefaultHttpRetryHandlerFactory(HttpRetryConfig? config = null) this.Config = config; } - public DelegatingHandler Create(ILogger? logger) + public DelegatingHandler Create(ILoggerFactory? loggerFactory) { - return new DefaultHttpRetryHandler(this.Config, logger); + return new DefaultHttpRetryHandler(this.Config, loggerFactory); } public HttpRetryConfig? Config { get; } diff --git a/dotnet/src/SemanticKernel.Abstractions/Reliability/IDelegatingHandlerFactory.cs b/dotnet/src/SemanticKernel.Abstractions/Reliability/IDelegatingHandlerFactory.cs index 68f09e6fdc2d..710b46aaeef8 100644 --- a/dotnet/src/SemanticKernel.Abstractions/Reliability/IDelegatingHandlerFactory.cs +++ b/dotnet/src/SemanticKernel.Abstractions/Reliability/IDelegatingHandlerFactory.cs @@ -10,5 +10,5 @@ namespace Microsoft.SemanticKernel.Reliability; /// public interface IDelegatingHandlerFactory { - DelegatingHandler Create(ILogger? logger); + DelegatingHandler Create(ILoggerFactory? loggerFactory); } diff --git a/dotnet/src/SemanticKernel.Abstractions/SkillDefinition/IReadOnlySkillCollection.cs b/dotnet/src/SemanticKernel.Abstractions/SkillDefinition/IReadOnlySkillCollection.cs index abcebb7bf762..ab11f32de871 100644 --- a/dotnet/src/SemanticKernel.Abstractions/SkillDefinition/IReadOnlySkillCollection.cs +++ b/dotnet/src/SemanticKernel.Abstractions/SkillDefinition/IReadOnlySkillCollection.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using System.Diagnostics.CodeAnalysis; +using Microsoft.SemanticKernel.Diagnostics; namespace Microsoft.SemanticKernel.SkillDefinition; @@ -15,7 +16,7 @@ public interface IReadOnlySkillCollection /// /// The name of the function to retrieve. /// The function retrieved from the collection. - /// The specified function could not be found in the collection. + /// The specified function could not be found in the collection. ISKFunction GetFunction(string functionName); /// @@ -24,7 +25,7 @@ public interface IReadOnlySkillCollection /// The name of the skill with which the function is associated. /// The name of the function to retrieve. /// The function retrieved from the collection. - /// The specified function could not be found in the collection. + /// The specified function could not be found in the collection. ISKFunction GetFunction(string skillName, string functionName); /// diff --git a/dotnet/src/SemanticKernel.Abstractions/SkillDefinition/NullReadOnlySkillCollection.cs b/dotnet/src/SemanticKernel.Abstractions/SkillDefinition/NullReadOnlySkillCollection.cs index 2b833e9b4151..7e78c8c0fc7f 100644 --- a/dotnet/src/SemanticKernel.Abstractions/SkillDefinition/NullReadOnlySkillCollection.cs +++ b/dotnet/src/SemanticKernel.Abstractions/SkillDefinition/NullReadOnlySkillCollection.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using Microsoft.SemanticKernel.Diagnostics; namespace Microsoft.SemanticKernel.SkillDefinition; @@ -10,28 +11,33 @@ internal sealed class NullReadOnlySkillCollection : IReadOnlySkillCollection { public static readonly NullReadOnlySkillCollection Instance = new(); + /// public ISKFunction GetFunction(string functionName) { return ThrowFunctionNotAvailable(functionName); } + /// public ISKFunction GetFunction(string skillName, string functionName) { return ThrowFunctionNotAvailable(skillName, functionName); } + /// public bool TryGetFunction(string functionName, [NotNullWhen(true)] out ISKFunction? availableFunction) { availableFunction = null; return false; } + /// public bool TryGetFunction(string skillName, string functionName, [NotNullWhen(true)] out ISKFunction? availableFunction) { availableFunction = null; return false; } + /// public FunctionsView GetFunctionsView(bool includeSemantic = true, bool includeNative = true) { return new(); @@ -44,16 +50,12 @@ private NullReadOnlySkillCollection() [DoesNotReturn] private static ISKFunction ThrowFunctionNotAvailable(string skillName, string functionName) { - throw new KernelException( - KernelException.ErrorCodes.FunctionNotAvailable, - $"Function not available: {skillName}.{functionName}"); + throw new SKException($"Function not available: {skillName}.{functionName}"); } [DoesNotReturn] private static ISKFunction ThrowFunctionNotAvailable(string functionName) { - throw new KernelException( - KernelException.ErrorCodes.FunctionNotAvailable, - $"Function not available: {functionName}"); + throw new SKException($"Function not available: {functionName}"); } } diff --git a/dotnet/src/SemanticKernel.Abstractions/TemplateEngine/Blocks/Block.cs b/dotnet/src/SemanticKernel.Abstractions/TemplateEngine/Blocks/Block.cs index 47b4649d4683..6a762a6949d5 100644 --- a/dotnet/src/SemanticKernel.Abstractions/TemplateEngine/Blocks/Block.cs +++ b/dotnet/src/SemanticKernel.Abstractions/TemplateEngine/Blocks/Block.cs @@ -22,18 +22,17 @@ public abstract class Block /// /// App logger /// - protected ILogger Logger { get; } = NullLogger.Instance; + private protected ILogger Logger { get; } /// - /// Base constructor + /// Base constructor. Prevent external instantiation. /// /// Block content - /// App logger - protected Block(string? content, ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + private protected Block(string? content, ILoggerFactory? loggerFactory) { - if (logger != null) { this.Logger = logger; } - this.Content = content ?? string.Empty; + this.Logger = loggerFactory is not null ? loggerFactory.CreateLogger(this.GetType().Name) : NullLogger.Instance; } /// diff --git a/dotnet/src/SemanticKernel.UnitTests/AI/Embeddings/EmbeddingTests.cs b/dotnet/src/SemanticKernel.UnitTests/AI/Embeddings/EmbeddingTests.cs deleted file mode 100644 index 060e82b6bb67..000000000000 --- a/dotnet/src/SemanticKernel.UnitTests/AI/Embeddings/EmbeddingTests.cs +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Text.Json; -using Microsoft.SemanticKernel.AI.Embeddings; -using Xunit; - -namespace SemanticKernel.UnitTests.AI.Embeddings; - -public class EmbeddingTests -{ - // Vector has length of 3, magnitude of 5 - private readonly float[] _vector = new float[] { 0, 3, -4 }; - private readonly float[] _empty = Array.Empty(); - - [Fact] - public void ItTreatsDefaultEmbeddingAsEmpty() - { - // Arrange - Embedding target = default; - - // Assert - Assert.True(target.IsEmpty); - Assert.Equal(0, target.Count); - Assert.Empty(target.Vector); - Assert.Same(Array.Empty(), target.Vector); - Assert.Same(Array.Empty(), (float[])target); - Assert.True(target.AsReadOnlySpan().IsEmpty); - Assert.True(((ReadOnlySpan)target).IsEmpty); - Assert.True(target.Equals(Embedding.Empty)); - Assert.True(target.Equals(new Embedding())); - Assert.True(target == Embedding.Empty); - Assert.True(target == new Embedding()); - Assert.False(target != Embedding.Empty); - Assert.Equal(0, target.GetHashCode()); - } - - [Fact] - public void ItThrowsFromCtorWithUnsupportedType() - { - // Assert - Assert.Throws(() => new Embedding(new int[] { 1, 2, 3 })); - Assert.Throws(() => new Embedding(Array.Empty())); - } - - [Fact] - public void ItThrowsFromEmptyWithUnsupportedType() - { - // Assert - Assert.Throws(() => Embedding.Empty); - } - - [Fact] - public void ItAllowsUnsupportedTypesOnEachOperation() - { - // Arrange - Embedding target = default; - - // Act - Assert.True(target.IsEmpty); - Assert.Equal(0, target.Count); - } - - [Fact] - public void ItThrowsWithNullVector() - { - // Assert - Assert.Throws("vector", () => new Embedding(null!)); - } - - [Fact] - public void ItCreatesEmptyEmbedding() - { - // Arrange - var target = new Embedding(this._empty); - - // Assert - Assert.Empty(target.Vector); - Assert.Equal(0, target.Count); - Assert.False(Embedding.IsSupported()); - } - - [Fact] - public void ItCreatesExpectedEmbedding() - { - // Arrange - var target = new Embedding(this._vector); - - // Assert - Assert.True(target.Vector.SequenceEqual(this._vector)); - } - - [Fact] - public void ItSerializesEmbedding() - { - // Arrange - var target = new Embedding(this._vector); - - // Act - string json = JsonSerializer.Serialize(target); - var copy = JsonSerializer.Deserialize>(json); - - // Assert - Assert.True(copy.Vector.SequenceEqual(this._vector)); - } - - [Fact] - public void ItDoesntCopyVectorWhenCastingToSpan() - { - // Arrange - var target = new Embedding(this._vector); - - // Act - ReadOnlySpan span1 = target.AsReadOnlySpan(); - ReadOnlySpan span2 = (ReadOnlySpan)target; - - // Assert - Assert.False(Unsafe.AreSame(ref MemoryMarshal.GetReference(span1), ref MemoryMarshal.GetArrayDataReference(this._vector))); - Assert.True(Unsafe.AreSame(ref MemoryMarshal.GetReference(span1), ref MemoryMarshal.GetReference(span2))); - } - - [Fact] - public void ItTransfersOwnershipWhenRequested() - { - // Assert - Assert.False(ReferenceEquals(this._vector, new Embedding(this._vector).Vector)); - Assert.False(ReferenceEquals(this._vector, new Embedding(this._vector, transferOwnership: false).Vector)); - Assert.True(ReferenceEquals(this._vector, new Embedding(this._vector, transferOwnership: true).Vector)); - } -} diff --git a/dotnet/src/SemanticKernel.UnitTests/KernelConfigTests.cs b/dotnet/src/SemanticKernel.UnitTests/KernelConfigTests.cs index 3c310302e372..a0bea300cc2e 100644 --- a/dotnet/src/SemanticKernel.UnitTests/KernelConfigTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/KernelConfigTests.cs @@ -19,7 +19,7 @@ public KernelConfigTests() { var kernelConfig = new KernelConfig(); this._kernel = new Mock(); - this._kernel.SetupGet(x => x.Logger).Returns(NullLogger.Instance); + this._kernel.SetupGet(x => x.LoggerFactory).Returns(NullLoggerFactory.Instance); this._kernel.SetupGet(x => x.Config).Returns(kernelConfig); } diff --git a/dotnet/src/SemanticKernel.UnitTests/KernelExceptionTests.cs b/dotnet/src/SemanticKernel.UnitTests/KernelExceptionTests.cs deleted file mode 100644 index 3bb684cda1ec..000000000000 --- a/dotnet/src/SemanticKernel.UnitTests/KernelExceptionTests.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using Microsoft.SemanticKernel; -using Xunit; - -namespace SemanticKernel.UnitTests; - -public class KernelExceptionTests -{ - [Fact] - public void ItRoundtripsArgsToErrorCodeCtor() - { - // Arrange - var e = new KernelException(KernelException.ErrorCodes.FunctionNotAvailable); - - // Assert - Assert.Equal(KernelException.ErrorCodes.FunctionNotAvailable, e.ErrorCode); - Assert.Contains("Function not available", e.Message, StringComparison.Ordinal); - Assert.Null(e.InnerException); - } - - [Fact] - public void ItRoundtripsArgsToErrorCodeMessageCtor() - { - // Arrange - const string Message = "this is a test"; - var e = new KernelException(KernelException.ErrorCodes.FunctionNotAvailable, Message); - - // Assert - Assert.Equal(KernelException.ErrorCodes.FunctionNotAvailable, e.ErrorCode); - Assert.Contains("Function not available", e.Message, StringComparison.Ordinal); - Assert.Contains(Message, e.Message, StringComparison.Ordinal); - Assert.Null(e.InnerException); - } - - [Fact] - public void ItRoundtripsArgsToErrorCodeMessageExceptionCtor() - { - // Arrange - const string Message = "this is a test"; - var inner = new FormatException(); - var e = new KernelException(KernelException.ErrorCodes.FunctionNotAvailable, Message, inner); - - // Assert - Assert.Equal(KernelException.ErrorCodes.FunctionNotAvailable, e.ErrorCode); - Assert.Contains("Function not available", e.Message, StringComparison.Ordinal); - Assert.Contains(Message, e.Message, StringComparison.Ordinal); - Assert.Same(inner, e.InnerException); - } - - [Fact] - public void ItAllowsNullMessageAndInnerExceptionInCtors() - { - // Arrange - var e = new KernelException(KernelException.ErrorCodes.FunctionNotAvailable, null, null); - - // Assert - Assert.Equal(KernelException.ErrorCodes.FunctionNotAvailable, e.ErrorCode); - Assert.Contains("Function not available", e.Message, StringComparison.Ordinal); - Assert.Null(e.InnerException); - } -} diff --git a/dotnet/src/SemanticKernel.UnitTests/KernelTests.cs b/dotnet/src/SemanticKernel.UnitTests/KernelTests.cs index cf3662aa3290..5afa75238cec 100644 --- a/dotnet/src/SemanticKernel.UnitTests/KernelTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/KernelTests.cs @@ -23,7 +23,7 @@ public class KernelTests public void ItProvidesAccessToFunctionsViaSkillCollection() { // Arrange - var factory = new Mock>(); + var factory = new Mock>(); var kernel = Kernel.Builder .WithDefaultAIService(factory.Object) .Build(); @@ -52,7 +52,7 @@ public void ItProvidesAccessToFunctionsViaSkillCollection() public async Task ItProvidesAccessToFunctionsViaSKContextAsync() { // Arrange - var factory = new Mock>(); + var factory = new Mock>(); var kernel = Kernel.Builder .WithAIService("x", factory.Object) .Build(); diff --git a/dotnet/src/SemanticKernel.UnitTests/Memory/MemoryRecordTests.cs b/dotnet/src/SemanticKernel.UnitTests/Memory/MemoryRecordTests.cs index 4c85a9366fdd..d1200af829c6 100644 --- a/dotnet/src/SemanticKernel.UnitTests/Memory/MemoryRecordTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/Memory/MemoryRecordTests.cs @@ -2,7 +2,6 @@ using System; using System.Text.Json; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Memory; using Xunit; @@ -16,7 +15,7 @@ public class MemoryRecordTests private readonly string _description = "description"; private readonly string _externalSourceName = "externalSourceName"; private readonly string _additionalMetadata = "value"; - private readonly Embedding _embedding = new(new float[] { 1, 2, 3 }); + private readonly ReadOnlyMemory _embedding = new(new float[] { 1, 2, 3 }); [Fact] public void ItCanBeConstructedFromMetadataAndVector() @@ -39,7 +38,7 @@ public void ItCanBeConstructedFromMetadataAndVector() Assert.Equal(this._text, memoryRecord.Metadata.Text); Assert.Equal(this._description, memoryRecord.Metadata.Description); Assert.Equal(this._externalSourceName, memoryRecord.Metadata.ExternalSourceName); - Assert.Equal(this._embedding.Vector, memoryRecord.Embedding.Vector); + Assert.True(this._embedding.Span.SequenceEqual(memoryRecord.Embedding.Span)); } [Fact] @@ -58,7 +57,7 @@ public void ItCanBeCreatedToRepresentLocalData() Assert.Equal(this._text, memoryRecord.Metadata.Text); Assert.Equal(this._description, memoryRecord.Metadata.Description); Assert.Equal(string.Empty, memoryRecord.Metadata.ExternalSourceName); - Assert.Equal(this._embedding.Vector, memoryRecord.Embedding.Vector); + Assert.True(this._embedding.Span.SequenceEqual(memoryRecord.Embedding.Span)); } [Fact] @@ -77,7 +76,7 @@ public void ItCanBeCreatedToRepresentExternalData() Assert.Equal(string.Empty, memoryRecord.Metadata.Text); Assert.Equal(this._description, memoryRecord.Metadata.Description); Assert.Equal(this._externalSourceName, memoryRecord.Metadata.ExternalSourceName); - Assert.Equal(this._embedding.Vector, memoryRecord.Embedding.Vector); + Assert.True(this._embedding.Span.SequenceEqual(memoryRecord.Embedding.Span)); } [Fact] @@ -103,7 +102,7 @@ public void ItCanBeCreatedFromSerializedMetadata() Assert.Equal(this._description, memoryRecord.Metadata.Description); Assert.Equal(this._externalSourceName, memoryRecord.Metadata.ExternalSourceName); Assert.Equal(this._additionalMetadata, memoryRecord.Metadata.AdditionalMetadata); - Assert.Equal(this._embedding.Vector, memoryRecord.Embedding.Vector); + Assert.True(this._embedding.Span.SequenceEqual(memoryRecord.Embedding.Span)); } [Fact] @@ -119,13 +118,12 @@ public void ItCanBeDeserializedFromJson() ""external_source_name"": ""externalSourceName"", ""additional_metadata"": ""value"" }, - ""embedding"": { - ""vector"": [ - 1, - 2, - 3 - ] - } + ""embedding"": + [ + 1, + 2, + 3 + ] }"; // Act @@ -139,7 +137,7 @@ public void ItCanBeDeserializedFromJson() Assert.Equal(this._description, memoryRecord.Metadata.Description); Assert.Equal(this._externalSourceName, memoryRecord.Metadata.ExternalSourceName); Assert.Equal(this._externalSourceName, memoryRecord.Metadata.ExternalSourceName); - Assert.Equal(this._embedding.Vector, memoryRecord.Embedding.Vector); + Assert.True(this._embedding.Span.SequenceEqual(memoryRecord.Embedding.Span)); } [Fact] @@ -147,13 +145,12 @@ public void ItCanBeSerialized() { // Arrange string jsonString = @"{ - ""embedding"": { - ""vector"": [ - 1, - 2, - 3 - ] - }, + ""embedding"": + [ + 1, + 2, + 3 + ], ""metadata"": { ""is_reference"": false, ""external_source_name"": ""externalSourceName"", diff --git a/dotnet/src/SemanticKernel.UnitTests/Memory/VolatileMemoryStoreTests.cs b/dotnet/src/SemanticKernel.UnitTests/Memory/VolatileMemoryStoreTests.cs index 0980c5557b5e..3683352c28ab 100644 --- a/dotnet/src/SemanticKernel.UnitTests/Memory/VolatileMemoryStoreTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/Memory/VolatileMemoryStoreTests.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; using Xunit; @@ -35,7 +34,7 @@ private IEnumerable CreateBatchRecords(int numRecords) id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); records = records.Append(testRecord); } @@ -45,7 +44,7 @@ private IEnumerable CreateBatchRecords(int numRecords) externalId: "test" + i, sourceName: "sourceName" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); records = records.Append(testRecord); } @@ -93,7 +92,7 @@ public async Task ItCannotInsertIntoNonExistentCollectionAsync() id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 }), + embedding: new float[] { 1, 2, 3 }, key: null, timestamp: null); string collection = "test_collection" + this._collectionNum; @@ -111,7 +110,7 @@ public async Task GetAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync() id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 }), + embedding: new float[] { 1, 2, 3 }, key: null, timestamp: null); string collection = "test_collection" + this._collectionNum; @@ -126,8 +125,8 @@ public async Task GetAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync() // Assert Assert.NotNull(actualDefault); Assert.NotNull(actualWithEmbedding); - Assert.Empty(actualDefault.Embedding.Vector); - Assert.NotEmpty(actualWithEmbedding.Embedding.Vector); + Assert.True(actualDefault.Embedding.IsEmpty); + Assert.False(actualWithEmbedding.Embedding.IsEmpty); Assert.NotEqual(testRecord, actualDefault); Assert.Equal(testRecord, actualWithEmbedding); } @@ -140,7 +139,7 @@ public async Task ItCanUpsertAndRetrieveARecordWithNoTimestampAsync() id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 }), + embedding: new float[] { 1, 2, 3 }, key: null, timestamp: null); string collection = "test_collection" + this._collectionNum; @@ -164,7 +163,7 @@ public async Task ItCanUpsertAndRetrieveARecordWithTimestampAsync() id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 }), + embedding: new float[] { 1, 2, 3 }, key: null, timestamp: DateTimeOffset.UtcNow); string collection = "test_collection" + this._collectionNum; @@ -189,12 +188,12 @@ public async Task UpsertReplacesExistingRecordWithSameIdAsync() id: commonId, text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); MemoryRecord testRecord2 = MemoryRecord.LocalRecord( id: commonId, text: "text2", description: "description2", - embedding: new Embedding(new float[] { 1, 2, 4 })); + embedding: new float[] { 1, 2, 4 }); string collection = "test_collection" + this._collectionNum; this._collectionNum++; @@ -219,7 +218,7 @@ public async Task ExistingRecordCanBeRemovedAsync() id: "test", text: "text", description: "description", - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); string collection = "test_collection" + this._collectionNum; this._collectionNum++; @@ -279,7 +278,7 @@ public async Task ItCanListAllDatabaseCollectionsAsync() public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() { // Arrange - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + var compareEmbedding = new float[] { 1, 1, 1 }; int topN = 4; string collection = "test_collection" + this._collectionNum; this._collectionNum++; @@ -289,7 +288,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); _ = await this._db.UpsertAsync(collection, testRecord); i++; @@ -297,7 +296,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -1, -1 })); + embedding: new float[] { -1, -1, -1 }); _ = await this._db.UpsertAsync(collection, testRecord); i++; @@ -305,7 +304,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); _ = await this._db.UpsertAsync(collection, testRecord); i++; @@ -313,7 +312,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -2, -3 })); + embedding: new float[] { -1, -2, -3 }); _ = await this._db.UpsertAsync(collection, testRecord); i++; @@ -321,7 +320,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, -1, -2 })); + embedding: new float[] { 1, -1, -2 }); _ = await this._db.UpsertAsync(collection, testRecord); // Act @@ -341,7 +340,7 @@ public async Task GetNearestMatchesReturnsAllResultsWithNoMinScoreAsync() public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync() { // Arrange - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + var compareEmbedding = new float[] { 1, 1, 1 }; string collection = "test_collection" + this._collectionNum; this._collectionNum++; await this._db.CreateCollectionAsync(collection); @@ -350,7 +349,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); _ = await this._db.UpsertAsync(collection, testRecord); i++; @@ -358,7 +357,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -1, -1 })); + embedding: new float[] { -1, -1, -1 }); _ = await this._db.UpsertAsync(collection, testRecord); i++; @@ -366,7 +365,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); _ = await this._db.UpsertAsync(collection, testRecord); i++; @@ -374,7 +373,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -2, -3 })); + embedding: new float[] { -1, -2, -3 }); _ = await this._db.UpsertAsync(collection, testRecord); i++; @@ -382,7 +381,7 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, -1, -2 })); + embedding: new float[] { 1, -1, -2 }); _ = await this._db.UpsertAsync(collection, testRecord); // Act @@ -393,15 +392,15 @@ public async Task GetNearestMatchAsyncReturnsEmptyEmbeddingUnlessSpecifiedAsync( // Assert Assert.NotNull(topNResultDefault); Assert.NotNull(topNResultWithEmbedding); - Assert.Empty(topNResultDefault.Value.Item1.Embedding.Vector); - Assert.NotEmpty(topNResultWithEmbedding.Value.Item1.Embedding.Vector); + Assert.True(topNResultDefault.Value.Item1.Embedding.IsEmpty); + Assert.False(topNResultWithEmbedding.Value.Item1.Embedding.IsEmpty); } [Fact] public async Task GetNearestMatchAsyncReturnsExpectedAsync() { // Arrange - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + var compareEmbedding = new float[] { 1, 1, 1 }; string collection = "test_collection" + this._collectionNum; this._collectionNum++; await this._db.CreateCollectionAsync(collection); @@ -410,7 +409,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); _ = await this._db.UpsertAsync(collection, testRecord); i++; @@ -418,7 +417,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -1, -1 })); + embedding: new float[] { -1, -1, -1 }); _ = await this._db.UpsertAsync(collection, testRecord); i++; @@ -426,7 +425,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 2, 3 })); + embedding: new float[] { 1, 2, 3 }); _ = await this._db.UpsertAsync(collection, testRecord); i++; @@ -434,7 +433,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { -1, -2, -3 })); + embedding: new float[] { -1, -2, -3 }); _ = await this._db.UpsertAsync(collection, testRecord); i++; @@ -442,7 +441,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, -1, -2 })); + embedding: new float[] { 1, -1, -2 }); _ = await this._db.UpsertAsync(collection, testRecord); // Act @@ -459,7 +458,7 @@ public async Task GetNearestMatchAsyncReturnsExpectedAsync() public async Task GetNearestMatchesDifferentiatesIdenticalVectorsByKeyAsync() { // Arrange - var compareEmbedding = new Embedding(new float[] { 1, 1, 1 }); + var compareEmbedding = new float[] { 1, 1, 1 }; int topN = 4; string collection = "test_collection" + this._collectionNum; this._collectionNum++; @@ -471,7 +470,7 @@ public async Task GetNearestMatchesDifferentiatesIdenticalVectorsByKeyAsync() id: "test" + i, text: "text" + i, description: "description" + i, - embedding: new Embedding(new float[] { 1, 1, 1 })); + embedding: new float[] { 1, 1, 1 }); _ = await this._db.UpsertAsync(collection, testRecord); } diff --git a/dotnet/src/SemanticKernel.UnitTests/Planning/PlanSerializationTests.cs b/dotnet/src/SemanticKernel.UnitTests/Planning/PlanSerializationTests.cs index 09b64562d5cd..ff8dd756f059 100644 --- a/dotnet/src/SemanticKernel.UnitTests/Planning/PlanSerializationTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/Planning/PlanSerializationTests.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.AI.TextCompletion; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Orchestration; using Microsoft.SemanticKernel.Planning; using Microsoft.SemanticKernel.SkillDefinition; @@ -84,8 +85,7 @@ public void CanSerializePlanWithPlanStep() var plan = new Plan(goal); // Arrange Mocks - var kernel = new Mock(); - var logger = new Mock(); + var logger = new Mock(); var skills = new Mock(); var returnContext = new SKContext( @@ -122,8 +122,7 @@ public void CanSerializePlanWithFunctionStep() var plan = new Plan(goal); // Arrange - var kernel = new Mock(); - var logger = new Mock(); + var logger = new Mock(); var skills = new Mock(); var returnContext = new SKContext( @@ -160,8 +159,7 @@ public void CanSerializePlanWithFunctionSteps() var plan = new Plan(goal); // Arrange - var kernel = new Mock(); - var logger = new Mock(); + var logger = new Mock(); var skills = new Mock(); var returnContext = new SKContext( @@ -198,8 +196,7 @@ public void CanSerializePlanWithStepsAndFunction() var plan = new Plan(goal); // Arrange - var kernel = new Mock(); - var logger = new Mock(); + var logger = new Mock(); var skills = new Mock(); var returnContext = new SKContext( @@ -235,8 +232,7 @@ public void CanSerializePlanWithSteps() var plan = new Plan(goal); // Arrange - var kernel = new Mock(); - var logger = new Mock(); + var logger = new Mock(); var skills = new Mock(); var returnContext = new SKContext( @@ -272,7 +268,7 @@ public async Task CanStepAndSerializePlanWithStepsAsync() // Arrange var kernel = new Mock(); - var logger = new Mock(); + var logger = new Mock(); var skills = new Mock(); var returnContext = new SKContext( @@ -329,7 +325,7 @@ public async Task CanStepAndSerializePlanWithStepsAndContextAsync() // Arrange var kernel = new Mock(); - var logger = new Mock(); + var logger = new Mock(); var skills = new Mock(); var returnContext = new SKContext( @@ -399,7 +395,7 @@ public async Task CanStepAndSerializeAndDeserializePlanWithStepsAndContextAsync( // Arrange var kernel = new Mock(); - var logger = new Mock(); + var logger = new Mock(); var skills = new Mock(); var returnContext = new SKContext( @@ -483,8 +479,7 @@ public void CanDeserializePlan(bool requireFunctions) var plan = new Plan(goal); // Arrange - var kernel = new Mock(); - var logger = new Mock(); + var logger = new Mock(); var skills = new Mock(); var returnContext = new SKContext( @@ -541,7 +536,7 @@ public void DeserializeWithMissingFunctions(bool requireFunctions) // Arrange var kernel = new Mock(); - var logger = new Mock(); + var logger = new Mock(); var skills = new Mock(); var returnContext = new SKContext( @@ -563,7 +558,7 @@ public void DeserializeWithMissingFunctions(bool requireFunctions) if (requireFunctions) { // Act + Assert - Assert.Throws(() => Plan.FromJson(serializedPlan, returnContext)); + Assert.Throws(() => Plan.FromJson(serializedPlan, returnContext)); } else { diff --git a/dotnet/src/SemanticKernel.UnitTests/Planning/PlanTests.cs b/dotnet/src/SemanticKernel.UnitTests/Planning/PlanTests.cs index 84fb7157e7b2..efcc33ae3d2e 100644 --- a/dotnet/src/SemanticKernel.UnitTests/Planning/PlanTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/Planning/PlanTests.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.Logging; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.AI.TextCompletion; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Orchestration; using Microsoft.SemanticKernel.Planning; using Microsoft.SemanticKernel.SkillDefinition; @@ -359,7 +360,7 @@ public async Task StepExceptionIsThrownAsync() // Act var cv = new ContextVariables(planInput); - await Assert.ThrowsAsync(async () => await kernel.Object.StepAsync(cv, plan)); + await Assert.ThrowsAsync(async () => await kernel.Object.StepAsync(cv, plan)); mockFunction.Verify(x => x.InvokeAsync(It.IsAny(), null, It.IsAny()), Times.Once); } @@ -388,7 +389,7 @@ public async Task PlanStepExceptionIsThrownAsync() // Act var cv = new ContextVariables(planInput); - await Assert.ThrowsAsync(async () => await kernel.Object.StepAsync(cv, plan)); + await Assert.ThrowsAsync(async () => await kernel.Object.StepAsync(cv, plan)); mockFunction.Verify(x => x.InvokeAsync(It.IsAny(), null, It.IsAny()), Times.Once); } diff --git a/dotnet/src/SemanticKernel.UnitTests/Planning/PlanningExceptionTests.cs b/dotnet/src/SemanticKernel.UnitTests/Planning/PlanningExceptionTests.cs deleted file mode 100644 index 5cae12c12cec..000000000000 --- a/dotnet/src/SemanticKernel.UnitTests/Planning/PlanningExceptionTests.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using Microsoft.SemanticKernel.Planning; -using Xunit; - -namespace SemanticKernel.UnitTests.Planning; - -public class PlanningExceptionTests -{ - [Fact] - public void ItRoundtripsArgsToErrorCodeCtor() - { - // Arrange - var e = new PlanningException(PlanningException.ErrorCodes.InvalidGoal); - - // Assert - Assert.Equal(PlanningException.ErrorCodes.InvalidGoal, e.ErrorCode); - Assert.Contains("Invalid goal", e.Message, StringComparison.Ordinal); - Assert.Null(e.InnerException); - } - - [Fact] - public void ItRoundtripsArgsToErrorCodeMessageCtor() - { - // Arrange - const string Message = "this is a test"; - var e = new PlanningException(PlanningException.ErrorCodes.InvalidGoal, Message); - - // Assert - Assert.Equal(PlanningException.ErrorCodes.InvalidGoal, e.ErrorCode); - Assert.Contains("Invalid goal", e.Message, StringComparison.Ordinal); - Assert.Contains(Message, e.Message, StringComparison.Ordinal); - Assert.Null(e.InnerException); - } - - [Fact] - public void ItRoundtripsArgsToErrorCodeMessageExceptionCtor() - { - // Arrange - const string Message = "this is a test"; - var inner = new FormatException(); - var e = new PlanningException(PlanningException.ErrorCodes.InvalidGoal, Message, inner); - - // Assert - Assert.Equal(PlanningException.ErrorCodes.InvalidGoal, e.ErrorCode); - Assert.Contains("Invalid goal", e.Message, StringComparison.Ordinal); - Assert.Contains(Message, e.Message, StringComparison.Ordinal); - Assert.Same(inner, e.InnerException); - } - - [Fact] - public void ItAllowsNullMessageAndInnerExceptionInCtors() - { - // Arrange - var e = new PlanningException(PlanningException.ErrorCodes.InvalidGoal, null, null); - - // Assert - Assert.Equal(PlanningException.ErrorCodes.InvalidGoal, e.ErrorCode); - Assert.Contains("Invalid goal", e.Message, StringComparison.Ordinal); - Assert.Null(e.InnerException); - } -} diff --git a/dotnet/src/SemanticKernel.UnitTests/Reliability/DefaultHttpRetryHandlerTests.cs b/dotnet/src/SemanticKernel.UnitTests/Reliability/DefaultHttpRetryHandlerTests.cs index d89088cf5136..8bf395d8ac6b 100644 --- a/dotnet/src/SemanticKernel.UnitTests/Reliability/DefaultHttpRetryHandlerTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/Reliability/DefaultHttpRetryHandlerTests.cs @@ -7,7 +7,7 @@ using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.SemanticKernel.Reliability; using Moq; using Moq.Protected; @@ -25,7 +25,7 @@ public class DefaultHttpRetryHandlerTests public async Task NoMaxRetryCountCallsOnceForStatusAsync(HttpStatusCode statusCode) { // Arrange - using var retry = new DefaultHttpRetryHandler(new HttpRetryConfig() { MaxRetryCount = 0 }, Mock.Of()); + using var retry = new DefaultHttpRetryHandler(new HttpRetryConfig() { MaxRetryCount = 0 }, NullLoggerFactory.Instance); using var mockResponse = new HttpResponseMessage(statusCode); using var testContent = new StringContent("test"); var mockHandler = GetHttpMessageHandlerMock(mockResponse); @@ -653,7 +653,8 @@ private static DefaultHttpRetryHandler ConfigureRetryHandler(HttpRetryConfig? co { delayProvider ??= new Mock(); timeProvider ??= new Mock(); - var retry = new DefaultHttpRetryHandler(config ?? new HttpRetryConfig(), Mock.Of(), delayProvider.Object, timeProvider.Object); + + var retry = new DefaultHttpRetryHandler(config ?? new HttpRetryConfig(), null, delayProvider.Object, timeProvider.Object); return retry; } diff --git a/dotnet/src/SemanticKernel.UnitTests/SkillDefinition/SKContextTests.cs b/dotnet/src/SemanticKernel.UnitTests/SkillDefinition/SKContextTests.cs index f313277b28ad..4d0f951bbbe9 100644 --- a/dotnet/src/SemanticKernel.UnitTests/SkillDefinition/SKContextTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/SkillDefinition/SKContextTests.cs @@ -16,12 +16,12 @@ namespace SemanticKernel.UnitTests.SkillDefinition; public class SKContextTests { private readonly Mock _skills; - private readonly Mock _logger; + private readonly Mock _logger; public SKContextTests() { this._skills = new Mock(); - this._logger = new Mock(); + this._logger = new Mock(); } [Fact] @@ -29,7 +29,7 @@ public void ItHasHelpersForContextVariables() { // Arrange var variables = new ContextVariables(); - var target = new SKContext(variables, skills: this._skills.Object, logger: this._logger.Object); + var target = new SKContext(variables, skills: this._skills.Object, loggerFactory: this._logger.Object); variables.Set("foo1", "bar1"); // Act diff --git a/dotnet/src/SemanticKernel.UnitTests/SkillDefinition/SKFunctionTests2.cs b/dotnet/src/SemanticKernel.UnitTests/SkillDefinition/SKFunctionTests2.cs index 16bbaf661cfa..5e177d2f5ca3 100644 --- a/dotnet/src/SemanticKernel.UnitTests/SkillDefinition/SKFunctionTests2.cs +++ b/dotnet/src/SemanticKernel.UnitTests/SkillDefinition/SKFunctionTests2.cs @@ -15,7 +15,7 @@ namespace SemanticKernel.UnitTests.SkillDefinition; public sealed class SKFunctionTests2 { - private readonly Mock _logger; + private readonly Mock _logger; private readonly Mock _skills; private static string s_expected = string.Empty; @@ -23,7 +23,7 @@ public sealed class SKFunctionTests2 public SKFunctionTests2() { - this._logger = new Mock(); + this._logger = new Mock(); this._skills = new Mock(); s_expected = Guid.NewGuid().ToString("D"); @@ -41,7 +41,7 @@ static void Test() var context = this.MockContext(""); // Act - var function = SKFunction.FromNativeMethod(Method(Test), logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(Test), loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -63,7 +63,7 @@ static string Test() var context = this.MockContext(""); // Act - var function = SKFunction.FromNativeMethod(Method(Test), logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(Test), loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -87,7 +87,7 @@ static Task Test() var context = this.MockContext(""); // Act - var function = SKFunction.FromNativeMethod(Method(Test), logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(Test), loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -112,7 +112,7 @@ static async ValueTask Test() var context = this.MockContext(""); // Act - var function = SKFunction.FromNativeMethod(Method(Test), logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(Test), loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -137,7 +137,7 @@ static void Test(SKContext context) context.Variables["someVar"] = "qz"; // Act - var function = SKFunction.FromNativeMethod(Method(Test), logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(Test), loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -161,7 +161,7 @@ static string Test(SKContext context) context.Variables["someVar"] = s_expected; // Act - var function = SKFunction.FromNativeMethod(Method(Test), logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(Test), loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -189,7 +189,7 @@ public async Task ItSupportsInstanceContextStringNullableAsync() // Act Func method = Test; - var function = SKFunction.FromNativeMethod(Method(method), method.Target, logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(method), method.Target, loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -218,7 +218,7 @@ Task Test(SKContext context) // Act Func> method = Test; - var function = SKFunction.FromNativeMethod(Method(method), method.Target, logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(method), method.Target, loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -250,7 +250,7 @@ async Task TestAsync(SKContext context) // Act Func> method = TestAsync; - var function = SKFunction.FromNativeMethod(Method(method), method.Target, logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(method), method.Target, loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -278,7 +278,7 @@ void Test(string input) // Act Action method = Test; - var function = SKFunction.FromNativeMethod(Method(method), method.Target, logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(method), method.Target, loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -305,7 +305,7 @@ string Test(string input) // Act Func method = Test; - var function = SKFunction.FromNativeMethod(Method(method), method.Target, logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(method), method.Target, loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -333,7 +333,7 @@ Task Test(string input) // Act Func> method = Test; - var function = SKFunction.FromNativeMethod(Method(method), method.Target, logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(method), method.Target, loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -362,7 +362,7 @@ void Test(string input, SKContext context) // Act Action method = Test; - var function = SKFunction.FromNativeMethod(Method(method), method.Target, logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(method), method.Target, loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -392,7 +392,7 @@ void Test(SKContext context, string input) // Act Action method = Test; - var function = SKFunction.FromNativeMethod(Method(method), method.Target, logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(method), method.Target, loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -420,7 +420,7 @@ static string Test(string input, SKContext context) var context = this.MockContext(""); // Act - var function = SKFunction.FromNativeMethod(Method(Test), logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(Test), loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -447,7 +447,7 @@ static Task Test(string input, SKContext context) var context = this.MockContext(""); // Act - var function = SKFunction.FromNativeMethod(Method(Test), logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(Test), loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -483,7 +483,7 @@ static Task Test(string input, SKContext context) oldContext.Variables["legacy"] = "something"; // Act - var function = SKFunction.FromNativeMethod(Method(Test), logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(Test), loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext newContext = await function.InvokeAsync(oldContext); @@ -526,7 +526,7 @@ static ValueTask Test(string input, SKContext context) var oldContext = this.MockContext("test"); // Act - var function = SKFunction.FromNativeMethod(Method(Test), logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(Test), loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext newContext = await function.InvokeAsync(oldContext); @@ -547,7 +547,7 @@ static Task TestAsync(string input) var context = this.MockContext(""); // Act - var function = SKFunction.FromNativeMethod(Method(TestAsync), logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(TestAsync), loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -569,7 +569,7 @@ static ValueTask TestAsync(string input) var context = this.MockContext(""); // Act - var function = SKFunction.FromNativeMethod(Method(TestAsync), logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(TestAsync), loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -593,7 +593,7 @@ static Task TestAsync(SKContext context) var context = this.MockContext(""); // Act - var function = SKFunction.FromNativeMethod(Method(TestAsync), logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(TestAsync), loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -619,7 +619,7 @@ static Task TestAsync(string input, SKContext context) var context = this.MockContext("input:"); // Act - var function = SKFunction.FromNativeMethod(Method(TestAsync), logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(TestAsync), loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -643,7 +643,7 @@ static Task TestAsync() var context = this.MockContext(""); // Act - var function = SKFunction.FromNativeMethod(Method(TestAsync), logger: this._logger.Object); + var function = SKFunction.FromNativeMethod(Method(TestAsync), loggerFactory: this._logger.Object); Assert.NotNull(function); SKContext result = await function.InvokeAsync(context); @@ -909,6 +909,6 @@ private SKContext MockContext(string input) return new SKContext( new ContextVariables(input), skills: this._skills.Object, - logger: this._logger.Object); + loggerFactory: this._logger.Object); } } diff --git a/dotnet/src/SemanticKernel.UnitTests/SkillDefinition/SKFunctionTests3.cs b/dotnet/src/SemanticKernel.UnitTests/SkillDefinition/SKFunctionTests3.cs index 0e8acbbb1d35..2d3d1777204c 100644 --- a/dotnet/src/SemanticKernel.UnitTests/SkillDefinition/SKFunctionTests3.cs +++ b/dotnet/src/SemanticKernel.UnitTests/SkillDefinition/SKFunctionTests3.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Orchestration; using Microsoft.SemanticKernel.SkillDefinition; using Xunit; @@ -67,7 +68,7 @@ public void ItThrowsForInvalidFunctions() { SKFunction.FromNativeMethod(method, instance, "skill"); } - catch (KernelException e) when (e.ErrorCode is KernelException.ErrorCodes.FunctionTypeNotSupported or KernelException.ErrorCodes.InvalidFunctionDescription) + catch (SKException) { count++; } diff --git a/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/Blocks/CodeBlockTests.cs b/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/Blocks/CodeBlockTests.cs index 1d2fda34c8ac..c03300c4f68f 100644 --- a/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/Blocks/CodeBlockTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/Blocks/CodeBlockTests.cs @@ -7,9 +7,9 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.SemanticKernel.AI.TextCompletion; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Orchestration; using Microsoft.SemanticKernel.SkillDefinition; -using Microsoft.SemanticKernel.TemplateEngine; using Microsoft.SemanticKernel.TemplateEngine.Blocks; using Moq; using Xunit; @@ -19,34 +19,30 @@ namespace SemanticKernel.UnitTests.TemplateEngine.Blocks; public class CodeBlockTests { private readonly Mock _skills; - private readonly Mock _logger; + private readonly ILoggerFactory _logger = NullLoggerFactory.Instance; public CodeBlockTests() { this._skills = new Mock(); - this._logger = new Mock(); } [Fact] public async Task ItThrowsIfAFunctionDoesntExistAsync() { // Arrange - var context = new SKContext(skills: this._skills.Object, logger: this._logger.Object); + var context = new SKContext(skills: this._skills.Object, loggerFactory: this._logger); this._skills.Setup(x => x.TryGetFunction("functionName", out It.Ref.IsAny)).Returns(false); - var target = new CodeBlock("functionName", this._logger.Object); + var target = new CodeBlock("functionName", this._logger); - // Act - var exception = await Assert.ThrowsAsync(async () => await target.RenderCodeAsync(context)); - - // Assert - Assert.Equal(TemplateException.ErrorCodes.FunctionNotFound, exception.ErrorCode); + // Act & Assert + await Assert.ThrowsAsync(async () => await target.RenderCodeAsync(context)); } [Fact] public async Task ItThrowsIfAFunctionCallThrowsAsync() { // Arrange - var context = new SKContext(skills: this._skills.Object, logger: this._logger.Object); + var context = new SKContext(skills: this._skills.Object, loggerFactory: this._logger); var function = new Mock(); function .Setup(x => x.InvokeAsync(It.IsAny(), It.IsAny(), It.IsAny())) @@ -54,20 +50,17 @@ public async Task ItThrowsIfAFunctionCallThrowsAsync() ISKFunction? outFunc = function.Object; this._skills.Setup(x => x.TryGetFunction("functionName", out outFunc)).Returns(true); this._skills.Setup(x => x.GetFunction("functionName")).Returns(function.Object); - var target = new CodeBlock("functionName", this._logger.Object); + var target = new CodeBlock("functionName", this._logger); - // Act - var exception = await Assert.ThrowsAsync(async () => await target.RenderCodeAsync(context)); - - // Assert - Assert.Equal(TemplateException.ErrorCodes.RuntimeError, exception.ErrorCode); + // Act & Assert + await Assert.ThrowsAsync(async () => await target.RenderCodeAsync(context)); } [Fact] public void ItHasTheCorrectType() { // Act - var target = new CodeBlock("", NullLogger.Instance); + var target = new CodeBlock("", NullLoggerFactory.Instance); // Assert Assert.Equal(BlockTypes.Code, target.Type); @@ -77,7 +70,7 @@ public void ItHasTheCorrectType() public void ItTrimsSpaces() { // Act + Assert - Assert.Equal("aa", new CodeBlock(" aa ", NullLogger.Instance).Content); + Assert.Equal("aa", new CodeBlock(" aa ", NullLoggerFactory.Instance).Content); } [Fact] @@ -89,8 +82,8 @@ public void ItChecksValidityOfInternalBlocks() var invalidBlock = new VarBlock(""); // Act - var codeBlock1 = new CodeBlock(new List { validBlock1, validBlock2 }, "", NullLogger.Instance); - var codeBlock2 = new CodeBlock(new List { validBlock1, invalidBlock }, "", NullLogger.Instance); + var codeBlock1 = new CodeBlock(new List { validBlock1, validBlock2 }, "", NullLoggerFactory.Instance); + var codeBlock2 = new CodeBlock(new List { validBlock1, invalidBlock }, "", NullLoggerFactory.Instance); // Assert Assert.True(codeBlock1.IsValid(out _)); @@ -106,10 +99,10 @@ public void ItRequiresAValidFunctionCall() var varBlock = new VarBlock("$var"); // Act - var codeBlock1 = new CodeBlock(new List { funcId, valBlock }, "", NullLogger.Instance); - var codeBlock2 = new CodeBlock(new List { funcId, varBlock }, "", NullLogger.Instance); - var codeBlock3 = new CodeBlock(new List { funcId, funcId }, "", NullLogger.Instance); - var codeBlock4 = new CodeBlock(new List { funcId, varBlock, varBlock }, "", NullLogger.Instance); + var codeBlock1 = new CodeBlock(new List { funcId, valBlock }, "", NullLoggerFactory.Instance); + var codeBlock2 = new CodeBlock(new List { funcId, varBlock }, "", NullLoggerFactory.Instance); + var codeBlock3 = new CodeBlock(new List { funcId, funcId }, "", NullLoggerFactory.Instance); + var codeBlock4 = new CodeBlock(new List { funcId, varBlock, varBlock }, "", NullLoggerFactory.Instance); // Assert Assert.True(codeBlock1.IsValid(out _)); @@ -130,7 +123,7 @@ public async Task ItRendersCodeBlockConsistingOfJustAVarBlock1Async() var context = new SKContext(variables); // Act - var codeBlock = new CodeBlock("$varName", NullLogger.Instance); + var codeBlock = new CodeBlock("$varName", NullLoggerFactory.Instance); var result = await codeBlock.RenderCodeAsync(context); // Assert @@ -146,7 +139,7 @@ public async Task ItRendersCodeBlockConsistingOfJustAVarBlock2Async() var varBlock = new VarBlock("$varName"); // Act - var codeBlock = new CodeBlock(new List { varBlock }, "", NullLogger.Instance); + var codeBlock = new CodeBlock(new List { varBlock }, "", NullLoggerFactory.Instance); var result = await codeBlock.RenderCodeAsync(context); // Assert @@ -160,7 +153,7 @@ public async Task ItRendersCodeBlockConsistingOfJustAValBlock1Async() var context = new SKContext(); // Act - var codeBlock = new CodeBlock("'ciao'", NullLogger.Instance); + var codeBlock = new CodeBlock("'ciao'", NullLoggerFactory.Instance); var result = await codeBlock.RenderCodeAsync(context); // Assert @@ -175,7 +168,7 @@ public async Task ItRendersCodeBlockConsistingOfJustAValBlock2Async() var valBlock = new ValBlock("'arrivederci'"); // Act - var codeBlock = new CodeBlock(new List { valBlock }, "", NullLogger.Instance); + var codeBlock = new CodeBlock(new List { valBlock }, "", NullLoggerFactory.Instance); var result = await codeBlock.RenderCodeAsync(context); // Assert @@ -215,7 +208,7 @@ public async Task ItInvokesFunctionCloningAllVariablesAsync() this._skills.Setup(x => x.GetFunction(Func)).Returns(function.Object); // Act - var codeBlock = new CodeBlock(new List { funcId }, "", NullLogger.Instance); + var codeBlock = new CodeBlock(new List { funcId }, "", NullLoggerFactory.Instance); string result = await codeBlock.RenderCodeAsync(context); // Assert - Values are received @@ -257,7 +250,7 @@ public async Task ItInvokesFunctionWithCustomVariableAsync() this._skills.Setup(x => x.GetFunction(Func)).Returns(function.Object); // Act - var codeBlock = new CodeBlock(new List { funcId, varBlock }, "", NullLogger.Instance); + var codeBlock = new CodeBlock(new List { funcId, varBlock }, "", NullLoggerFactory.Instance); string result = await codeBlock.RenderCodeAsync(context); // Assert @@ -291,7 +284,7 @@ public async Task ItInvokesFunctionWithCustomValueAsync() this._skills.Setup(x => x.GetFunction(Func)).Returns(function.Object); // Act - var codeBlock = new CodeBlock(new List { funcId, valBlock }, "", NullLogger.Instance); + var codeBlock = new CodeBlock(new List { funcId, valBlock }, "", NullLoggerFactory.Instance); string result = await codeBlock.RenderCodeAsync(context); // Assert diff --git a/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/Blocks/FunctionIdBlockTests.cs b/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/Blocks/FunctionIdBlockTests.cs index d4839cb33012..e7e4db0e95c8 100644 --- a/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/Blocks/FunctionIdBlockTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/Blocks/FunctionIdBlockTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.SemanticKernel.TemplateEngine; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.TemplateEngine.Blocks; using Xunit; @@ -13,7 +13,7 @@ public class FunctionIdBlockTests public void ItHasTheCorrectType() { // Act - var target = new FunctionIdBlock("", NullLogger.Instance); + var target = new FunctionIdBlock("", NullLoggerFactory.Instance); // Assert Assert.Equal(BlockTypes.FunctionId, target.Type); @@ -23,7 +23,7 @@ public void ItHasTheCorrectType() public void ItTrimsSpaces() { // Act + Assert - Assert.Equal("aa", new FunctionIdBlock(" aa ", NullLogger.Instance).Content); + Assert.Equal("aa", new FunctionIdBlock(" aa ", NullLoggerFactory.Instance).Content); } [Theory] @@ -86,7 +86,7 @@ public void ItAllowsOnlyOneDot() // Arrange var target1 = new FunctionIdBlock("functionName"); var target2 = new FunctionIdBlock("skillName.functionName"); - Assert.Throws(() => new FunctionIdBlock("foo.skillName.functionName")); + Assert.Throws(() => new FunctionIdBlock("foo.skillName.functionName")); // Act + Assert Assert.True(target1.IsValid(out _)); diff --git a/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/Blocks/VarBlockTests.cs b/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/Blocks/VarBlockTests.cs index 1d777488cb6a..a3dc5cc9faaa 100644 --- a/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/Blocks/VarBlockTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/Blocks/VarBlockTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Orchestration; -using Microsoft.SemanticKernel.TemplateEngine; using Microsoft.SemanticKernel.TemplateEngine.Blocks; using Xunit; @@ -96,8 +96,7 @@ public void ItThrowsIfTheVarNameIsEmpty() var target = new VarBlock(" $ "); // Act + Assert - var ex = Assert.Throws(() => target.Render(variables)); - Assert.Equal(TemplateException.ErrorCodes.SyntaxError, ex.ErrorCode); + Assert.Throws(() => target.Render(variables)); } [Theory] diff --git a/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/CodeTokenizerTests.cs b/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/CodeTokenizerTests.cs index 5490f09440b9..6485a2fa01be 100644 --- a/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/CodeTokenizerTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/CodeTokenizerTests.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.TemplateEngine; using Microsoft.SemanticKernel.TemplateEngine.Blocks; using Xunit; @@ -129,15 +130,12 @@ public void ItSupportsEscaping() Assert.Equal("'f\'oo'", blocks[1].Content); } - [Fact] - public void ItThrowsWhenSeparatorsAreMissing() + [Theory] + [InlineData(@"call 'f\\'xy'")] + [InlineData(@"call 'f\\'x")] + public void ItThrowsWhenSeparatorsAreMissing(string template) { - // Arrange - var template1 = @"call 'f\\'xy'"; - var template2 = @"call 'f\\'x"; - - // Act - Assert.Throws(() => this._target.Tokenize(template1)); - Assert.Throws(() => this._target.Tokenize(template2)); + // Act & Assert + Assert.Throws(() => this._target.Tokenize(template)); } } diff --git a/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/PromptTemplateEngineTests.cs b/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/PromptTemplateEngineTests.cs index 255d8a570c20..c49defb8cc60 100644 --- a/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/PromptTemplateEngineTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/PromptTemplateEngineTests.cs @@ -25,7 +25,7 @@ public sealed class PromptTemplateEngineTests public PromptTemplateEngineTests(ITestOutputHelper testOutputHelper) { this._logger = testOutputHelper; - this._target = new PromptTemplateEngine(TestConsoleLogger.Logger); + this._target = new PromptTemplateEngine(TestConsoleLogger.LoggerFactory); this._variables = new ContextVariables(Guid.NewGuid().ToString("X")); this._skills = new Mock(); } @@ -268,6 +268,6 @@ private SKContext MockContext() return new SKContext( this._variables, skills: this._skills.Object, - logger: TestConsoleLogger.Logger); + loggerFactory: TestConsoleLogger.LoggerFactory); } } diff --git a/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/TemplateExceptionTests.cs b/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/TemplateExceptionTests.cs deleted file mode 100644 index 48c1fc1fc292..000000000000 --- a/dotnet/src/SemanticKernel.UnitTests/TemplateEngine/TemplateExceptionTests.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using Microsoft.SemanticKernel.TemplateEngine; -using Xunit; - -namespace SemanticKernel.UnitTests.TemplateEngine; - -public class TemplateExceptionTests -{ - [Fact] - public void ItRoundtripsArgsToErrorCodeCtor() - { - // Arrange - var e = new TemplateException(TemplateException.ErrorCodes.RuntimeError); - - // Assert - Assert.Equal(TemplateException.ErrorCodes.RuntimeError, e.ErrorCode); - Assert.Contains("Runtime error", e.Message, StringComparison.Ordinal); - Assert.Null(e.InnerException); - } - - [Fact] - public void ItRoundtripsArgsToErrorCodeMessageCtor() - { - // Arrange - const string Message = "this is a test"; - var e = new TemplateException(TemplateException.ErrorCodes.RuntimeError, Message); - - // Assert - Assert.Equal(TemplateException.ErrorCodes.RuntimeError, e.ErrorCode); - Assert.Contains("Runtime error", e.Message, StringComparison.Ordinal); - Assert.Contains(Message, e.Message, StringComparison.Ordinal); - Assert.Null(e.InnerException); - } - - [Fact] - public void ItRoundtripsArgsToErrorCodeMessageExceptionCtor() - { - // Arrange - const string Message = "this is a test"; - var inner = new FormatException(); - var e = new TemplateException(TemplateException.ErrorCodes.RuntimeError, Message, inner); - - // Assert - Assert.Equal(TemplateException.ErrorCodes.RuntimeError, e.ErrorCode); - Assert.Contains("Runtime error", e.Message, StringComparison.Ordinal); - Assert.Contains(Message, e.Message, StringComparison.Ordinal); - Assert.Same(inner, e.InnerException); - } - - [Fact] - public void ItAllowsNullMessageAndInnerExceptionInCtors() - { - // Arrange - var e = new TemplateException(TemplateException.ErrorCodes.RuntimeError, null, null); - - // Assert - Assert.Equal(TemplateException.ErrorCodes.RuntimeError, e.ErrorCode); - Assert.Contains("Runtime error", e.Message, StringComparison.Ordinal); - Assert.Null(e.InnerException); - } -} diff --git a/dotnet/src/SemanticKernel.UnitTests/Text/TextChunkerTests.cs b/dotnet/src/SemanticKernel.UnitTests/Text/TextChunkerTests.cs index ecac27fa740c..a7feb52d538f 100644 --- a/dotnet/src/SemanticKernel.UnitTests/Text/TextChunkerTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/Text/TextChunkerTests.cs @@ -478,4 +478,121 @@ public void CanSplitVeryLargeDocumentsWithoutStackOverflowing() Assert.NotEmpty(paragraphs); #pragma warning restore CA5394 } + + [Fact] + public void CanSplitPlainTextLinesWithCustomTokenCounter() + { + const string Input = "This is a test of the emergency broadcast system. This is only a test."; + var expected = new[] + { + "This is a test of the emergency broadcast system.", + "This is only a test." + }; + + var result = TextChunker.SplitPlainTextLines(Input, 60, (input) => input.Length); + + Assert.Equal(expected, result); + } + + [Fact] + public void CanSplitMarkdownParagraphsWithCustomTokenCounter() + { + List input = new() + { + "This is a test of the emergency broadcast system. This is only a test.", + "We repeat, this is only a test. A unit test." + }; + var expected = new[] + { + "This is a test of the emergency broadcast system.", + "This is only a test.", + "We repeat, this is only a test. A unit test." + }; + + var result = TextChunker.SplitMarkdownParagraphs(input, 52, tokenCounter: (input) => input.Length); + + Assert.Equal(expected, result); + } + + [Fact] + public void CanSplitMarkdownParagraphsWithOverlapAndCustomTokenCounter() + { + List input = new() + { + "This is a test of the emergency broadcast system. This is only a test.", + "We repeat, this is only a test. A unit test." + }; + + var expected = new[] + { + "This is a test of the emergency broadcast system.", + "emergency broadcast system. This is only a test.", + "This is only a test. We repeat, this is only a test.", + "We repeat, this is only a test. A unit test.", + "A unit test." + }; + + var result = TextChunker.SplitMarkdownParagraphs(input, 75, 40, (input) => input.Length); + + Assert.Equal(expected, result); + } + + [Fact] + public void CanSplitTextParagraphsWithCustomTokenCounter() + { + List input = new() + { + "This is a test of the emergency broadcast system. This is only a test.", + "We repeat, this is only a test. A unit test." + }; + + var expected = new[] + { + "This is a test of the emergency broadcast system.", + "This is only a test.", + "We repeat, this is only a test. A unit test." + }; + + var result = TextChunker.SplitPlainTextParagraphs(input, 52, tokenCounter: (input) => input.Length); + + Assert.Equal(expected, result); + } + + [Fact] + public void CanSplitTextParagraphsWithOverlapAndCustomTokenCounter() + { + List input = new() + { + "This is a test of the emergency broadcast system. This is only a test.", + "We repeat, this is only a test. A unit test." + }; + + var expected = new[] + { + "This is a test of the emergency broadcast system.", + "emergency broadcast system. This is only a test.", + "This is only a test. We repeat, this is only a test.", + "We repeat, this is only a test. A unit test.", + "A unit test." + }; + + var result = TextChunker.SplitPlainTextParagraphs(input, 75, 40, (input) => input.Length); + + Assert.Equal(expected, result); + } + + [Fact] + public void CanSplitMarkDownLinesWithCustomTokenCounter() + { + const string Input = "This is a test of the emergency broadcast system. This is only a test."; + var expected = new[] + { + "This is a test of the emergency broadcast system.", + "This is only a test." + }; + + var result = TextChunker.SplitMarkDownLines(Input, 60, (input) => input.Length); + + Assert.Equal(expected, result); + } } diff --git a/dotnet/src/SemanticKernel.UnitTests/VectorOperations/VectorOperationTests.cs b/dotnet/src/SemanticKernel.UnitTests/VectorOperations/VectorOperationTests.cs index 30e970f11d24..ead243411962 100644 --- a/dotnet/src/SemanticKernel.UnitTests/VectorOperations/VectorOperationTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/VectorOperations/VectorOperationTests.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.AI.Embeddings.VectorOperations; using Xunit; @@ -17,18 +16,6 @@ public class VectorOperationTests private readonly double[] _doubleV1 = new double[] { 1.0, 2.0, -4.0, 10.0 }; private readonly double[] _doubleV2 = new double[] { 3.0, -7.0, 1.0, 6.0 }; - [Fact] - public void ItOnlySupportsFPDataTypes() - { - // Arrange - var target = Embedding.SupportedTypes; - - // Assert - Assert.Equal(2, target.Count()); - Assert.Contains(typeof(float), target); - Assert.Contains(typeof(double), target); - } - [Fact] public void ItComputesCosineSimilarityFloat() { diff --git a/dotnet/src/SemanticKernel.UnitTests/VectorOperations/VectorSpanTests.cs b/dotnet/src/SemanticKernel.UnitTests/VectorOperations/VectorSpanTests.cs index db829cb6cb98..eae7c7734d72 100644 --- a/dotnet/src/SemanticKernel.UnitTests/VectorOperations/VectorSpanTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/VectorOperations/VectorSpanTests.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. +#pragma warning disable CS0618 // Type or member is obsolete + using System; using Microsoft.SemanticKernel.AI.Embeddings; using Xunit; @@ -14,68 +16,6 @@ public class VectorSpanTests private readonly double[] _doubleV1 = new double[] { 1.0, 2.0, -4.0, 10.0 }; private readonly double[] _doubleV2 = new double[] { 3.0, -7.0, 1.0, 6.0 }; - [Fact] - public void ItOnlySupportsFPDataTypes() - { - // Assert - TestTypeUnmanaged(true); - TestTypeUnmanaged(true); - - TestTypeUnmanaged(false); - TestTypeUnmanaged(false); - TestTypeUnmanaged(false); - TestTypeUnmanaged(false); - TestTypeUnmanaged(false); - TestTypeUnmanaged(false); - TestTypeUnmanaged(false); - TestTypeUnmanaged(false); - TestTypeUnmanaged(false); - TestTypeUnmanaged(false); - TestTypeUnmanaged(false); - TestTypeUnmanaged(false); - TestTypeUnmanaged(false); - TestType(false); - TestType(false); - - static void TestType(bool expected) - { - if (expected) - { - Assert.True(Embedding.IsSupported()); - Assert.True(Embedding.IsSupported(typeof(T))); - Assert.Contains(typeof(T), Embedding.SupportedTypes); - } - else - { - Assert.False(Embedding.IsSupported()); - Assert.False(Embedding.IsSupported(typeof(T))); - Assert.DoesNotContain(typeof(T), Embedding.SupportedTypes); - } - } - - static void TestTypeUnmanaged(bool expected) where T : unmanaged - { - TestType(expected); - if (expected) - { - _ = new Embedding(Array.Empty()); - _ = new Embedding(Array.Empty(), transferOwnership: true); - _ = new EmbeddingSpan(Array.Empty()); - _ = new EmbeddingReadOnlySpan(Array.Empty()); - } - else - { - Assert.False(Embedding.IsSupported()); - Assert.False(Embedding.IsSupported(typeof(T))); - Assert.DoesNotContain(typeof(T), Embedding.SupportedTypes); - Assert.Throws(() => new Embedding(Array.Empty())); - Assert.Throws(() => new Embedding(Array.Empty(), transferOwnership: true)); - Assert.Throws(() => new EmbeddingSpan(Array.Empty())); - Assert.Throws(() => new EmbeddingReadOnlySpan(Array.Empty())); - } - } - } - [Fact] public void ItCanComputeCosineSimilarityFloats() { diff --git a/dotnet/src/SemanticKernel.UnitTests/XunitHelpers/TestConsoleLogger.cs b/dotnet/src/SemanticKernel.UnitTests/XunitHelpers/TestConsoleLogger.cs index 6efbc88bd646..9990760dc746 100644 --- a/dotnet/src/SemanticKernel.UnitTests/XunitHelpers/TestConsoleLogger.cs +++ b/dotnet/src/SemanticKernel.UnitTests/XunitHelpers/TestConsoleLogger.cs @@ -10,14 +10,14 @@ namespace SemanticKernel.UnitTests.XunitHelpers; /// internal static class TestConsoleLogger { - internal static ILogger Logger => LogFactory.CreateLogger(); + internal static ILogger Logger => LoggerFactory.CreateLogger(); - private static ILoggerFactory LogFactory => s_loggerFactory.Value; + internal static ILoggerFactory LoggerFactory => s_loggerFactory.Value; private static readonly Lazy s_loggerFactory = new(LogBuilder); private static ILoggerFactory LogBuilder() { - return LoggerFactory.Create(builder => + return Microsoft.Extensions.Logging.LoggerFactory.Create(builder => { builder.SetMinimumLevel(LogLevel.Trace); // builder.AddFilter("Microsoft", LogLevel.Trace); diff --git a/dotnet/src/SemanticKernel/AI/Embeddings/EmbeddingReadOnlySpan.cs b/dotnet/src/SemanticKernel/AI/Embeddings/EmbeddingReadOnlySpan.cs index d85ad1e88a97..d3f42401daed 100644 --- a/dotnet/src/SemanticKernel/AI/Embeddings/EmbeddingReadOnlySpan.cs +++ b/dotnet/src/SemanticKernel/AI/Embeddings/EmbeddingReadOnlySpan.cs @@ -9,6 +9,7 @@ namespace Microsoft.SemanticKernel.AI.Embeddings; /// A view of a vector that allows for low-level, optimized, read-only mathematical operations. /// /// The unmanaged data type (, currently supported). +[Obsolete("This class is obsolete and will be removed in a future release. Please use ReadOnlyMemory instead.")] public readonly ref struct EmbeddingReadOnlySpan where TEmbedding : unmanaged { diff --git a/dotnet/src/SemanticKernel/AI/Embeddings/EmbeddingSpan.cs b/dotnet/src/SemanticKernel/AI/Embeddings/EmbeddingSpan.cs index 6c49551c5f5c..af0813410ba3 100644 --- a/dotnet/src/SemanticKernel/AI/Embeddings/EmbeddingSpan.cs +++ b/dotnet/src/SemanticKernel/AI/Embeddings/EmbeddingSpan.cs @@ -9,6 +9,7 @@ namespace Microsoft.SemanticKernel.AI.Embeddings; /// A view of a vector that allows for low-level, optimized, read-write mathematical operations. /// /// The unmanaged data type (, currently supported). +[Obsolete("This class is obsolete and will be removed in a future release. Please use ReadOnlyMemory instead.")] public readonly ref struct EmbeddingSpan where TEmbedding : unmanaged { diff --git a/dotnet/src/SemanticKernel/AI/Embeddings/VectorOperations/CosineSimilarityOperation.cs b/dotnet/src/SemanticKernel/AI/Embeddings/VectorOperations/CosineSimilarityOperation.cs index 99bcd57d332b..ae33a8227eaf 100644 --- a/dotnet/src/SemanticKernel/AI/Embeddings/VectorOperations/CosineSimilarityOperation.cs +++ b/dotnet/src/SemanticKernel/AI/Embeddings/VectorOperations/CosineSimilarityOperation.cs @@ -36,8 +36,7 @@ public static double CosineSimilarity(this ReadOnlySpan x, Rea return CosineSimilarityImplementation(doubleSpanX, doubleSpanY); } - EmbeddingSpan.ThrowTEmbeddingNotSupported(); - return default; + throw new NotSupportedException(); } /// diff --git a/dotnet/src/SemanticKernel/AI/Embeddings/VectorOperations/DivideOperation.cs b/dotnet/src/SemanticKernel/AI/Embeddings/VectorOperations/DivideOperation.cs index 85f2a7cceb13..af232906ee36 100644 --- a/dotnet/src/SemanticKernel/AI/Embeddings/VectorOperations/DivideOperation.cs +++ b/dotnet/src/SemanticKernel/AI/Embeddings/VectorOperations/DivideOperation.cs @@ -32,7 +32,7 @@ public static void DivideByInPlace(this Span span, double divi } else { - EmbeddingSpan.ThrowTEmbeddingNotSupported(); + throw new NotSupportedException(); } } diff --git a/dotnet/src/SemanticKernel/AI/Embeddings/VectorOperations/DotProductOperation.cs b/dotnet/src/SemanticKernel/AI/Embeddings/VectorOperations/DotProductOperation.cs index 6498e6b525a5..4dccf62b4eb1 100644 --- a/dotnet/src/SemanticKernel/AI/Embeddings/VectorOperations/DotProductOperation.cs +++ b/dotnet/src/SemanticKernel/AI/Embeddings/VectorOperations/DotProductOperation.cs @@ -36,9 +36,10 @@ public static double DotProduct(this ReadOnlySpan x, ReadOnlyS ReadOnlySpan doubleSpanY = MemoryMarshal.Cast(y); return DotProductImplementation(doubleSpanX, doubleSpanY); } - - EmbeddingSpan.ThrowTEmbeddingNotSupported(); - return default; + else + { + throw new NotSupportedException(); + } } /// diff --git a/dotnet/src/SemanticKernel/AI/Embeddings/VectorOperations/MultiplyOperation.cs b/dotnet/src/SemanticKernel/AI/Embeddings/VectorOperations/MultiplyOperation.cs index 06e09800512f..c300c4b110f9 100644 --- a/dotnet/src/SemanticKernel/AI/Embeddings/VectorOperations/MultiplyOperation.cs +++ b/dotnet/src/SemanticKernel/AI/Embeddings/VectorOperations/MultiplyOperation.cs @@ -33,7 +33,7 @@ public static void MultiplyByInPlace(this Span vector, double } else { - EmbeddingSpan.ThrowTEmbeddingNotSupported(); + throw new NotSupportedException(); } } diff --git a/dotnet/src/SemanticKernel/Kernel.cs b/dotnet/src/SemanticKernel/Kernel.cs index d9b9bf8b91b5..1d68dc8e6fc5 100644 --- a/dotnet/src/SemanticKernel/Kernel.cs +++ b/dotnet/src/SemanticKernel/Kernel.cs @@ -37,7 +37,7 @@ public sealed class Kernel : IKernel, IDisposable public KernelConfig Config { get; } /// - public ILogger Logger { get; } + public ILoggerFactory LoggerFactory { get; } /// public ISemanticTextMemory Memory => this._memory; @@ -61,22 +61,24 @@ public sealed class Kernel : IKernel, IDisposable /// /// /// - /// + /// The to use for logging. If null, no logging will be performed. public Kernel( ISkillCollection skillCollection, IAIServiceProvider aiServiceProvider, IPromptTemplateEngine promptTemplateEngine, ISemanticTextMemory memory, KernelConfig config, - ILogger logger) + ILoggerFactory loggerFactory) { - this.Logger = logger; + this.LoggerFactory = loggerFactory; this.Config = config; this.PromptTemplateEngine = promptTemplateEngine; this._memory = memory; this._aiServiceProvider = aiServiceProvider; this._promptTemplateEngine = promptTemplateEngine; this._skillCollection = skillCollection; + + this._logger = loggerFactory.CreateLogger(nameof(Kernel)); } /// @@ -106,17 +108,18 @@ public IDictionary ImportSkill(object skillInstance, string if (string.IsNullOrWhiteSpace(skillName)) { skillName = SkillCollection.GlobalSkill; - this.Logger.LogTrace("Importing skill {0} in the global namespace", skillInstance.GetType().FullName); + this._logger.LogTrace("Importing skill {0} in the global namespace", skillInstance.GetType().FullName); } else { - this.Logger.LogTrace("Importing skill {0}", skillName); + this._logger.LogTrace("Importing skill {0}", skillName); } Dictionary skill = ImportSkill( skillInstance, skillName!, - this.Logger + this._logger, + this.LoggerFactory ); foreach (KeyValuePair f in skill) { @@ -176,14 +179,14 @@ public async Task RunAsync(ContextVariables variables, CancellationTo var context = new SKContext( variables, this._skillCollection, - this.Logger); + this.LoggerFactory); int pipelineStepCount = -1; foreach (ISKFunction f in pipeline) { if (context.ErrorOccurred) { - this.Logger.LogError( + this._logger.LogError( context.LastException, "Something went wrong in pipeline step {0}:'{1}'", pipelineStepCount, context.LastException?.Message); return context; @@ -198,14 +201,14 @@ public async Task RunAsync(ContextVariables variables, CancellationTo if (context.ErrorOccurred) { - this.Logger.LogError("Function call fail during pipeline step {0}: {1}.{2}. Error: {3}", + this._logger.LogError("Function call fail during pipeline step {0}: {1}.{2}. Error: {3}", pipelineStepCount, f.SkillName, f.Name, context.LastException?.Message); return context; } } catch (Exception e) when (!e.IsCriticalException()) { - this.Logger.LogError(e, "Something went wrong in pipeline step {0}: {1}.{2}. Error: {3}", + this._logger.LogError(e, "Something went wrong in pipeline step {0}: {1}.{2}. Error: {3}", pipelineStepCount, f.SkillName, f.Name, e.Message); context.LastException = e; return context; @@ -226,7 +229,7 @@ public SKContext CreateNewContext() { return new SKContext( skills: this._skillCollection, - logger: this.Logger); + loggerFactory: this.LoggerFactory); } /// @@ -238,7 +241,7 @@ public T GetService(string? name = null) where T : IAIService return service; } - throw new KernelException(KernelException.ErrorCodes.ServiceNotFound, $"Service of type {typeof(T)} and name {name ?? ""} not registered."); + throw new SKException($"Service of type {typeof(T)} and name {name ?? ""} not registered."); } /// @@ -259,6 +262,7 @@ public void Dispose() private ISemanticTextMemory _memory; private readonly IPromptTemplateEngine _promptTemplateEngine; private readonly IAIServiceProvider _aiServiceProvider; + private readonly ILogger _logger; private ISKFunction CreateSemanticFunction( string skillName, @@ -276,7 +280,7 @@ private ISKFunction CreateSemanticFunction( skillName, functionName, functionConfig, - this.Logger + this.LoggerFactory ); // Connect the function to the current kernel skill collection, in case the function @@ -297,8 +301,9 @@ private ISKFunction CreateSemanticFunction( /// Skill class instance /// Skill name, used to group functions under a shared namespace /// Application logger + /// The to use for logging. If null, no logging will be performed. /// Dictionary of functions imported from the given class instance, case-insensitively indexed by name. - private static Dictionary ImportSkill(object skillInstance, string skillName, ILogger logger) + private static Dictionary ImportSkill(object skillInstance, string skillName, ILogger logger, ILoggerFactory loggerFactory) { MethodInfo[] methods = skillInstance.GetType().GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public); logger.LogTrace("Importing skill name: {0}. Potential methods found: {1}", skillName, methods.Length); @@ -309,12 +314,10 @@ private static Dictionary ImportSkill(object skillInstance, { if (method.GetCustomAttribute() is not null) { - ISKFunction function = SKFunction.FromNativeMethod(method, skillInstance, skillName, logger); + ISKFunction function = SKFunction.FromNativeMethod(method, skillInstance, skillName, loggerFactory); if (result.ContainsKey(function.Name)) { - throw new KernelException( - KernelException.ErrorCodes.FunctionOverloadNotSupported, - "Function overloads are not supported, please differentiate function names"); + throw new SKException("Function overloads are not supported, please differentiate function names"); } result.Add(function.Name, function); @@ -333,7 +336,7 @@ private static Dictionary ImportSkill(object skillInstance, /// [Obsolete("Use Logger instead. This will be removed in a future release.")] [EditorBrowsable(EditorBrowsableState.Never)] - public ILogger Log => this.Logger; + public ILogger Log => this._logger; /// /// Create a new instance of a context, linked to the kernel internal state. diff --git a/dotnet/src/SemanticKernel/KernelBuilder.cs b/dotnet/src/SemanticKernel/KernelBuilder.cs index 223ff8e5c92a..3db8b7602a28 100644 --- a/dotnet/src/SemanticKernel/KernelBuilder.cs +++ b/dotnet/src/SemanticKernel/KernelBuilder.cs @@ -19,7 +19,7 @@ public sealed class KernelBuilder { private KernelConfig _config = new(); private Func _memoryFactory = () => NullMemory.Instance; - private ILogger _logger = NullLogger.Instance; + private ILoggerFactory _loggerFactory = NullLoggerFactory.Instance; private Func? _memoryStorageFactory = null; private IDelegatingHandlerFactory? _httpHandlerFactory = null; private IPromptTemplateEngine? _promptTemplateEngine; @@ -47,12 +47,12 @@ public IKernel Build() } var instance = new Kernel( - new SkillCollection(this._logger), + new SkillCollection(this._loggerFactory), this._aiServices.Build(), - this._promptTemplateEngine ?? new PromptTemplateEngine(this._logger), + this._promptTemplateEngine ?? new PromptTemplateEngine(this._loggerFactory), this._memoryFactory.Invoke(), this._config, - this._logger + this._loggerFactory ); // TODO: decouple this from 'UseMemory' kernel extension @@ -67,12 +67,12 @@ public IKernel Build() /// /// Add a logger to the kernel to be built. /// - /// Logger to add. + /// The to use for logging. If null, no logging will be performed. /// Updated kernel builder including the logger. - public KernelBuilder WithLogger(ILogger logger) + public KernelBuilder WithLoggerFactory(ILoggerFactory loggerFactory) { - Verify.NotNull(logger); - this._logger = logger; + Verify.NotNull(loggerFactory); + this._loggerFactory = loggerFactory; return this; } @@ -93,10 +93,10 @@ public KernelBuilder WithMemory(ISemanticTextMemory memory) /// /// The store factory. /// Updated kernel builder including the semantic text memory entity. - public KernelBuilder WithMemory(Func<(ILogger Logger, KernelConfig Config), TStore> factory) where TStore : ISemanticTextMemory + public KernelBuilder WithMemory(Func factory) where TStore : ISemanticTextMemory { Verify.NotNull(factory); - this._memoryFactory = () => factory((this._logger, this._config)); + this._memoryFactory = () => factory(this._loggerFactory, this._config); return this; } @@ -117,10 +117,10 @@ public KernelBuilder WithMemoryStorage(IMemoryStore storage) /// /// The storage factory. /// Updated kernel builder including the memory storage. - public KernelBuilder WithMemoryStorage(Func<(ILogger Logger, KernelConfig Config), TStore> factory) where TStore : IMemoryStore + public KernelBuilder WithMemoryStorage(Func factory) where TStore : IMemoryStore { Verify.NotNull(factory); - this._memoryStorageFactory = () => factory((this._logger, this._config)); + this._memoryStorageFactory = () => factory(this._loggerFactory, this._config); return this; } @@ -201,9 +201,9 @@ public KernelBuilder WithAIService( /// Adds a factory method to the services collection /// /// The factory method that creates the AI service instances of type . - public KernelBuilder WithDefaultAIService(Func factory) where TService : IAIService + public KernelBuilder WithDefaultAIService(Func factory) where TService : IAIService { - this._aiServices.SetService(() => factory(this._logger)); + this._aiServices.SetService(() => factory(this._loggerFactory)); return this; } @@ -215,10 +215,10 @@ public KernelBuilder WithDefaultAIService(Func fact /// Optional: set as the default AI service for type public KernelBuilder WithAIService( string? serviceId, - Func<(ILogger Logger, KernelConfig Config), TService> factory, + Func factory, bool setAsDefault = false) where TService : IAIService { - this._aiServices.SetService(serviceId, () => factory((this._logger, this._config)), setAsDefault); + this._aiServices.SetService(serviceId, () => factory(this._loggerFactory, this._config), setAsDefault); return this; } } diff --git a/dotnet/src/SemanticKernel/Memory/SemanticTextMemory.cs b/dotnet/src/SemanticKernel/Memory/SemanticTextMemory.cs index 57b94fc8262d..43166b273181 100644 --- a/dotnet/src/SemanticKernel/Memory/SemanticTextMemory.cs +++ b/dotnet/src/SemanticKernel/Memory/SemanticTextMemory.cs @@ -101,7 +101,7 @@ public async IAsyncEnumerable SearchAsync( bool withEmbeddings = false, [EnumeratorCancellation] CancellationToken cancellationToken = default) { - Embedding queryEmbedding = await this._embeddingGenerator.GenerateEmbeddingAsync(query, cancellationToken).ConfigureAwait(false); + ReadOnlyMemory queryEmbedding = await this._embeddingGenerator.GenerateEmbeddingAsync(query, cancellationToken).ConfigureAwait(false); IAsyncEnumerable<(MemoryRecord, double)> results = this._storage.GetNearestMatchesAsync( collectionName: collection, diff --git a/dotnet/src/SemanticKernel/Memory/VolatileMemoryStore.cs b/dotnet/src/SemanticKernel/Memory/VolatileMemoryStore.cs index 629d3f37282f..5926835deacb 100644 --- a/dotnet/src/SemanticKernel/Memory/VolatileMemoryStore.cs +++ b/dotnet/src/SemanticKernel/Memory/VolatileMemoryStore.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; @@ -7,7 +8,6 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Microsoft.SemanticKernel.AI.Embeddings; using Microsoft.SemanticKernel.AI.Embeddings.VectorOperations; using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory.Collections; @@ -133,7 +133,7 @@ public Task RemoveBatchAsync(string collectionName, IEnumerable keys, Ca public IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesAsync( string collectionName, - Embedding embedding, + ReadOnlyMemory embedding, int limit, double minRelevanceScore = 0.0, bool withEmbeddings = false, @@ -162,11 +162,11 @@ public Task RemoveBatchAsync(string collectionName, IEnumerable keys, Ca if (record != null) { double similarity = embedding - .AsReadOnlySpan() - .CosineSimilarity(record.Embedding.AsReadOnlySpan()); + .Span + .CosineSimilarity(record.Embedding.Span); if (similarity >= minRelevanceScore) { - var entry = withEmbeddings ? record : MemoryRecord.FromMetadata(record.Metadata, Embedding.Empty, record.Key, record.Timestamp); + var entry = withEmbeddings ? record : MemoryRecord.FromMetadata(record.Metadata, ReadOnlyMemory.Empty, record.Key, record.Timestamp); embeddings.Add(new(entry, similarity)); } } @@ -180,7 +180,7 @@ public Task RemoveBatchAsync(string collectionName, IEnumerable keys, Ca /// public async Task<(MemoryRecord, double)?> GetNearestMatchAsync( string collectionName, - Embedding embedding, + ReadOnlyMemory embedding, double minRelevanceScore = 0.0, bool withEmbedding = false, CancellationToken cancellationToken = default) diff --git a/dotnet/src/SemanticKernel/Planning/InstrumentedPlan.cs b/dotnet/src/SemanticKernel/Planning/InstrumentedPlan.cs index 5264f3fb5d87..745f87292f65 100644 --- a/dotnet/src/SemanticKernel/Planning/InstrumentedPlan.cs +++ b/dotnet/src/SemanticKernel/Planning/InstrumentedPlan.cs @@ -37,13 +37,13 @@ public sealed class InstrumentedPlan : IPlan /// Initialize a new instance of the class. /// /// Instance of to decorate. - /// Optional logger. + /// The to use for logging. If null, no logging will be performed. public InstrumentedPlan( IPlan plan, - ILogger? logger = null) + ILoggerFactory? loggerFactory = null) { this._plan = plan; - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(InstrumentedPlan)) : NullLogger.Instance; } /// diff --git a/dotnet/src/SemanticKernel/Planning/Plan.cs b/dotnet/src/SemanticKernel/Planning/Plan.cs index d15a11545ef5..401b47ea737c 100644 --- a/dotnet/src/SemanticKernel/Planning/Plan.cs +++ b/dotnet/src/SemanticKernel/Planning/Plan.cs @@ -10,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.SemanticKernel.AI.TextCompletion; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Orchestration; using Microsoft.SemanticKernel.SkillDefinition; @@ -228,7 +229,7 @@ public Task RunNextStepAsync(IKernel kernel, ContextVariables variables, C var context = new SKContext( variables, kernel.Skills, - kernel.Logger); + kernel.LoggerFactory); return this.InvokeNextStepAsync(context, cancellationToken); } @@ -239,7 +240,7 @@ public Task RunNextStepAsync(IKernel kernel, ContextVariables variables, C /// Context to use /// The to monitor for cancellation requests. The default is . /// The updated plan - /// If an error occurs while running the plan + /// If an error occurs while running the plan public async Task InvokeNextStepAsync(SKContext context, CancellationToken cancellationToken = default) { if (this.HasNextStep) @@ -250,14 +251,13 @@ public async Task InvokeNextStepAsync(SKContext context, CancellationToken var functionVariables = this.GetNextStepVariables(context.Variables, step); // Execute the step - var functionContext = new SKContext(functionVariables, context.Skills, context.Logger); + var functionContext = new SKContext(functionVariables, context.Skills, context.LoggerFactory); var result = await step.InvokeAsync(functionContext, cancellationToken: cancellationToken).ConfigureAwait(false); var resultValue = result.Result.Trim(); if (result.ErrorOccurred) { - throw new KernelException(KernelException.ErrorCodes.FunctionInvokeError, - $"Error occurred while running plan step: {result.LastException?.Message}", result.LastException); + throw new SKException($"Error occurred while running plan step: {result.LastException?.Message}", result.LastException); } #region Update State @@ -311,7 +311,7 @@ public async Task InvokeAsync( { AddVariablesToContext(this.State, context); var result = await this.Function - .WithInstrumentation(context.Logger) + .WithInstrumentation(context.LoggerFactory) .InvokeAsync(context, settings, cancellationToken) .ConfigureAwait(false); @@ -398,9 +398,7 @@ private static Plan SetAvailableFunctions(Plan plan, SKContext context, bool req { if (context.Skills == null) { - throw new KernelException( - KernelException.ErrorCodes.SkillCollectionNotSet, - "Skill collection not found in the context"); + throw new SKException("Skill collection not found in the context"); } if (context.Skills.TryGetFunction(plan.SkillName, plan.Name, out var skillFunction)) @@ -409,9 +407,7 @@ private static Plan SetAvailableFunctions(Plan plan, SKContext context, bool req } else if (requireFunctions) { - throw new KernelException( - KernelException.ErrorCodes.FunctionNotAvailable, - $"Function '{plan.SkillName}.{plan.Name}' not found in skill collection"); + throw new SKException($"Function '{plan.SkillName}.{plan.Name}' not found in skill collection"); } } else diff --git a/dotnet/src/SemanticKernel/Planning/PlanExtensions.cs b/dotnet/src/SemanticKernel/Planning/PlanExtensions.cs index 485a1a22e806..f20bc215d960 100644 --- a/dotnet/src/SemanticKernel/Planning/PlanExtensions.cs +++ b/dotnet/src/SemanticKernel/Planning/PlanExtensions.cs @@ -72,9 +72,9 @@ public static string ToPlanString(this Plan plan, string indent = " ") /// Returns decorated instance of with enabled instrumentation. /// /// Instance of to decorate. - /// Optional logger. - public static IPlan WithInstrumentation(this IPlan plan, ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public static IPlan WithInstrumentation(this IPlan plan, ILoggerFactory? loggerFactory = null) { - return new InstrumentedPlan(plan, logger); + return new InstrumentedPlan(plan, loggerFactory); } } diff --git a/dotnet/src/SemanticKernel/Reliability/NullHttpRetryHandler.cs b/dotnet/src/SemanticKernel/Reliability/NullHttpRetryHandler.cs index c227ca74abd3..4d855bfa532a 100644 --- a/dotnet/src/SemanticKernel/Reliability/NullHttpRetryHandler.cs +++ b/dotnet/src/SemanticKernel/Reliability/NullHttpRetryHandler.cs @@ -7,7 +7,7 @@ namespace Microsoft.SemanticKernel.Reliability; public class NullHttpRetryHandlerFactory : IDelegatingHandlerFactory { - public DelegatingHandler Create(ILogger? logger) + public DelegatingHandler Create(ILoggerFactory? loggerFactory) { return new NullHttpRetryHandler(); } diff --git a/dotnet/src/SemanticKernel/SkillDefinition/ImportSemanticSkillFromDirectory.cs b/dotnet/src/SemanticKernel/SkillDefinition/ImportSemanticSkillFromDirectory.cs index b38044246852..879274ffe6ff 100644 --- a/dotnet/src/SemanticKernel/SkillDefinition/ImportSemanticSkillFromDirectory.cs +++ b/dotnet/src/SemanticKernel/SkillDefinition/ImportSemanticSkillFromDirectory.cs @@ -73,6 +73,7 @@ public static IDictionary ImportSemanticSkillFromDirectory( var skill = new Dictionary(); + ILogger? logger = null; foreach (string skillDirectoryName in skillDirectoryNames) { Verify.ValidSkillName(skillDirectoryName); @@ -96,14 +97,22 @@ public static IDictionary ImportSemanticSkillFromDirectory( config = PromptTemplateConfig.FromJson(File.ReadAllText(configPath)); } - kernel.Logger.LogTrace("Config {0}: {1}", functionName, config.ToJson()); + logger ??= kernel.LoggerFactory.CreateLogger(nameof(IKernel)); + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace("Config {0}: {1}", functionName, config.ToJson()); + } // Load prompt template var template = new PromptTemplate(File.ReadAllText(promptPath), config, kernel.PromptTemplateEngine); var functionConfig = new SemanticFunctionConfig(config, template); - kernel.Logger.LogTrace("Registering function {0}.{1} loaded from {2}", skillDirectoryName, functionName, dir); + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace("Registering function {0}.{1} loaded from {2}", skillDirectoryName, functionName, dir); + } + skill[functionName] = kernel.RegisterSemanticFunction(skillDirectoryName, functionName, functionConfig); } } diff --git a/dotnet/src/SemanticKernel/SkillDefinition/InstrumentedSKFunction.cs b/dotnet/src/SemanticKernel/SkillDefinition/InstrumentedSKFunction.cs index 038397554818..0c4e0e327b36 100644 --- a/dotnet/src/SemanticKernel/SkillDefinition/InstrumentedSKFunction.cs +++ b/dotnet/src/SemanticKernel/SkillDefinition/InstrumentedSKFunction.cs @@ -36,13 +36,13 @@ public sealed class InstrumentedSKFunction : ISKFunction /// Initialize a new instance of the class. /// /// Instance of to decorate. - /// Optional logger. + /// The to use for logging. If null, no logging will be performed. public InstrumentedSKFunction( ISKFunction function, - ILogger? logger = null) + ILoggerFactory? loggerFactory = null) { this._function = function; - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(InstrumentedSKFunction)) : NullLogger.Instance; this._executionTimeHistogram = s_meter.CreateHistogram( name: $"SK.{this.SkillName}.{this.Name}.ExecutionTime", diff --git a/dotnet/src/SemanticKernel/SkillDefinition/NativeFunction.cs b/dotnet/src/SemanticKernel/SkillDefinition/NativeFunction.cs index c47b1889251f..55f59e04da3e 100644 --- a/dotnet/src/SemanticKernel/SkillDefinition/NativeFunction.cs +++ b/dotnet/src/SemanticKernel/SkillDefinition/NativeFunction.cs @@ -18,7 +18,6 @@ using Microsoft.SemanticKernel.AI.TextCompletion; using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Orchestration; -using Microsoft.SemanticKernel.SemanticFunctions; namespace Microsoft.SemanticKernel.SkillDefinition; @@ -58,13 +57,13 @@ internal sealed class NativeFunction : ISKFunction, IDisposable /// Signature of the method to invoke /// Object containing the method to invoke /// SK skill name - /// Application logger + /// The to use for logging. If null, no logging will be performed. /// SK function instance public static ISKFunction FromNativeMethod( MethodInfo method, object? target = null, string? skillName = null, - ILogger? logger = null) + ILoggerFactory? loggerFactory = null) { if (!method.IsStatic && target is null) { @@ -76,6 +75,8 @@ public static ISKFunction FromNativeMethod( skillName = SkillCollection.GlobalSkill; } + ILogger logger = loggerFactory?.CreateLogger(method.DeclaringType ?? typeof(SKFunction)) ?? NullLogger.Instance; + MethodDetails methodDetails = GetMethodDetails(method, target, logger); return new NativeFunction( @@ -95,7 +96,7 @@ public static ISKFunction FromNativeMethod( /// SK function name /// SK function description /// SK function parameters - /// Application logger + /// The to use for logging. If null, no logging will be performed. /// SK function instance public static ISKFunction FromNativeFunction( Delegate nativeFunction, @@ -103,8 +104,10 @@ public static ISKFunction FromNativeFunction( string? functionName = null, string? description = null, IEnumerable? parameters = null, - ILogger? logger = null) + ILoggerFactory? loggerFactory = null) { + ILogger logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(ISKFunction)) : NullLogger.Instance; + MethodDetails methodDetails = GetMethodDetails(nativeFunction.Method, nativeFunction.Target, logger); functionName ??= nativeFunction.Method.Name; @@ -124,23 +127,6 @@ public static ISKFunction FromNativeFunction( logger: logger); } - /// - /// Create a native function instance, given a semantic function configuration. - /// - /// Name of the skill to which the function to create belongs. - /// Name of the function to create. - /// Semantic function configuration. - /// Optional logger for the function. - /// The to monitor for cancellation requests. The default is . - /// SK function instance. - public static ISKFunction FromSemanticConfig( - string skillName, - string functionName, - SemanticFunctionConfig functionConfig, - ILogger? logger = null, - CancellationToken cancellationToken = default) => - SemanticFunction.FromSemanticConfig(skillName, functionName, functionConfig, logger, cancellationToken); - /// public FunctionView Describe() { @@ -241,14 +227,14 @@ internal NativeFunction( string skillName, string functionName, string description, - ILogger? logger = null) + ILogger logger) { Verify.NotNull(delegateFunction); Verify.ValidSkillName(skillName); Verify.ValidFunctionName(functionName); Verify.ParametersUniqueness(parameters); - this._logger = logger ?? NullLogger.Instance; + this._logger = logger; this._function = delegateFunction; this.Parameters = parameters; @@ -261,14 +247,12 @@ internal NativeFunction( /// /// Throw an exception if the function is not semantic, use this method when some logic makes sense only for semantic functions. /// - /// + /// [DoesNotReturn] private void ThrowNotSemantic() { this._logger.LogError("The function is not semantic"); - throw new KernelException( - KernelException.ErrorCodes.InvalidFunctionType, - "Invalid operation, the method requires a semantic function"); + throw new SKException("Invalid operation, the method requires a semantic function"); } private static MethodDetails GetMethodDetails( @@ -408,10 +392,12 @@ private static (Func, ParameterView?) Get return (static (SKContext context, CancellationToken _) => context, null); } - if (type == typeof(ILogger)) + if (type == typeof(ILogger) || type == typeof(ILoggerFactory)) { - TrackUniqueParameterType(ref hasLoggerParam, method, $"At most one {nameof(ILogger)} parameter is permitted."); - return (static (SKContext context, CancellationToken _) => context.Logger, null); + TrackUniqueParameterType(ref hasLoggerParam, method, $"At most one {nameof(ILogger)}/{nameof(ILoggerFactory)} parameter is permitted."); + return type == typeof(ILogger) ? + ((SKContext context, CancellationToken _) => context.LoggerFactory.CreateLogger(method?.DeclaringType ?? typeof(SKFunction)), null) : + ((SKContext context, CancellationToken _) => context.LoggerFactory, null); } if (type == typeof(CultureInfo) || type == typeof(IFormatProvider)) @@ -495,7 +481,7 @@ private static (Func, ParameterView?) Get } // 4. Otherwise, fail. - throw new KernelException(KernelException.ErrorCodes.FunctionInvokeError, $"Missing value for parameter '{name}'"); + throw new SKException($"Missing value for parameter '{name}'"); object? Process(string value) { @@ -662,15 +648,13 @@ private static (Func, ParameterView?) Get // Throws an exception if a result is found to be null unexpectedly static object ThrowIfNullResult(object? result) => result ?? - throw new KernelException(KernelException.ErrorCodes.FunctionInvokeError, "Function returned null unexpectedly."); + throw new SKException("Function returned null unexpectedly."); } /// Gets an exception that can be thrown indicating an invalid signature. [DoesNotReturn] private static Exception GetExceptionForInvalidSignature(MethodInfo method, string reason) => - throw new KernelException( - KernelException.ErrorCodes.FunctionTypeNotSupported, - $"Function '{method.Name}' is not supported by the kernel. {reason}"); + throw new SKException($"Function '{method.Name}' is not supported by the kernel. {reason}"); /// Throws an exception indicating an invalid SKFunction signature if the specified condition is not met. private static void ThrowForInvalidSignatureIf([DoesNotReturnIf(true)] bool condition, MethodInfo method, string reason) diff --git a/dotnet/src/SemanticKernel/SkillDefinition/SKFunction.cs b/dotnet/src/SemanticKernel/SkillDefinition/SKFunction.cs index 75af9eb92ee4..1af390e2a3be 100644 --- a/dotnet/src/SemanticKernel/SkillDefinition/SKFunction.cs +++ b/dotnet/src/SemanticKernel/SkillDefinition/SKFunction.cs @@ -22,14 +22,14 @@ public static class SKFunction /// Signature of the method to invoke /// Object containing the method to invoke /// SK skill name - /// Application logger + /// The to use for logging. If null, no logging will be performed. /// SK function instance public static ISKFunction FromNativeMethod( MethodInfo method, object? target = null, string? skillName = null, - ILogger? logger = null) - => NativeFunction.FromNativeMethod(method, target, skillName, logger); + ILoggerFactory? loggerFactory = null) + => NativeFunction.FromNativeMethod(method, target, skillName, loggerFactory); /// /// Create a native function instance, wrapping a delegate function @@ -39,7 +39,7 @@ public static ISKFunction FromNativeMethod( /// SK function name /// SK function description /// SK function parameters - /// Application logger + /// The to use for logging. If null, no logging will be performed. /// SK function instance public static ISKFunction FromNativeFunction( Delegate nativeFunction, @@ -47,8 +47,8 @@ public static ISKFunction FromNativeFunction( string? functionName = null, string? description = null, IEnumerable? parameters = null, - ILogger? logger = null) - => NativeFunction.FromNativeFunction(nativeFunction, skillName, functionName, description, parameters, logger); + ILoggerFactory? loggerFactory = null) + => NativeFunction.FromNativeFunction(nativeFunction, skillName, functionName, description, parameters, loggerFactory); /// /// Create a native function instance, given a semantic function configuration. @@ -56,14 +56,14 @@ public static ISKFunction FromNativeFunction( /// Name of the skill to which the function to create belongs. /// Name of the function to create. /// Semantic function configuration. - /// Optional logger for the function. + /// The to use for logging. If null, no logging will be performed. /// The to monitor for cancellation requests. The default is . /// SK function instance. public static ISKFunction FromSemanticConfig( string skillName, string functionName, SemanticFunctionConfig functionConfig, - ILogger? logger = null, + ILoggerFactory? loggerFactory = null, CancellationToken cancellationToken = default) - => SemanticFunction.FromSemanticConfig(skillName, functionName, functionConfig, logger, cancellationToken); + => SemanticFunction.FromSemanticConfig(skillName, functionName, functionConfig, loggerFactory, cancellationToken); } diff --git a/dotnet/src/SemanticKernel/SkillDefinition/SKFunctionExtensions.cs b/dotnet/src/SemanticKernel/SkillDefinition/SKFunctionExtensions.cs index 492a68cde5c7..7f4057ffa6d0 100644 --- a/dotnet/src/SemanticKernel/SkillDefinition/SKFunctionExtensions.cs +++ b/dotnet/src/SemanticKernel/SkillDefinition/SKFunctionExtensions.cs @@ -96,7 +96,7 @@ public static ISKFunction UseFrequencyPenalty(this ISKFunction skFunction, doubl /// Skills that the function can access /// Culture to use for the function execution /// LLM completion settings (for semantic functions only) - /// Logger to use for the function execution + /// The to use for logging. If null, no logging will be performed. /// The to monitor for cancellation requests. The default is . /// The result of the function execution public static Task InvokeAsync(this ISKFunction function, @@ -104,10 +104,10 @@ public static Task InvokeAsync(this ISKFunction function, IReadOnlySkillCollection? skills = null, CultureInfo? culture = null, CompleteRequestSettings? settings = null, - ILogger? logger = null, + ILoggerFactory? loggerFactory = null, CancellationToken cancellationToken = default) { - var context = new SKContext(variables, skills, logger) + var context = new SKContext(variables, skills, loggerFactory) { Culture = culture! }; @@ -123,7 +123,7 @@ public static Task InvokeAsync(this ISKFunction function, /// Skills that the function can access /// Culture to use for the function execution /// LLM completion settings (for semantic functions only) - /// Logger to use for the function execution + /// The to use for logging. If null, no logging will be performed. /// The to monitor for cancellation requests. The default is . /// The result of the function execution public static Task InvokeAsync(this ISKFunction function, @@ -131,17 +131,17 @@ public static Task InvokeAsync(this ISKFunction function, IReadOnlySkillCollection? skills = null, CultureInfo? culture = null, CompleteRequestSettings? settings = null, - ILogger? logger = null, + ILoggerFactory? loggerFactory = null, CancellationToken cancellationToken = default) - => function.InvokeAsync(new ContextVariables(input), skills, culture, settings, logger, cancellationToken); + => function.InvokeAsync(new ContextVariables(input), skills, culture, settings, loggerFactory, cancellationToken); /// /// Returns decorated instance of with enabled instrumentation. /// /// Instance of to decorate. - /// Optional logger. - public static ISKFunction WithInstrumentation(this ISKFunction function, ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public static ISKFunction WithInstrumentation(this ISKFunction function, ILoggerFactory? loggerFactory = null) { - return new InstrumentedSKFunction(function, logger); + return new InstrumentedSKFunction(function, loggerFactory); } } diff --git a/dotnet/src/SemanticKernel/SkillDefinition/SemanticFunction.cs b/dotnet/src/SemanticKernel/SkillDefinition/SemanticFunction.cs index cd910c53b1b6..01ff6c5ebdc4 100644 --- a/dotnet/src/SemanticKernel/SkillDefinition/SemanticFunction.cs +++ b/dotnet/src/SemanticKernel/SkillDefinition/SemanticFunction.cs @@ -51,14 +51,14 @@ internal sealed class SemanticFunction : ISKFunction, IDisposable /// Name of the skill to which the function to create belongs. /// Name of the function to create. /// Semantic function configuration. - /// Optional logger for the function. + /// The to use for logging. If null, no logging will be performed. /// The to monitor for cancellation requests. The default is . /// SK function instance. public static ISKFunction FromSemanticConfig( string skillName, string functionName, SemanticFunctionConfig functionConfig, - ILogger? logger = null, + ILoggerFactory? loggerFactory = null, CancellationToken cancellationToken = default) { Verify.NotNull(functionConfig); @@ -68,7 +68,7 @@ public static ISKFunction FromSemanticConfig( description: functionConfig.PromptTemplateConfig.Description, skillName: skillName, functionName: functionName, - logger: logger + loggerFactory: loggerFactory ); return func; @@ -149,13 +149,13 @@ internal SemanticFunction( string skillName, string functionName, string description, - ILogger? logger = null) + ILoggerFactory? loggerFactory = null) { Verify.NotNull(template); Verify.ValidSkillName(skillName); Verify.ValidFunctionName(functionName); - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(SemanticFunction)) : NullLogger.Instance; this._promptTemplate = template; this.Parameters = template.GetParameters(); diff --git a/dotnet/src/SemanticKernel/SkillDefinition/SkillCollection.cs b/dotnet/src/SemanticKernel/SkillDefinition/SkillCollection.cs index a0d0ca5833ba..588b70620d6d 100644 --- a/dotnet/src/SemanticKernel/SkillDefinition/SkillCollection.cs +++ b/dotnet/src/SemanticKernel/SkillDefinition/SkillCollection.cs @@ -23,9 +23,9 @@ public class SkillCollection : ISkillCollection { internal const string GlobalSkill = "_GLOBAL_FUNCTIONS_"; - public SkillCollection(ILogger? logger = null) + public SkillCollection(ILoggerFactory? loggerFactory = null) { - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(SkillCollection)) : NullLogger.Instance; // Important: names are case insensitive this._skillCollection = new(StringComparer.OrdinalIgnoreCase); @@ -106,9 +106,7 @@ public FunctionsView GetFunctionsView(bool includeSemantic = true, bool includeN private void ThrowFunctionNotAvailable(string skillName, string functionName) { this._logger.LogError("Function not available: skill:{0} function:{1}", skillName, functionName); - throw new KernelException( - KernelException.ErrorCodes.FunctionNotAvailable, - $"Function not available {skillName}.{functionName}"); + throw new SKException($"Function not available {skillName}.{functionName}"); } private readonly ILogger _logger; diff --git a/dotnet/src/SemanticKernel/TemplateEngine/Blocks/CodeBlock.cs b/dotnet/src/SemanticKernel/TemplateEngine/Blocks/CodeBlock.cs index 6c7e047069e7..a765f1479219 100644 --- a/dotnet/src/SemanticKernel/TemplateEngine/Blocks/CodeBlock.cs +++ b/dotnet/src/SemanticKernel/TemplateEngine/Blocks/CodeBlock.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Orchestration; using Microsoft.SemanticKernel.SkillDefinition; @@ -21,9 +22,9 @@ internal sealed class CodeBlock : Block, ICodeRendering /// Initializes a new instance of the class. /// /// Block content - /// App logger - public CodeBlock(string? content, ILogger logger) - : this(new CodeTokenizer(logger).Tokenize(content), content?.Trim(), logger) + /// The to use for logging. If null, no logging will be performed. + public CodeBlock(string? content, ILoggerFactory? loggerFactory) + : this(new CodeTokenizer(loggerFactory).Tokenize(content), content?.Trim(), loggerFactory) { } @@ -32,9 +33,9 @@ public CodeBlock(string? content, ILogger logger) /// /// A list of blocks /// Block content - /// App logger - public CodeBlock(List tokens, string? content, ILogger logger) - : base(content?.Trim(), logger) + /// The to use for logging. If null, no logging will be performed. + public CodeBlock(List tokens, string? content, ILoggerFactory? loggerFactory) + : base(content?.Trim(), loggerFactory) { this._tokens = tokens; } @@ -87,7 +88,7 @@ public async Task RenderCodeAsync(SKContext context, CancellationToken c { if (!this._validated && !this.IsValid(out var error)) { - throw new TemplateException(TemplateException.ErrorCodes.SyntaxError, error); + throw new SKException(error); } this.Logger.LogTrace("Rendering code: `{0}`", this.Content); @@ -102,8 +103,7 @@ public async Task RenderCodeAsync(SKContext context, CancellationToken c return await this.RenderFunctionCallAsync((FunctionIdBlock)this._tokens[0], context).ConfigureAwait(false); } - throw new TemplateException(TemplateException.ErrorCodes.UnexpectedBlockType, - $"Unexpected first token type: {this._tokens[0].Type:G}"); + throw new SKException($"Unexpected first token type: {this._tokens[0].Type:G}"); } #region private ================================================================================ @@ -115,16 +115,14 @@ private async Task RenderFunctionCallAsync(FunctionIdBlock fBlock, SKCon { if (context.Skills == null) { - throw new KernelException( - KernelException.ErrorCodes.SkillCollectionNotSet, - "Skill collection not found in the context"); + throw new SKException("Skill collection not found in the context"); } if (!this.GetFunctionFromSkillCollection(context.Skills!, fBlock, out ISKFunction? function)) { var errorMsg = $"Function `{fBlock.Content}` not found"; this.Logger.LogError(errorMsg); - throw new TemplateException(TemplateException.ErrorCodes.FunctionNotFound, errorMsg); + throw new SKException(errorMsg); } SKContext contextClone = context.Clone(); @@ -156,7 +154,7 @@ private async Task RenderFunctionCallAsync(FunctionIdBlock fBlock, SKCon { var errorMsg = $"Function `{fBlock.Content}` execution failed. {contextClone.LastException?.GetType().FullName}: {contextClone.LastException?.Message}"; this.Logger.LogError(errorMsg); - throw new TemplateException(TemplateException.ErrorCodes.RuntimeError, errorMsg, contextClone.LastException); + throw new SKException(errorMsg, contextClone.LastException); } return contextClone.Result; diff --git a/dotnet/src/SemanticKernel/TemplateEngine/Blocks/FunctionIdBlock.cs b/dotnet/src/SemanticKernel/TemplateEngine/Blocks/FunctionIdBlock.cs index e72f5f82e01f..23b96e336516 100644 --- a/dotnet/src/SemanticKernel/TemplateEngine/Blocks/FunctionIdBlock.cs +++ b/dotnet/src/SemanticKernel/TemplateEngine/Blocks/FunctionIdBlock.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text.RegularExpressions; using Microsoft.Extensions.Logging; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Orchestration; namespace Microsoft.SemanticKernel.TemplateEngine.Blocks; @@ -15,15 +16,14 @@ internal sealed class FunctionIdBlock : Block, ITextRendering internal string FunctionName { get; } = string.Empty; - public FunctionIdBlock(string? text, ILogger? logger = null) - : base(text?.Trim(), logger) + public FunctionIdBlock(string? text, ILoggerFactory? loggerFactory = null) + : base(text?.Trim(), loggerFactory) { var functionNameParts = this.Content.Split('.'); if (functionNameParts.Length > 2) { - this.Logger.LogError("Invalid function name `{0}`", this.Content); - throw new TemplateException(TemplateException.ErrorCodes.SyntaxError, - "A function name can contain at most one dot separating the skill name from the function name"); + this.Logger.LogError("Invalid function name `{FunctionName}`.", this.Content); + throw new SKException($"Invalid function name `{this.Content}`. A function name can contain at most one dot separating the skill name from the function name"); } if (functionNameParts.Length == 2) diff --git a/dotnet/src/SemanticKernel/TemplateEngine/Blocks/TextBlock.cs b/dotnet/src/SemanticKernel/TemplateEngine/Blocks/TextBlock.cs index c4aac3edcd2e..478bcba79557 100644 --- a/dotnet/src/SemanticKernel/TemplateEngine/Blocks/TextBlock.cs +++ b/dotnet/src/SemanticKernel/TemplateEngine/Blocks/TextBlock.cs @@ -9,13 +9,13 @@ internal sealed class TextBlock : Block, ITextRendering { internal override BlockTypes Type => BlockTypes.Text; - public TextBlock(string? text, ILogger? logger = null) - : base(text, logger) + public TextBlock(string? text, ILoggerFactory? loggerFactory = null) + : base(text, loggerFactory) { } - public TextBlock(string text, int startIndex, int stopIndex, ILogger logger) - : base(text.Substring(startIndex, stopIndex - startIndex), logger) + public TextBlock(string text, int startIndex, int stopIndex, ILoggerFactory? loggerFactory) + : base(text.Substring(startIndex, stopIndex - startIndex), loggerFactory) { } diff --git a/dotnet/src/SemanticKernel/TemplateEngine/Blocks/ValBlock.cs b/dotnet/src/SemanticKernel/TemplateEngine/Blocks/ValBlock.cs index f15ad4667aab..8f4572f2c48f 100644 --- a/dotnet/src/SemanticKernel/TemplateEngine/Blocks/ValBlock.cs +++ b/dotnet/src/SemanticKernel/TemplateEngine/Blocks/ValBlock.cs @@ -21,9 +21,9 @@ internal sealed class ValBlock : Block, ITextRendering /// Create an instance /// /// Block content, including the delimiting chars - /// Optional logger - public ValBlock(string? quotedValue, ILogger? logger = null) - : base(quotedValue?.Trim(), logger) + /// The to use for logging. If null, no logging will be performed. + public ValBlock(string? quotedValue, ILoggerFactory? loggerFactory = null) + : base(quotedValue?.Trim(), loggerFactory) { if (this.Content.Length < 2) { diff --git a/dotnet/src/SemanticKernel/TemplateEngine/Blocks/VarBlock.cs b/dotnet/src/SemanticKernel/TemplateEngine/Blocks/VarBlock.cs index bf09819a3c47..95b39bf80b99 100644 --- a/dotnet/src/SemanticKernel/TemplateEngine/Blocks/VarBlock.cs +++ b/dotnet/src/SemanticKernel/TemplateEngine/Blocks/VarBlock.cs @@ -2,6 +2,7 @@ using System.Text.RegularExpressions; using Microsoft.Extensions.Logging; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Orchestration; namespace Microsoft.SemanticKernel.TemplateEngine.Blocks; @@ -12,7 +13,7 @@ internal sealed class VarBlock : Block, ITextRendering internal string Name { get; } = string.Empty; - public VarBlock(string? content, ILogger? logger = null) : base(content?.Trim(), logger) + public VarBlock(string? content, ILoggerFactory? loggerFactory = null) : base(content?.Trim(), loggerFactory) { if (this.Content.Length < 2) { @@ -70,7 +71,7 @@ public string Render(ContextVariables? variables) { const string ErrMsg = "Variable rendering failed, the variable name is empty"; this.Logger.LogError(ErrMsg); - throw new TemplateException(TemplateException.ErrorCodes.SyntaxError, ErrMsg); + throw new SKException(ErrMsg); } if (variables.TryGetValue(this.Name, out string? value)) diff --git a/dotnet/src/SemanticKernel/TemplateEngine/CodeTokenizer.cs b/dotnet/src/SemanticKernel/TemplateEngine/CodeTokenizer.cs index 84ace98c53bc..bf95a3025f89 100644 --- a/dotnet/src/SemanticKernel/TemplateEngine/CodeTokenizer.cs +++ b/dotnet/src/SemanticKernel/TemplateEngine/CodeTokenizer.cs @@ -4,6 +4,7 @@ using System.Text; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.TemplateEngine.Blocks; using Microsoft.SemanticKernel.Text; @@ -43,11 +44,11 @@ private enum TokenTypes FunctionId = 3, } - private readonly ILogger _logger; + private readonly ILoggerFactory _loggerFactory; - public CodeTokenizer(ILogger? logger = null) + public CodeTokenizer(ILoggerFactory? loggerFactory = null) { - this._logger = logger ?? NullLogger.Instance; + this._loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; } /// @@ -83,16 +84,16 @@ public List Tokenize(string? text) switch (nextChar) { case Symbols.VarPrefix: - blocks.Add(new VarBlock(text, this._logger)); + blocks.Add(new VarBlock(text, this._loggerFactory)); break; case Symbols.DblQuote: case Symbols.SglQuote: - blocks.Add(new ValBlock(text, this._logger)); + blocks.Add(new ValBlock(text, this._loggerFactory)); break; default: - blocks.Add(new FunctionIdBlock(text, this._logger)); + blocks.Add(new FunctionIdBlock(text, this._loggerFactory)); break; } @@ -151,7 +152,7 @@ public List Tokenize(string? text) // When we reach the end of the value if (currentChar == textValueDelimiter) { - blocks.Add(new ValBlock(currentTokenContent.ToString(), this._logger)); + blocks.Add(new ValBlock(currentTokenContent.ToString(), this._loggerFactory)); currentTokenContent.Clear(); currentTokenType = TokenTypes.None; spaceSeparatorFound = false; @@ -166,12 +167,12 @@ public List Tokenize(string? text) { if (currentTokenType == TokenTypes.Variable) { - blocks.Add(new VarBlock(currentTokenContent.ToString(), this._logger)); + blocks.Add(new VarBlock(currentTokenContent.ToString(), this._loggerFactory)); currentTokenContent.Clear(); } else if (currentTokenType == TokenTypes.FunctionId) { - blocks.Add(new FunctionIdBlock(currentTokenContent.ToString(), this._logger)); + blocks.Add(new FunctionIdBlock(currentTokenContent.ToString(), this._loggerFactory)); currentTokenContent.Clear(); } @@ -188,8 +189,7 @@ public List Tokenize(string? text) { if (!spaceSeparatorFound) { - throw new TemplateException(TemplateException.ErrorCodes.SyntaxError, - "Tokens must be separated by one space least"); + throw new SKException("Tokens must be separated by one space least"); } if (IsQuote(currentChar)) @@ -216,20 +216,19 @@ public List Tokenize(string? text) switch (currentTokenType) { case TokenTypes.Value: - blocks.Add(new ValBlock(currentTokenContent.ToString(), this._logger)); + blocks.Add(new ValBlock(currentTokenContent.ToString(), this._loggerFactory)); break; case TokenTypes.Variable: - blocks.Add(new VarBlock(currentTokenContent.ToString(), this._logger)); + blocks.Add(new VarBlock(currentTokenContent.ToString(), this._loggerFactory)); break; case TokenTypes.FunctionId: - blocks.Add(new FunctionIdBlock(currentTokenContent.ToString(), this._logger)); + blocks.Add(new FunctionIdBlock(currentTokenContent.ToString(), this._loggerFactory)); break; case TokenTypes.None: - throw new TemplateException(TemplateException.ErrorCodes.SyntaxError, - "Tokens must be separated by one space least"); + throw new SKException("Tokens must be separated by one space least"); } return blocks; diff --git a/dotnet/src/SemanticKernel/TemplateEngine/PromptTemplateEngine.cs b/dotnet/src/SemanticKernel/TemplateEngine/PromptTemplateEngine.cs index 5b063d84ef7e..d0770939d76b 100644 --- a/dotnet/src/SemanticKernel/TemplateEngine/PromptTemplateEngine.cs +++ b/dotnet/src/SemanticKernel/TemplateEngine/PromptTemplateEngine.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Orchestration; using Microsoft.SemanticKernel.TemplateEngine.Blocks; @@ -24,14 +25,15 @@ namespace Microsoft.SemanticKernel.TemplateEngine; /// public class PromptTemplateEngine : IPromptTemplateEngine { + private readonly ILoggerFactory _loggerFactory; private readonly ILogger _logger; - private readonly TemplateTokenizer _tokenizer; - public PromptTemplateEngine(ILogger? logger = null) + public PromptTemplateEngine(ILoggerFactory? loggerFactory = null) { - this._logger = logger ?? NullLogger.Instance; - this._tokenizer = new TemplateTokenizer(this._logger); + this._loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; + this._logger = this._loggerFactory.CreateLogger(nameof(PromptTemplateEngine)); + this._tokenizer = new TemplateTokenizer(loggerFactory); } /// @@ -46,7 +48,7 @@ public IList ExtractBlocks(string? templateText, bool validate = true) { if (!block.IsValid(out var error)) { - throw new TemplateException(TemplateException.ErrorCodes.SyntaxError, error); + throw new SKException(error); } } } @@ -88,7 +90,7 @@ internal async Task RenderAsync(IList blocks, SKContext context, default: const string Error = "Unexpected block type, the block doesn't have a rendering method"; this._logger.LogError(Error); - throw new TemplateException(TemplateException.ErrorCodes.UnexpectedBlockType, Error); + throw new SKException(Error); } } @@ -115,6 +117,6 @@ internal IList RenderVariables(IList blocks, ContextVariables? var this._logger.LogTrace("Rendering variables"); return blocks.Select(block => block.Type != BlockTypes.Variable ? block - : new TextBlock(((ITextRendering)block).Render(variables), this._logger)).ToList(); + : new TextBlock(((ITextRendering)block).Render(variables), this._loggerFactory)).ToList(); } } diff --git a/dotnet/src/SemanticKernel/TemplateEngine/TemplateException.cs b/dotnet/src/SemanticKernel/TemplateEngine/TemplateException.cs deleted file mode 100644 index 6eafbe90d9df..000000000000 --- a/dotnet/src/SemanticKernel/TemplateEngine/TemplateException.cs +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using Microsoft.SemanticKernel.Diagnostics; - -namespace Microsoft.SemanticKernel.TemplateEngine; - -#pragma warning disable RCS1194 // Implement exception constructors - -/// -/// Exception thrown for errors related to templating. -/// -public class TemplateException : SKException -{ - /// - /// Initializes a new instance of the class with a provided error code and message. - /// - /// The error code. - /// The exception message. - public TemplateException(ErrorCodes errorCode, string? message) - : this(errorCode, message, innerException: null) - { - } - - /// - /// Initializes a new instance of the class with a provided error code, message, and inner exception. - /// - /// The error code. - /// A string that describes the error. - /// The exception that is the cause of the current exception. - public TemplateException(ErrorCodes errorCode, string? message = null, Exception? innerException = null) - : base(GetDefaultMessage(errorCode, message), innerException) - { - this.ErrorCode = errorCode; - } - - /// - /// Gets the error code for this exception. - /// - public ErrorCodes ErrorCode { get; } - - /// Translate the error code into a default message. - private static string GetDefaultMessage(ErrorCodes errorCode, string? message) - { - string description = errorCode switch - { - ErrorCodes.SyntaxError => "Syntax error", - ErrorCodes.UnexpectedBlockType => "Unexpected block type", - ErrorCodes.FunctionNotFound => "Function not found", - ErrorCodes.RuntimeError => "Runtime error", - _ => $"Unknown error ({errorCode:G})", - }; - - return message is not null ? $"{description}: {message}" : description; - } - - /// - /// Error codes for . - /// - public enum ErrorCodes - { - /// - /// Unknown error. - /// - UnknownError = -1, - - /// - /// Syntax error, the template syntax used is not valid. - /// - SyntaxError = 0, - - /// - /// The block type produced be the tokenizer was not expected - /// - UnexpectedBlockType = 1, - - /// - /// The template requires an unknown function. - /// - FunctionNotFound = 2, - - /// - /// The template execution failed, e.g. a function call threw an exception. - /// - RuntimeError = 3, - } -} diff --git a/dotnet/src/SemanticKernel/TemplateEngine/TemplateTokenizer.cs b/dotnet/src/SemanticKernel/TemplateEngine/TemplateTokenizer.cs index 52782c6dee95..892f2a6834d8 100644 --- a/dotnet/src/SemanticKernel/TemplateEngine/TemplateTokenizer.cs +++ b/dotnet/src/SemanticKernel/TemplateEngine/TemplateTokenizer.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.TemplateEngine.Blocks; using Microsoft.SemanticKernel.Text; @@ -37,11 +38,11 @@ internal sealed class TemplateTokenizer /// /// Create a new instance of SK tokenizer /// - /// - public TemplateTokenizer(ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public TemplateTokenizer(ILoggerFactory? loggerFactory = null) { - this._logger = logger ?? NullLogger.Instance; - this._codeTokenizer = new CodeTokenizer(this._logger); + this._loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; + this._codeTokenizer = new CodeTokenizer(loggerFactory); } /// @@ -59,13 +60,13 @@ public IList Tokenize(string? text) // Render NULL to "" if (text.IsNullOrEmpty()) { - return new List { new TextBlock(string.Empty, this._logger) }; + return new List { new TextBlock(string.Empty, this._loggerFactory) }; } // If the template is "empty" return the content as a text block if (text.Length < MinCodeBlockLength) { - return new List { new TextBlock(text, this._logger) }; + return new List { new TextBlock(text, this._loggerFactory) }; } var blocks = new List(); @@ -133,7 +134,7 @@ public IList Tokenize(string? text) // If there is plain text between the current var/val/code block and the previous one, capture that as a TextBlock if (blockStartPos > endOfLastBlock) { - blocks.Add(new TextBlock(text, endOfLastBlock, blockStartPos, this._logger)); + blocks.Add(new TextBlock(text, endOfLastBlock, blockStartPos, this._loggerFactory)); } // Extract raw block @@ -147,7 +148,7 @@ public IList Tokenize(string? text) if (contentWithoutDelimiters.Length == 0) { // If what is left is empty, consider the raw block a Text Block - blocks.Add(new TextBlock(contentWithDelimiters, this._logger)); + blocks.Add(new TextBlock(contentWithDelimiters, this._loggerFactory)); } else { @@ -158,8 +159,7 @@ public IList Tokenize(string? text) case BlockTypes.Variable: if (codeBlocks.Count > 1) { - throw new TemplateException(TemplateException.ErrorCodes.SyntaxError, - $"Invalid token detected after the variable: {contentWithoutDelimiters}"); + throw new SKException($"Invalid token detected after the variable: {contentWithoutDelimiters}"); } blocks.Add(codeBlocks[0]); @@ -168,8 +168,7 @@ public IList Tokenize(string? text) case BlockTypes.Value: if (codeBlocks.Count > 1) { - throw new TemplateException(TemplateException.ErrorCodes.SyntaxError, - $"Invalid token detected after the value: {contentWithoutDelimiters}"); + throw new SKException($"Invalid token detected after the value: {contentWithoutDelimiters}"); } blocks.Add(codeBlocks[0]); @@ -178,19 +177,17 @@ public IList Tokenize(string? text) case BlockTypes.FunctionId: if (codeBlocks.Count > 2) { - throw new TemplateException(TemplateException.ErrorCodes.SyntaxError, - $"Functions support only one parameter: {contentWithoutDelimiters}"); + throw new SKException($"Functions support only one parameter: {contentWithoutDelimiters}"); } - blocks.Add(new CodeBlock(codeBlocks, contentWithoutDelimiters, this._logger)); + blocks.Add(new CodeBlock(codeBlocks, contentWithoutDelimiters, this._loggerFactory)); break; case BlockTypes.Code: case BlockTypes.Text: case BlockTypes.Undefined: default: - throw new TemplateException(TemplateException.ErrorCodes.UnexpectedBlockType, - $"Code tokenizer returned an incorrect first token type {codeBlocks[0].Type:G}"); + throw new SKException($"Code tokenizer returned an incorrect first token type {codeBlocks[0].Type:G}"); } } @@ -204,7 +201,7 @@ public IList Tokenize(string? text) // If there is something left after the last block, capture it as a TextBlock if (endOfLastBlock < text.Length) { - blocks.Add(new TextBlock(text, endOfLastBlock, text.Length, this._logger)); + blocks.Add(new TextBlock(text, endOfLastBlock, text.Length, this._loggerFactory)); } return blocks; @@ -212,7 +209,7 @@ public IList Tokenize(string? text) #region private ================================================================================ - private readonly ILogger _logger; + private readonly ILoggerFactory _loggerFactory; private readonly CodeTokenizer _codeTokenizer; private static string SubStr(string text, int startIndex, int stopIndex) diff --git a/dotnet/src/SemanticKernel/Text/TextChunker.cs b/dotnet/src/SemanticKernel/Text/TextChunker.cs index 3bbe91ac0740..3f17f0f933b1 100644 --- a/dotnet/src/SemanticKernel/Text/TextChunker.cs +++ b/dotnet/src/SemanticKernel/Text/TextChunker.cs @@ -15,6 +15,8 @@ namespace Microsoft.SemanticKernel.Text; /// public static class TextChunker { + public delegate int TokenCounter(string input); + private static readonly char[] s_spaceChar = new[] { ' ' }; private static readonly string?[] s_plaintextSplitOptions = new[] { "\n\r", ".", "?!", ";", ":", ",", ")]}", " ", "-", null }; private static readonly string?[] s_markdownSplitOptions = new[] { ".", "?!", ";", ":", ",", ")]}", " ", "-", "\n\r", null }; @@ -24,10 +26,13 @@ public static class TextChunker /// /// Text to split /// Maximum number of tokens per line. + /// Function to count tokens in a string. If not supplied, the default counter will be used. /// List of lines. - public static List SplitPlainTextLines(string text, int maxTokensPerLine) + public static List SplitPlainTextLines(string text, int maxTokensPerLine, TokenCounter? tokenCounter = null) { - return InternalSplitLines(text, maxTokensPerLine, trim: true, s_plaintextSplitOptions); + tokenCounter ??= DefaultTokenCounter; + + return InternalSplitLines(text, maxTokensPerLine, trim: true, s_plaintextSplitOptions, tokenCounter); } /// @@ -35,10 +40,13 @@ public static List SplitPlainTextLines(string text, int maxTokensPerLine /// /// Text to split /// Maximum number of tokens per line. + /// Function to count tokens in a string. If not supplied, the default counter will be used. /// List of lines. - public static List SplitMarkDownLines(string text, int maxTokensPerLine) + public static List SplitMarkDownLines(string text, int maxTokensPerLine, TokenCounter? tokenCounter = null) { - return InternalSplitLines(text, maxTokensPerLine, trim: true, s_markdownSplitOptions); + tokenCounter ??= DefaultTokenCounter; + + return InternalSplitLines(text, maxTokensPerLine, trim: true, s_markdownSplitOptions, tokenCounter); } /// @@ -47,10 +55,13 @@ public static List SplitMarkDownLines(string text, int maxTokensPerLine) /// Lines of text. /// Maximum number of tokens per paragraph. /// Number of tokens to overlap between paragraphs. + /// Function to count tokens in a string. If not supplied, the default counter will be used. /// List of paragraphs. - public static List SplitPlainTextParagraphs(List lines, int maxTokensPerParagraph, int overlapTokens = 0) + public static List SplitPlainTextParagraphs(List lines, int maxTokensPerParagraph, int overlapTokens = 0, TokenCounter? tokenCounter = null) { - return InternalSplitTextParagraphs(lines, maxTokensPerParagraph, overlapTokens, (text, maxTokens) => InternalSplitLines(text, maxTokens, trim: false, s_plaintextSplitOptions)); + tokenCounter ??= DefaultTokenCounter; + + return InternalSplitTextParagraphs(lines, maxTokensPerParagraph, overlapTokens, (text, maxTokens) => InternalSplitLines(text, maxTokens, trim: false, s_plaintextSplitOptions, tokenCounter), tokenCounter); } /// @@ -59,13 +70,16 @@ public static List SplitPlainTextParagraphs(List lines, int maxT /// Lines of text. /// Maximum number of tokens per paragraph. /// Number of tokens to overlap between paragraphs. + /// Function to count tokens in a string. If not supplied, the default counter will be used. /// List of paragraphs. - public static List SplitMarkdownParagraphs(List lines, int maxTokensPerParagraph, int overlapTokens = 0) + public static List SplitMarkdownParagraphs(List lines, int maxTokensPerParagraph, int overlapTokens = 0, TokenCounter? tokenCounter = null) { - return InternalSplitTextParagraphs(lines, maxTokensPerParagraph, overlapTokens, (text, maxTokens) => InternalSplitLines(text, maxTokens, trim: false, s_markdownSplitOptions)); + tokenCounter ??= DefaultTokenCounter; + + return InternalSplitTextParagraphs(lines, maxTokensPerParagraph, overlapTokens, (text, maxTokens) => InternalSplitLines(text, maxTokens, trim: false, s_markdownSplitOptions, tokenCounter), tokenCounter); } - private static List InternalSplitTextParagraphs(List lines, int maxTokensPerParagraph, int overlapTokens, Func> longLinesSplitter) + private static List InternalSplitTextParagraphs(List lines, int maxTokensPerParagraph, int overlapTokens, Func> longLinesSplitter, TokenCounter tokenCounter) { if (maxTokensPerParagraph <= 0) { @@ -87,14 +101,14 @@ private static List InternalSplitTextParagraphs(List lines, int // Split long lines first IEnumerable truncatedLines = lines.SelectMany(line => longLinesSplitter(line, adjustedMaxTokensPerParagraph)); - var paragraphs = BuildParagraph(truncatedLines, adjustedMaxTokensPerParagraph, longLinesSplitter); + var paragraphs = BuildParagraph(truncatedLines, adjustedMaxTokensPerParagraph, longLinesSplitter, tokenCounter); // distribute text more evenly in the last paragraphs when the last paragraph is too short. if (paragraphs.Count > 1) { var lastParagraph = paragraphs[paragraphs.Count - 1]; var secondLastParagraph = paragraphs[paragraphs.Count - 2]; - if (TokenCount(lastParagraph.Length) < adjustedMaxTokensPerParagraph / 4) + if (tokenCounter(lastParagraph) < adjustedMaxTokensPerParagraph / 4) { var lastParagraphTokens = lastParagraph.Split(s_spaceChar, StringSplitOptions.RemoveEmptyEntries); var secondLastParagraphTokens = secondLastParagraph.Split(s_spaceChar, StringSplitOptions.RemoveEmptyEntries); @@ -129,14 +143,14 @@ private static List InternalSplitTextParagraphs(List lines, int return paragraphs; } - private static List BuildParagraph(IEnumerable truncatedLines, int maxTokensPerParagraph, Func> longLinesSplitter) + private static List BuildParagraph(IEnumerable truncatedLines, int maxTokensPerParagraph, Func> longLinesSplitter, TokenCounter tokenCounter) { StringBuilder paragraphBuilder = new(); List paragraphs = new(); foreach (string line in truncatedLines) { - if (paragraphBuilder.Length > 0 && TokenCount(paragraphBuilder.Length) + TokenCount(line.Length) + 1 >= maxTokensPerParagraph) + if (paragraphBuilder.Length > 0 && tokenCounter(paragraphBuilder.ToString()) + tokenCounter(line) + 1 >= maxTokensPerParagraph) { // Complete the paragraph and prepare for the next paragraphs.Add(paragraphBuilder.ToString().Trim()); @@ -155,7 +169,7 @@ private static List BuildParagraph(IEnumerable truncatedLines, i return paragraphs; } - private static List InternalSplitLines(string text, int maxTokensPerLine, bool trim, string?[] splitOptions) + private static List InternalSplitLines(string text, int maxTokensPerLine, bool trim, string?[] splitOptions, TokenCounter tokenCounter) { var result = new List(); @@ -164,7 +178,7 @@ private static List InternalSplitLines(string text, int maxTokensPerLine for (int i = 0; i < splitOptions.Length; i++) { int count = result.Count; // track where the original input left off - var (splits2, inputWasSplit2) = Split(result, maxTokensPerLine, splitOptions[i].AsSpan(), trim); + var (splits2, inputWasSplit2) = Split(result, maxTokensPerLine, splitOptions[i].AsSpan(), trim, tokenCounter); result.AddRange(splits2); result.RemoveRange(0, count); // remove the original input if (!inputWasSplit2) @@ -175,26 +189,26 @@ private static List InternalSplitLines(string text, int maxTokensPerLine return result; } - private static (List, bool) Split(List input, int maxTokens, ReadOnlySpan separators, bool trim) + private static (List, bool) Split(List input, int maxTokens, ReadOnlySpan separators, bool trim, TokenCounter tokenCounter) { bool inputWasSplit = false; List result = new(); int count = input.Count; for (int i = 0; i < count; i++) { - var (splits, split) = Split(input[i].AsSpan(), input[i], maxTokens, separators, trim); + var (splits, split) = Split(input[i].AsSpan(), input[i], maxTokens, separators, trim, tokenCounter); result.AddRange(splits); inputWasSplit |= split; } return (result, inputWasSplit); } - private static (List, bool) Split(ReadOnlySpan input, string? inputString, int maxTokens, ReadOnlySpan separators, bool trim) + private static (List, bool) Split(ReadOnlySpan input, string? inputString, int maxTokens, ReadOnlySpan separators, bool trim, TokenCounter tokenCounter) { Debug.Assert(inputString is null || input.SequenceEqual(inputString.AsSpan())); List result = new(); var inputWasSplit = false; - if (TokenCount(input.Length) > maxTokens) + if (tokenCounter(input.ToString()) > maxTokens) { inputWasSplit = true; @@ -238,9 +252,9 @@ private static (List, bool) Split(ReadOnlySpan input, string? inpu } // Recursion - var (splits1, split1) = Split(firstHalf, null, maxTokens, separators, trim); + var (splits1, split1) = Split(firstHalf, null, maxTokens, separators, trim, tokenCounter); result.AddRange(splits1); - var (splits2, split2) = Split(secondHalf, null, maxTokens, separators, trim); + var (splits2, split2) = Split(secondHalf, null, maxTokens, separators, trim, tokenCounter); result.AddRange(splits2); inputWasSplit = split1 || split2; @@ -259,10 +273,8 @@ private static (List, bool) Split(ReadOnlySpan input, string? inpu return (result, inputWasSplit); } - private static int TokenCount(int inputLength) + private static int DefaultTokenCounter(string input) { - // TODO: partitioning methods should be configurable to allow for different tokenization strategies - // depending on the model to be called. For now, we use an extremely rough estimate. - return inputLength / 4; + return input.Length / 4; } } diff --git a/dotnet/src/Skills/Skills.Core/TextMemorySkill.cs b/dotnet/src/Skills/Skills.Core/TextMemorySkill.cs index 693e48bac379..f092249cf59a 100644 --- a/dotnet/src/Skills/Skills.Core/TextMemorySkill.cs +++ b/dotnet/src/Skills/Skills.Core/TextMemorySkill.cs @@ -7,7 +7,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Memory; using Microsoft.SemanticKernel.SkillDefinition; @@ -64,7 +63,7 @@ public TextMemorySkill(ISemanticTextMemory memory) /// /// Memories collection associated with the memory to retrieve /// The key associated with the memory to retrieve. - /// Application logger + /// The to use for logging. If null, no logging will be performed. /// The to monitor for cancellation requests. The default is . /// /// SKContext.Variables[TextMemorySkill.KeyParam] = "countryInfo1" @@ -74,14 +73,13 @@ public TextMemorySkill(ISemanticTextMemory memory) public async Task RetrieveAsync( [SKName(CollectionParam), Description("Memories collection associated with the memory to retrieve"), DefaultValue(DefaultCollection)] string? collection, [SKName(KeyParam), Description("The key associated with the memory to retrieve")] string key, - ILogger? logger, + ILoggerFactory? loggerFactory, CancellationToken cancellationToken = default) { Verify.NotNullOrWhiteSpace(collection); Verify.NotNullOrWhiteSpace(key); - logger ??= NullLogger.Instance; - logger.LogDebug("Recalling memory with key '{0}' from collection '{1}'", key, collection); + loggerFactory?.CreateLogger(nameof(TextMemorySkill)).LogDebug("Recalling memory with key '{0}' from collection '{1}'", key, collection); var memory = await this._memory.GetAsync(collection, key, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -99,7 +97,7 @@ public async Task RetrieveAsync( /// Memories collection to search. /// The relevance score, from 0.0 to 1.0, where 1.0 means perfect match. /// The maximum number of relevant memories to recall. - /// Application logger + /// The to use for logging. If null, no logging will be performed. /// The to monitor for cancellation requests. The default is . [SKFunction, Description("Semantic search and return up to N memories related to the input text")] public async Task RecallAsync( @@ -107,15 +105,16 @@ public async Task RecallAsync( [SKName(CollectionParam), Description("Memories collection to search"), DefaultValue(DefaultCollection)] string collection, [SKName(RelevanceParam), Description("The relevance score, from 0.0 to 1.0, where 1.0 means perfect match"), DefaultValue(DefaultRelevance)] double? relevance, [SKName(LimitParam), Description("The maximum number of relevant memories to recall"), DefaultValue(DefaultLimit)] int? limit, - ILogger? logger, + ILoggerFactory? loggerFactory, CancellationToken cancellationToken = default) { Verify.NotNullOrWhiteSpace(collection); - logger ??= NullLogger.Instance; relevance ??= DefaultRelevance; limit ??= DefaultLimit; - logger.LogDebug("Searching memories in collection '{0}', relevance '{1}'", collection, relevance); + ILogger? logger = loggerFactory?.CreateLogger(nameof(TextMemorySkill)); + + logger?.LogDebug("Searching memories in collection '{0}', relevance '{1}'", collection, relevance); // Search memory List memories = await this._memory @@ -125,11 +124,11 @@ public async Task RecallAsync( if (memories.Count == 0) { - logger.LogWarning("Memories not found in collection: {0}", collection); + logger?.LogWarning("Memories not found in collection: {0}", collection); return string.Empty; } - logger.LogTrace("Done looking for memories in collection '{0}')", collection); + logger?.LogTrace("Done looking for memories in collection '{0}')", collection); return limit == 1 ? memories[0].Metadata.Text : JsonSerializer.Serialize(memories.Select(x => x.Metadata.Text)); } @@ -144,21 +143,20 @@ public async Task RecallAsync( /// The information to save /// Memories collection associated with the information to save /// The key associated with the information to save - /// Application logger + /// The to use for logging. If null, no logging will be performed. /// The to monitor for cancellation requests. The default is . [SKFunction, Description("Save information to semantic memory")] public async Task SaveAsync( [Description("The information to save")] string input, [SKName(CollectionParam), Description("Memories collection associated with the information to save"), DefaultValue(DefaultCollection)] string collection, [SKName(KeyParam), Description("The key associated with the information to save")] string key, - ILogger? logger, + ILoggerFactory? loggerFactory, CancellationToken cancellationToken = default) { Verify.NotNullOrWhiteSpace(collection); Verify.NotNullOrWhiteSpace(key); - logger ??= NullLogger.Instance; - logger.LogDebug("Saving memory to collection '{0}'", collection); + loggerFactory?.CreateLogger(nameof(TextMemorySkill)).LogDebug("Saving memory to collection '{0}'", collection); await this._memory.SaveInformationAsync(collection, text: input, id: key, cancellationToken: cancellationToken).ConfigureAwait(false); } @@ -172,20 +170,19 @@ public async Task SaveAsync( /// /// Memories collection associated with the information to save /// The key associated with the information to save - /// Application logger + /// The to use for logging. If null, no logging will be performed. /// The to monitor for cancellation requests. The default is . [SKFunction, Description("Remove specific memory")] public async Task RemoveAsync( [SKName(CollectionParam), Description("Memories collection associated with the information to save"), DefaultValue(DefaultCollection)] string collection, [SKName(KeyParam), Description("The key associated with the information to save")] string key, - ILogger? logger, + ILoggerFactory? loggerFactory, CancellationToken cancellationToken = default) { Verify.NotNullOrWhiteSpace(collection); Verify.NotNullOrWhiteSpace(key); - logger ??= NullLogger.Instance; - logger.LogDebug("Removing memory from collection '{0}'", collection); + loggerFactory?.CreateLogger(nameof(TextMemorySkill)).LogDebug("Removing memory from collection '{0}'", collection); await this._memory.RemoveAsync(collection, key, cancellationToken: cancellationToken).ConfigureAwait(false); } diff --git a/dotnet/src/Skills/Skills.Document/DocumentSkill.cs b/dotnet/src/Skills/Skills.Document/DocumentSkill.cs index b88e68cac813..35b2f8d37924 100644 --- a/dotnet/src/Skills/Skills.Document/DocumentSkill.cs +++ b/dotnet/src/Skills/Skills.Document/DocumentSkill.cs @@ -52,19 +52,19 @@ public static class Parameters private readonly IDocumentConnector _documentConnector; private readonly IFileSystemConnector _fileSystemConnector; - private readonly ILogger _logger; + private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// Document connector /// File system connector - /// Optional logger - public DocumentSkill(IDocumentConnector documentConnector, IFileSystemConnector fileSystemConnector, ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public DocumentSkill(IDocumentConnector documentConnector, IFileSystemConnector fileSystemConnector, ILoggerFactory? loggerFactory = null) { this._documentConnector = documentConnector ?? throw new ArgumentNullException(nameof(documentConnector)); this._fileSystemConnector = fileSystemConnector ?? throw new ArgumentNullException(nameof(fileSystemConnector)); - this._logger = logger ?? new NullLogger(); + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(DocumentSkill)) : NullLogger.Instance; } /// diff --git a/dotnet/src/Skills/Skills.Grpc/Extensions/KernelGrpcExtensions.cs b/dotnet/src/Skills/Skills.Grpc/Extensions/KernelGrpcExtensions.cs index 16ca31f94360..90915633d1a1 100644 --- a/dotnet/src/Skills/Skills.Grpc/Extensions/KernelGrpcExtensions.cs +++ b/dotnet/src/Skills/Skills.Grpc/Extensions/KernelGrpcExtensions.cs @@ -48,7 +48,7 @@ public static IDictionary ImportGrpcSkillFromDirectory( throw new FileNotFoundException($"No .proto document for the specified path - {filePath} is found."); } - kernel.Logger.LogTrace("Registering gRPC functions from {0} .proto document", filePath); + kernel.LoggerFactory.CreateLogger(nameof(KernelGrpcExtensions)).LogTrace("Registering gRPC functions from {0} .proto document", filePath); using var stream = File.OpenRead(filePath); @@ -74,7 +74,7 @@ public static IDictionary ImportGrpcSkillFromFile( throw new FileNotFoundException($"No .proto document for the specified path - {filePath} is found."); } - kernel.Logger.LogTrace("Registering gRPC functions from {0} .proto document", filePath); + kernel.LoggerFactory.CreateLogger(nameof(KernelGrpcExtensions)).LogTrace("Registering gRPC functions from {0} .proto document", filePath); using var stream = File.OpenRead(filePath); @@ -105,22 +105,23 @@ public static IDictionary RegisterGrpcSkill( var skill = new Dictionary(); - var client = HttpClientProvider.GetHttpClient(kernel.Config, httpClient, kernel.Logger); + var client = HttpClientProvider.GetHttpClient(kernel.Config, httpClient, kernel.LoggerFactory); var runner = new GrpcOperationRunner(client); + ILogger logger = kernel.LoggerFactory.CreateLogger(nameof(KernelGrpcExtensions)); foreach (var operation in operations) { try { - kernel.Logger.LogTrace("Registering gRPC function {0}.{1}", skillName, operation.Name); + logger.LogTrace("Registering gRPC function {0}.{1}", skillName, operation.Name); var function = kernel.RegisterGrpcFunction(runner, skillName, operation); skill[function.Name] = function; } catch (Exception ex) when (!ex.IsCriticalException()) { //Logging the exception and keep registering other gRPC functions - kernel.Logger.LogWarning(ex, "Something went wrong while rendering the gRPC function. Function: {0}.{1}. Error: {2}", + logger.LogWarning(ex, "Something went wrong while rendering the gRPC function. Function: {0}.{1}. Error: {2}", skillName, operation.Name, ex.Message); } } @@ -175,7 +176,7 @@ async Task ExecuteAsync(SKContext context) } catch (Exception ex) when (!ex.IsCriticalException()) { - kernel.Logger.LogWarning(ex, "Something went wrong while rendering the gRPC function. Function: {0}.{1}. Error: {2}", skillName, operation.Name, + kernel.LoggerFactory.CreateLogger(nameof(KernelGrpcExtensions)).LogWarning(ex, "Something went wrong while rendering the gRPC function. Function: {0}.{1}. Error: {2}", skillName, operation.Name, ex.Message); throw; } @@ -189,7 +190,7 @@ async Task ExecuteAsync(SKContext context) description: operation.Name, skillName: skillName, functionName: operation.Name, - logger: kernel.Logger); + loggerFactory: kernel.LoggerFactory); return kernel.RegisterCustomFunction(function); } diff --git a/dotnet/src/Skills/Skills.MsGraph/CalendarSkill.cs b/dotnet/src/Skills/Skills.MsGraph/CalendarSkill.cs index 63f34d2510ac..333b2f7d79a6 100644 --- a/dotnet/src/Skills/Skills.MsGraph/CalendarSkill.cs +++ b/dotnet/src/Skills/Skills.MsGraph/CalendarSkill.cs @@ -64,7 +64,7 @@ public static class Parameters } private readonly ICalendarConnector _connector; - private readonly ILogger _logger; + private readonly ILogger _logger; private static readonly JsonSerializerOptions s_options = new() { WriteIndented = false, @@ -75,13 +75,13 @@ public static class Parameters /// Initializes a new instance of the class. /// /// Calendar connector. - /// Logger. - public CalendarSkill(ICalendarConnector connector, ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public CalendarSkill(ICalendarConnector connector, ILoggerFactory? loggerFactory = null) { Ensure.NotNull(connector, nameof(connector)); this._connector = connector; - this._logger = logger ?? new NullLogger(); + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(CalendarSkill)) : NullLogger.Instance; } /// diff --git a/dotnet/src/Skills/Skills.MsGraph/CloudDriveSkill.cs b/dotnet/src/Skills/Skills.MsGraph/CloudDriveSkill.cs index 572d8317f2b1..9b696f0a11b5 100644 --- a/dotnet/src/Skills/Skills.MsGraph/CloudDriveSkill.cs +++ b/dotnet/src/Skills/Skills.MsGraph/CloudDriveSkill.cs @@ -30,14 +30,14 @@ public static class Parameters } private readonly ICloudDriveConnector _connector; - private readonly ILogger _logger; + private readonly ILogger _logger; - public CloudDriveSkill(ICloudDriveConnector connector, ILogger? logger = null) + public CloudDriveSkill(ICloudDriveConnector connector, ILoggerFactory? loggerFactory = null) { Ensure.NotNull(connector, nameof(connector)); this._connector = connector; - this._logger = logger ?? new NullLogger(); + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(CloudDriveSkill)) : NullLogger.Instance; } /// diff --git a/dotnet/src/Skills/Skills.MsGraph/EmailSkill.cs b/dotnet/src/Skills/Skills.MsGraph/EmailSkill.cs index f6f7222d686b..07e31c04aa95 100644 --- a/dotnet/src/Skills/Skills.MsGraph/EmailSkill.cs +++ b/dotnet/src/Skills/Skills.MsGraph/EmailSkill.cs @@ -48,7 +48,7 @@ public static class Parameters } private readonly IEmailConnector _connector; - private readonly ILogger _logger; + private readonly ILogger _logger; private static readonly JsonSerializerOptions s_options = new() { WriteIndented = false, @@ -59,13 +59,13 @@ public static class Parameters /// Initializes a new instance of the class. /// /// Email connector. - /// Logger. - public EmailSkill(IEmailConnector connector, ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public EmailSkill(IEmailConnector connector, ILoggerFactory? loggerFactory = null) { Ensure.NotNull(connector, nameof(connector)); this._connector = connector; - this._logger = logger ?? new NullLogger(); + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(EmailSkill)) : NullLogger.Instance; } /// diff --git a/dotnet/src/Skills/Skills.MsGraph/TaskListSkill.cs b/dotnet/src/Skills/Skills.MsGraph/TaskListSkill.cs index 550f1d6dadd7..5650f467264c 100644 --- a/dotnet/src/Skills/Skills.MsGraph/TaskListSkill.cs +++ b/dotnet/src/Skills/Skills.MsGraph/TaskListSkill.cs @@ -37,19 +37,19 @@ public static class Parameters } private readonly ITaskManagementConnector _connector; - private readonly ILogger _logger; + private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// Task list connector. - /// Logger. - public TaskListSkill(ITaskManagementConnector connector, ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public TaskListSkill(ITaskManagementConnector connector, ILoggerFactory? loggerFactory = null) { Ensure.NotNull(connector, nameof(connector)); this._connector = connector; - this._logger = logger ?? new NullLogger(); + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(TaskListSkill)) : NullLogger.Instance; } /// diff --git a/dotnet/src/Skills/Skills.OpenAPI/Authentication/CustomAuthenticationProvider.cs b/dotnet/src/Skills/Skills.OpenAPI/Authentication/CustomAuthenticationProvider.cs new file mode 100644 index 000000000000..7ad9cb09dcfc --- /dev/null +++ b/dotnet/src/Skills/Skills.OpenAPI/Authentication/CustomAuthenticationProvider.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Microsoft.SemanticKernel.Skills.OpenAPI.Authentication; + +/// +/// Retrieves authentication content (scheme and value) via the provided delegate and applies it to HTTP requests. +/// +public sealed class CustomAuthenticationProvider +{ + private readonly Func> _header; + private readonly Func> _value; + + /// + /// Creates an instance of the class. + /// + /// Delegate for retrieving the header name. + /// Delegate for retrieving the value. + public CustomAuthenticationProvider(Func> header, Func> value) + { + this._header = header; + this._value = value; + } + + /// + /// Applies the header and value to the provided HTTP request message. + /// + /// The HTTP request message. + /// + public async Task AuthenticateRequestAsync(HttpRequestMessage request) + { + var header = await this._header().ConfigureAwait(false); + var value = await this._value().ConfigureAwait(false); + request.Headers.Add(header, value); + } +} diff --git a/dotnet/src/Skills/Skills.OpenAPI/Extensions/KernelAIPluginExtensions.cs b/dotnet/src/Skills/Skills.OpenAPI/Extensions/KernelAIPluginExtensions.cs index dc77b59de1f1..f5d1bdfa87ae 100644 --- a/dotnet/src/Skills/Skills.OpenAPI/Extensions/KernelAIPluginExtensions.cs +++ b/dotnet/src/Skills/Skills.OpenAPI/Extensions/KernelAIPluginExtensions.cs @@ -43,7 +43,7 @@ public static async Task> ImportAIPluginAsync( Verify.ValidSkillName(skillName); #pragma warning disable CA2000 // Dispose objects before losing scope. No need to dispose the Http client here. It can either be an internal client using NonDisposableHttpClientHandler or an external client managed by the calling code, which should handle its disposal. - var internalHttpClient = HttpClientProvider.GetHttpClient(kernel.Config, executionParameters?.HttpClient, kernel.Logger); + var internalHttpClient = HttpClientProvider.GetHttpClient(kernel.Config, executionParameters?.HttpClient, kernel.LoggerFactory); #pragma warning restore CA2000 var pluginContents = await LoadDocumentFromFilePath( @@ -82,7 +82,7 @@ public static async Task> ImportAIPluginAsync( Verify.ValidSkillName(skillName); #pragma warning disable CA2000 // Dispose objects before losing scope. No need to dispose the Http client here. It can either be an internal client using NonDisposableHttpClientHandler or an external client managed by the calling code, which should handle its disposal. - var internalHttpClient = HttpClientProvider.GetHttpClient(kernel.Config, executionParameters?.HttpClient, kernel.Logger); + var internalHttpClient = HttpClientProvider.GetHttpClient(kernel.Config, executionParameters?.HttpClient, kernel.LoggerFactory); #pragma warning restore CA2000 var pluginContents = await LoadDocumentFromUri( @@ -121,7 +121,7 @@ public static async Task> ImportAIPluginAsync( Verify.ValidSkillName(skillName); #pragma warning disable CA2000 // Dispose objects before losing scope. No need to dispose the Http client here. It can either be an internal client using NonDisposableHttpClientHandler or an external client managed by the calling code, which should handle its disposal. - var internalHttpClient = HttpClientProvider.GetHttpClient(kernel.Config, executionParameters?.HttpClient, kernel.Logger); + var internalHttpClient = HttpClientProvider.GetHttpClient(kernel.Config, executionParameters?.HttpClient, kernel.LoggerFactory); #pragma warning restore CA2000 var pluginContents = await LoadDocumentFromStream(kernel, stream).ConfigureAwait(false); @@ -173,7 +173,7 @@ private static async Task> LoadSkill( string pluginJson, CancellationToken cancellationToken) { - var parser = new OpenApiDocumentParser(kernel.Logger); + var parser = new OpenApiDocumentParser(kernel.LoggerFactory); using (var documentStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(pluginJson))) { @@ -183,18 +183,19 @@ private static async Task> LoadSkill( var skill = new Dictionary(); + ILogger logger = kernel.LoggerFactory.CreateLogger(nameof(KernelAIPluginExtensions)); foreach (var operation in operations) { try { - kernel.Logger.LogTrace("Registering Rest function {0}.{1}", skillName, operation.Id); + logger.LogTrace("Registering Rest function {0}.{1}", skillName, operation.Id); var function = kernel.RegisterRestApiFunction(skillName, runner, operation, executionParameters?.ServerUrlOverride, cancellationToken); skill[function.Name] = function; } catch (Exception ex) when (!ex.IsCriticalException()) { //Logging the exception and keep registering other Rest functions - kernel.Logger.LogWarning(ex, "Something went wrong while rendering the Rest function. Function: {0}.{1}. Error: {2}", + logger.LogWarning(ex, "Something went wrong while rendering the Rest function. Function: {0}.{1}. Error: {2}", skillName, operation.Id, ex.Message); } } @@ -237,7 +238,7 @@ private static async Task LoadDocumentFromFilePath( throw new FileNotFoundException($"Invalid URI. The specified path '{filePath}' does not exist."); } - kernel.Logger.LogTrace("Importing AI Plugin from {0}", filePath); + kernel.LoggerFactory.CreateLogger(nameof(KernelAIPluginExtensions)).LogTrace("Importing AI Plugin from {0}", filePath); using (var sr = File.OpenText(filePath)) { @@ -305,7 +306,7 @@ private static ISKFunction RegisterRestApiFunction( { var restOperationParameters = operation.GetParameters(serverUrlOverride); - var logger = kernel.Logger ?? NullLogger.Instance; + var logger = kernel.LoggerFactory is not null ? kernel.LoggerFactory.CreateLogger(nameof(KernelAIPluginExtensions)) : NullLogger.Instance; async Task ExecuteAsync(SKContext context) { @@ -368,7 +369,7 @@ async Task ExecuteAsync(SKContext context) description: operation.Description, skillName: skillName, functionName: ConvertOperationIdToValidFunctionName(operation.Id, logger), - logger: logger); + loggerFactory: kernel.LoggerFactory); return kernel.RegisterCustomFunction(function); } @@ -387,7 +388,7 @@ private static string ConvertOperationIdToValidFunctionName(string operationId, Verify.ValidFunctionName(operationId); return operationId; } - catch (KernelException) + catch (SKException) { } diff --git a/dotnet/src/Skills/Skills.OpenAPI/Extensions/KernelChatGptPluginExtensions.cs b/dotnet/src/Skills/Skills.OpenAPI/Extensions/KernelChatGptPluginExtensions.cs index afd9c45a0dc3..8ccdcda11bf1 100644 --- a/dotnet/src/Skills/Skills.OpenAPI/Extensions/KernelChatGptPluginExtensions.cs +++ b/dotnet/src/Skills/Skills.OpenAPI/Extensions/KernelChatGptPluginExtensions.cs @@ -45,7 +45,7 @@ public static async Task> ImportChatGptPluginFr Verify.ValidSkillName(skillName); #pragma warning disable CA2000 // Dispose objects before losing scope. No need to dispose the Http client here. It can either be an internal client using NonDisposableHttpClientHandler or an external client managed by the calling code, which should handle its disposal. - var internalHttpClient = HttpClientProvider.GetHttpClient(kernel.Config, executionParameters?.HttpClient, kernel.Logger); + var internalHttpClient = HttpClientProvider.GetHttpClient(kernel.Config, executionParameters?.HttpClient, kernel.LoggerFactory); #pragma warning restore CA2000 // Dispose objects before losing scope. No need to dispose the Http client here. It can either be an internal client using NonDisposableHttpClientHandler or an external client managed by the calling code, which should handle its disposal. using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); @@ -129,7 +129,7 @@ public static async Task> ImportChatGptPluginFr throw new FileNotFoundException($"No ChatGPT plugin for the specified path - {chatGptPluginPath} is found"); } - kernel.Logger.LogTrace("Registering Rest functions from {0} ChatGPT Plugin", chatGptPluginPath); + kernel.LoggerFactory.CreateLogger(nameof(KernelChatGptPluginExtensions)).LogTrace("Registering Rest functions from {0} ChatGPT Plugin", chatGptPluginPath); using var stream = File.OpenRead(chatGptPluginPath); @@ -160,7 +160,7 @@ public static async Task> ImportChatGptPluginFr throw new FileNotFoundException($"No ChatGPT plugin for the specified path - {filePath} is found"); } - kernel.Logger.LogTrace("Registering Rest functions from {0} ChatGPT Plugin", filePath); + kernel.LoggerFactory.CreateLogger(nameof(KernelChatGptPluginExtensions)).LogTrace("Registering Rest functions from {0} ChatGPT Plugin", filePath); using var stream = File.OpenRead(filePath); diff --git a/dotnet/src/Skills/Skills.OpenAPI/Extensions/KernelOpenApiExtensions.cs b/dotnet/src/Skills/Skills.OpenAPI/Extensions/KernelOpenApiExtensions.cs index 6d296a3b9562..dcc6ee07523b 100644 --- a/dotnet/src/Skills/Skills.OpenAPI/Extensions/KernelOpenApiExtensions.cs +++ b/dotnet/src/Skills/Skills.OpenAPI/Extensions/KernelOpenApiExtensions.cs @@ -52,7 +52,7 @@ public static async Task> ImportOpenApiSkillFro Verify.ValidSkillName(skillName); #pragma warning disable CA2000 // Dispose objects before losing scope. No need to dispose the Http client here. It can either be an internal client using NonDisposableHttpClientHandler or an external client managed by the calling code, which should handle its disposal. - var internalHttpClient = HttpClientProvider.GetHttpClient(kernel.Config, executionParameters?.HttpClient, kernel.Logger); + var internalHttpClient = HttpClientProvider.GetHttpClient(kernel.Config, executionParameters?.HttpClient, kernel.LoggerFactory); #pragma warning restore CA2000 // Dispose objects before losing scope. No need to dispose the Http client here. It can either be an internal client using NonDisposableHttpClientHandler or an external client managed by the calling code, which should handle its disposal. using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); @@ -133,7 +133,7 @@ public static async Task> ImportOpenApiSkillFro throw new FileNotFoundException($"No OpenApi document for the specified path - {openApiDocumentPath} is found."); } - kernel.Logger.LogTrace("Registering Rest functions from {0} OpenApi document", openApiDocumentPath); + kernel.LoggerFactory.CreateLogger(nameof(KernelOpenApiExtensions)).LogTrace("Registering Rest functions from {0} OpenApi document", openApiDocumentPath); var skill = new Dictionary(); @@ -164,7 +164,7 @@ public static async Task> ImportOpenApiSkillFro throw new FileNotFoundException($"No OpenApi document for the specified path - {filePath} is found."); } - kernel.Logger.LogTrace("Registering Rest functions from {0} OpenApi document", filePath); + kernel.LoggerFactory.CreateLogger(nameof(KernelOpenApiExtensions)).LogTrace("Registering Rest functions from {0} OpenApi document", filePath); using var stream = File.OpenRead(filePath); @@ -192,29 +192,30 @@ public static async Task> RegisterOpenApiSkillA Verify.ValidSkillName(skillName); // Parse - var parser = new OpenApiDocumentParser(kernel.Logger); + var parser = new OpenApiDocumentParser(kernel.LoggerFactory); var operations = await parser.ParseAsync(documentStream, executionParameters?.IgnoreNonCompliantErrors ?? false, cancellationToken).ConfigureAwait(false); - var internalHttpClient = HttpClientProvider.GetHttpClient(kernel.Config, executionParameters?.HttpClient, kernel.Logger); + var internalHttpClient = HttpClientProvider.GetHttpClient(kernel.Config, executionParameters?.HttpClient, kernel.LoggerFactory); var userAgent = executionParameters?.UserAgent ?? Telemetry.HttpUserAgent; var runner = new RestApiOperationRunner(internalHttpClient, executionParameters?.AuthCallback, userAgent); var skill = new Dictionary(); + ILogger logger = kernel.LoggerFactory.CreateLogger(nameof(KernelOpenApiExtensions)); foreach (var operation in operations) { try { - kernel.Logger.LogTrace("Registering Rest function {0}.{1}", skillName, operation.Id); + logger.LogTrace("Registering Rest function {0}.{1}", skillName, operation.Id); var function = kernel.RegisterRestApiFunction(skillName, runner, operation, executionParameters?.ServerUrlOverride, cancellationToken); skill[function.Name] = function; } catch (Exception ex) when (!ex.IsCriticalException()) { //Logging the exception and keep registering other Rest functions - kernel.Logger.LogWarning(ex, "Something went wrong while rendering the Rest function. Function: {0}.{1}. Error: {2}", + logger.LogWarning(ex, "Something went wrong while rendering the Rest function. Function: {0}.{1}. Error: {2}", skillName, operation.Id, ex.Message); } } @@ -244,7 +245,7 @@ private static ISKFunction RegisterRestApiFunction( { var restOperationParameters = operation.GetParameters(serverUrlOverride); - var logger = kernel.Logger ?? NullLogger.Instance; + var logger = kernel.LoggerFactory is not null ? kernel.LoggerFactory.CreateLogger(nameof(KernelOpenApiExtensions)) : NullLogger.Instance; async Task ExecuteAsync(SKContext context) { @@ -306,7 +307,7 @@ async Task ExecuteAsync(SKContext context) description: operation.Description, skillName: skillName, functionName: ConvertOperationIdToValidFunctionName(operation.Id, logger), - logger: logger); + loggerFactory: kernel.LoggerFactory); return kernel.RegisterCustomFunction(function); } @@ -325,7 +326,7 @@ private static string ConvertOperationIdToValidFunctionName(string operationId, Verify.ValidFunctionName(operationId); return operationId; } - catch (KernelException) + catch (SKException) { } diff --git a/dotnet/src/Skills/Skills.OpenAPI/OpenApi/OpenApiDocumentParser.cs b/dotnet/src/Skills/Skills.OpenAPI/OpenApi/OpenApiDocumentParser.cs index c74b12669fdf..8c7f4b27d377 100644 --- a/dotnet/src/Skills/Skills.OpenAPI/OpenApi/OpenApiDocumentParser.cs +++ b/dotnet/src/Skills/Skills.OpenAPI/OpenApi/OpenApiDocumentParser.cs @@ -30,10 +30,10 @@ internal sealed class OpenApiDocumentParser : IOpenApiDocumentParser /// /// Initializes a new instance of the class. /// - /// Optional logger instance. - public OpenApiDocumentParser(ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public OpenApiDocumentParser(ILoggerFactory? loggerFactory = null) { - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(OpenApiDocumentParser)) : NullLogger.Instance; } /// diff --git a/dotnet/src/Skills/Skills.UnitTests/MsGraph/CalendarSkillTests.cs b/dotnet/src/Skills/Skills.UnitTests/MsGraph/CalendarSkillTests.cs index f9447c499243..10e52198b825 100644 --- a/dotnet/src/Skills/Skills.UnitTests/MsGraph/CalendarSkillTests.cs +++ b/dotnet/src/Skills/Skills.UnitTests/MsGraph/CalendarSkillTests.cs @@ -4,7 +4,7 @@ using System.Globalization; using System.Threading; using System.Threading.Tasks; -using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Diagnostics; using Microsoft.SemanticKernel.Skills.MsGraph; using Microsoft.SemanticKernel.Skills.MsGraph.Models; using Moq; @@ -191,8 +191,7 @@ public async Task AddEventAsyncWithoutStartFailsAsync() // Assert Assert.True(context.ErrorOccurred); - KernelException e = Assert.IsType(context.LastException); - Assert.Equal(KernelException.ErrorCodes.FunctionInvokeError, e.ErrorCode); + Assert.IsType(context.LastException); } [Fact] @@ -219,8 +218,7 @@ public async Task AddEventAsyncWithoutEndFailsAsync() // Assert Assert.True(context.ErrorOccurred); - KernelException e = Assert.IsType(context.LastException); - Assert.Equal(KernelException.ErrorCodes.FunctionInvokeError, e.ErrorCode); + Assert.IsType(context.LastException); } [Fact] diff --git a/dotnet/src/Skills/Skills.UnitTests/OpenAPI/Authentication/CustomAuthenticationProviderTests.cs b/dotnet/src/Skills/Skills.UnitTests/OpenAPI/Authentication/CustomAuthenticationProviderTests.cs new file mode 100644 index 000000000000..8132da75b52c --- /dev/null +++ b/dotnet/src/Skills/Skills.UnitTests/OpenAPI/Authentication/CustomAuthenticationProviderTests.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.SemanticKernel.Skills.OpenAPI.Authentication; +using Xunit; + +namespace SemanticKernel.Skills.UnitTests.OpenAPI.Authentication; + +public class CustomAuthenticationProviderTests +{ + [Fact] + public async Task AuthenticateRequestAsyncSucceedsAsync() + { + // Arrange + var header = "X-MyHeader"; + var value = Guid.NewGuid().ToString(); + + using var request = new HttpRequestMessage(); + + var target = new CustomAuthenticationProvider(() => Task.FromResult(header), () => Task.FromResult(value)); + + // Act + await target.AuthenticateRequestAsync(request); + + // Assert + Assert.True(request.Headers.Contains(header)); + Assert.Equal(request.Headers.GetValues(header).FirstOrDefault(), value); + } +} diff --git a/dotnet/src/Skills/Skills.Web/Bing/BingConnector.cs b/dotnet/src/Skills/Skills.Web/Bing/BingConnector.cs index 552d77741e1e..708f181ebd69 100644 --- a/dotnet/src/Skills/Skills.Web/Bing/BingConnector.cs +++ b/dotnet/src/Skills/Skills.Web/Bing/BingConnector.cs @@ -28,9 +28,9 @@ public sealed class BingConnector : IWebSearchEngineConnector /// Initializes a new instance of the class. /// /// The API key to authenticate the connector. - /// An optional logger to log connector-related information. - public BingConnector(string apiKey, ILogger? logger = null) : - this(apiKey, new HttpClient(NonDisposableHttpClientHandler.Instance, false), logger) + /// The to use for logging. If null, no logging will be performed. + public BingConnector(string apiKey, ILoggerFactory? loggerFactory = null) : + this(apiKey, new HttpClient(NonDisposableHttpClientHandler.Instance, false), loggerFactory) { } @@ -39,13 +39,13 @@ public BingConnector(string apiKey, ILogger? logger = null) : /// /// The API key to authenticate the connector. /// The HTTP client to use for making requests. - /// An optional logger to log connector-related information. - public BingConnector(string apiKey, HttpClient httpClient, ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public BingConnector(string apiKey, HttpClient httpClient, ILoggerFactory? loggerFactory = null) { Verify.NotNull(httpClient); this._apiKey = apiKey; - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(BingConnector)) : NullLogger.Instance; this._httpClient = httpClient; this._httpClient.DefaultRequestHeaders.Add("User-Agent", Telemetry.HttpUserAgent); } diff --git a/dotnet/src/Skills/Skills.Web/Google/GoogleConnector.cs b/dotnet/src/Skills/Skills.Web/Google/GoogleConnector.cs index 4a2c8871bc3f..894445d54ed2 100644 --- a/dotnet/src/Skills/Skills.Web/Google/GoogleConnector.cs +++ b/dotnet/src/Skills/Skills.Web/Google/GoogleConnector.cs @@ -27,11 +27,11 @@ public sealed class GoogleConnector : IWebSearchEngineConnector, IDisposable /// /// Google Custom Search API (looks like "ABcdEfG1...") /// Google Search Engine ID (looks like "a12b345...") - /// Optional logger + /// The to use for logging. If null, no logging will be performed. public GoogleConnector( string apiKey, string searchEngineId, - ILogger? logger = null) : this(new BaseClientService.Initializer { ApiKey = apiKey }, searchEngineId, logger) + ILoggerFactory? loggerFactory = null) : this(new BaseClientService.Initializer { ApiKey = apiKey }, searchEngineId, loggerFactory) { Verify.NotNullOrWhiteSpace(apiKey); } @@ -41,18 +41,18 @@ public GoogleConnector( /// /// The connector initializer /// Google Search Engine ID (looks like "a12b345...") - /// Optional logger + /// The to use for logging. If null, no logging will be performed. public GoogleConnector( BaseClientService.Initializer initializer, string searchEngineId, - ILogger? logger = null) + ILoggerFactory? loggerFactory = null) { Verify.NotNull(initializer); Verify.NotNullOrWhiteSpace(searchEngineId); this._search = new CustomSearchAPIService(initializer); this._searchEngineId = searchEngineId; - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(GoogleConnector)) : NullLogger.Instance; } /// diff --git a/dotnet/src/Skills/Skills.Web/WebFileDownloadSkill.cs b/dotnet/src/Skills/Skills.Web/WebFileDownloadSkill.cs index 9b8a7c5bd60e..24807891bd88 100644 --- a/dotnet/src/Skills/Skills.Web/WebFileDownloadSkill.cs +++ b/dotnet/src/Skills/Skills.Web/WebFileDownloadSkill.cs @@ -29,9 +29,9 @@ public sealed class WebFileDownloadSkill /// /// Initializes a new instance of the class. /// - /// An optional logger to log skill-related information. - public WebFileDownloadSkill(ILogger? logger = null) : - this(new HttpClient(NonDisposableHttpClientHandler.Instance, false), logger) + /// The to use for logging. If null, no logging will be performed. + public WebFileDownloadSkill(ILoggerFactory? loggerFactory = null) : + this(new HttpClient(NonDisposableHttpClientHandler.Instance, false), loggerFactory) { } @@ -39,11 +39,11 @@ public WebFileDownloadSkill(ILogger? logger = null) : /// Initializes a new instance of the class. /// /// The HTTP client to use for making requests. - /// An optional logger to log skill-related information. - public WebFileDownloadSkill(HttpClient httpClient, ILogger? logger = null) + /// The to use for logging. If null, no logging will be performed. + public WebFileDownloadSkill(HttpClient httpClient, ILoggerFactory? loggerFactory = null) { this._httpClient = httpClient; - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger(nameof(WebFileDownloadSkill)) : NullLogger.Instance; } /// diff --git a/python/.env.example b/python/.env.example index 7355d443e186..55510add9916 100644 --- a/python/.env.example +++ b/python/.env.example @@ -8,5 +8,5 @@ AZURE_COGNITIVE_SEARCH_ADMIN_KEY="" PINECONE_API_KEY="" PINECONE_ENVIRONMENT="" POSTGRES_CONNECTION_STRING="" -GOOGLE_API_KEY="" +GOOGLE_PALM_API_KEY="" GOOGLE_SEARCH_ENGINE_ID="" \ No newline at end of file diff --git a/python/README.md b/python/README.md index 3c99966d576a..d53fa366f289 100644 --- a/python/README.md +++ b/python/README.md @@ -32,7 +32,7 @@ kernel = sk.Kernel() # Prepare OpenAI service using credentials stored in the `.env` file api_key, org_id = sk.openai_settings_from_dot_env() -kernel.add_text_completion_service("dv", OpenAITextCompletion("text-davinci-003", api_key, org_id)) +kernel.add_chat_service("chat-gpt", OpenAIChatCompletion("gpt-3.5-turbo", api_key, org_id)) # Alternative using Azure: # deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env() diff --git a/python/notebooks/00-getting-started.ipynb b/python/notebooks/00-getting-started.ipynb index 7af76d883313..44bfa2741581 100644 --- a/python/notebooks/00-getting-started.ipynb +++ b/python/notebooks/00-getting-started.ipynb @@ -44,7 +44,7 @@ "OPENAI_ORG_ID=\"\"\n", "```\n", "\n", - "and add OpenAI Text Completion to the kernel:" + "and add OpenAI Chat Completion to the kernel:" ] }, { @@ -53,11 +53,11 @@ "metadata": {}, "outputs": [], "source": [ - "from semantic_kernel.connectors.ai.open_ai import OpenAITextCompletion\n", + "from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion\n", "\n", "api_key, org_id = sk.openai_settings_from_dot_env()\n", "\n", - "kernel.add_text_completion_service(\"dv\", OpenAITextCompletion(\"text-davinci-003\", api_key, org_id))" + "kernel.add_chat_service(\"chat-gpt\", OpenAIChatCompletion(\"gpt-3.5-turbo\", api_key, org_id))" ] }, { @@ -75,7 +75,7 @@ "AZURE_OPENAI_DEPLOYMENT_NAME=\"...\"\n", "```\n", "\n", - "and add Azure OpenAI Text Completion to the kernel:" + "and add Azure OpenAI Chat Completion to the kernel:" ] }, { @@ -84,11 +84,11 @@ "metadata": {}, "outputs": [], "source": [ - "from semantic_kernel.connectors.ai.open_ai import AzureTextCompletion\n", + "from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion\n", "\n", "deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n", "\n", - "kernel.add_text_completion_service(\"dv\", AzureTextCompletion(deployment, endpoint, api_key))" + "kernel.add_chat_service(\"chat_completion\", AzureChatCompletion(\"gpt-35-turbo\", endpoint, api_key))\n" ] }, { diff --git a/python/notebooks/01-basic-loading-the-kernel.ipynb b/python/notebooks/01-basic-loading-the-kernel.ipynb index f8fcfbb5b62a..1e053247e057 100644 --- a/python/notebooks/01-basic-loading-the-kernel.ipynb +++ b/python/notebooks/01-basic-loading-the-kernel.ipynb @@ -35,7 +35,7 @@ "outputs": [], "source": [ "import semantic_kernel as sk\n", - "from semantic_kernel.connectors.ai.open_ai import AzureTextCompletion, OpenAITextCompletion" + "from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion, OpenAIChatCompletion" ] }, { @@ -87,19 +87,19 @@ "source": [ "kernel = sk.Kernel()\n", "\n", - "kernel.add_text_completion_service( # We are adding a text service\n", + "kernel.add_chat_service( # We are adding a text service\n", " \"Azure_curie\", # The alias we can use in prompt templates' config.json\n", - " AzureTextCompletion(\n", + " AzureChatCompletion(\n", " \"my-finetuned-Curie\", # Azure OpenAI *Deployment name*\n", " \"https://contoso.openai.azure.com/\", # Azure OpenAI *Endpoint*\n", " \"...your Azure OpenAI Key...\" # Azure OpenAI *Key*\n", " )\n", ")\n", "\n", - "kernel.add_text_completion_service( # We are adding a text service\n", - " \"OpenAI_davinci\", # The alias we can use in prompt templates' config.json\n", - " OpenAITextCompletion(\n", - " \"text-davinci-003\", # OpenAI Model Name\n", + "kernel.add_chat_service( # We are adding a text service\n", + " \"OpenAI_chat_gpt\", # The alias we can use in prompt templates' config.json\n", + " OpenAIChatCompletion(\n", + " \"gpt-3.5-turbo\", # OpenAI Model Name\n", " \"...your OpenAI API Key...\", # OpenAI API key\n", " \"...your OpenAI Org ID...\" # *optional* OpenAI Organization ID\n", " )\n", diff --git a/python/notebooks/02-running-prompts-from-file.ipynb b/python/notebooks/02-running-prompts-from-file.ipynb index 313710a5dcde..eeb07d3a3b24 100644 --- a/python/notebooks/02-running-prompts-from-file.ipynb +++ b/python/notebooks/02-running-prompts-from-file.ipynb @@ -100,7 +100,7 @@ "outputs": [], "source": [ "import semantic_kernel as sk\n", - "from semantic_kernel.connectors.ai.open_ai import AzureTextCompletion, OpenAITextCompletion\n", + "from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion, OpenAIChatCompletion\n", "\n", "kernel = sk.Kernel()\n", "\n", @@ -109,10 +109,10 @@ "# Configure AI service used by the kernel\n", "if useAzureOpenAI:\n", " deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n", - " kernel.add_text_completion_service(\"dv\", AzureTextCompletion(deployment, endpoint, api_key))\n", + " kernel.add_chat_service(\"chat_completion\", AzureChatCompletion(\"gpt-35-turbo\", endpoint, api_key))\n", "else:\n", " api_key, org_id = sk.openai_settings_from_dot_env()\n", - " kernel.add_text_completion_service(\"dv\", OpenAITextCompletion(\"text-davinci-003\", api_key, org_id))" + " kernel.add_chat_service(\"chat-gpt\", OpenAIChatCompletion(\"gpt-3.5-turbo\", api_key, org_id))" ] }, { diff --git a/python/notebooks/03-semantic-function-inline.ipynb b/python/notebooks/03-semantic-function-inline.ipynb index 98d4026c736d..3c1064a71aa4 100644 --- a/python/notebooks/03-semantic-function-inline.ipynb +++ b/python/notebooks/03-semantic-function-inline.ipynb @@ -205,7 +205,7 @@ " deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n", " kernel.add_chat_service(\n", " \"chat_completion\",\n", - " AzureChatCompletion(\"gpt-35-turbo\", endpoint, api_key),\n", + " AzureChatCompletion(deployment, endpoint, api_key),\n", " )\n", "else:\n", " api_key, org_id = sk.openai_settings_from_dot_env()\n", diff --git a/python/notebooks/04-context-variables-chat.ipynb b/python/notebooks/04-context-variables-chat.ipynb index 13eb27fcc15c..34f0a2c82c89 100644 --- a/python/notebooks/04-context-variables-chat.ipynb +++ b/python/notebooks/04-context-variables-chat.ipynb @@ -37,7 +37,7 @@ "outputs": [], "source": [ "import semantic_kernel as sk\n", - "from semantic_kernel.connectors.ai.open_ai import AzureTextCompletion, OpenAITextCompletion\n", + "from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion, OpenAIChatCompletion\n", "\n", "kernel = sk.Kernel()\n", "\n", @@ -46,10 +46,10 @@ "# Configure AI service used by the kernel\n", "if useAzureOpenAI:\n", " deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n", - " kernel.add_text_completion_service(\"dv\", AzureTextCompletion(deployment, endpoint, api_key))\n", + " kernel.add_chat_service(\"chat_completion\", AzureChatCompletion(\"gpt-35-turbo\", endpoint, api_key))\n", "else:\n", " api_key, org_id = sk.openai_settings_from_dot_env()\n", - " kernel.add_text_completion_service(\"dv\", OpenAITextCompletion(\"text-davinci-003\", api_key, org_id))" + " kernel.add_chat_service(\"chat-gpt\", OpenAIChatCompletion(\"gpt-3.5-turbo\", api_key, org_id))\n" ] }, { diff --git a/python/notebooks/06-memory-and-embeddings.ipynb b/python/notebooks/06-memory-and-embeddings.ipynb index 867e06ce6641..a768fdeaa37a 100644 --- a/python/notebooks/06-memory-and-embeddings.ipynb +++ b/python/notebooks/06-memory-and-embeddings.ipynb @@ -41,7 +41,7 @@ "from typing import Tuple\n", "\n", "import semantic_kernel as sk\n", - "from semantic_kernel.connectors.ai.open_ai import OpenAITextCompletion, OpenAITextEmbedding, AzureTextCompletion, AzureTextEmbedding" + "from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion, OpenAITextEmbedding, AzureChatCompletion, AzureTextEmbedding" ] }, { @@ -70,12 +70,12 @@ "# Configure AI service used by the kernel\n", "if useAzureOpenAI:\n", " deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n", - " kernel.add_text_completion_service(\"dv\", AzureTextCompletion(deployment, endpoint, api_key))\n", + " kernel.add_chat_service(\"chat_completion\", AzureChatCompletion(\"gpt-35-turbo\", endpoint, api_key))\n", " # next line assumes embeddings deployment name is \"text-embedding-ada-002\", adjust this if appropriate \n", " kernel.add_text_embedding_generation_service(\"ada\", AzureTextEmbedding(\"text-embedding-ada-002\", endpoint, api_key))\n", "else:\n", " api_key, org_id = sk.openai_settings_from_dot_env()\n", - " kernel.add_text_completion_service(\"dv\", OpenAITextCompletion(\"text-davinci-003\", api_key, org_id))\n", + " kernel.add_chat_service(\"chat-gpt\", OpenAIChatCompletion(\"gpt-3.5-turbo\", api_key, org_id))\n", " kernel.add_text_embedding_generation_service(\"ada\", OpenAITextEmbedding(\"text-embedding-ada-002\", api_key, org_id))\n", "\n", "kernel.register_memory_store(memory_store=sk.memory.VolatileMemoryStore())\n", diff --git a/python/notebooks/08-native-function-inline.ipynb b/python/notebooks/08-native-function-inline.ipynb index 3162d78e566d..b5c8ffeae785 100644 --- a/python/notebooks/08-native-function-inline.ipynb +++ b/python/notebooks/08-native-function-inline.ipynb @@ -59,7 +59,7 @@ "import os\n", "import sys\n", "import semantic_kernel as sk\n", - "from semantic_kernel.connectors.ai.open_ai import AzureTextCompletion, OpenAITextCompletion\n", + "from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion, OpenAIChatCompletion\n", "\n", "kernel = sk.Kernel()\n", "\n", @@ -69,10 +69,10 @@ "# Configure AI service used by the kernel\n", "if useAzureOpenAI:\n", " deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n", - " kernel.add_text_completion_service(\"dv\", AzureTextCompletion(deployment, endpoint, api_key))\n", + " kernel.add_chat_service(\"chat_completion\", AzureChatCompletion(\"gpt-35-turbo\", endpoint, api_key))\n", "else:\n", " api_key, org_id = sk.openai_settings_from_dot_env()\n", - " kernel.add_text_completion_service(\"dv\", OpenAITextCompletion(\"text-davinci-003\", api_key, org_id))" + " kernel.add_chat_service(\"chat-gpt\", OpenAIChatCompletion(\"gpt-3.5-turbo\", api_key, org_id))" ] }, { @@ -225,7 +225,7 @@ "import os\n", "import sys\n", "import semantic_kernel as sk\n", - "from semantic_kernel.connectors.ai.open_ai import AzureTextCompletion, OpenAITextCompletion\n", + "from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion, OpenAIChatCompletion\n", "\n", "kernel = sk.Kernel()\n", "\n", @@ -235,10 +235,10 @@ "# Configure AI service used by the kernel\n", "if useAzureOpenAI:\n", " deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n", - " kernel.add_text_completion_service(\"dv\", AzureTextCompletion(deployment, endpoint, api_key))\n", + " kernel.add_chat_service(\"chat_completion\", AzureChatCompletion(\"gpt-35-turbo\", endpoint, api_key))\n", "else:\n", " api_key, org_id = sk.openai_settings_from_dot_env()\n", - " kernel.add_text_completion_service(\"dv\", OpenAITextCompletion(\"text-davinci-003\", api_key, org_id))" + " kernel.add_chat_service(\"chat-gpt\", OpenAIChatCompletion(\"gpt-3.5-turbo\", api_key, org_id))\n" ] }, { diff --git a/python/notebooks/09-groundedness-checking.ipynb b/python/notebooks/09-groundedness-checking.ipynb index b345997d43b5..81d06ad8e679 100644 --- a/python/notebooks/09-groundedness-checking.ipynb +++ b/python/notebooks/09-groundedness-checking.ipynb @@ -83,7 +83,7 @@ "outputs": [], "source": [ "import semantic_kernel as sk\n", - "from semantic_kernel.connectors.ai.open_ai import AzureTextCompletion, OpenAITextCompletion\n", + "from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion, OpenAIChatCompletion\n", "\n", "kernel = sk.Kernel()\n", "\n", @@ -92,10 +92,10 @@ "# Configure AI service used by the kernel\n", "if useAzureOpenAI:\n", " deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()\n", - " kernel.add_text_completion_service(\"dv\", AzureTextCompletion(deployment, endpoint, api_key))\n", + " kernel.add_chat_service(\"chat_completion\", AzureChatCompletion(\"gpt-35-turbo\", endpoint, api_key))\n", "else:\n", " api_key, org_id = sk.openai_settings_from_dot_env()\n", - " kernel.add_text_completion_service(\"dv\", OpenAITextCompletion(\"text-davinci-003\", api_key, org_id))" + " kernel.add_chat_service(\"chat-gpt\", OpenAIChatCompletion(\"gpt-3.5-turbo\", api_key, org_id))" ] }, { diff --git a/python/notebooks/third_party/weaviate-persistent-memory.ipynb b/python/notebooks/third_party/weaviate-persistent-memory.ipynb index 3880fc3705af..993470370787 100644 --- a/python/notebooks/third_party/weaviate-persistent-memory.ipynb +++ b/python/notebooks/third_party/weaviate-persistent-memory.ipynb @@ -190,7 +190,7 @@ "\n", "import semantic_kernel as sk\n", "from semantic_kernel.connectors.ai.open_ai import (\n", - " OpenAITextCompletion,\n", + " OpenAIChatCompletion,\n", " OpenAITextEmbedding,\n", ")\n", "\n", @@ -264,8 +264,8 @@ "kernel = sk.Kernel()\n", "\n", "api_key, org_id = sk.openai_settings_from_dot_env()\n", - "kernel.add_text_completion_service(\n", - " \"dv\", OpenAITextCompletion(\"text-davinci-003\", api_key, org_id)\n", + "kernel.add_chat_service(\n", + " \"chat-gpt\", OpenAIChatCompletion(\"gpt-3.5-turbo\", api_key, org_id)\n", ")\n", "kernel.add_text_embedding_generation_service(\n", " \"ada\", OpenAITextEmbedding(\"text-embedding-ada-002\", api_key, org_id)\n", diff --git a/python/poetry.lock b/python/poetry.lock index ee5e83b66bec..1f76a4e56b4e 100644 --- a/python/poetry.lock +++ b/python/poetry.lock @@ -1,20 +1,22 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. [[package]] name = "aiofiles" -version = "23.1.0" +version = "23.2.1" description = "File support for asyncio." +category = "main" optional = false -python-versions = ">=3.7,<4.0" +python-versions = ">=3.7" files = [ - {file = "aiofiles-23.1.0-py3-none-any.whl", hash = "sha256:9312414ae06472eb6f1d163f555e466a23aed1c8f60c30cccf7121dba2e53eb2"}, - {file = "aiofiles-23.1.0.tar.gz", hash = "sha256:edd247df9a19e0db16534d4baaf536d6609a43e1de5401d7a4c1c148753a1635"}, + {file = "aiofiles-23.2.1-py3-none-any.whl", hash = "sha256:19297512c647d4b27a2cf7c34caa7e405c0d60b5560618a29a9fe027b18b0107"}, + {file = "aiofiles-23.2.1.tar.gz", hash = "sha256:84ec2218d8419404abcb9f0c02df3f34c6e0a68ed41072acfb1cef5cbc29051a"}, ] [[package]] name = "aiohttp" version = "3.8.5" description = "Async http client/server framework (asyncio)" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -123,6 +125,7 @@ speedups = ["Brotli", "aiodns", "cchardet"] name = "aiosignal" version = "1.3.1" description = "aiosignal: a list of registered asynchronous callbacks" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -137,6 +140,7 @@ frozenlist = ">=1.1.0" name = "anyio" version = "3.7.1" description = "High level compatibility layer for multiple asynchronous event loop implementations" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -158,6 +162,7 @@ trio = ["trio (<0.22)"] name = "appnope" version = "0.1.3" description = "Disable App Nap on macOS >= 10.9" +category = "dev" optional = false python-versions = "*" files = [ @@ -169,6 +174,7 @@ files = [ name = "asgiref" version = "3.7.2" description = "ASGI specs, helper code, and adapters" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -186,6 +192,7 @@ tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] name = "asttokens" version = "2.2.1" description = "Annotate AST trees with source code positions" +category = "dev" optional = false python-versions = "*" files = [ @@ -201,19 +208,21 @@ test = ["astroid", "pytest"] [[package]] name = "async-timeout" -version = "4.0.2" +version = "4.0.3" description = "Timeout context manager for asyncio programs" +category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"}, - {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, ] [[package]] name = "attrs" version = "23.1.0" description = "Classes Without Boilerplate" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -232,6 +241,7 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte name = "authlib" version = "1.2.1" description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." +category = "dev" optional = false python-versions = "*" files = [ @@ -246,6 +256,7 @@ cryptography = ">=3.2" name = "azure-common" version = "1.1.28" description = "Microsoft Azure Client Library for Python (Common)" +category = "dev" optional = false python-versions = "*" files = [ @@ -255,19 +266,20 @@ files = [ [[package]] name = "azure-core" -version = "1.29.0" +version = "1.29.3" description = "Microsoft Azure Core Library for Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "azure-core-1.29.0.zip", hash = "sha256:552b2010983ab3d3d35e4d4bcc7bb24fc98e40fe70fe534f5700dc90831e2396"}, - {file = "azure_core-1.29.0-py3-none-any.whl", hash = "sha256:b591cada3644523ae61fd8f120e14e2486ac70485206355db49d7de36709303a"}, + {file = "azure-core-1.29.3.tar.gz", hash = "sha256:c92700af982e71c8c73de9f4c20da8b3f03ce2c22d13066e4d416b4629c87903"}, + {file = "azure_core-1.29.3-py3-none-any.whl", hash = "sha256:f8b2910f92b66293d93bd00564924ad20ad48f4a1e150577cf18d1e7d4f9263c"}, ] [package.dependencies] requests = ">=2.18.4" six = ">=1.11.0" -typing-extensions = ">=4.3.0" +typing-extensions = ">=4.6.0" [package.extras] aio = ["aiohttp (>=3.0)"] @@ -276,6 +288,7 @@ aio = ["aiohttp (>=3.0)"] name = "azure-identity" version = "1.14.0" description = "Microsoft Azure Identity Library for Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -291,13 +304,14 @@ msal-extensions = ">=0.3.0,<2.0.0" [[package]] name = "azure-search-documents" -version = "11.4.0b6" +version = "11.4.0b8" description = "Microsoft Azure Cognitive Search Client Library for Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "azure-search-documents-11.4.0b6.zip", hash = "sha256:c9ebd7d99d3c7b879f48acad66141e1f50eae4468cfb8389a4b25d4c620e8df1"}, - {file = "azure_search_documents-11.4.0b6-py3-none-any.whl", hash = "sha256:24ff85bf2680c36b38d8092bcbbe2d90699aac7c4a228b0839c0ce595a41628c"}, + {file = "azure-search-documents-11.4.0b8.zip", hash = "sha256:b178ff52918590191a9cb7f411a9ab3cb517663666a501a3e84b715d19b0d93b"}, + {file = "azure_search_documents-11.4.0b8-py3-none-any.whl", hash = "sha256:4137daa2db75bff9484d394c16c0604822a51281cad2f50e11d7c48dd8d4b4cf"}, ] [package.dependencies] @@ -309,6 +323,7 @@ isodate = ">=0.6.0" name = "backcall" version = "0.2.0" description = "Specifications for callback functions passed in to an API" +category = "dev" optional = false python-versions = "*" files = [ @@ -320,6 +335,7 @@ files = [ name = "backoff" version = "2.2.1" description = "Function decoration for backoff and retry" +category = "dev" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -331,6 +347,7 @@ files = [ name = "backports-zoneinfo" version = "0.2.1" description = "Backport of the standard library zoneinfo module" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -359,6 +376,7 @@ tzdata = ["tzdata"] name = "black" version = "23.7.0" description = "The uncompromising code formatter." +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -401,10 +419,23 @@ d = ["aiohttp (>=3.7.4)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] +[[package]] +name = "cachetools" +version = "5.3.1" +description = "Extensible memoizing collections and decorators" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, + {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, +] + [[package]] name = "certifi" version = "2023.7.22" description = "Python package for providing Mozilla's CA Bundle." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -416,6 +447,7 @@ files = [ name = "cffi" version = "1.15.1" description = "Foreign Function Interface for Python calling C code." +category = "dev" optional = false python-versions = "*" files = [ @@ -490,19 +522,21 @@ pycparser = "*" [[package]] name = "cfgv" -version = "3.3.1" +version = "3.4.0" description = "Validate configuration and produce human readable error messages." +category = "dev" optional = false -python-versions = ">=3.6.1" +python-versions = ">=3.8" files = [ - {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, - {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, ] [[package]] name = "chardet" version = "5.2.0" description = "Universal encoding detector for Python 3" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -514,6 +548,7 @@ files = [ name = "charset-normalizer" version = "3.2.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -594,10 +629,26 @@ files = [ {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, ] +[[package]] +name = "cheap-repr" +version = "0.5.1" +description = "Better version of repr/reprlib for short, cheap string representations." +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "cheap_repr-0.5.1-py2.py3-none-any.whl", hash = "sha256:30096998aeb49367a4a153988d7a99dce9dc59bbdd4b19740da6b4f3f97cf2ff"}, + {file = "cheap_repr-0.5.1.tar.gz", hash = "sha256:31ec63b9d8394aa23d746c8376c8307f75f9fca0b983566b8bcf13cc661fe6dd"}, +] + +[package.extras] +tests = ["Django", "Django (<2)", "Django (<3)", "chainmap", "numpy (>=1.16.3)", "numpy (>=1.16.3,<1.17)", "numpy (>=1.16.3,<1.19)", "pandas (>=0.24.2)", "pandas (>=0.24.2,<0.25)", "pandas (>=0.24.2,<0.26)", "pytest"] + [[package]] name = "chroma-hnswlib" version = "0.7.2" description = "Chromas fork of hnswlib" +category = "dev" optional = false python-versions = "*" files = [ @@ -610,13 +661,14 @@ numpy = "*" [[package]] name = "chromadb" -version = "0.4.5" +version = "0.4.6" description = "Chroma." +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "chromadb-0.4.5-py3-none-any.whl", hash = "sha256:ec87e700c604934558e6c066c701c577069415157e0fedd1b19bc7a3964ff70b"}, - {file = "chromadb-0.4.5.tar.gz", hash = "sha256:c75e739bfec167dff45ad67a52d6c7bbb00d2515400e31817cc347440bac7e56"}, + {file = "chromadb-0.4.6-py3-none-any.whl", hash = "sha256:551c8731c9db9816f9a66d0c252ebe9064f7d07fea57d52bfc54ee3fa5561e55"}, + {file = "chromadb-0.4.6.tar.gz", hash = "sha256:817f8bb7915741703916e527556184ab2132f3caa8411307e2015cad7d1c7b03"}, ] [package.dependencies] @@ -639,13 +691,14 @@ uvicorn = {version = ">=0.18.3", extras = ["standard"]} [[package]] name = "click" -version = "8.1.6" +version = "8.1.7" description = "Composable command line interface toolkit" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "click-8.1.6-py3-none-any.whl", hash = "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5"}, - {file = "click-8.1.6.tar.gz", hash = "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd"}, + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, ] [package.dependencies] @@ -653,28 +706,29 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "cmake" -version = "3.27.0" +version = "3.27.2" description = "CMake is an open-source, cross-platform family of tools designed to build, test and package software" +category = "dev" optional = false python-versions = "*" files = [ - {file = "cmake-3.27.0-py2.py3-none-macosx_10_10_universal2.macosx_10_10_x86_64.macosx_11_0_arm64.macosx_11_0_universal2.whl", hash = "sha256:9ccab4cd93578d3c2df32e66b44b313b75a7484032645040431dc06a583ca4aa"}, - {file = "cmake-3.27.0-py2.py3-none-manylinux2010_i686.manylinux_2_12_i686.whl", hash = "sha256:199bfaefb752e82d8067aeee5d6a6e0414fe0d60e9a3fd08e95d537a97e0db16"}, - {file = "cmake-3.27.0-py2.py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:8745eff805f36762d3e8e904698b853cb4a9da8b4b07d1c12bcd1e1a6c4a1709"}, - {file = "cmake-3.27.0-py2.py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58a3f39d3d1bc897f05e531bfa676246a2b25d424c6a47e4b6bbc193fb560db7"}, - {file = "cmake-3.27.0-py2.py3-none-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b470ccd3f86cf19a63f6b221c9cceebcc58e32d3787d0d5f9f43d1d91a095090"}, - {file = "cmake-3.27.0-py2.py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:35a8d397ce883e93b5e6561e2803ce9470df52283862264093c1078530f98189"}, - {file = "cmake-3.27.0-py2.py3-none-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:1f38d87b2c65763a0113f4a6c652e6f4b5adf90b384c1e1d69e4f8a3104a57d6"}, - {file = "cmake-3.27.0-py2.py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b9d5811954dcedcaa6c915c4a9bb6d64b55ac189e9cbc74be726307d9d084804"}, - {file = "cmake-3.27.0-py2.py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:073e4f196d0888216e6794c08cd984ddabc108c0e4e66f48fbd7610d1e6d726d"}, - {file = "cmake-3.27.0-py2.py3-none-musllinux_1_1_i686.whl", hash = "sha256:e58e48643903e6fad76274337f9a4d3c575b8e21cd05c6214780b2c98bb0c706"}, - {file = "cmake-3.27.0-py2.py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:9740ed9f61a3bd8708a41cadd5c057c04f38e5b89bd773e369df2e210a1c55a3"}, - {file = "cmake-3.27.0-py2.py3-none-musllinux_1_1_s390x.whl", hash = "sha256:1b3189171665f5c8d748ae7fe10a29fff1ebeedeaef57b16f1ea54b1ec0fe514"}, - {file = "cmake-3.27.0-py2.py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:c4c968c188e7518deb463a14e64f3a19f242c9dcf7f24e1dbcc1419690cd54e0"}, - {file = "cmake-3.27.0-py2.py3-none-win32.whl", hash = "sha256:5561aca62b65aac844f3931e74cfeb696e4534de145e3307bf942e735736541e"}, - {file = "cmake-3.27.0-py2.py3-none-win_amd64.whl", hash = "sha256:48be3afe62c9513a49be007896a4058fafec512cb1f269a50126da30aacad97f"}, - {file = "cmake-3.27.0-py2.py3-none-win_arm64.whl", hash = "sha256:6f46a170b0c9c552d52da4346534570f818195dfc4f1d0c03264e24cc348fc60"}, - {file = "cmake-3.27.0.tar.gz", hash = "sha256:d03f0a76a2b96805044ad1178b92aeeb5f695caa6776a32522bb5c430a55b4e8"}, + {file = "cmake-3.27.2-py2.py3-none-macosx_10_10_universal2.macosx_10_10_x86_64.macosx_11_0_arm64.macosx_11_0_universal2.whl", hash = "sha256:96ac856c4d6b2104408848f0005a8ab2229d4135b171ea9a03e8c33039ede420"}, + {file = "cmake-3.27.2-py2.py3-none-manylinux2010_i686.manylinux_2_12_i686.whl", hash = "sha256:11fe6129d07982721c5965fd804a4056b8c6e9c4f482ac9e0fe41bb3abc1ab5f"}, + {file = "cmake-3.27.2-py2.py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:f0c64e89e2ea59592980c4fe3821d712fee0e74cf87c2aaec5b3ab9aa809a57c"}, + {file = "cmake-3.27.2-py2.py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ca7650477dff2a1138776b28b79c0e99127be733d3978922e8f87b56a433eed6"}, + {file = "cmake-3.27.2-py2.py3-none-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:ab2e40fe09e76a7ef67da2bbbf7a4cd1f52db4f1c7b6ccdda2539f918830343a"}, + {file = "cmake-3.27.2-py2.py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:980ee19f12c808cb8ddb56fdcee832501a9f9631799d8b4fc625c0a0b5fb4c55"}, + {file = "cmake-3.27.2-py2.py3-none-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:115d30ca0760e3861d9ad6b3288cd11ee72a785b81227da0c1765d3b84e2c009"}, + {file = "cmake-3.27.2-py2.py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efc338c939d6d435890a52458a260bf0942bd8392b648d7532a72c1ec0764e18"}, + {file = "cmake-3.27.2-py2.py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:7f7438c60ccc01765b67abfb1797787c3b9459d500a804ed70a4cc181bc02204"}, + {file = "cmake-3.27.2-py2.py3-none-musllinux_1_1_i686.whl", hash = "sha256:294f008734267e0eee1574ad1b911bed137bc907ab19d60a618dab4615aa1fca"}, + {file = "cmake-3.27.2-py2.py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:197a34dc62ee149ced343545fac67e5a30b93fda65250b065726f86ce92bdada"}, + {file = "cmake-3.27.2-py2.py3-none-musllinux_1_1_s390x.whl", hash = "sha256:afb46ad883b174fb64347802ba5878423551dbd5847bb64669c39a5957c06eb7"}, + {file = "cmake-3.27.2-py2.py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:83611ffd155e270a6b13bbf0cfd4e8688ebda634f448aa2e3734006c745bf33f"}, + {file = "cmake-3.27.2-py2.py3-none-win32.whl", hash = "sha256:53e12deb893da935e236f93accd47dbe2806620cd7654986234dc4487cc49652"}, + {file = "cmake-3.27.2-py2.py3-none-win_amd64.whl", hash = "sha256:611f9722c68c40352d38a6c01960ab038c3d0419e7aee3bf18f95b23031e0dfe"}, + {file = "cmake-3.27.2-py2.py3-none-win_arm64.whl", hash = "sha256:30620326b51ac2ce0d8f476747af6367a7ea21075c4d065fad9443904b07476a"}, + {file = "cmake-3.27.2.tar.gz", hash = "sha256:7cd6e2d7d5a1125f8c26c4f65214f8c942e3f276f98c16cb62ae382c35609f25"}, ] [package.extras] @@ -684,6 +738,7 @@ test = ["coverage (>=4.2)", "flake8 (>=3.0.4)", "path.py (>=11.5.0)", "pytest (> name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -695,6 +750,7 @@ files = [ name = "coloredlogs" version = "15.0.1" description = "Colored terminal output for Python's logging module" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -712,6 +768,7 @@ cron = ["capturer (>=2.4)"] name = "comm" version = "0.1.4" description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -731,6 +788,7 @@ typing = ["mypy (>=0.990)"] name = "cryptography" version = "41.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -774,35 +832,37 @@ test-randomorder = ["pytest-randomly"] [[package]] name = "debugpy" -version = "1.6.7" +version = "1.6.7.post1" description = "An implementation of the Debug Adapter Protocol for Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "debugpy-1.6.7-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b3e7ac809b991006ad7f857f016fa92014445085711ef111fdc3f74f66144096"}, - {file = "debugpy-1.6.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3876611d114a18aafef6383695dfc3f1217c98a9168c1aaf1a02b01ec7d8d1e"}, - {file = "debugpy-1.6.7-cp310-cp310-win32.whl", hash = "sha256:33edb4afa85c098c24cc361d72ba7c21bb92f501104514d4ffec1fb36e09c01a"}, - {file = "debugpy-1.6.7-cp310-cp310-win_amd64.whl", hash = "sha256:ed6d5413474e209ba50b1a75b2d9eecf64d41e6e4501977991cdc755dc83ab0f"}, - {file = "debugpy-1.6.7-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:38ed626353e7c63f4b11efad659be04c23de2b0d15efff77b60e4740ea685d07"}, - {file = "debugpy-1.6.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:279d64c408c60431c8ee832dfd9ace7c396984fd7341fa3116aee414e7dcd88d"}, - {file = "debugpy-1.6.7-cp37-cp37m-win32.whl", hash = "sha256:dbe04e7568aa69361a5b4c47b4493d5680bfa3a911d1e105fbea1b1f23f3eb45"}, - {file = "debugpy-1.6.7-cp37-cp37m-win_amd64.whl", hash = "sha256:f90a2d4ad9a035cee7331c06a4cf2245e38bd7c89554fe3b616d90ab8aab89cc"}, - {file = "debugpy-1.6.7-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:5224eabbbeddcf1943d4e2821876f3e5d7d383f27390b82da5d9558fd4eb30a9"}, - {file = "debugpy-1.6.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bae1123dff5bfe548ba1683eb972329ba6d646c3a80e6b4c06cd1b1dd0205e9b"}, - {file = "debugpy-1.6.7-cp38-cp38-win32.whl", hash = "sha256:9cd10cf338e0907fdcf9eac9087faa30f150ef5445af5a545d307055141dd7a4"}, - {file = "debugpy-1.6.7-cp38-cp38-win_amd64.whl", hash = "sha256:aaf6da50377ff4056c8ed470da24632b42e4087bc826845daad7af211e00faad"}, - {file = "debugpy-1.6.7-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:0679b7e1e3523bd7d7869447ec67b59728675aadfc038550a63a362b63029d2c"}, - {file = "debugpy-1.6.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de86029696e1b3b4d0d49076b9eba606c226e33ae312a57a46dca14ff370894d"}, - {file = "debugpy-1.6.7-cp39-cp39-win32.whl", hash = "sha256:d71b31117779d9a90b745720c0eab54ae1da76d5b38c8026c654f4a066b0130a"}, - {file = "debugpy-1.6.7-cp39-cp39-win_amd64.whl", hash = "sha256:c0ff93ae90a03b06d85b2c529eca51ab15457868a377c4cc40a23ab0e4e552a3"}, - {file = "debugpy-1.6.7-py2.py3-none-any.whl", hash = "sha256:53f7a456bc50706a0eaabecf2d3ce44c4d5010e46dfc65b6b81a518b42866267"}, - {file = "debugpy-1.6.7.zip", hash = "sha256:c4c2f0810fa25323abfdfa36cbbbb24e5c3b1a42cb762782de64439c575d67f2"}, + {file = "debugpy-1.6.7.post1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:903bd61d5eb433b6c25b48eae5e23821d4c1a19e25c9610205f5aeaccae64e32"}, + {file = "debugpy-1.6.7.post1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d16882030860081e7dd5aa619f30dec3c2f9a421e69861125f83cc372c94e57d"}, + {file = "debugpy-1.6.7.post1-cp310-cp310-win32.whl", hash = "sha256:eea8d8cfb9965ac41b99a61f8e755a8f50e9a20330938ad8271530210f54e09c"}, + {file = "debugpy-1.6.7.post1-cp310-cp310-win_amd64.whl", hash = "sha256:85969d864c45f70c3996067cfa76a319bae749b04171f2cdeceebe4add316155"}, + {file = "debugpy-1.6.7.post1-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:890f7ab9a683886a0f185786ffbda3b46495c4b929dab083b8c79d6825832a52"}, + {file = "debugpy-1.6.7.post1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4ac7a4dba28801d184b7fc0e024da2635ca87d8b0a825c6087bb5168e3c0d28"}, + {file = "debugpy-1.6.7.post1-cp37-cp37m-win32.whl", hash = "sha256:3370ef1b9951d15799ef7af41f8174194f3482ee689988379763ef61a5456426"}, + {file = "debugpy-1.6.7.post1-cp37-cp37m-win_amd64.whl", hash = "sha256:65b28435a17cba4c09e739621173ff90c515f7b9e8ea469b92e3c28ef8e5cdfb"}, + {file = "debugpy-1.6.7.post1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:92b6dae8bfbd497c90596bbb69089acf7954164aea3228a99d7e43e5267f5b36"}, + {file = "debugpy-1.6.7.post1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72f5d2ecead8125cf669e62784ef1e6300f4067b0f14d9f95ee00ae06fc7c4f7"}, + {file = "debugpy-1.6.7.post1-cp38-cp38-win32.whl", hash = "sha256:f0851403030f3975d6e2eaa4abf73232ab90b98f041e3c09ba33be2beda43fcf"}, + {file = "debugpy-1.6.7.post1-cp38-cp38-win_amd64.whl", hash = "sha256:3de5d0f97c425dc49bce4293df6a04494309eedadd2b52c22e58d95107e178d9"}, + {file = "debugpy-1.6.7.post1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:38651c3639a4e8bbf0ca7e52d799f6abd07d622a193c406be375da4d510d968d"}, + {file = "debugpy-1.6.7.post1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:038c51268367c9c935905a90b1c2d2dbfe304037c27ba9d19fe7409f8cdc710c"}, + {file = "debugpy-1.6.7.post1-cp39-cp39-win32.whl", hash = "sha256:4b9eba71c290852f959d2cf8a03af28afd3ca639ad374d393d53d367f7f685b2"}, + {file = "debugpy-1.6.7.post1-cp39-cp39-win_amd64.whl", hash = "sha256:973a97ed3b434eab0f792719a484566c35328196540676685c975651266fccf9"}, + {file = "debugpy-1.6.7.post1-py2.py3-none-any.whl", hash = "sha256:1093a5c541af079c13ac8c70ab8b24d1d35c8cacb676306cf11e57f699c02926"}, + {file = "debugpy-1.6.7.post1.zip", hash = "sha256:fe87ec0182ef624855d05e6ed7e0b7cb1359d2ffa2a925f8ec2d22e98b75d0ca"}, ] [[package]] name = "decorator" version = "5.1.1" description = "Decorators for Humans" +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -814,6 +874,7 @@ files = [ name = "distlib" version = "0.3.7" description = "Distribution utilities" +category = "dev" optional = false python-versions = "*" files = [ @@ -823,13 +884,14 @@ files = [ [[package]] name = "dnspython" -version = "2.4.1" +version = "2.4.2" description = "DNS toolkit" +category = "dev" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "dnspython-2.4.1-py3-none-any.whl", hash = "sha256:5b7488477388b8c0b70a8ce93b227c5603bc7b77f1565afe8e729c36c51447d7"}, - {file = "dnspython-2.4.1.tar.gz", hash = "sha256:c33971c79af5be968bb897e95c2448e11a645ee84d93b265ce0b7aabe5dfdca8"}, + {file = "dnspython-2.4.2-py3-none-any.whl", hash = "sha256:57c6fbaaeaaf39c891292012060beb141791735dbb4004798328fc2c467402d8"}, + {file = "dnspython-2.4.2.tar.gz", hash = "sha256:8dcfae8c7460a2f84b4072e26f1c9f4101ca20c071649cb7c34e8b6a93d58984"}, ] [package.extras] @@ -844,6 +906,7 @@ wmi = ["wmi (>=1.5.1,<2.0.0)"] name = "environs" version = "9.5.0" description = "simplified environment variable parsing" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -863,13 +926,14 @@ tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"] [[package]] name = "exceptiongroup" -version = "1.1.2" +version = "1.1.3" description = "Backport of PEP 654 (exception groups)" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.1.2-py3-none-any.whl", hash = "sha256:e346e69d186172ca7cf029c8c1d16235aa0e04035e5750b4b95039e65204328f"}, - {file = "exceptiongroup-1.1.2.tar.gz", hash = "sha256:12c3e887d6485d16943a309616de20ae5582633e0a2eda17f4e10fd61c1e8af5"}, + {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, + {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, ] [package.extras] @@ -879,6 +943,7 @@ test = ["pytest (>=6)"] name = "executing" version = "1.2.0" description = "Get the currently executing AST node of a frame, and other information" +category = "dev" optional = false python-versions = "*" files = [ @@ -893,6 +958,7 @@ tests = ["asttokens", "littleutils", "pytest", "rich"] name = "fastapi" version = "0.99.1" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -912,6 +978,7 @@ all = ["email-validator (>=1.1.1)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)" name = "filelock" version = "3.12.2" description = "A platform independent file lock." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -927,6 +994,7 @@ testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "p name = "flatbuffers" version = "23.5.26" description = "The FlatBuffers serialization format for Python" +category = "dev" optional = false python-versions = "*" files = [ @@ -938,6 +1006,7 @@ files = [ name = "frozenlist" version = "1.4.0" description = "A list-like structure which implements collections.abc.MutableSequence" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1008,6 +1077,7 @@ files = [ name = "fsspec" version = "2023.6.0" description = "File-system specification" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1039,10 +1109,123 @@ smb = ["smbprotocol"] ssh = ["paramiko"] tqdm = ["tqdm"] +[[package]] +name = "google-ai-generativelanguage" +version = "0.2.0" +description = "Google Ai Generativelanguage API client library" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-ai-generativelanguage-0.2.0.tar.gz", hash = "sha256:4d5440a7df7f495f016e5ccd4d9903514264392b240c40d40d28a1356bd9fad3"}, + {file = "google_ai_generativelanguage-0.2.0-py3-none-any.whl", hash = "sha256:1a82949622da9fbdfbcf10c65084d3789b671fec231ba2a5b2ede3392ebbfeb5"}, +] + +[package.dependencies] +google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} +proto-plus = [ + {version = ">=1.22.0,<2.0.0dev", markers = "python_version < \"3.11\""}, + {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""}, +] +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" + +[[package]] +name = "google-api-core" +version = "2.11.1" +description = "Google API client core library" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.11.1.tar.gz", hash = "sha256:25d29e05a0058ed5f19c61c0a78b1b53adea4d9364b464d014fbda941f6d1c9a"}, + {file = "google_api_core-2.11.1-py3-none-any.whl", hash = "sha256:d92a5a92dc36dd4f4b9ee4e55528a90e432b059f93aee6ad857f9de8cc7ae94a"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0.dev0" +googleapis-common-protos = ">=1.56.2,<2.0.dev0" +grpcio = [ + {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""}, + {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, +] +grpcio-status = [ + {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "extra == \"grpc\""}, + {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, +] +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" +requests = ">=2.18.0,<3.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] + +[[package]] +name = "google-auth" +version = "2.22.0" +description = "Google Authentication Library" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "google-auth-2.22.0.tar.gz", hash = "sha256:164cba9af4e6e4e40c3a4f90a1a6c12ee56f14c0b4868d1ca91b32826ab334ce"}, + {file = "google_auth-2.22.0-py2.py3-none-any.whl", hash = "sha256:d61d1b40897407b574da67da1a833bdc10d5a11642566e506565d1b1a46ba873"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = ">=3.1.4,<5" +six = ">=1.9.0" +urllib3 = "<2.0" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0.dev0)"] + +[[package]] +name = "google-generativeai" +version = "0.1.0" +description = "Google Generative AI High level API client library and tools." +category = "dev" +optional = false +python-versions = ">=3.9" +files = [ + {file = "google_generativeai-0.1.0-py3-none-any.whl", hash = "sha256:1cdfbef1bfc280a56172c48f480b71665122796f9f98f464e7918b840cc80c07"}, +] + +[package.dependencies] +google-ai-generativelanguage = "0.2.0" + +[package.extras] +dev = ["absl-py", "asynctest", "black", "nose2", "pandas", "pytype", "pyyaml"] + +[[package]] +name = "googleapis-common-protos" +version = "1.60.0" +description = "Common protobufs used in Google APIs" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.60.0.tar.gz", hash = "sha256:e73ebb404098db405ba95d1e1ae0aa91c3e15a71da031a2eeb6b2e23e7bc3708"}, + {file = "googleapis_common_protos-1.60.0-py2.py3-none-any.whl", hash = "sha256:69f9bbcc6acde92cab2db95ce30a70bd2b81d20b12eff3f1aabaffcbe8a93918"}, +] + +[package.dependencies] +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + [[package]] name = "graphlib-backport" version = "1.0.3" description = "Backport of the Python 3.9 graphlib module for Python 3.6+" +category = "dev" optional = false python-versions = ">=3.6,<4.0" files = [ @@ -1054,6 +1237,7 @@ files = [ name = "grpcio" version = "1.56.0" description = "HTTP/2-based RPC framework" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1107,10 +1291,28 @@ files = [ [package.extras] protobuf = ["grpcio-tools (>=1.56.0)"] +[[package]] +name = "grpcio-status" +version = "1.56.0" +description = "Status proto mapping for gRPC" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "grpcio-status-1.56.0.tar.gz", hash = "sha256:9eca0b2dcda0782d3702df225918efd6d820f75f93cd5c51c7fb6a4ffbfea12c"}, + {file = "grpcio_status-1.56.0-py3-none-any.whl", hash = "sha256:e5f101c96686e9d4e94a114567960fdb00052aa3c818b029745e3db37dc9c613"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.56.0" +protobuf = ">=4.21.6" + [[package]] name = "grpcio-tools" version = "1.56.0" description = "Protobuf code generator for gRPC" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1170,6 +1372,7 @@ setuptools = "*" name = "h11" version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1181,6 +1384,7 @@ files = [ name = "h2" version = "4.1.0" description = "HTTP/2 State-Machine based protocol implementation" +category = "dev" optional = false python-versions = ">=3.6.1" files = [ @@ -1196,6 +1400,7 @@ hyperframe = ">=6.0,<7" name = "hpack" version = "4.0.0" description = "Pure-Python HPACK header compression" +category = "dev" optional = false python-versions = ">=3.6.1" files = [ @@ -1207,6 +1412,7 @@ files = [ name = "httpcore" version = "0.17.3" description = "A minimal low-level HTTP client." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1218,16 +1424,17 @@ files = [ anyio = ">=3.0,<5.0" certifi = "*" h11 = ">=0.13,<0.15" -sniffio = "==1.*" +sniffio = ">=1.0.0,<2.0.0" [package.extras] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] [[package]] name = "httptools" version = "0.6.0" description = "A collection of framework independent HTTP protocol utils." +category = "dev" optional = false python-versions = ">=3.5.0" files = [ @@ -1275,6 +1482,7 @@ test = ["Cython (>=0.29.24,<0.30.0)"] name = "httpx" version = "0.24.1" description = "The next generation HTTP client." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1291,14 +1499,15 @@ sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] -cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] [[package]] name = "huggingface-hub" version = "0.16.4" description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" +category = "dev" optional = false python-versions = ">=3.7.0" files = [ @@ -1331,6 +1540,7 @@ typing = ["pydantic", "types-PyYAML", "types-requests", "types-simplejson", "typ name = "humanfriendly" version = "10.0" description = "Human friendly output for text interfaces using Python" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -1345,6 +1555,7 @@ pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_ve name = "hyperframe" version = "6.0.1" description = "HTTP/2 framing layer for Python" +category = "dev" optional = false python-versions = ">=3.6.1" files = [ @@ -1354,13 +1565,14 @@ files = [ [[package]] name = "identify" -version = "2.5.26" +version = "2.5.27" description = "File identification library for Python" +category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.26-py2.py3-none-any.whl", hash = "sha256:c22a8ead0d4ca11f1edd6c9418c3220669b3b7533ada0a0ffa6cc0ef85cf9b54"}, - {file = "identify-2.5.26.tar.gz", hash = "sha256:7243800bce2f58404ed41b7c002e53d4d22bcf3ae1b7900c2d7aefd95394bf7f"}, + {file = "identify-2.5.27-py2.py3-none-any.whl", hash = "sha256:fdb527b2dfe24602809b2201e033c2a113d7bdf716db3ca8e3243f735dcecaba"}, + {file = "identify-2.5.27.tar.gz", hash = "sha256:287b75b04a0e22d727bc9a41f0d4f3c1bcada97490fa6eabb5b28f0e9097e733"}, ] [package.extras] @@ -1370,6 +1582,7 @@ license = ["ukkonen"] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1381,6 +1594,7 @@ files = [ name = "importlib-metadata" version = "6.8.0" description = "Read metadata from Python packages" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1400,6 +1614,7 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs name = "importlib-resources" version = "5.13.0" description = "Read resources from Python packages" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1418,6 +1633,7 @@ testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1429,6 +1645,7 @@ files = [ name = "ipykernel" version = "6.25.1" description = "IPython Kernel for Jupyter" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1442,7 +1659,7 @@ comm = ">=0.1.1" debugpy = ">=1.6.5" ipython = ">=7.23.1" jupyter-client = ">=6.1.12" -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +jupyter-core = ">=4.12,<5.0.0 || >=5.1.0" matplotlib-inline = ">=0.1" nest-asyncio = "*" packaging = "*" @@ -1462,6 +1679,7 @@ test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio" name = "ipython" version = "8.12.2" description = "IPython: Productive Interactive Computing" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1501,6 +1719,7 @@ test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pa name = "isodate" version = "0.6.1" description = "An ISO 8601 date/time/duration parser and formatter" +category = "main" optional = false python-versions = "*" files = [ @@ -1515,6 +1734,7 @@ six = "*" name = "jedi" version = "0.19.0" description = "An autocompletion tool for Python that can be used for text editors." +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1534,6 +1754,7 @@ testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1549,24 +1770,26 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "joblib" -version = "1.3.1" +version = "1.3.2" description = "Lightweight pipelining with Python functions" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "joblib-1.3.1-py3-none-any.whl", hash = "sha256:89cf0529520e01b3de7ac7b74a8102c90d16d54c64b5dd98cafcd14307fdf915"}, - {file = "joblib-1.3.1.tar.gz", hash = "sha256:1f937906df65329ba98013dc9692fe22a4c5e4a648112de500508b18a21b41e3"}, + {file = "joblib-1.3.2-py3-none-any.whl", hash = "sha256:ef4331c65f239985f3f2220ecc87db222f08fd22097a3dd5698f693875f8cbb9"}, + {file = "joblib-1.3.2.tar.gz", hash = "sha256:92f865e621e17784e7955080b6d042489e3b8e294949cc44c6eac304f59772b1"}, ] [[package]] name = "jsonschema" -version = "4.18.6" +version = "4.19.0" description = "An implementation of JSON Schema validation for Python" +category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "jsonschema-4.18.6-py3-none-any.whl", hash = "sha256:dc274409c36175aad949c68e5ead0853aaffbe8e88c830ae66bb3c7a1728ad2d"}, - {file = "jsonschema-4.18.6.tar.gz", hash = "sha256:ce71d2f8c7983ef75a756e568317bf54bc531dc3ad7e66a128eae0d51623d8a3"}, + {file = "jsonschema-4.19.0-py3-none-any.whl", hash = "sha256:043dc26a3845ff09d20e4420d6012a9c91c9aa8999fa184e7efcfeccb41e32cb"}, + {file = "jsonschema-4.19.0.tar.gz", hash = "sha256:6e1e7569ac13be8139b2dd2c21a55d350066ee3f80df06c608b398cdc6f30e8f"}, ] [package.dependencies] @@ -1583,25 +1806,27 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- [[package]] name = "jsonschema-spec" -version = "0.2.3" +version = "0.2.4" description = "JSONSchema Spec with object-oriented paths" +category = "main" optional = false python-versions = ">=3.8.0,<4.0.0" files = [ - {file = "jsonschema_spec-0.2.3-py3-none-any.whl", hash = "sha256:ee005ddeca73229560ac2b8f1849590929c4b2cd17a932b229b03566e517f2a6"}, - {file = "jsonschema_spec-0.2.3.tar.gz", hash = "sha256:e01b8b100f0676177b0b39027a5cab7e7a16ce4316a3d0d15e576293d954fafc"}, + {file = "jsonschema_spec-0.2.4-py3-none-any.whl", hash = "sha256:e6dcf7056734ec6854f7888da6c08ce6c421f28aeeddce96bb90de0fb6d711ef"}, + {file = "jsonschema_spec-0.2.4.tar.gz", hash = "sha256:873e396ad1ba6edf9f52d6174c110d4fafb7b5f5894744246a53fe75e5251ec2"}, ] [package.dependencies] pathable = ">=0.4.1,<0.5.0" PyYAML = ">=5.1" -referencing = ">=0.28.0,<0.30.0" +referencing = ">=0.28.0,<0.31.0" requests = ">=2.31.0,<3.0.0" [[package]] name = "jsonschema-specifications" version = "2023.7.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1617,6 +1842,7 @@ referencing = ">=0.28.0" name = "jupyter-client" version = "8.3.0" description = "Jupyter protocol implementation and client libraries" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1626,7 +1852,7 @@ files = [ [package.dependencies] importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +jupyter-core = ">=4.12,<5.0.0 || >=5.1.0" python-dateutil = ">=2.8.2" pyzmq = ">=23.0" tornado = ">=6.2" @@ -1640,6 +1866,7 @@ test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pyt name = "jupyter-core" version = "5.3.1" description = "Jupyter core package. A base package on which Jupyter projects rely." +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1660,6 +1887,7 @@ test = ["ipykernel", "pre-commit", "pytest", "pytest-cov", "pytest-timeout"] name = "lazy-object-proxy" version = "1.9.0" description = "A fast and thorough lazy object proxy." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1705,6 +1933,7 @@ files = [ name = "lit" version = "16.0.6" description = "A Software Testing Tool" +category = "dev" optional = false python-versions = "*" files = [ @@ -1715,6 +1944,7 @@ files = [ name = "loguru" version = "0.7.0" description = "Python logging made (stupidly) simple" +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1733,6 +1963,7 @@ dev = ["Sphinx (==5.3.0)", "colorama (==0.4.5)", "colorama (==0.4.6)", "freezegu name = "markupsafe" version = "2.1.3" description = "Safely add untrusted strings to HTML/XML markup." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1792,6 +2023,7 @@ files = [ name = "marshmallow" version = "3.20.1" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1812,6 +2044,7 @@ tests = ["pytest", "pytz", "simplejson"] name = "matplotlib-inline" version = "0.1.6" description = "Inline Matplotlib backend for Jupyter" +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1824,15 +2057,16 @@ traitlets = "*" [[package]] name = "milvus" -version = "2.2.12" +version = "2.2.13" description = "Embeded Milvus" +category = "dev" optional = false python-versions = ">=3.6" files = [ - {file = "milvus-2.2.12-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:b123db37e157fd8fc77d9857016eca69962f0950f931ea9390ab1044f9ab18bb"}, - {file = "milvus-2.2.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fd6b030b039f48ca6ad600d3225a9a2656fbe56fe85f113c4efb3ec1456504e8"}, - {file = "milvus-2.2.12-py3-none-manylinux2014_x86_64.whl", hash = "sha256:348bd7f1ad63586cffc554b6d53e27bf88c72da388a02551bf276ab4c0ad9b4a"}, - {file = "milvus-2.2.12-py3-none-win_amd64.whl", hash = "sha256:f3f4029800c8670c73b9371bd9b13fe68ea3c5b3146539f1c31e3409ca4ca0f5"}, + {file = "milvus-2.2.13-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:fcaff6cdc885ab46f432b79294a298e2cac542ffdbcb8c61be5f4c1f1c27dbeb"}, + {file = "milvus-2.2.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:58dfe6b2630a981475c56dc06fe414d2d821c94651b3341e62e7de8bc2850ec9"}, + {file = "milvus-2.2.13-py3-none-manylinux2014_x86_64.whl", hash = "sha256:9f95afcdb7b8912ae7c8a02604ed3a057462df25e6fa22112db1ead828e371a1"}, + {file = "milvus-2.2.13-py3-none-win_amd64.whl", hash = "sha256:03c55043321a72d41d2dcb49745836f10156c61bfc9c48fa58345ae27dbbf164"}, ] [package.extras] @@ -1842,6 +2076,7 @@ client = ["pymilvus (>=2.2.0,!=2.2.14,<2.3.0)"] name = "monotonic" version = "1.6" description = "An implementation of time.monotonic() for Python 2 & < 3.3" +category = "dev" optional = false python-versions = "*" files = [ @@ -1853,6 +2088,7 @@ files = [ name = "more-itertools" version = "10.1.0" description = "More routines for operating on iterables, beyond itertools" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1864,6 +2100,7 @@ files = [ name = "mpmath" version = "1.3.0" description = "Python library for arbitrary-precision floating-point arithmetic" +category = "dev" optional = false python-versions = "*" files = [ @@ -1881,6 +2118,7 @@ tests = ["pytest (>=4.6)"] name = "msal" version = "1.23.0" description = "The Microsoft Authentication Library (MSAL) for Python library enables your app to access the Microsoft Cloud by supporting authentication of users with Microsoft Azure Active Directory accounts (AAD) and Microsoft Accounts (MSA) using industry standard OAuth2 and OpenID Connect." +category = "dev" optional = false python-versions = "*" files = [ @@ -1900,6 +2138,7 @@ broker = ["pymsalruntime (>=0.13.2,<0.14)"] name = "msal-extensions" version = "1.0.0" description = "Microsoft Authentication Library extensions (MSAL EX) provides a persistence API that can save your data on disk, encrypted on Windows, macOS and Linux. Concurrent data access will be coordinated by a file lock mechanism." +category = "dev" optional = false python-versions = "*" files = [ @@ -1918,6 +2157,7 @@ portalocker = [ name = "multidict" version = "6.0.4" description = "multidict implementation" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2001,6 +2241,7 @@ files = [ name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -2012,6 +2253,7 @@ files = [ name = "nest-asyncio" version = "1.5.7" description = "Patch asyncio to allow nested event loops" +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -2023,6 +2265,7 @@ files = [ name = "networkx" version = "3.1" description = "Python package for creating and manipulating graphs and networks" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2041,6 +2284,7 @@ test = ["codecov (>=2.1)", "pytest (>=7.2)", "pytest-cov (>=4.0)"] name = "nltk" version = "3.8.1" description = "Natural Language Toolkit" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2066,6 +2310,7 @@ twitter = ["twython"] name = "nodeenv" version = "1.8.0" description = "Node.js virtual environment builder" +category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ @@ -2080,6 +2325,7 @@ setuptools = "*" name = "numpy" version = "1.24.4" description = "Fundamental package for array computing in Python" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2117,6 +2363,7 @@ files = [ name = "nvidia-cublas-cu11" version = "11.10.3.66" description = "CUBLAS native runtime libraries" +category = "dev" optional = false python-versions = ">=3" files = [ @@ -2132,6 +2379,7 @@ wheel = "*" name = "nvidia-cuda-cupti-cu11" version = "11.7.101" description = "CUDA profiling tools runtime libs." +category = "dev" optional = false python-versions = ">=3" files = [ @@ -2147,6 +2395,7 @@ wheel = "*" name = "nvidia-cuda-nvrtc-cu11" version = "11.7.99" description = "NVRTC native runtime libraries" +category = "dev" optional = false python-versions = ">=3" files = [ @@ -2163,6 +2412,7 @@ wheel = "*" name = "nvidia-cuda-runtime-cu11" version = "11.7.99" description = "CUDA Runtime native Libraries" +category = "dev" optional = false python-versions = ">=3" files = [ @@ -2178,6 +2428,7 @@ wheel = "*" name = "nvidia-cudnn-cu11" version = "8.5.0.96" description = "cuDNN runtime libraries" +category = "dev" optional = false python-versions = ">=3" files = [ @@ -2193,6 +2444,7 @@ wheel = "*" name = "nvidia-cufft-cu11" version = "10.9.0.58" description = "CUFFT native runtime libraries" +category = "dev" optional = false python-versions = ">=3" files = [ @@ -2204,6 +2456,7 @@ files = [ name = "nvidia-curand-cu11" version = "10.2.10.91" description = "CURAND native runtime libraries" +category = "dev" optional = false python-versions = ">=3" files = [ @@ -2219,6 +2472,7 @@ wheel = "*" name = "nvidia-cusolver-cu11" version = "11.4.0.1" description = "CUDA solver native runtime libraries" +category = "dev" optional = false python-versions = ">=3" files = [ @@ -2235,6 +2489,7 @@ wheel = "*" name = "nvidia-cusparse-cu11" version = "11.7.4.91" description = "CUSPARSE native runtime libraries" +category = "dev" optional = false python-versions = ">=3" files = [ @@ -2250,6 +2505,7 @@ wheel = "*" name = "nvidia-nccl-cu11" version = "2.14.3" description = "NVIDIA Collective Communication Library (NCCL) Runtime" +category = "dev" optional = false python-versions = ">=3" files = [ @@ -2260,6 +2516,7 @@ files = [ name = "nvidia-nvtx-cu11" version = "11.7.91" description = "NVIDIA Tools Extension" +category = "dev" optional = false python-versions = ">=3" files = [ @@ -2275,6 +2532,7 @@ wheel = "*" name = "onnxruntime" version = "1.15.1" description = "ONNX Runtime is a runtime accelerator for Machine Learning models" +category = "dev" optional = false python-versions = "*" files = [ @@ -2314,13 +2572,14 @@ sympy = "*" [[package]] name = "openai" -version = "0.27.8" +version = "0.27.9" description = "Python client library for the OpenAI API" +category = "main" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-0.27.8-py3-none-any.whl", hash = "sha256:e0a7c2f7da26bdbe5354b03c6d4b82a2f34bd4458c7a17ae1a7092c3e397e03c"}, - {file = "openai-0.27.8.tar.gz", hash = "sha256:2483095c7db1eee274cebac79e315a986c4e55207bb4fa7b82d185b3a2ed9536"}, + {file = "openai-0.27.9-py3-none-any.whl", hash = "sha256:6a3cf8e276d1a6262b50562fbc0cba7967cfebb78ed827d375986b48fdad6475"}, + {file = "openai-0.27.9.tar.gz", hash = "sha256:b687761c82f5ebb6f61efc791b2083d2d068277b94802d4d1369efe39851813d"}, ] [package.dependencies] @@ -2330,7 +2589,7 @@ tqdm = "*" [package.extras] datalib = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] -dev = ["black (>=21.6b0,<22.0)", "pytest (==6.*)", "pytest-asyncio", "pytest-mock"] +dev = ["black (>=21.6b0,<22.0)", "pytest (>=6.0.0,<7.0.0)", "pytest-asyncio", "pytest-mock"] embeddings = ["matplotlib", "numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "plotly", "scikit-learn (>=1.0.2)", "scipy", "tenacity (>=8.0.1)"] wandb = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "wandb"] @@ -2338,6 +2597,7 @@ wandb = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1 name = "openapi-core" version = "0.18.0" description = "client-side and server-side support for the OpenAPI Specification v3" +category = "main" optional = false python-versions = ">=3.8.0,<4.0.0" files = [ @@ -2368,6 +2628,7 @@ starlette = ["starlette (>=0.26.1,<0.29.0)"] name = "openapi-schema-validator" version = "0.6.0" description = "OpenAPI schema validation for Python" +category = "main" optional = false python-versions = ">=3.8.0,<4.0.0" files = [ @@ -2384,6 +2645,7 @@ rfc3339-validator = "*" name = "openapi-spec-validator" version = "0.6.0" description = "OpenAPI 2.0 (aka Swagger) and OpenAPI 3 spec validator" +category = "main" optional = false python-versions = ">=3.8.0,<4.0.0" files = [ @@ -2400,19 +2662,21 @@ openapi-schema-validator = ">=0.6.0,<0.7.0" [[package]] name = "overrides" -version = "7.3.1" +version = "7.4.0" description = "A decorator to automatically detect mismatch when overriding a method." +category = "dev" optional = false python-versions = ">=3.6" files = [ - {file = "overrides-7.3.1-py3-none-any.whl", hash = "sha256:6187d8710a935d09b0bcef8238301d6ee2569d2ac1ae0ec39a8c7924e27f58ca"}, - {file = "overrides-7.3.1.tar.gz", hash = "sha256:8b97c6c1e1681b78cbc9424b138d880f0803c2254c5ebaabdde57bb6c62093f2"}, + {file = "overrides-7.4.0-py3-none-any.whl", hash = "sha256:3ad24583f86d6d7a49049695efe9933e67ba62f0c7625d53c59fa832ce4b8b7d"}, + {file = "overrides-7.4.0.tar.gz", hash = "sha256:9502a3cca51f4fac40b5feca985b6703a5c1f6ad815588a7ca9e285b9dca6757"}, ] [[package]] name = "packaging" version = "23.1" description = "Core utilities for Python packages" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2424,6 +2688,7 @@ files = [ name = "pandas" version = "2.0.3" description = "Powerful data structures for data analysis, time series, and statistics" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2491,6 +2756,7 @@ xml = ["lxml (>=4.6.3)"] name = "parse" version = "1.19.1" description = "parse() is the opposite of format()" +category = "main" optional = false python-versions = "*" files = [ @@ -2502,6 +2768,7 @@ files = [ name = "parso" version = "0.8.3" description = "A Python Parser" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -2517,6 +2784,7 @@ testing = ["docopt", "pytest (<6.0.0)"] name = "pathable" version = "0.4.3" description = "Object-oriented paths" +category = "main" optional = false python-versions = ">=3.7.0,<4.0.0" files = [ @@ -2528,6 +2796,7 @@ files = [ name = "pathspec" version = "0.11.2" description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2539,6 +2808,7 @@ files = [ name = "pexpect" version = "4.8.0" description = "Pexpect allows easy control of interactive console applications." +category = "dev" optional = false python-versions = "*" files = [ @@ -2553,6 +2823,7 @@ ptyprocess = ">=0.5" name = "pickleshare" version = "0.7.5" description = "Tiny 'shelve'-like database with concurrency support" +category = "dev" optional = false python-versions = "*" files = [ @@ -2564,6 +2835,7 @@ files = [ name = "pillow" version = "10.0.0" description = "Python Imaging Library (Fork)" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2585,7 +2857,6 @@ files = [ {file = "Pillow-10.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3b08d4cc24f471b2c8ca24ec060abf4bebc6b144cb89cba638c720546b1cf538"}, {file = "Pillow-10.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d737a602fbd82afd892ca746392401b634e278cb65d55c4b7a8f48e9ef8d008d"}, {file = "Pillow-10.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:3a82c40d706d9aa9734289740ce26460a11aeec2d9c79b7af87bb35f0073c12f"}, - {file = "Pillow-10.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:bc2ec7c7b5d66b8ec9ce9f720dbb5fa4bace0f545acd34870eff4a369b44bf37"}, {file = "Pillow-10.0.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:d80cf684b541685fccdd84c485b31ce73fc5c9b5d7523bf1394ce134a60c6883"}, {file = "Pillow-10.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76de421f9c326da8f43d690110f0e79fe3ad1e54be811545d7d91898b4c8493e"}, {file = "Pillow-10.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81ff539a12457809666fef6624684c008e00ff6bf455b4b89fd00a140eecd640"}, @@ -2595,7 +2866,6 @@ files = [ {file = "Pillow-10.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d50b6aec14bc737742ca96e85d6d0a5f9bfbded018264b3b70ff9d8c33485551"}, {file = "Pillow-10.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:00e65f5e822decd501e374b0650146063fbb30a7264b4d2744bdd7b913e0cab5"}, {file = "Pillow-10.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:f31f9fdbfecb042d046f9d91270a0ba28368a723302786c0009ee9b9f1f60199"}, - {file = "Pillow-10.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:1ce91b6ec08d866b14413d3f0bbdea7e24dfdc8e59f562bb77bc3fe60b6144ca"}, {file = "Pillow-10.0.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:349930d6e9c685c089284b013478d6f76e3a534e36ddfa912cde493f235372f3"}, {file = "Pillow-10.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3a684105f7c32488f7153905a4e3015a3b6c7182e106fe3c37fbb5ef3e6994c3"}, {file = "Pillow-10.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4f69b3700201b80bb82c3a97d5e9254084f6dd5fb5b16fc1a7b974260f89f43"}, @@ -2633,6 +2903,7 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa name = "pinecone-client" version = "2.2.2" description = "Pinecone client and SDK" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2658,6 +2929,7 @@ grpc = ["googleapis-common-protos (>=1.53.0)", "grpc-gateway-protoc-gen-openapiv name = "pkgutil-resolve-name" version = "1.3.10" description = "Resolve a name to an object." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -2669,6 +2941,7 @@ files = [ name = "platformdirs" version = "3.10.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2684,6 +2957,7 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-co name = "pluggy" version = "1.2.0" description = "plugin and hook calling mechanisms for python" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2699,6 +2973,7 @@ testing = ["pytest", "pytest-benchmark"] name = "portalocker" version = "2.7.0" description = "Wraps the portalocker recipe for easy usage" +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -2716,13 +2991,14 @@ tests = ["pytest (>=5.4.1)", "pytest-cov (>=2.8.1)", "pytest-mypy (>=0.8.0)", "p [[package]] name = "posthog" -version = "3.0.1" +version = "3.0.2" description = "Integrate PostHog into any python application." +category = "dev" optional = false python-versions = "*" files = [ - {file = "posthog-3.0.1-py2.py3-none-any.whl", hash = "sha256:9c7f92fecc713257d4b2710d05b456569c9156fbdd3e85655ba7ba5ba6c7b3ae"}, - {file = "posthog-3.0.1.tar.gz", hash = "sha256:57d2791ff5752ce56ba0f9bb8876faf3ca9208f1c2c6ceaeb5a2504c34493767"}, + {file = "posthog-3.0.2-py2.py3-none-any.whl", hash = "sha256:a8c0af6f2401fbe50f90e68c4143d0824b54e872de036b1c2f23b5abb39d88ce"}, + {file = "posthog-3.0.2.tar.gz", hash = "sha256:701fba6e446a4de687c6e861b587e7b7741955ad624bf34fe013c06a0fec6fb3"}, ] [package.dependencies] @@ -2741,6 +3017,7 @@ test = ["coverage", "flake8", "freezegun (==0.3.15)", "mock (>=2.0.0)", "pylint" name = "prance" version = "23.6.21.0" description = "Resolving Swagger/OpenAPI 2.0 and 3.0.0 Parser" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2767,6 +3044,7 @@ ssv = ["swagger-spec-validator (>=2.4,<3.0)"] name = "pre-commit" version = "3.3.3" description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2785,6 +3063,7 @@ virtualenv = ">=20.10.0" name = "prompt-toolkit" version = "3.0.39" description = "Library for building powerful interactive command lines in Python" +category = "dev" optional = false python-versions = ">=3.7.0" files = [ @@ -2795,32 +3074,52 @@ files = [ [package.dependencies] wcwidth = "*" +[[package]] +name = "proto-plus" +version = "1.22.3" +description = "Beautiful, Pythonic protocol buffers." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "proto-plus-1.22.3.tar.gz", hash = "sha256:fdcd09713cbd42480740d2fe29c990f7fbd885a67efc328aa8be6ee3e9f76a6b"}, + {file = "proto_plus-1.22.3-py3-none-any.whl", hash = "sha256:a49cd903bc0b6ab41f76bf65510439d56ca76f868adf0274e738bfdd096894df"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<5.0.0dev" + +[package.extras] +testing = ["google-api-core[grpc] (>=1.31.5)"] + [[package]] name = "protobuf" -version = "4.23.4" +version = "4.24.1" description = "" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "protobuf-4.23.4-cp310-abi3-win32.whl", hash = "sha256:5fea3c64d41ea5ecf5697b83e41d09b9589e6f20b677ab3c48e5f242d9b7897b"}, - {file = "protobuf-4.23.4-cp310-abi3-win_amd64.whl", hash = "sha256:7b19b6266d92ca6a2a87effa88ecc4af73ebc5cfde194dc737cf8ef23a9a3b12"}, - {file = "protobuf-4.23.4-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8547bf44fe8cec3c69e3042f5c4fb3e36eb2a7a013bb0a44c018fc1e427aafbd"}, - {file = "protobuf-4.23.4-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:fee88269a090ada09ca63551bf2f573eb2424035bcf2cb1b121895b01a46594a"}, - {file = "protobuf-4.23.4-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:effeac51ab79332d44fba74660d40ae79985901ac21bca408f8dc335a81aa597"}, - {file = "protobuf-4.23.4-cp37-cp37m-win32.whl", hash = "sha256:c3e0939433c40796ca4cfc0fac08af50b00eb66a40bbbc5dee711998fb0bbc1e"}, - {file = "protobuf-4.23.4-cp37-cp37m-win_amd64.whl", hash = "sha256:9053df6df8e5a76c84339ee4a9f5a2661ceee4a0dab019e8663c50ba324208b0"}, - {file = "protobuf-4.23.4-cp38-cp38-win32.whl", hash = "sha256:e1c915778d8ced71e26fcf43c0866d7499891bca14c4368448a82edc61fdbc70"}, - {file = "protobuf-4.23.4-cp38-cp38-win_amd64.whl", hash = "sha256:351cc90f7d10839c480aeb9b870a211e322bf05f6ab3f55fcb2f51331f80a7d2"}, - {file = "protobuf-4.23.4-cp39-cp39-win32.whl", hash = "sha256:6dd9b9940e3f17077e820b75851126615ee38643c2c5332aa7a359988820c720"}, - {file = "protobuf-4.23.4-cp39-cp39-win_amd64.whl", hash = "sha256:0a5759f5696895de8cc913f084e27fd4125e8fb0914bb729a17816a33819f474"}, - {file = "protobuf-4.23.4-py3-none-any.whl", hash = "sha256:e9d0be5bf34b275b9f87ba7407796556abeeba635455d036c7351f7c183ef8ff"}, - {file = "protobuf-4.23.4.tar.gz", hash = "sha256:ccd9430c0719dce806b93f89c91de7977304729e55377f872a92465d548329a9"}, + {file = "protobuf-4.24.1-cp310-abi3-win32.whl", hash = "sha256:d414199ca605eeb498adc4d2ba82aedc0379dca4a7c364ff9bc9a179aa28e71b"}, + {file = "protobuf-4.24.1-cp310-abi3-win_amd64.whl", hash = "sha256:5906c5e79ff50fe38b2d49d37db5874e3c8010826f2362f79996d83128a8ed9b"}, + {file = "protobuf-4.24.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:970c701ee16788d74f3de20938520d7a0aebc7e4fff37096a48804c80d2908cf"}, + {file = "protobuf-4.24.1-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:fc361148e902949dcb953bbcb148c99fe8f8854291ad01107e4120361849fd0e"}, + {file = "protobuf-4.24.1-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:5d32363d14aca6e5c9e9d5918ad8fb65b091b6df66740ae9de50ac3916055e43"}, + {file = "protobuf-4.24.1-cp37-cp37m-win32.whl", hash = "sha256:df015c47d6855b8efa0b9be706c70bf7f050a4d5ac6d37fb043fbd95157a0e25"}, + {file = "protobuf-4.24.1-cp37-cp37m-win_amd64.whl", hash = "sha256:d4af4fd9e9418e819be30f8df2a16e72fbad546a7576ac7f3653be92a6966d30"}, + {file = "protobuf-4.24.1-cp38-cp38-win32.whl", hash = "sha256:302e8752c760549ed4c7a508abc86b25d46553c81989343782809e1a062a2ef9"}, + {file = "protobuf-4.24.1-cp38-cp38-win_amd64.whl", hash = "sha256:06437f0d4bb0d5f29e3d392aba69600188d4be5ad1e0a3370e581a9bf75a3081"}, + {file = "protobuf-4.24.1-cp39-cp39-win32.whl", hash = "sha256:0b2b224e9541fe9f046dd7317d05f08769c332b7e4c54d93c7f0f372dedb0b1a"}, + {file = "protobuf-4.24.1-cp39-cp39-win_amd64.whl", hash = "sha256:bd39b9094a4cc003a1f911b847ab379f89059f478c0b611ba1215053e295132e"}, + {file = "protobuf-4.24.1-py3-none-any.whl", hash = "sha256:55dd644adc27d2a624339332755fe077c7f26971045b469ebb9732a69ce1f2ca"}, + {file = "protobuf-4.24.1.tar.gz", hash = "sha256:44837a5ed9c9418ad5d502f89f28ba102e9cd172b6668bc813f21716f9273348"}, ] [[package]] name = "psutil" version = "5.9.5" description = "Cross-platform lib for process and system monitoring in Python." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -2845,13 +3144,14 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] [[package]] name = "psycopg" -version = "3.1.9" +version = "3.1.10" description = "PostgreSQL database adapter for Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "psycopg-3.1.9-py3-none-any.whl", hash = "sha256:fbbac339274d8733ee70ba9822297af3e8871790a26e967b5ea53e30a4b74dcc"}, - {file = "psycopg-3.1.9.tar.gz", hash = "sha256:ab400f207a8c120bafdd8077916d8f6c0106e809401378708485b016508c30c9"}, + {file = "psycopg-3.1.10-py3-none-any.whl", hash = "sha256:8bbeddae5075c7890b2fa3e3553440376d3c5e28418335dee3c3656b06fa2b52"}, + {file = "psycopg-3.1.10.tar.gz", hash = "sha256:15b25741494344c24066dc2479b0f383dd1b82fa5e75612fa4fa5bb30726e9b6"}, ] [package.dependencies] @@ -2860,80 +3160,82 @@ typing-extensions = ">=4.1" tzdata = {version = "*", markers = "sys_platform == \"win32\""} [package.extras] -binary = ["psycopg-binary (==3.1.9)"] -c = ["psycopg-c (==3.1.9)"] -dev = ["black (>=23.1.0)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.2)", "types-setuptools (>=57.4)", "wheel (>=0.37)"] +binary = ["psycopg-binary (==3.1.10)"] +c = ["psycopg-c (==3.1.10)"] +dev = ["black (>=23.1.0)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.4.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"] docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] pool = ["psycopg-pool"] -test = ["anyio (>=3.6.2)", "mypy (>=1.2)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"] +test = ["anyio (>=3.6.2)", "mypy (>=1.4.1)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"] [[package]] name = "psycopg-binary" -version = "3.1.9" +version = "3.1.10" description = "PostgreSQL database adapter for Python -- C optimisation distribution" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "psycopg_binary-3.1.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:284038cbe3f5a0f3de417af9b5eaa2a9524a3a06211523cf245111c71b566506"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d2cea4bb0b19245c83486868d7c66f73238c4caa266b5b3c3d664d10dab2ab56"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfe5c5c31f59ccb1d1f473466baa93d800138186286e80e251f930e49c80d208"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82704a899d57c29beba5399d41eab5ef5c238b810d7e25e2d1916d2b34c4b1a3"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eab449e39db1c429cac79b7aa27e6827aad4995f32137e922db7254f43fed7b5"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87e0c97733b11eeca3d24e56df70f3f9d792b2abd46f48be2fb2348ffc3e7e39"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:81e34d6df54329424944d5ca91b1cc77df6b8a9130cb5480680d56f53d4e485c"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e2f463079d99568a343ed0b766150b30627e9ed41de99fd82e945e7e2bec764a"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f2cbdef6568da21c39dfd45c2074e85eabbd00e1b721832ba94980f01f582dd4"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53afb0cc2ebe74651f339e22d05ec082a0f44939715d9138d357852f074fcf55"}, - {file = "psycopg_binary-3.1.9-cp310-cp310-win_amd64.whl", hash = "sha256:09167f106e7685591b4cdf58eff0191fb7435d586f384133a0dd30df646cf409"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a8aaa47c1791fc05c0229ec1003dd49e13238fba9434e1fc3b879632f749c3c4"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3d91ee0d33ac7b42d0488a9be2516efa2ec00901b81d69566ff34a7a94b66c0b"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5e36504373e5bcdc954b1da1c6fe66379007fe1e329790e8fb72b879a01e097"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c1def6c2d28e257325b3b208cf1966343b498282a0f4d390fda7b7e0577da64"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:055537a9c20efe9bf17cb72bd879602eda71de6f737ebafa1953e017c6a37fbe"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b164355d023a91b23dcc4bb3112bc7d6e9b9c938fb5abcb6e54457d2da1f317"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03b08545ce1c627f4d5e6384eda2946660c4ba6ceb0a09ae47de07419f725669"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1e31bac3d2d41e6446b20b591f638943328c958f4d1ce13d6f1c5db97c3a8dee"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:a274c63c8fb9d419509bed2ef72befc1fd04243972e17e7f5afc5725cb13a560"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:98d9d156b9ada08c271a79662fc5fcc1731b4d7c1f651ef5843d818d35f15ba0"}, - {file = "psycopg_binary-3.1.9-cp311-cp311-win_amd64.whl", hash = "sha256:c3a13aa022853891cadbc7256a9804e5989def760115c82334bddf0d19783b0b"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1a321ef3579a8de0545ade6ff1edfde0c88b8847d58c5615c03751c76054796"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5833bda4c14f24c6a8ac08d3c5712acaa4f35aab31f9ccd2265e9e9a7d0151c8"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a207d5a7f4212443b7452851c9ccd88df9c6d4d58fa2cea2ead4dd9cb328e578"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:07414daa86662f7657e9fabe49af85a32a975e92e6568337887d9c9ffedc224f"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17c5d4936c746f5125c6ef9eb43655e27d4d0c9ffe34c3073878b43c3192511d"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5cdc13c8ec1437240801e43d07e27ff6479ac9dd8583ecf647345bfd2e8390e4"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3836bdaf030a5648bd5f5b452e4b068b265e28f9199060c5b70dbf4a218cde6e"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:96725d9691a84a21eb3e81c884a2e043054e33e176801a57a05e9ac38d142c6e"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dade344aa90bb0b57d1cfc13304ed83ab9a36614b8ddd671381b2de72fe1483d"}, - {file = "psycopg_binary-3.1.9-cp37-cp37m-win_amd64.whl", hash = "sha256:db866cc557d9761036771d666d17fa4176c537af7e6098f42a6bf8f64217935f"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3b62545cc64dd69ea0ae5ffe18d7c97e03660ab8244aa8c5172668a21c41daa0"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:058ab0d79be0b229338f0e61fec6f475077518cba63c22c593645a69f01c3e23"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2340ca2531f69e5ebd9d18987362ba57ed6ab6a271511d8026814a46a2a87b59"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b816ce0e27a2a8786d34b61d3e36e01029245025879d64b88554326b794a4f0"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b36fe4314a784fbe45c9fd71c902b9bf57341aff9b97c0cbd22f8409a271e2f"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b246fed629482b06f938b23e9281c4af592329daa3ec2cd4a6841ccbfdeb4d68"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:90787ac05b932c0fc678cbf470ccea9c385b8077583f0490136b4569ed3fb652"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9c114f678e8f4a96530fa79cfd84f65f26358ecfc6cca70cfa2d5e3ae5ef217a"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3a82e77400d1ef6c5bbcf3e600e8bdfacf1a554512f96c090c43ceca3d1ce3b6"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c7d990f14a37345ca05a5192cd5ac938c9cbedca9c929872af6ae311158feb0e"}, - {file = "psycopg_binary-3.1.9-cp38-cp38-win_amd64.whl", hash = "sha256:e0ca74fd85718723bb9f08e0c6898e901a0c365aef20b3c3a4ef8709125d6210"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ce8f4dea5934aa6c4933e559c74bef4beb3413f51fbcf17f306ce890216ac33a"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f41a9e0de4db194c053bcc7c00c35422a4d19d92a8187e8065b1c560626efe35"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f94a7985135e084e122b143956c6f589d17aef743ecd0a434a3d3a222631d5a"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bb86d58b90faefdc0bbedf08fdea4cc2afcb1cfa4340f027d458bfd01d8b812"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c696dc84f9ff155761df15779181d8e4af7746b98908e130add8259912e4bb7"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4213953da44324850c8f789301cf665f46fb94301ba403301e7af58546c3a428"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:25e3ce947aaaa1bd9f1920fca76d7281660646304f9ea5bc036b201dd8790655"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9c75be2a9b986139e3ff6bc0a2852081ac00811040f9b82d3aa539821311122e"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:63e8d1dbe253657c70dbfa9c59423f4654d82698fc5ed6868b8dc0765abe20b6"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f4da4ca9b2365fc1d3fc741c3bbd3efccd892ce813444b884c8911a1acf1c932"}, - {file = "psycopg_binary-3.1.9-cp39-cp39-win_amd64.whl", hash = "sha256:c0b8d6bbeff1dba760a208d8bc205a05b745e6cee02b839f969f72cf56a8b80d"}, + {file = "psycopg_binary-3.1.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a529c203f6e0f4c67ba27cf8f9739eb3bc880ad70d6ad6c0e56c2230a66b5a09"}, + {file = "psycopg_binary-3.1.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bd6e14d1aeb12754a43446c77a5ce819b68875cc25ae6538089ef90d7f6dd6f7"}, + {file = "psycopg_binary-3.1.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1583ced5948cf88124212c4503dfe5b01ac3e2dd1a2833c083917f4c4aabe8b4"}, + {file = "psycopg_binary-3.1.10-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2098721c486478987be700723b28ec7a48f134eba339de36af0e745f37dfe461"}, + {file = "psycopg_binary-3.1.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7e61f7b412fca7b15dd043a0b22fd528d2ed8276e76b3764c3889e29fa65082b"}, + {file = "psycopg_binary-3.1.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0f33e33a072e3d5af51ee4d4a439e10dbe623fe87ef295d5d688180d529f13f"}, + {file = "psycopg_binary-3.1.10-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f6f7738c59262d8d19154164d99c881ed58ed377fb6f1d685eb0dc43bbcd8022"}, + {file = "psycopg_binary-3.1.10-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:511d38b1e1961d179d47d5103ba9634ecfc7ead431d19a9337ef82f3a2bca807"}, + {file = "psycopg_binary-3.1.10-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:666e7acf2ffdb5e8a58e8b0c1759facdb9688c7e90ee8ca7aed675803b57404d"}, + {file = "psycopg_binary-3.1.10-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:57b93c756fee5f7c7bd580c34cd5d244f7d5638f8b2cf25333f97b9b8b2ebfd1"}, + {file = "psycopg_binary-3.1.10-cp310-cp310-win_amd64.whl", hash = "sha256:a1d61b7724c7215a8ea4495a5c6b704656f4b7bb6165f4cb9989b685886ebc48"}, + {file = "psycopg_binary-3.1.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:36fff836a7823c9d71fa7faa333c74b2b081af216cebdbb0f481dce55ee2d974"}, + {file = "psycopg_binary-3.1.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:32caf98cb00881bfcbbbae39a15f2a4e08b79ff983f1c0f13b60a888ef6e8431"}, + {file = "psycopg_binary-3.1.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5565a6a86fee8d74f30de89e07f399567cdf59367aeb09624eb690d524339076"}, + {file = "psycopg_binary-3.1.10-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9fb0d64520b29bd80a6731476ad8e1c20348dfdee00ab098899d23247b641675"}, + {file = "psycopg_binary-3.1.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfc05ed4e74fa8615d7cc2bd57f00f97662f4e865a731dbd43da9a527e289c8c"}, + {file = "psycopg_binary-3.1.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5b59c8cff887757ddf438ff9489d79c5e6b717112c96f5c68e16f367ff8724e"}, + {file = "psycopg_binary-3.1.10-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbaf12361136afefc5faab21a174a437e71c803b083f410e5140c7605bc66b"}, + {file = "psycopg_binary-3.1.10-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ff72576061c774bcce5f5440b93e63d4c430032dd056d30f6cb1988e549dd92c"}, + {file = "psycopg_binary-3.1.10-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:a4e91e1a8d61c60f592a1dfcebdf55e52a29fe4fdb650c5bd5414c848e77d029"}, + {file = "psycopg_binary-3.1.10-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f7187269d825e84c945be7d93dd5088a4e0b6481a4bdaba3bf7069d4ac13703d"}, + {file = "psycopg_binary-3.1.10-cp311-cp311-win_amd64.whl", hash = "sha256:ba7812a593c16d9d661844dc8dd4d81548fd1c2a0ee676f3e3d8638369f4c5e4"}, + {file = "psycopg_binary-3.1.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88caa5859740507b3596c6c2e00ceaccee2c6ab5317bc535887801ad3cc7f3e1"}, + {file = "psycopg_binary-3.1.10-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a3a7e99ba10c2e83a48d79431560e0d5ca7865f68f2bac3a462dc2b151e9926"}, + {file = "psycopg_binary-3.1.10-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:848f4f4707dc73f4b4e844c92f3de795b2ddb728f75132602bda5e6ba55084fc"}, + {file = "psycopg_binary-3.1.10-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:415961e839bb49cfd75cd961503fb8846c0768f247db1fa7171c1ac61d38711b"}, + {file = "psycopg_binary-3.1.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0471869e658d0c6b8c3ed53153794739c18d7dad2dd5b8e6ff023a364c20f7df"}, + {file = "psycopg_binary-3.1.10-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4290060ee0d856caa979ecf675c0e6959325f508272ccf27f64c3801c7bcbde7"}, + {file = "psycopg_binary-3.1.10-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:abf04bc06c8f6a1ac3dc2106d3b79c8661352e9d8a57ca2934ffa6aae8fe600a"}, + {file = "psycopg_binary-3.1.10-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:51fe70708243b83bf16710d8c11b61bd46562e6a24a6300d5434380b35911059"}, + {file = "psycopg_binary-3.1.10-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8b658f7f8b49fb60a1c52e3f6692f690a85bdf1ad30aafe0f3f1fd74f6958cf8"}, + {file = "psycopg_binary-3.1.10-cp37-cp37m-win_amd64.whl", hash = "sha256:ffc8c796194f23b9b07f6d25f927ec4df84a194bbc7a1f9e73316734eef512f9"}, + {file = "psycopg_binary-3.1.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:74ce92122be34cf0e5f06d79869e1001c8421a68fa7ddf6fe38a717155cf3a64"}, + {file = "psycopg_binary-3.1.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:75608a900984061c8898be68fbddc6f3da5eefdffce6e0624f5371645740d172"}, + {file = "psycopg_binary-3.1.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6670d160d054466e8fdedfbc749ef8bf7dfdf69296048954d24645dd4d3d3c01"}, + {file = "psycopg_binary-3.1.10-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d32026cfab7ba7ac687a42c33345026a2fb6fc5608a6144077f767af4386be0b"}, + {file = "psycopg_binary-3.1.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:908fa388a5b75dfd17a937acb24708bd272e21edefca9a495004c6f70ec2636a"}, + {file = "psycopg_binary-3.1.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e46b97073bd4de114f475249d681eaf054e950699c5d7af554d3684db39b82d"}, + {file = "psycopg_binary-3.1.10-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9cf56bb4b115def3a18157f3b3b7d8322ee94a8dea30028db602c8f9ae34ad1e"}, + {file = "psycopg_binary-3.1.10-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3b6c6f90241c4c5a6ca3f0d8827e37ef90fdc4deb9d8cfa5678baa0ea374b391"}, + {file = "psycopg_binary-3.1.10-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:747176a6aeb058079f56c5397bd90339581ab7b3cc0d62e7445654e6a484c7e1"}, + {file = "psycopg_binary-3.1.10-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:41a415e78c457b06497fa0084e4ea7245ca1a377b55756dd757034210b64da7e"}, + {file = "psycopg_binary-3.1.10-cp38-cp38-win_amd64.whl", hash = "sha256:a7bbe9017edd898d7b3a8747700ed045dda96a907dff87f45e642e28d8584481"}, + {file = "psycopg_binary-3.1.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0f062f20256708929a58c41d44f350efced4c00a603323d1413f6dc0b84d95a5"}, + {file = "psycopg_binary-3.1.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dea30f2704337ca2d0322fccfe1fa30f61ce9185de3937eb986321063114a51f"}, + {file = "psycopg_binary-3.1.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9d88ac72531034ebf7ec09114e732b066a9078f4ce213cf65cc5e42eb538d30"}, + {file = "psycopg_binary-3.1.10-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2bea0940d69c3e24a72530730952687912893b34c53aa39e79045e7b446174d"}, + {file = "psycopg_binary-3.1.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6a691dc8e2436d9c1e5cf93902d63e9501688fccc957eb22f952d37886257470"}, + {file = "psycopg_binary-3.1.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa92661f99351765673835a4d936d79bd24dfbb358b29b084d83be38229a90e4"}, + {file = "psycopg_binary-3.1.10-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:30eb731ed5525d8df892db6532cc8ffd8a163b73bc355127dee9c49334e16eee"}, + {file = "psycopg_binary-3.1.10-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:50bf7a59d3a85a82d466fed341d352b44d09d6adc18656101d163a7cfc6509a0"}, + {file = "psycopg_binary-3.1.10-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f48665947c55f8d6eb3f0be98de80411508e1ec329f354685329b57fced82c7f"}, + {file = "psycopg_binary-3.1.10-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:caa771569da01fc0389ca34920c331a284425a68f92d1ba0a80cc08935f8356e"}, + {file = "psycopg_binary-3.1.10-cp39-cp39-win_amd64.whl", hash = "sha256:b30887e631fd67affaed98f6cd2135b44f2d1a6d9bca353a69c3889c78bd7aa8"}, ] [[package]] name = "psycopg-pool" version = "3.1.7" description = "Connection Pool for Psycopg" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2948,6 +3250,7 @@ typing-extensions = ">=3.10" name = "ptyprocess" version = "0.7.0" description = "Run a subprocess in a pseudo terminal" +category = "dev" optional = false python-versions = "*" files = [ @@ -2959,6 +3262,7 @@ files = [ name = "pulsar-client" version = "3.2.0" description = "Apache Pulsar Python client library" +category = "dev" optional = false python-versions = "*" files = [ @@ -3006,6 +3310,7 @@ functions = ["apache-bookkeeper-client (>=4.16.1)", "grpcio (>=1.8.2)", "prometh name = "pure-eval" version = "0.2.2" description = "Safely evaluate AST nodes without side effects" +category = "dev" optional = false python-versions = "*" files = [ @@ -3016,10 +3321,76 @@ files = [ [package.extras] tests = ["pytest"] +[[package]] +name = "pyarrow" +version = "12.0.1" +description = "Python library for Apache Arrow" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyarrow-12.0.1-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:6d288029a94a9bb5407ceebdd7110ba398a00412c5b0155ee9813a40d246c5df"}, + {file = "pyarrow-12.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345e1828efdbd9aa4d4de7d5676778aba384a2c3add896d995b23d368e60e5af"}, + {file = "pyarrow-12.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d6009fdf8986332b2169314da482baed47ac053311c8934ac6651e614deacd6"}, + {file = "pyarrow-12.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d3c4cbbf81e6dd23fe921bc91dc4619ea3b79bc58ef10bce0f49bdafb103daf"}, + {file = "pyarrow-12.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:cdacf515ec276709ac8042c7d9bd5be83b4f5f39c6c037a17a60d7ebfd92c890"}, + {file = "pyarrow-12.0.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:749be7fd2ff260683f9cc739cb862fb11be376de965a2a8ccbf2693b098db6c7"}, + {file = "pyarrow-12.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6895b5fb74289d055c43db3af0de6e16b07586c45763cb5e558d38b86a91e3a7"}, + {file = "pyarrow-12.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1887bdae17ec3b4c046fcf19951e71b6a619f39fa674f9881216173566c8f718"}, + {file = "pyarrow-12.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2c9cb8eeabbadf5fcfc3d1ddea616c7ce893db2ce4dcef0ac13b099ad7ca082"}, + {file = "pyarrow-12.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:ce4aebdf412bd0eeb800d8e47db854f9f9f7e2f5a0220440acf219ddfddd4f63"}, + {file = "pyarrow-12.0.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:e0d8730c7f6e893f6db5d5b86eda42c0a130842d101992b581e2138e4d5663d3"}, + {file = "pyarrow-12.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43364daec02f69fec89d2315f7fbfbeec956e0d991cbbef471681bd77875c40f"}, + {file = "pyarrow-12.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:051f9f5ccf585f12d7de836e50965b3c235542cc896959320d9776ab93f3b33d"}, + {file = "pyarrow-12.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:be2757e9275875d2a9c6e6052ac7957fbbfc7bc7370e4a036a9b893e96fedaba"}, + {file = "pyarrow-12.0.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:cf812306d66f40f69e684300f7af5111c11f6e0d89d6b733e05a3de44961529d"}, + {file = "pyarrow-12.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:459a1c0ed2d68671188b2118c63bac91eaef6fc150c77ddd8a583e3c795737bf"}, + {file = "pyarrow-12.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85e705e33eaf666bbe508a16fd5ba27ca061e177916b7a317ba5a51bee43384c"}, + {file = "pyarrow-12.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9120c3eb2b1f6f516a3b7a9714ed860882d9ef98c4b17edcdc91d95b7528db60"}, + {file = "pyarrow-12.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:c780f4dc40460015d80fcd6a6140de80b615349ed68ef9adb653fe351778c9b3"}, + {file = "pyarrow-12.0.1-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:a3c63124fc26bf5f95f508f5d04e1ece8cc23a8b0af2a1e6ab2b1ec3fdc91b24"}, + {file = "pyarrow-12.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b13329f79fa4472324f8d32dc1b1216616d09bd1e77cfb13104dec5463632c36"}, + {file = "pyarrow-12.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb656150d3d12ec1396f6dde542db1675a95c0cc8366d507347b0beed96e87ca"}, + {file = "pyarrow-12.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6251e38470da97a5b2e00de5c6a049149f7b2bd62f12fa5dbb9ac674119ba71a"}, + {file = "pyarrow-12.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:3de26da901216149ce086920547dfff5cd22818c9eab67ebc41e863a5883bac7"}, + {file = "pyarrow-12.0.1.tar.gz", hash = "sha256:cce317fc96e5b71107bf1f9f184d5e54e2bd14bbf3f9a3d62819961f0af86fec"}, +] + +[package.dependencies] +numpy = ">=1.16.6" + +[[package]] +name = "pyasn1" +version = "0.5.0" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1-0.5.0-py2.py3-none-any.whl", hash = "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57"}, + {file = "pyasn1-0.5.0.tar.gz", hash = "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, + {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + [[package]] name = "pycparser" version = "2.21" description = "C parser in Python" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -3031,6 +3402,7 @@ files = [ name = "pydantic" version = "1.10.12" description = "Data validation and settings management using python type hints" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3081,13 +3453,14 @@ email = ["email-validator (>=1.0.3)"] [[package]] name = "pygments" -version = "2.15.1" +version = "2.16.1" description = "Pygments is a syntax highlighting package written in Python." +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"}, - {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"}, + {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, + {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, ] [package.extras] @@ -3097,6 +3470,7 @@ plugins = ["importlib-metadata"] name = "pyjwt" version = "2.8.0" description = "JSON Web Token implementation in Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3115,13 +3489,14 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] [[package]] name = "pymilvus" -version = "2.2.14" +version = "2.3.0" description = "Python Sdk for Milvus" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pymilvus-2.2.14-py3-none-any.whl", hash = "sha256:c19c757832d46cb2688078cb09fbcaa2c8dea1acebcb3b9a087505c0435d10cc"}, - {file = "pymilvus-2.2.14.tar.gz", hash = "sha256:c89b983823b8fb8d9e0ee30977b272e93c31056622d9cea942ec259130bf6e36"}, + {file = "pymilvus-2.3.0-py3-none-any.whl", hash = "sha256:0be97b387eb1ef16520d33b2ee876f369bafd8e8f383affd3e17ddb2cab0bbba"}, + {file = "pymilvus-2.3.0.tar.gz", hash = "sha256:0a1baafa99d37e6c2159bfb1462d2052f177b973375c60f487984130b036faf5"}, ] [package.dependencies] @@ -3136,6 +3511,7 @@ ujson = ">=2.0.0" name = "pypika" version = "0.48.9" description = "A SQL query builder API for Python" +category = "dev" optional = false python-versions = "*" files = [ @@ -3146,6 +3522,7 @@ files = [ name = "pyreadline3" version = "3.4.1" description = "A python implementation of GNU readline." +category = "dev" optional = false python-versions = "*" files = [ @@ -3157,6 +3534,7 @@ files = [ name = "pytest" version = "7.4.0" description = "pytest: simple powerful testing with Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3179,6 +3557,7 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "pytest-asyncio" version = "0.21.1" description = "Pytest support for asyncio" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3197,6 +3576,7 @@ testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -3211,6 +3591,7 @@ six = ">=1.5" name = "python-dotenv" version = "1.0.0" description = "Read key-value pairs from a .env file and set them as environment variables" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3225,6 +3606,7 @@ cli = ["click (>=5.0)"] name = "pytz" version = "2023.3" description = "World timezone definitions, modern and historical" +category = "dev" optional = false python-versions = "*" files = [ @@ -3236,6 +3618,7 @@ files = [ name = "pywin32" version = "306" description = "Python for Window Extensions" +category = "dev" optional = false python-versions = "*" files = [ @@ -3259,6 +3642,7 @@ files = [ name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -3306,88 +3690,105 @@ files = [ [[package]] name = "pyzmq" -version = "25.1.0" +version = "25.1.1" description = "Python bindings for 0MQ" +category = "dev" optional = false python-versions = ">=3.6" files = [ - {file = "pyzmq-25.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:1a6169e69034eaa06823da6a93a7739ff38716142b3596c180363dee729d713d"}, - {file = "pyzmq-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:19d0383b1f18411d137d891cab567de9afa609b214de68b86e20173dc624c101"}, - {file = "pyzmq-25.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1e931d9a92f628858a50f5bdffdfcf839aebe388b82f9d2ccd5d22a38a789dc"}, - {file = "pyzmq-25.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97d984b1b2f574bc1bb58296d3c0b64b10e95e7026f8716ed6c0b86d4679843f"}, - {file = "pyzmq-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:154bddda2a351161474b36dba03bf1463377ec226a13458725183e508840df89"}, - {file = "pyzmq-25.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cb6d161ae94fb35bb518b74bb06b7293299c15ba3bc099dccd6a5b7ae589aee3"}, - {file = "pyzmq-25.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:90146ab578931e0e2826ee39d0c948d0ea72734378f1898939d18bc9c823fcf9"}, - {file = "pyzmq-25.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:831ba20b660b39e39e5ac8603e8193f8fce1ee03a42c84ade89c36a251449d80"}, - {file = "pyzmq-25.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3a522510e3434e12aff80187144c6df556bb06fe6b9d01b2ecfbd2b5bfa5c60c"}, - {file = "pyzmq-25.1.0-cp310-cp310-win32.whl", hash = "sha256:be24a5867b8e3b9dd5c241de359a9a5217698ff616ac2daa47713ba2ebe30ad1"}, - {file = "pyzmq-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:5693dcc4f163481cf79e98cf2d7995c60e43809e325b77a7748d8024b1b7bcba"}, - {file = "pyzmq-25.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:13bbe36da3f8aaf2b7ec12696253c0bf6ffe05f4507985a8844a1081db6ec22d"}, - {file = "pyzmq-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:69511d604368f3dc58d4be1b0bad99b61ee92b44afe1cd9b7bd8c5e34ea8248a"}, - {file = "pyzmq-25.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a983c8694667fd76d793ada77fd36c8317e76aa66eec75be2653cef2ea72883"}, - {file = "pyzmq-25.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:332616f95eb400492103ab9d542b69d5f0ff628b23129a4bc0a2fd48da6e4e0b"}, - {file = "pyzmq-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58416db767787aedbfd57116714aad6c9ce57215ffa1c3758a52403f7c68cff5"}, - {file = "pyzmq-25.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:cad9545f5801a125f162d09ec9b724b7ad9b6440151b89645241d0120e119dcc"}, - {file = "pyzmq-25.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d6128d431b8dfa888bf51c22a04d48bcb3d64431caf02b3cb943269f17fd2994"}, - {file = "pyzmq-25.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:2b15247c49d8cbea695b321ae5478d47cffd496a2ec5ef47131a9e79ddd7e46c"}, - {file = "pyzmq-25.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:442d3efc77ca4d35bee3547a8e08e8d4bb88dadb54a8377014938ba98d2e074a"}, - {file = "pyzmq-25.1.0-cp311-cp311-win32.whl", hash = "sha256:65346f507a815a731092421d0d7d60ed551a80d9b75e8b684307d435a5597425"}, - {file = "pyzmq-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:8b45d722046fea5a5694cba5d86f21f78f0052b40a4bbbbf60128ac55bfcc7b6"}, - {file = "pyzmq-25.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f45808eda8b1d71308c5416ef3abe958f033fdbb356984fabbfc7887bed76b3f"}, - {file = "pyzmq-25.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b697774ea8273e3c0460cf0bba16cd85ca6c46dfe8b303211816d68c492e132"}, - {file = "pyzmq-25.1.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b324fa769577fc2c8f5efcd429cef5acbc17d63fe15ed16d6dcbac2c5eb00849"}, - {file = "pyzmq-25.1.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:5873d6a60b778848ce23b6c0ac26c39e48969823882f607516b91fb323ce80e5"}, - {file = "pyzmq-25.1.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:f0d9e7ba6a815a12c8575ba7887da4b72483e4cfc57179af10c9b937f3f9308f"}, - {file = "pyzmq-25.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:414b8beec76521358b49170db7b9967d6974bdfc3297f47f7d23edec37329b00"}, - {file = "pyzmq-25.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:01f06f33e12497dca86353c354461f75275a5ad9eaea181ac0dc1662da8074fa"}, - {file = "pyzmq-25.1.0-cp36-cp36m-win32.whl", hash = "sha256:b5a07c4f29bf7cb0164664ef87e4aa25435dcc1f818d29842118b0ac1eb8e2b5"}, - {file = "pyzmq-25.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:968b0c737797c1809ec602e082cb63e9824ff2329275336bb88bd71591e94a90"}, - {file = "pyzmq-25.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:47b915ba666c51391836d7ed9a745926b22c434efa76c119f77bcffa64d2c50c"}, - {file = "pyzmq-25.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5af31493663cf76dd36b00dafbc839e83bbca8a0662931e11816d75f36155897"}, - {file = "pyzmq-25.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5489738a692bc7ee9a0a7765979c8a572520d616d12d949eaffc6e061b82b4d1"}, - {file = "pyzmq-25.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1fc56a0221bdf67cfa94ef2d6ce5513a3d209c3dfd21fed4d4e87eca1822e3a3"}, - {file = "pyzmq-25.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:75217e83faea9edbc29516fc90c817bc40c6b21a5771ecb53e868e45594826b0"}, - {file = "pyzmq-25.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3830be8826639d801de9053cf86350ed6742c4321ba4236e4b5568528d7bfed7"}, - {file = "pyzmq-25.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3575699d7fd7c9b2108bc1c6128641a9a825a58577775ada26c02eb29e09c517"}, - {file = "pyzmq-25.1.0-cp37-cp37m-win32.whl", hash = "sha256:95bd3a998d8c68b76679f6b18f520904af5204f089beebb7b0301d97704634dd"}, - {file = "pyzmq-25.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:dbc466744a2db4b7ca05589f21ae1a35066afada2f803f92369f5877c100ef62"}, - {file = "pyzmq-25.1.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:3bed53f7218490c68f0e82a29c92335daa9606216e51c64f37b48eb78f1281f4"}, - {file = "pyzmq-25.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eb52e826d16c09ef87132c6e360e1879c984f19a4f62d8a935345deac43f3c12"}, - {file = "pyzmq-25.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ddbef8b53cd16467fdbfa92a712eae46dd066aa19780681a2ce266e88fbc7165"}, - {file = "pyzmq-25.1.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9301cf1d7fc1ddf668d0abbe3e227fc9ab15bc036a31c247276012abb921b5ff"}, - {file = "pyzmq-25.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e23a8c3b6c06de40bdb9e06288180d630b562db8ac199e8cc535af81f90e64b"}, - {file = "pyzmq-25.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4a82faae00d1eed4809c2f18b37f15ce39a10a1c58fe48b60ad02875d6e13d80"}, - {file = "pyzmq-25.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c8398a1b1951aaa330269c35335ae69744be166e67e0ebd9869bdc09426f3871"}, - {file = "pyzmq-25.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d40682ac60b2a613d36d8d3a0cd14fbdf8e7e0618fbb40aa9fa7b796c9081584"}, - {file = "pyzmq-25.1.0-cp38-cp38-win32.whl", hash = "sha256:33d5c8391a34d56224bccf74f458d82fc6e24b3213fc68165c98b708c7a69325"}, - {file = "pyzmq-25.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:c66b7ff2527e18554030319b1376d81560ca0742c6e0b17ff1ee96624a5f1afd"}, - {file = "pyzmq-25.1.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:af56229ea6527a849ac9fb154a059d7e32e77a8cba27e3e62a1e38d8808cb1a5"}, - {file = "pyzmq-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bdca18b94c404af6ae5533cd1bc310c4931f7ac97c148bbfd2cd4bdd62b96253"}, - {file = "pyzmq-25.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0b6b42f7055bbc562f63f3df3b63e3dd1ebe9727ff0f124c3aa7bcea7b3a00f9"}, - {file = "pyzmq-25.1.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4c2fc7aad520a97d64ffc98190fce6b64152bde57a10c704b337082679e74f67"}, - {file = "pyzmq-25.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be86a26415a8b6af02cd8d782e3a9ae3872140a057f1cadf0133de685185c02b"}, - {file = "pyzmq-25.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:851fb2fe14036cfc1960d806628b80276af5424db09fe5c91c726890c8e6d943"}, - {file = "pyzmq-25.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2a21fec5c3cea45421a19ccbe6250c82f97af4175bc09de4d6dd78fb0cb4c200"}, - {file = "pyzmq-25.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bad172aba822444b32eae54c2d5ab18cd7dee9814fd5c7ed026603b8cae2d05f"}, - {file = "pyzmq-25.1.0-cp39-cp39-win32.whl", hash = "sha256:4d67609b37204acad3d566bb7391e0ecc25ef8bae22ff72ebe2ad7ffb7847158"}, - {file = "pyzmq-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:71c7b5896e40720d30cd77a81e62b433b981005bbff0cb2f739e0f8d059b5d99"}, - {file = "pyzmq-25.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4cb27ef9d3bdc0c195b2dc54fcb8720e18b741624686a81942e14c8b67cc61a6"}, - {file = "pyzmq-25.1.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0c4fc2741e0513b5d5a12fe200d6785bbcc621f6f2278893a9ca7bed7f2efb7d"}, - {file = "pyzmq-25.1.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fc34fdd458ff77a2a00e3c86f899911f6f269d393ca5675842a6e92eea565bae"}, - {file = "pyzmq-25.1.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8751f9c1442624da391bbd92bd4b072def6d7702a9390e4479f45c182392ff78"}, - {file = "pyzmq-25.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:6581e886aec3135964a302a0f5eb68f964869b9efd1dbafdebceaaf2934f8a68"}, - {file = "pyzmq-25.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5482f08d2c3c42b920e8771ae8932fbaa0a67dff925fc476996ddd8155a170f3"}, - {file = "pyzmq-25.1.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7fbcafa3ea16d1de1f213c226005fea21ee16ed56134b75b2dede5a2129e62"}, - {file = "pyzmq-25.1.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:adecf6d02b1beab8d7c04bc36f22bb0e4c65a35eb0b4750b91693631d4081c70"}, - {file = "pyzmq-25.1.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6d39e42a0aa888122d1beb8ec0d4ddfb6c6b45aecb5ba4013c27e2f28657765"}, - {file = "pyzmq-25.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7018289b402ebf2b2c06992813523de61d4ce17bd514c4339d8f27a6f6809492"}, - {file = "pyzmq-25.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9e68ae9864d260b18f311b68d29134d8776d82e7f5d75ce898b40a88df9db30f"}, - {file = "pyzmq-25.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e21cc00e4debe8f54c3ed7b9fcca540f46eee12762a9fa56feb8512fd9057161"}, - {file = "pyzmq-25.1.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f666ae327a6899ff560d741681fdcdf4506f990595201ed39b44278c471ad98"}, - {file = "pyzmq-25.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f5efcc29056dfe95e9c9db0dfbb12b62db9c4ad302f812931b6d21dd04a9119"}, - {file = "pyzmq-25.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:48e5e59e77c1a83162ab3c163fc01cd2eebc5b34560341a67421b09be0891287"}, - {file = "pyzmq-25.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:108c96ebbd573d929740d66e4c3d1bdf31d5cde003b8dc7811a3c8c5b0fc173b"}, - {file = "pyzmq-25.1.0.tar.gz", hash = "sha256:80c41023465d36280e801564a69cbfce8ae85ff79b080e1913f6e90481fb8957"}, + {file = "pyzmq-25.1.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:381469297409c5adf9a0e884c5eb5186ed33137badcbbb0560b86e910a2f1e76"}, + {file = "pyzmq-25.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:955215ed0604dac5b01907424dfa28b40f2b2292d6493445dd34d0dfa72586a8"}, + {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:985bbb1316192b98f32e25e7b9958088431d853ac63aca1d2c236f40afb17c83"}, + {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:afea96f64efa98df4da6958bae37f1cbea7932c35878b185e5982821bc883369"}, + {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76705c9325d72a81155bb6ab48d4312e0032bf045fb0754889133200f7a0d849"}, + {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:77a41c26205d2353a4c94d02be51d6cbdf63c06fbc1295ea57dad7e2d3381b71"}, + {file = "pyzmq-25.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:12720a53e61c3b99d87262294e2b375c915fea93c31fc2336898c26d7aed34cd"}, + {file = "pyzmq-25.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:57459b68e5cd85b0be8184382cefd91959cafe79ae019e6b1ae6e2ba8a12cda7"}, + {file = "pyzmq-25.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:292fe3fc5ad4a75bc8df0dfaee7d0babe8b1f4ceb596437213821f761b4589f9"}, + {file = "pyzmq-25.1.1-cp310-cp310-win32.whl", hash = "sha256:35b5ab8c28978fbbb86ea54958cd89f5176ce747c1fb3d87356cf698048a7790"}, + {file = "pyzmq-25.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:11baebdd5fc5b475d484195e49bae2dc64b94a5208f7c89954e9e354fc609d8f"}, + {file = "pyzmq-25.1.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:d20a0ddb3e989e8807d83225a27e5c2eb2260eaa851532086e9e0fa0d5287d83"}, + {file = "pyzmq-25.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e1c1be77bc5fb77d923850f82e55a928f8638f64a61f00ff18a67c7404faf008"}, + {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d89528b4943d27029a2818f847c10c2cecc79fa9590f3cb1860459a5be7933eb"}, + {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:90f26dc6d5f241ba358bef79be9ce06de58d477ca8485e3291675436d3827cf8"}, + {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2b92812bd214018e50b6380ea3ac0c8bb01ac07fcc14c5f86a5bb25e74026e9"}, + {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2f957ce63d13c28730f7fd6b72333814221c84ca2421298f66e5143f81c9f91f"}, + {file = "pyzmq-25.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:047a640f5c9c6ade7b1cc6680a0e28c9dd5a0825135acbd3569cc96ea00b2505"}, + {file = "pyzmq-25.1.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7f7e58effd14b641c5e4dec8c7dab02fb67a13df90329e61c869b9cc607ef752"}, + {file = "pyzmq-25.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c2910967e6ab16bf6fbeb1f771c89a7050947221ae12a5b0b60f3bca2ee19bca"}, + {file = "pyzmq-25.1.1-cp311-cp311-win32.whl", hash = "sha256:76c1c8efb3ca3a1818b837aea423ff8a07bbf7aafe9f2f6582b61a0458b1a329"}, + {file = "pyzmq-25.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:44e58a0554b21fc662f2712814a746635ed668d0fbc98b7cb9d74cb798d202e6"}, + {file = "pyzmq-25.1.1-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:e1ffa1c924e8c72778b9ccd386a7067cddf626884fd8277f503c48bb5f51c762"}, + {file = "pyzmq-25.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1af379b33ef33757224da93e9da62e6471cf4a66d10078cf32bae8127d3d0d4a"}, + {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cff084c6933680d1f8b2f3b4ff5bbb88538a4aac00d199ac13f49d0698727ecb"}, + {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2400a94f7dd9cb20cd012951a0cbf8249e3d554c63a9c0cdfd5cbb6c01d2dec"}, + {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d81f1ddae3858b8299d1da72dd7d19dd36aab654c19671aa8a7e7fb02f6638a"}, + {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:255ca2b219f9e5a3a9ef3081512e1358bd4760ce77828e1028b818ff5610b87b"}, + {file = "pyzmq-25.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a882ac0a351288dd18ecae3326b8a49d10c61a68b01419f3a0b9a306190baf69"}, + {file = "pyzmq-25.1.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:724c292bb26365659fc434e9567b3f1adbdb5e8d640c936ed901f49e03e5d32e"}, + {file = "pyzmq-25.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ca1ed0bb2d850aa8471387882247c68f1e62a4af0ce9c8a1dbe0d2bf69e41fb"}, + {file = "pyzmq-25.1.1-cp312-cp312-win32.whl", hash = "sha256:b3451108ab861040754fa5208bca4a5496c65875710f76789a9ad27c801a0075"}, + {file = "pyzmq-25.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:eadbefd5e92ef8a345f0525b5cfd01cf4e4cc651a2cffb8f23c0dd184975d787"}, + {file = "pyzmq-25.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:db0b2af416ba735c6304c47f75d348f498b92952f5e3e8bff449336d2728795d"}, + {file = "pyzmq-25.1.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c133e93b405eb0d36fa430c94185bdd13c36204a8635470cccc200723c13bb"}, + {file = "pyzmq-25.1.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:273bc3959bcbff3f48606b28229b4721716598d76b5aaea2b4a9d0ab454ec062"}, + {file = "pyzmq-25.1.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cbc8df5c6a88ba5ae385d8930da02201165408dde8d8322072e3e5ddd4f68e22"}, + {file = "pyzmq-25.1.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:18d43df3f2302d836f2a56f17e5663e398416e9dd74b205b179065e61f1a6edf"}, + {file = "pyzmq-25.1.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:73461eed88a88c866656e08f89299720a38cb4e9d34ae6bf5df6f71102570f2e"}, + {file = "pyzmq-25.1.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:34c850ce7976d19ebe7b9d4b9bb8c9dfc7aac336c0958e2651b88cbd46682123"}, + {file = "pyzmq-25.1.1-cp36-cp36m-win32.whl", hash = "sha256:d2045d6d9439a0078f2a34b57c7b18c4a6aef0bee37f22e4ec9f32456c852c71"}, + {file = "pyzmq-25.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:458dea649f2f02a0b244ae6aef8dc29325a2810aa26b07af8374dc2a9faf57e3"}, + {file = "pyzmq-25.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7cff25c5b315e63b07a36f0c2bab32c58eafbe57d0dce61b614ef4c76058c115"}, + {file = "pyzmq-25.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1579413ae492b05de5a6174574f8c44c2b9b122a42015c5292afa4be2507f28"}, + {file = "pyzmq-25.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3d0a409d3b28607cc427aa5c30a6f1e4452cc44e311f843e05edb28ab5e36da0"}, + {file = "pyzmq-25.1.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:21eb4e609a154a57c520e3d5bfa0d97e49b6872ea057b7c85257b11e78068222"}, + {file = "pyzmq-25.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:034239843541ef7a1aee0c7b2cb7f6aafffb005ede965ae9cbd49d5ff4ff73cf"}, + {file = "pyzmq-25.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f8115e303280ba09f3898194791a153862cbf9eef722ad8f7f741987ee2a97c7"}, + {file = "pyzmq-25.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1a5d26fe8f32f137e784f768143728438877d69a586ddeaad898558dc971a5ae"}, + {file = "pyzmq-25.1.1-cp37-cp37m-win32.whl", hash = "sha256:f32260e556a983bc5c7ed588d04c942c9a8f9c2e99213fec11a031e316874c7e"}, + {file = "pyzmq-25.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:abf34e43c531bbb510ae7e8f5b2b1f2a8ab93219510e2b287a944432fad135f3"}, + {file = "pyzmq-25.1.1-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:87e34f31ca8f168c56d6fbf99692cc8d3b445abb5bfd08c229ae992d7547a92a"}, + {file = "pyzmq-25.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c9c6c9b2c2f80747a98f34ef491c4d7b1a8d4853937bb1492774992a120f475d"}, + {file = "pyzmq-25.1.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5619f3f5a4db5dbb572b095ea3cb5cc035335159d9da950830c9c4db2fbb6995"}, + {file = "pyzmq-25.1.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5a34d2395073ef862b4032343cf0c32a712f3ab49d7ec4f42c9661e0294d106f"}, + {file = "pyzmq-25.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25f0e6b78220aba09815cd1f3a32b9c7cb3e02cb846d1cfc526b6595f6046618"}, + {file = "pyzmq-25.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3669cf8ee3520c2f13b2e0351c41fea919852b220988d2049249db10046a7afb"}, + {file = "pyzmq-25.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2d163a18819277e49911f7461567bda923461c50b19d169a062536fffe7cd9d2"}, + {file = "pyzmq-25.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:df27ffddff4190667d40de7beba4a950b5ce78fe28a7dcc41d6f8a700a80a3c0"}, + {file = "pyzmq-25.1.1-cp38-cp38-win32.whl", hash = "sha256:a382372898a07479bd34bda781008e4a954ed8750f17891e794521c3e21c2e1c"}, + {file = "pyzmq-25.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:52533489f28d62eb1258a965f2aba28a82aa747202c8fa5a1c7a43b5db0e85c1"}, + {file = "pyzmq-25.1.1-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:03b3f49b57264909aacd0741892f2aecf2f51fb053e7d8ac6767f6c700832f45"}, + {file = "pyzmq-25.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:330f9e188d0d89080cde66dc7470f57d1926ff2fb5576227f14d5be7ab30b9fa"}, + {file = "pyzmq-25.1.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2ca57a5be0389f2a65e6d3bb2962a971688cbdd30b4c0bd188c99e39c234f414"}, + {file = "pyzmq-25.1.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d457aed310f2670f59cc5b57dcfced452aeeed77f9da2b9763616bd57e4dbaae"}, + {file = "pyzmq-25.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c56d748ea50215abef7030c72b60dd723ed5b5c7e65e7bc2504e77843631c1a6"}, + {file = "pyzmq-25.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8f03d3f0d01cb5a018debeb412441996a517b11c5c17ab2001aa0597c6d6882c"}, + {file = "pyzmq-25.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:820c4a08195a681252f46926de10e29b6bbf3e17b30037bd4250d72dd3ddaab8"}, + {file = "pyzmq-25.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17ef5f01d25b67ca8f98120d5fa1d21efe9611604e8eb03a5147360f517dd1e2"}, + {file = "pyzmq-25.1.1-cp39-cp39-win32.whl", hash = "sha256:04ccbed567171579ec2cebb9c8a3e30801723c575601f9a990ab25bcac6b51e2"}, + {file = "pyzmq-25.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:e61f091c3ba0c3578411ef505992d356a812fb200643eab27f4f70eed34a29ef"}, + {file = "pyzmq-25.1.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ade6d25bb29c4555d718ac6d1443a7386595528c33d6b133b258f65f963bb0f6"}, + {file = "pyzmq-25.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0c95ddd4f6e9fca4e9e3afaa4f9df8552f0ba5d1004e89ef0a68e1f1f9807c7"}, + {file = "pyzmq-25.1.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48e466162a24daf86f6b5ca72444d2bf39a5e58da5f96370078be67c67adc978"}, + {file = "pyzmq-25.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abc719161780932c4e11aaebb203be3d6acc6b38d2f26c0f523b5b59d2fc1996"}, + {file = "pyzmq-25.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1ccf825981640b8c34ae54231b7ed00271822ea1c6d8ba1090ebd4943759abf5"}, + {file = "pyzmq-25.1.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c2f20ce161ebdb0091a10c9ca0372e023ce24980d0e1f810f519da6f79c60800"}, + {file = "pyzmq-25.1.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:deee9ca4727f53464daf089536e68b13e6104e84a37820a88b0a057b97bba2d2"}, + {file = "pyzmq-25.1.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:aa8d6cdc8b8aa19ceb319aaa2b660cdaccc533ec477eeb1309e2a291eaacc43a"}, + {file = "pyzmq-25.1.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:019e59ef5c5256a2c7378f2fb8560fc2a9ff1d315755204295b2eab96b254d0a"}, + {file = "pyzmq-25.1.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:b9af3757495c1ee3b5c4e945c1df7be95562277c6e5bccc20a39aec50f826cd0"}, + {file = "pyzmq-25.1.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:548d6482dc8aadbe7e79d1b5806585c8120bafa1ef841167bc9090522b610fa6"}, + {file = "pyzmq-25.1.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:057e824b2aae50accc0f9a0570998adc021b372478a921506fddd6c02e60308e"}, + {file = "pyzmq-25.1.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2243700cc5548cff20963f0ca92d3e5e436394375ab8a354bbea2b12911b20b0"}, + {file = "pyzmq-25.1.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79986f3b4af059777111409ee517da24a529bdbd46da578b33f25580adcff728"}, + {file = "pyzmq-25.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:11d58723d44d6ed4dd677c5615b2ffb19d5c426636345567d6af82be4dff8a55"}, + {file = "pyzmq-25.1.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:49d238cf4b69652257db66d0c623cd3e09b5d2e9576b56bc067a396133a00d4a"}, + {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fedbdc753827cf014c01dbbee9c3be17e5a208dcd1bf8641ce2cd29580d1f0d4"}, + {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc16ac425cc927d0a57d242589f87ee093884ea4804c05a13834d07c20db203c"}, + {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11c1d2aed9079c6b0c9550a7257a836b4a637feb334904610f06d70eb44c56d2"}, + {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e8a701123029cc240cea61dd2d16ad57cab4691804143ce80ecd9286b464d180"}, + {file = "pyzmq-25.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:61706a6b6c24bdece85ff177fec393545a3191eeda35b07aaa1458a027ad1304"}, + {file = "pyzmq-25.1.1.tar.gz", hash = "sha256:259c22485b71abacdfa8bf79720cd7bcf4b9d128b30ea554f01ae71fdbfdaa23"}, ] [package.dependencies] @@ -3397,6 +3798,7 @@ cffi = {version = "*", markers = "implementation_name == \"pypy\""} name = "qdrant-client" version = "1.4.0" description = "Client library for the Qdrant vector search engine" +category = "dev" optional = false python-versions = ">=3.7,<3.12" files = [ @@ -3415,13 +3817,14 @@ urllib3 = ">=1.26.14,<2.0.0" [[package]] name = "referencing" -version = "0.29.3" +version = "0.30.2" description = "JSON Referencing + Python" +category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "referencing-0.29.3-py3-none-any.whl", hash = "sha256:7e059500cac23dc5d7d11687291ab60ba1e77ccdf9739b039c1177c06304e98c"}, - {file = "referencing-0.29.3.tar.gz", hash = "sha256:ebd2f5fe533001edaca3eae6caf09de0d70b6a3c78ebe34c98d26526e6fad3aa"}, + {file = "referencing-0.30.2-py3-none-any.whl", hash = "sha256:449b6669b6121a9e96a7f9e410b245d471e8d48964c67113ce9afe50c8dd7bdf"}, + {file = "referencing-0.30.2.tar.gz", hash = "sha256:794ad8003c65938edcdbc027f1933215e0d0ccc0291e3ce20a4d87432b59efc0"}, ] [package.dependencies] @@ -3430,105 +3833,107 @@ rpds-py = ">=0.7.0" [[package]] name = "regex" -version = "2023.6.3" +version = "2023.8.8" description = "Alternative regular expression module, to replace re." +category = "main" optional = false python-versions = ">=3.6" files = [ - {file = "regex-2023.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:824bf3ac11001849aec3fa1d69abcb67aac3e150a933963fb12bda5151fe1bfd"}, - {file = "regex-2023.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:05ed27acdf4465c95826962528f9e8d41dbf9b1aa8531a387dee6ed215a3e9ef"}, - {file = "regex-2023.6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b49c764f88a79160fa64f9a7b425620e87c9f46095ef9c9920542ab2495c8bc"}, - {file = "regex-2023.6.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8e3f1316c2293e5469f8f09dc2d76efb6c3982d3da91ba95061a7e69489a14ef"}, - {file = "regex-2023.6.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43e1dd9d12df9004246bacb79a0e5886b3b6071b32e41f83b0acbf293f820ee8"}, - {file = "regex-2023.6.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4959e8bcbfda5146477d21c3a8ad81b185cd252f3d0d6e4724a5ef11c012fb06"}, - {file = "regex-2023.6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:af4dd387354dc83a3bff67127a124c21116feb0d2ef536805c454721c5d7993d"}, - {file = "regex-2023.6.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2239d95d8e243658b8dbb36b12bd10c33ad6e6933a54d36ff053713f129aa536"}, - {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:890e5a11c97cf0d0c550eb661b937a1e45431ffa79803b942a057c4fb12a2da2"}, - {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a8105e9af3b029f243ab11ad47c19b566482c150c754e4c717900a798806b222"}, - {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:25be746a8ec7bc7b082783216de8e9473803706723b3f6bef34b3d0ed03d57e2"}, - {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:3676f1dd082be28b1266c93f618ee07741b704ab7b68501a173ce7d8d0d0ca18"}, - {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:10cb847aeb1728412c666ab2e2000ba6f174f25b2bdc7292e7dd71b16db07568"}, - {file = "regex-2023.6.3-cp310-cp310-win32.whl", hash = "sha256:dbbbfce33cd98f97f6bffb17801b0576e653f4fdb1d399b2ea89638bc8d08ae1"}, - {file = "regex-2023.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:c5f8037000eb21e4823aa485149f2299eb589f8d1fe4b448036d230c3f4e68e0"}, - {file = "regex-2023.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c123f662be8ec5ab4ea72ea300359023a5d1df095b7ead76fedcd8babbedf969"}, - {file = "regex-2023.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9edcbad1f8a407e450fbac88d89e04e0b99a08473f666a3f3de0fd292badb6aa"}, - {file = "regex-2023.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcba6dae7de533c876255317c11f3abe4907ba7d9aa15d13e3d9710d4315ec0e"}, - {file = "regex-2023.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29cdd471ebf9e0f2fb3cac165efedc3c58db841d83a518b082077e612d3ee5df"}, - {file = "regex-2023.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12b74fbbf6cbbf9dbce20eb9b5879469e97aeeaa874145517563cca4029db65c"}, - {file = "regex-2023.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c29ca1bd61b16b67be247be87390ef1d1ef702800f91fbd1991f5c4421ebae8"}, - {file = "regex-2023.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77f09bc4b55d4bf7cc5eba785d87001d6757b7c9eec237fe2af57aba1a071d9"}, - {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ea353ecb6ab5f7e7d2f4372b1e779796ebd7b37352d290096978fea83c4dba0c"}, - {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:10590510780b7541969287512d1b43f19f965c2ece6c9b1c00fc367b29d8dce7"}, - {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e2fbd6236aae3b7f9d514312cdb58e6494ee1c76a9948adde6eba33eb1c4264f"}, - {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:6b2675068c8b56f6bfd5a2bda55b8accbb96c02fd563704732fd1c95e2083461"}, - {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74419d2b50ecb98360cfaa2974da8689cb3b45b9deff0dcf489c0d333bcc1477"}, - {file = "regex-2023.6.3-cp311-cp311-win32.whl", hash = "sha256:fb5ec16523dc573a4b277663a2b5a364e2099902d3944c9419a40ebd56a118f9"}, - {file = "regex-2023.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:09e4a1a6acc39294a36b7338819b10baceb227f7f7dbbea0506d419b5a1dd8af"}, - {file = "regex-2023.6.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0654bca0cdf28a5956c83839162692725159f4cda8d63e0911a2c0dc76166525"}, - {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:463b6a3ceb5ca952e66550a4532cef94c9a0c80dc156c4cc343041951aec1697"}, - {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87b2a5bb5e78ee0ad1de71c664d6eb536dc3947a46a69182a90f4410f5e3f7dd"}, - {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6343c6928282c1f6a9db41f5fd551662310e8774c0e5ebccb767002fcf663ca9"}, - {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6192d5af2ccd2a38877bfef086d35e6659566a335b1492786ff254c168b1693"}, - {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74390d18c75054947e4194019077e243c06fbb62e541d8817a0fa822ea310c14"}, - {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:742e19a90d9bb2f4a6cf2862b8b06dea5e09b96c9f2df1779e53432d7275331f"}, - {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8abbc5d54ea0ee80e37fef009e3cec5dafd722ed3c829126253d3e22f3846f1e"}, - {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:c2b867c17a7a7ae44c43ebbeb1b5ff406b3e8d5b3e14662683e5e66e6cc868d3"}, - {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:d831c2f8ff278179705ca59f7e8524069c1a989e716a1874d6d1aab6119d91d1"}, - {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:ee2d1a9a253b1729bb2de27d41f696ae893507c7db224436abe83ee25356f5c1"}, - {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:61474f0b41fe1a80e8dfa70f70ea1e047387b7cd01c85ec88fa44f5d7561d787"}, - {file = "regex-2023.6.3-cp36-cp36m-win32.whl", hash = "sha256:0b71e63226e393b534105fcbdd8740410dc6b0854c2bfa39bbda6b0d40e59a54"}, - {file = "regex-2023.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bbb02fd4462f37060122e5acacec78e49c0fbb303c30dd49c7f493cf21fc5b27"}, - {file = "regex-2023.6.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b862c2b9d5ae38a68b92e215b93f98d4c5e9454fa36aae4450f61dd33ff48487"}, - {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:976d7a304b59ede34ca2921305b57356694f9e6879db323fd90a80f865d355a3"}, - {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:83320a09188e0e6c39088355d423aa9d056ad57a0b6c6381b300ec1a04ec3d16"}, - {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9427a399501818a7564f8c90eced1e9e20709ece36be701f394ada99890ea4b3"}, - {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178bbc1b2ec40eaca599d13c092079bf529679bf0371c602edaa555e10b41c3"}, - {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:837328d14cde912af625d5f303ec29f7e28cdab588674897baafaf505341f2fc"}, - {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2d44dc13229905ae96dd2ae2dd7cebf824ee92bc52e8cf03dcead37d926da019"}, - {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d54af539295392611e7efbe94e827311eb8b29668e2b3f4cadcfe6f46df9c777"}, - {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7117d10690c38a622e54c432dfbbd3cbd92f09401d622902c32f6d377e2300ee"}, - {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bb60b503ec8a6e4e3e03a681072fa3a5adcbfa5479fa2d898ae2b4a8e24c4591"}, - {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:65ba8603753cec91c71de423a943ba506363b0e5c3fdb913ef8f9caa14b2c7e0"}, - {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:271f0bdba3c70b58e6f500b205d10a36fb4b58bd06ac61381b68de66442efddb"}, - {file = "regex-2023.6.3-cp37-cp37m-win32.whl", hash = "sha256:9beb322958aaca059f34975b0df135181f2e5d7a13b84d3e0e45434749cb20f7"}, - {file = "regex-2023.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:fea75c3710d4f31389eed3c02f62d0b66a9da282521075061ce875eb5300cf23"}, - {file = "regex-2023.6.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8f56fcb7ff7bf7404becdfc60b1e81a6d0561807051fd2f1860b0d0348156a07"}, - {file = "regex-2023.6.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d2da3abc88711bce7557412310dfa50327d5769a31d1c894b58eb256459dc289"}, - {file = "regex-2023.6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a99b50300df5add73d307cf66abea093304a07eb017bce94f01e795090dea87c"}, - {file = "regex-2023.6.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5708089ed5b40a7b2dc561e0c8baa9535b77771b64a8330b684823cfd5116036"}, - {file = "regex-2023.6.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:687ea9d78a4b1cf82f8479cab23678aff723108df3edeac098e5b2498879f4a7"}, - {file = "regex-2023.6.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d3850beab9f527f06ccc94b446c864059c57651b3f911fddb8d9d3ec1d1b25d"}, - {file = "regex-2023.6.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8915cc96abeb8983cea1df3c939e3c6e1ac778340c17732eb63bb96247b91d2"}, - {file = "regex-2023.6.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:841d6e0e5663d4c7b4c8099c9997be748677d46cbf43f9f471150e560791f7ff"}, - {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9edce5281f965cf135e19840f4d93d55b3835122aa76ccacfd389e880ba4cf82"}, - {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b956231ebdc45f5b7a2e1f90f66a12be9610ce775fe1b1d50414aac1e9206c06"}, - {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:36efeba71c6539d23c4643be88295ce8c82c88bbd7c65e8a24081d2ca123da3f"}, - {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:cf67ca618b4fd34aee78740bea954d7c69fdda419eb208c2c0c7060bb822d747"}, - {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b4598b1897837067a57b08147a68ac026c1e73b31ef6e36deeeb1fa60b2933c9"}, - {file = "regex-2023.6.3-cp38-cp38-win32.whl", hash = "sha256:f415f802fbcafed5dcc694c13b1292f07fe0befdb94aa8a52905bd115ff41e88"}, - {file = "regex-2023.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:d4f03bb71d482f979bda92e1427f3ec9b220e62a7dd337af0aa6b47bf4498f72"}, - {file = "regex-2023.6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ccf91346b7bd20c790310c4147eee6ed495a54ddb6737162a36ce9dbef3e4751"}, - {file = "regex-2023.6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b28f5024a3a041009eb4c333863d7894d191215b39576535c6734cd88b0fcb68"}, - {file = "regex-2023.6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0bb18053dfcfed432cc3ac632b5e5e5c5b7e55fb3f8090e867bfd9b054dbcbf"}, - {file = "regex-2023.6.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a5bfb3004f2144a084a16ce19ca56b8ac46e6fd0651f54269fc9e230edb5e4a"}, - {file = "regex-2023.6.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c6b48d0fa50d8f4df3daf451be7f9689c2bde1a52b1225c5926e3f54b6a9ed1"}, - {file = "regex-2023.6.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:051da80e6eeb6e239e394ae60704d2b566aa6a7aed6f2890a7967307267a5dc6"}, - {file = "regex-2023.6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4c3b7fa4cdaa69268748665a1a6ff70c014d39bb69c50fda64b396c9116cf77"}, - {file = "regex-2023.6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:457b6cce21bee41ac292d6753d5e94dcbc5c9e3e3a834da285b0bde7aa4a11e9"}, - {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:aad51907d74fc183033ad796dd4c2e080d1adcc4fd3c0fd4fd499f30c03011cd"}, - {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0385e73da22363778ef2324950e08b689abdf0b108a7d8decb403ad7f5191938"}, - {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c6a57b742133830eec44d9b2290daf5cbe0a2f1d6acee1b3c7b1c7b2f3606df7"}, - {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3e5219bf9e75993d73ab3d25985c857c77e614525fac9ae02b1bebd92f7cecac"}, - {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e5087a3c59eef624a4591ef9eaa6e9a8d8a94c779dade95d27c0bc24650261cd"}, - {file = "regex-2023.6.3-cp39-cp39-win32.whl", hash = "sha256:20326216cc2afe69b6e98528160b225d72f85ab080cbdf0b11528cbbaba2248f"}, - {file = "regex-2023.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:bdff5eab10e59cf26bc479f565e25ed71a7d041d1ded04ccf9aee1d9f208487a"}, - {file = "regex-2023.6.3.tar.gz", hash = "sha256:72d1a25bf36d2050ceb35b517afe13864865268dfb45910e2e17a84be6cbfeb0"}, + {file = "regex-2023.8.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:88900f521c645f784260a8d346e12a1590f79e96403971241e64c3a265c8ecdb"}, + {file = "regex-2023.8.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3611576aff55918af2697410ff0293d6071b7e00f4b09e005d614686ac4cd57c"}, + {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8a0ccc8f2698f120e9e5742f4b38dc944c38744d4bdfc427616f3a163dd9de5"}, + {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c662a4cbdd6280ee56f841f14620787215a171c4e2d1744c9528bed8f5816c96"}, + {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf0633e4a1b667bfe0bb10b5e53fe0d5f34a6243ea2530eb342491f1adf4f739"}, + {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:551ad543fa19e94943c5b2cebc54c73353ffff08228ee5f3376bd27b3d5b9800"}, + {file = "regex-2023.8.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54de2619f5ea58474f2ac211ceea6b615af2d7e4306220d4f3fe690c91988a61"}, + {file = "regex-2023.8.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5ec4b3f0aebbbe2fc0134ee30a791af522a92ad9f164858805a77442d7d18570"}, + {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3ae646c35cb9f820491760ac62c25b6d6b496757fda2d51be429e0e7b67ae0ab"}, + {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ca339088839582d01654e6f83a637a4b8194d0960477b9769d2ff2cfa0fa36d2"}, + {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:d9b6627408021452dcd0d2cdf8da0534e19d93d070bfa8b6b4176f99711e7f90"}, + {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:bd3366aceedf274f765a3a4bc95d6cd97b130d1dda524d8f25225d14123c01db"}, + {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7aed90a72fc3654fba9bc4b7f851571dcc368120432ad68b226bd593f3f6c0b7"}, + {file = "regex-2023.8.8-cp310-cp310-win32.whl", hash = "sha256:80b80b889cb767cc47f31d2b2f3dec2db8126fbcd0cff31b3925b4dc6609dcdb"}, + {file = "regex-2023.8.8-cp310-cp310-win_amd64.whl", hash = "sha256:b82edc98d107cbc7357da7a5a695901b47d6eb0420e587256ba3ad24b80b7d0b"}, + {file = "regex-2023.8.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1e7d84d64c84ad97bf06f3c8cb5e48941f135ace28f450d86af6b6512f1c9a71"}, + {file = "regex-2023.8.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ce0f9fbe7d295f9922c0424a3637b88c6c472b75eafeaff6f910494a1fa719ef"}, + {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06c57e14ac723b04458df5956cfb7e2d9caa6e9d353c0b4c7d5d54fcb1325c46"}, + {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7a9aaa5a1267125eef22cef3b63484c3241aaec6f48949b366d26c7250e0357"}, + {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b7408511fca48a82a119d78a77c2f5eb1b22fe88b0d2450ed0756d194fe7a9a"}, + {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14dc6f2d88192a67d708341f3085df6a4f5a0c7b03dec08d763ca2cd86e9f559"}, + {file = "regex-2023.8.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48c640b99213643d141550326f34f0502fedb1798adb3c9eb79650b1ecb2f177"}, + {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0085da0f6c6393428bf0d9c08d8b1874d805bb55e17cb1dfa5ddb7cfb11140bf"}, + {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:964b16dcc10c79a4a2be9f1273fcc2684a9eedb3906439720598029a797b46e6"}, + {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7ce606c14bb195b0e5108544b540e2c5faed6843367e4ab3deb5c6aa5e681208"}, + {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:40f029d73b10fac448c73d6eb33d57b34607f40116e9f6e9f0d32e9229b147d7"}, + {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3b8e6ea6be6d64104d8e9afc34c151926f8182f84e7ac290a93925c0db004bfd"}, + {file = "regex-2023.8.8-cp311-cp311-win32.whl", hash = "sha256:942f8b1f3b223638b02df7df79140646c03938d488fbfb771824f3d05fc083a8"}, + {file = "regex-2023.8.8-cp311-cp311-win_amd64.whl", hash = "sha256:51d8ea2a3a1a8fe4f67de21b8b93757005213e8ac3917567872f2865185fa7fb"}, + {file = "regex-2023.8.8-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e951d1a8e9963ea51efd7f150450803e3b95db5939f994ad3d5edac2b6f6e2b4"}, + {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704f63b774218207b8ccc6c47fcef5340741e5d839d11d606f70af93ee78e4d4"}, + {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22283c769a7b01c8ac355d5be0715bf6929b6267619505e289f792b01304d898"}, + {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91129ff1bb0619bc1f4ad19485718cc623a2dc433dff95baadbf89405c7f6b57"}, + {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de35342190deb7b866ad6ba5cbcccb2d22c0487ee0cbb251efef0843d705f0d4"}, + {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b993b6f524d1e274a5062488a43e3f9f8764ee9745ccd8e8193df743dbe5ee61"}, + {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3026cbcf11d79095a32d9a13bbc572a458727bd5b1ca332df4a79faecd45281c"}, + {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:293352710172239bf579c90a9864d0df57340b6fd21272345222fb6371bf82b3"}, + {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d909b5a3fff619dc7e48b6b1bedc2f30ec43033ba7af32f936c10839e81b9217"}, + {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:3d370ff652323c5307d9c8e4c62efd1956fb08051b0e9210212bc51168b4ff56"}, + {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:b076da1ed19dc37788f6a934c60adf97bd02c7eea461b73730513921a85d4235"}, + {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e9941a4ada58f6218694f382e43fdd256e97615db9da135e77359da257a7168b"}, + {file = "regex-2023.8.8-cp36-cp36m-win32.whl", hash = "sha256:a8c65c17aed7e15a0c824cdc63a6b104dfc530f6fa8cb6ac51c437af52b481c7"}, + {file = "regex-2023.8.8-cp36-cp36m-win_amd64.whl", hash = "sha256:aadf28046e77a72f30dcc1ab185639e8de7f4104b8cb5c6dfa5d8ed860e57236"}, + {file = "regex-2023.8.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:423adfa872b4908843ac3e7a30f957f5d5282944b81ca0a3b8a7ccbbfaa06103"}, + {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ae594c66f4a7e1ea67232a0846649a7c94c188d6c071ac0210c3e86a5f92109"}, + {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e51c80c168074faa793685656c38eb7a06cbad7774c8cbc3ea05552d615393d8"}, + {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:09b7f4c66aa9d1522b06e31a54f15581c37286237208df1345108fcf4e050c18"}, + {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e73e5243af12d9cd6a9d6a45a43570dbe2e5b1cdfc862f5ae2b031e44dd95a8"}, + {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:941460db8fe3bd613db52f05259c9336f5a47ccae7d7def44cc277184030a116"}, + {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f0ccf3e01afeb412a1a9993049cb160d0352dba635bbca7762b2dc722aa5742a"}, + {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2e9216e0d2cdce7dbc9be48cb3eacb962740a09b011a116fd7af8c832ab116ca"}, + {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:5cd9cd7170459b9223c5e592ac036e0704bee765706445c353d96f2890e816c8"}, + {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4873ef92e03a4309b3ccd8281454801b291b689f6ad45ef8c3658b6fa761d7ac"}, + {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:239c3c2a339d3b3ddd51c2daef10874410917cd2b998f043c13e2084cb191684"}, + {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1005c60ed7037be0d9dea1f9c53cc42f836188227366370867222bda4c3c6bd7"}, + {file = "regex-2023.8.8-cp37-cp37m-win32.whl", hash = "sha256:e6bd1e9b95bc5614a7a9c9c44fde9539cba1c823b43a9f7bc11266446dd568e3"}, + {file = "regex-2023.8.8-cp37-cp37m-win_amd64.whl", hash = "sha256:9a96edd79661e93327cfeac4edec72a4046e14550a1d22aa0dd2e3ca52aec921"}, + {file = "regex-2023.8.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f2181c20ef18747d5f4a7ea513e09ea03bdd50884a11ce46066bb90fe4213675"}, + {file = "regex-2023.8.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a2ad5add903eb7cdde2b7c64aaca405f3957ab34f16594d2b78d53b8b1a6a7d6"}, + {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9233ac249b354c54146e392e8a451e465dd2d967fc773690811d3a8c240ac601"}, + {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:920974009fb37b20d32afcdf0227a2e707eb83fe418713f7a8b7de038b870d0b"}, + {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2b6c5dfe0929b6c23dde9624483380b170b6e34ed79054ad131b20203a1a63"}, + {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96979d753b1dc3b2169003e1854dc67bfc86edf93c01e84757927f810b8c3c93"}, + {file = "regex-2023.8.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ae54a338191e1356253e7883d9d19f8679b6143703086245fb14d1f20196be9"}, + {file = "regex-2023.8.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2162ae2eb8b079622176a81b65d486ba50b888271302190870b8cc488587d280"}, + {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c884d1a59e69e03b93cf0dfee8794c63d7de0ee8f7ffb76e5f75be8131b6400a"}, + {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf9273e96f3ee2ac89ffcb17627a78f78e7516b08f94dc435844ae72576a276e"}, + {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:83215147121e15d5f3a45d99abeed9cf1fe16869d5c233b08c56cdf75f43a504"}, + {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3f7454aa427b8ab9101f3787eb178057c5250478e39b99540cfc2b889c7d0586"}, + {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0640913d2c1044d97e30d7c41728195fc37e54d190c5385eacb52115127b882"}, + {file = "regex-2023.8.8-cp38-cp38-win32.whl", hash = "sha256:0c59122ceccb905a941fb23b087b8eafc5290bf983ebcb14d2301febcbe199c7"}, + {file = "regex-2023.8.8-cp38-cp38-win_amd64.whl", hash = "sha256:c12f6f67495ea05c3d542d119d270007090bad5b843f642d418eb601ec0fa7be"}, + {file = "regex-2023.8.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:82cd0a69cd28f6cc3789cc6adeb1027f79526b1ab50b1f6062bbc3a0ccb2dbc3"}, + {file = "regex-2023.8.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bb34d1605f96a245fc39790a117ac1bac8de84ab7691637b26ab2c5efb8f228c"}, + {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:987b9ac04d0b38ef4f89fbc035e84a7efad9cdd5f1e29024f9289182c8d99e09"}, + {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dd6082f4e2aec9b6a0927202c85bc1b09dcab113f97265127c1dc20e2e32495"}, + {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7eb95fe8222932c10d4436e7a6f7c99991e3fdd9f36c949eff16a69246dee2dc"}, + {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7098c524ba9f20717a56a8d551d2ed491ea89cbf37e540759ed3b776a4f8d6eb"}, + {file = "regex-2023.8.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b694430b3f00eb02c594ff5a16db30e054c1b9589a043fe9174584c6efa8033"}, + {file = "regex-2023.8.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2aeab3895d778155054abea5238d0eb9a72e9242bd4b43f42fd911ef9a13470"}, + {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:988631b9d78b546e284478c2ec15c8a85960e262e247b35ca5eaf7ee22f6050a"}, + {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:67ecd894e56a0c6108ec5ab1d8fa8418ec0cff45844a855966b875d1039a2e34"}, + {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:14898830f0a0eb67cae2bbbc787c1a7d6e34ecc06fbd39d3af5fe29a4468e2c9"}, + {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:f2200e00b62568cfd920127782c61bc1c546062a879cdc741cfcc6976668dfcf"}, + {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9691a549c19c22d26a4f3b948071e93517bdf86e41b81d8c6ac8a964bb71e5a6"}, + {file = "regex-2023.8.8-cp39-cp39-win32.whl", hash = "sha256:6ab2ed84bf0137927846b37e882745a827458689eb969028af8032b1b3dac78e"}, + {file = "regex-2023.8.8-cp39-cp39-win_amd64.whl", hash = "sha256:5543c055d8ec7801901e1193a51570643d6a6ab8751b1f7dd9af71af467538bb"}, + {file = "regex-2023.8.8.tar.gz", hash = "sha256:fcbdc5f2b0f1cd0f6a56cdb46fe41d2cce1e644e3b68832f3eeebc5fb0f7712e"}, ] [[package]] name = "requests" version = "2.31.0" description = "Python HTTP for Humans." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3550,6 +3955,7 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "rfc3339-validator" version = "0.1.4" description = "A pure python RFC3339 validator" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -3564,6 +3970,7 @@ six = "*" name = "rpds-py" version = "0.9.2" description = "Python bindings to Rust's persistent data structures (rpds)" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3666,10 +4073,26 @@ files = [ {file = "rpds_py-0.9.2.tar.gz", hash = "sha256:8d70e8f14900f2657c249ea4def963bed86a29b81f81f5b76b5a9215680de945"}, ] +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +category = "dev" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + [[package]] name = "ruamel-yaml" version = "0.17.32" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" +category = "main" optional = false python-versions = ">=3" files = [ @@ -3688,6 +4111,7 @@ jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] name = "ruamel-yaml-clib" version = "0.2.7" description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -3732,94 +4156,96 @@ files = [ [[package]] name = "ruff" -version = "0.0.283" +version = "0.0.285" description = "An extremely fast Python linter, written in Rust." +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.0.283-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:d59615628b43c40b8335af0fafd544b3a09e9891829461fa2eb0d67f00570df5"}, - {file = "ruff-0.0.283-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:742d3c09bb4272d92fcd0a01a203d837488060280c28a42461e166226651a12a"}, - {file = "ruff-0.0.283-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03622378270a37c61bb0f430c29f41bdf0699e8791d0d7548ad5745c737723fb"}, - {file = "ruff-0.0.283-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a4d36f0b3beecc01b50933795da718347ee442afa14cced5a60afe20e8335d24"}, - {file = "ruff-0.0.283-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d21b29dc63d8ec246207dd7115ec39814ca74ee0f0f7b261aa82fb9c1cd8dfcf"}, - {file = "ruff-0.0.283-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:fe095f2c3e8e557f2709945d611efd476b3eb39bdec5b258b2f88cfb8b5d136d"}, - {file = "ruff-0.0.283-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b773f1dc57e642f707ee0e8bd68a0bc5ec95441367166a276e5dfdf88b21e1bf"}, - {file = "ruff-0.0.283-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d539c73e207a13a915bde6c52ae8b2beb0b00c3b975e9e5d808fe288ea354a72"}, - {file = "ruff-0.0.283-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e43d3ab5c0bdb7b7a045411773b18ed115f0590a30c8d267c484393c6b0486ba"}, - {file = "ruff-0.0.283-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c5d72c97daa72f8914bf1b0c0ae4dbffc999e1945c291fa2d37c02ee4fa7f398"}, - {file = "ruff-0.0.283-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c32eb49ecf190a7bec0305270c864f796b362027b39a7d49c6fb120ea75e7b52"}, - {file = "ruff-0.0.283-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b1eae6990b078883c0cae60f1df0c31d2071f20afcec285baa363b9b6f7321cf"}, - {file = "ruff-0.0.283-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ad5a3042cbae1b82c3c953be77181a0934a6b02ba476fec4f177eb9c297e19ed"}, - {file = "ruff-0.0.283-py3-none-win32.whl", hash = "sha256:bd64f9775d96f35a236980ac98ed50fb5c755b227845612a873ad4f247c0cf8d"}, - {file = "ruff-0.0.283-py3-none-win_amd64.whl", hash = "sha256:28e3545ff24ae44e13da2b8fc706dd62a374cc74e4f5c1fbc8bc071ab0cc309f"}, - {file = "ruff-0.0.283-py3-none-win_arm64.whl", hash = "sha256:28732d956171f493b45c096d27f015e34fde065414330b68d59efcd0f3f67d5d"}, - {file = "ruff-0.0.283.tar.gz", hash = "sha256:6ee6928ad7b6b2b103d3b41517ff252cb81506dacbef01bab31fcfd0de39c5bb"}, + {file = "ruff-0.0.285-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:72a3a0936369b986b0e959f9090206ed3c18f9e5e439ea5b8e6867c6707aded5"}, + {file = "ruff-0.0.285-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:0d9ab6ad16742eb78919e0fba09f914f042409df40ad63423c34bb20d350162a"}, + {file = "ruff-0.0.285-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c48926156288b8ac005eb1db5e77c15e8a37309ae49d9fb6771d5cf5f777590"}, + {file = "ruff-0.0.285-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d2a60c102e7a5e147b58fc2cbea12a563c565383effc527c987ea2086a05742"}, + {file = "ruff-0.0.285-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b02aae62f922d088bb01943e1dbd861688ada13d735b78b8348a7d90121fd292"}, + {file = "ruff-0.0.285-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f572c4296d8c7ddd22c3204de4031965be524fdd1fdaaef273945932912b28c5"}, + {file = "ruff-0.0.285-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80effdf4fe69763d69eb4ab9443e186fd09e668b59fe70ba4b49f4c077d15a1b"}, + {file = "ruff-0.0.285-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5977ce304da35c263f5e082901bd7ac0bd2be845a8fcfd1a29e4d6680cddb307"}, + {file = "ruff-0.0.285-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72a087712d474fa17b915d7cb9ef807e1256182b12ddfafb105eb00aeee48d1a"}, + {file = "ruff-0.0.285-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7ce67736cd8dfe97162d1e7adfc2d9a1bac0efb9aaaff32e4042c7cde079f54b"}, + {file = "ruff-0.0.285-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5473a4c6cac34f583bff08c5f63b8def5599a0ea4dc96c0302fbd2cc0b3ecbad"}, + {file = "ruff-0.0.285-py3-none-musllinux_1_2_i686.whl", hash = "sha256:e6b1c961d608d373a032f047a20bf3c55ad05f56c32e7b96dcca0830a2a72348"}, + {file = "ruff-0.0.285-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:2933cc9631f453305399c7b8fb72b113ad76b49ae1d7103cc4afd3a423bed164"}, + {file = "ruff-0.0.285-py3-none-win32.whl", hash = "sha256:770c5eb6376de024111443022cda534fb28980a9dd3b4abc83992a8770167ba6"}, + {file = "ruff-0.0.285-py3-none-win_amd64.whl", hash = "sha256:a8c6ad6b9cd77489bf6d1510950cbbe47a843aa234adff0960bae64bd06c3b6d"}, + {file = "ruff-0.0.285-py3-none-win_arm64.whl", hash = "sha256:de44fbc6c3b25fccee473ddf851416fd4e246fc6027b2197c395b1b3b3897921"}, + {file = "ruff-0.0.285.tar.gz", hash = "sha256:45866048d1dcdcc80855998cb26c4b2b05881f9e043d2e3bfe1aa36d9a2e8f28"}, ] [[package]] name = "safetensors" -version = "0.3.1" +version = "0.3.3" description = "Fast and Safe Tensor serialization" +category = "dev" optional = false python-versions = "*" files = [ - {file = "safetensors-0.3.1-cp310-cp310-macosx_10_11_x86_64.whl", hash = "sha256:2ae9b7dd268b4bae6624729dac86deb82104820e9786429b0583e5168db2f770"}, - {file = "safetensors-0.3.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:08c85c1934682f1e2cd904d38433b53cd2a98245a7cc31f5689f9322a2320bbf"}, - {file = "safetensors-0.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba625c7af9e1c5d0d91cb83d2fba97d29ea69d4db2015d9714d24c7f6d488e15"}, - {file = "safetensors-0.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b57d5890c619ec10d9f1b6426b8690d0c9c2868a90dc52f13fae6f6407ac141f"}, - {file = "safetensors-0.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c9f562ea696d50b95cadbeb1716dc476714a87792ffe374280c0835312cbfe2"}, - {file = "safetensors-0.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c115951b3a865ece8d98ee43882f2fd0a999c0200d6e6fec24134715ebe3b57"}, - {file = "safetensors-0.3.1-cp310-cp310-win32.whl", hash = "sha256:118f8f7503ea312fc7af27e934088a1b589fb1eff5a7dea2cd1de6c71ee33391"}, - {file = "safetensors-0.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:54846eaae25fded28a7bebbb66be563cad221b4c80daee39e2f55df5e5e0266f"}, - {file = "safetensors-0.3.1-cp311-cp311-macosx_10_11_universal2.whl", hash = "sha256:5af82e10946c4822506db0f29269f43147e889054704dde994d4e22f0c37377b"}, - {file = "safetensors-0.3.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:626c86dd1d930963c8ea7f953a3787ae85322551e3a5203ac731d6e6f3e18f44"}, - {file = "safetensors-0.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12e30677e6af1f4cc4f2832546e91dbb3b0aa7d575bfa473d2899d524e1ace08"}, - {file = "safetensors-0.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d534b80bc8d39945bb902f34b0454773971fe9e5e1f2142af451759d7e52b356"}, - {file = "safetensors-0.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ddd0ddd502cf219666e7d30f23f196cb87e829439b52b39f3e7da7918c3416df"}, - {file = "safetensors-0.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997a2cc14023713f423e6d16536d55cb16a3d72850f142e05f82f0d4c76d383b"}, - {file = "safetensors-0.3.1-cp311-cp311-win32.whl", hash = "sha256:6ae9ca63d9e22f71ec40550207bd284a60a6b4916ae6ca12c85a8d86bf49e0c3"}, - {file = "safetensors-0.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:62aa7421ca455418423e35029524489480adda53e3f702453580180ecfebe476"}, - {file = "safetensors-0.3.1-cp37-cp37m-macosx_10_11_x86_64.whl", hash = "sha256:6d54b3ed367b6898baab75dfd057c24f36ec64d3938ffff2af981d56bfba2f42"}, - {file = "safetensors-0.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:262423aeda91117010f8c607889066028f680fbb667f50cfe6eae96f22f9d150"}, - {file = "safetensors-0.3.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10efe2513a8327fd628cea13167089588acc23093ba132aecfc536eb9a4560fe"}, - {file = "safetensors-0.3.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:689b3d6a7ebce70ee9438267ee55ea89b575c19923876645e927d08757b552fe"}, - {file = "safetensors-0.3.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14cd9a87bc73ce06903e9f8ee8b05b056af6f3c9f37a6bd74997a16ed36ff5f4"}, - {file = "safetensors-0.3.1-cp37-cp37m-win32.whl", hash = "sha256:a77cb39624480d5f143c1cc272184f65a296f573d61629eff5d495d2e0541d3e"}, - {file = "safetensors-0.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9eff3190bfbbb52eef729911345c643f875ca4dbb374aa6c559675cfd0ab73db"}, - {file = "safetensors-0.3.1-cp38-cp38-macosx_10_11_x86_64.whl", hash = "sha256:05cbfef76e4daa14796db1bbb52072d4b72a44050c368b2b1f6fd3e610669a89"}, - {file = "safetensors-0.3.1-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:c49061461f4a81e5ec3415070a3f135530834c89cbd6a7db7cd49e3cb9d9864b"}, - {file = "safetensors-0.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22cf7e73ca42974f098ce0cf4dd8918983700b6b07a4c6827d50c8daefca776e"}, - {file = "safetensors-0.3.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04f909442d6223ff0016cd2e1b2a95ef8039b92a558014627363a2e267213f62"}, - {file = "safetensors-0.3.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c573c5a0d5d45791ae8c179e26d74aff86e719056591aa7edb3ca7be55bc961"}, - {file = "safetensors-0.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6994043b12e717cf2a6ba69077ac41f0d3675b2819734f07f61819e854c622c7"}, - {file = "safetensors-0.3.1-cp38-cp38-win32.whl", hash = "sha256:158ede81694180a0dbba59422bc304a78c054b305df993c0c6e39c6330fa9348"}, - {file = "safetensors-0.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:afdc725beff7121ea8d39a7339f5a6abcb01daa189ea56290b67fe262d56e20f"}, - {file = "safetensors-0.3.1-cp39-cp39-macosx_10_11_x86_64.whl", hash = "sha256:cba910fcc9e5e64d32d62b837388721165e9c7e45d23bc3a38ad57694b77f40d"}, - {file = "safetensors-0.3.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:a4f7dbfe7285573cdaddd85ef6fa84ebbed995d3703ab72d71257944e384612f"}, - {file = "safetensors-0.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54aed0802f9eaa83ca7b1cbb986bfb90b8e2c67b6a4bcfe245627e17dad565d4"}, - {file = "safetensors-0.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34b75a766f3cfc99fd4c33e329b76deae63f5f388e455d863a5d6e99472fca8e"}, - {file = "safetensors-0.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a0f31904f35dc14919a145b2d7a2d8842a43a18a629affe678233c4ea90b4af"}, - {file = "safetensors-0.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcf527ecc5f58907fd9031510378105487f318cc91ecdc5aee3c7cc8f46030a8"}, - {file = "safetensors-0.3.1-cp39-cp39-win32.whl", hash = "sha256:e2f083112cf97aa9611e2a05cc170a2795eccec5f6ff837f4565f950670a9d83"}, - {file = "safetensors-0.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:5f4f614b8e8161cd8a9ca19c765d176a82b122fa3d3387b77862145bfe9b4e93"}, - {file = "safetensors-0.3.1.tar.gz", hash = "sha256:571da56ff8d0bec8ae54923b621cda98d36dcef10feb36fd492c4d0c2cd0e869"}, + {file = "safetensors-0.3.3-cp310-cp310-macosx_10_11_x86_64.whl", hash = "sha256:92e4d0c8b2836120fddd134474c5bda8963f322333941f8b9f643e5b24f041eb"}, + {file = "safetensors-0.3.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:3dcadb6153c42addc9c625a622ebde9293fabe1973f9ef31ba10fb42c16e8536"}, + {file = "safetensors-0.3.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:08f26b61e1b0a14dc959aa9d568776bd038805f611caef1de04a80c468d4a7a4"}, + {file = "safetensors-0.3.3-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:17f41344d9a075f2f21b289a49a62e98baff54b5754240ba896063bce31626bf"}, + {file = "safetensors-0.3.3-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:f1045f798e1a16a6ced98d6a42ec72936d367a2eec81dc5fade6ed54638cd7d2"}, + {file = "safetensors-0.3.3-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:eaf0e4bc91da13f21ac846a39429eb3f3b7ed06295a32321fa3eb1a59b5c70f3"}, + {file = "safetensors-0.3.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a07121f427e646a50d18c1be0fa1a2cbf6398624c31149cd7e6b35486d72189e"}, + {file = "safetensors-0.3.3-cp310-cp310-win32.whl", hash = "sha256:a85e29cbfddfea86453cc0f4889b4bcc6b9c155be9a60e27be479a34e199e7ef"}, + {file = "safetensors-0.3.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:cbc3312f134baf07334dd517341a4b470b2931f090bd9284888acb7dfaf4606f"}, + {file = "safetensors-0.3.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:d15030af39d5d30c22bcbc6d180c65405b7ea4c05b7bab14a570eac7d7d43722"}, + {file = "safetensors-0.3.3-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:f84a74cbe9859b28e3d6d7715ac1dd3097bebf8d772694098f6d42435245860c"}, + {file = "safetensors-0.3.3-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:10d637423d98ab2e6a4ad96abf4534eb26fcaf8ca3115623e64c00759374e90d"}, + {file = "safetensors-0.3.3-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:3b46f5de8b44084aff2e480874c550c399c730c84b2e8ad1bddb062c94aa14e9"}, + {file = "safetensors-0.3.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e8fdf7407dba44587ed5e79d5de3533d242648e1f2041760b21474bd5ea5c8c"}, + {file = "safetensors-0.3.3-cp311-cp311-win32.whl", hash = "sha256:7d3b744cee8d7a46ffa68db1a2ff1a1a432488e3f7a5a97856fe69e22139d50c"}, + {file = "safetensors-0.3.3-cp37-cp37m-macosx_10_11_x86_64.whl", hash = "sha256:2fff5b19a1b462c17322998b2f4b8bce43c16fe208968174d2f3a1446284ceed"}, + {file = "safetensors-0.3.3-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:41adb1d39e8aad04b16879e3e0cbcb849315999fad73bc992091a01e379cb058"}, + {file = "safetensors-0.3.3-cp37-cp37m-macosx_12_0_x86_64.whl", hash = "sha256:0f2b404250b3b877b11d34afcc30d80e7035714a1116a3df56acaca6b6c00096"}, + {file = "safetensors-0.3.3-cp37-cp37m-macosx_13_0_x86_64.whl", hash = "sha256:b43956ef20e9f4f2e648818a9e7b3499edd6b753a0f5526d4f6a6826fbee8446"}, + {file = "safetensors-0.3.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c32ee08f61cea56a5d62bbf94af95df6040c8ab574afffaeb7b44ae5da1e9e3"}, + {file = "safetensors-0.3.3-cp37-cp37m-win32.whl", hash = "sha256:351600f367badd59f7bfe86d317bb768dd8c59c1561c6fac43cafbd9c1af7827"}, + {file = "safetensors-0.3.3-cp38-cp38-macosx_10_11_x86_64.whl", hash = "sha256:8530399666748634bc0b301a6a5523756931b0c2680d188e743d16304afe917a"}, + {file = "safetensors-0.3.3-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:9d741c1f1621e489ba10aa3d135b54202684f6e205df52e219d5eecd673a80c9"}, + {file = "safetensors-0.3.3-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:0c345fd85b4d2093a5109596ff4cd9dfc2e84992e881b4857fbc4a93a3b89ddb"}, + {file = "safetensors-0.3.3-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:69ccee8d05f55cdf76f7e6c87d2bdfb648c16778ef8acfd2ecc495e273e9233e"}, + {file = "safetensors-0.3.3-cp38-cp38-macosx_13_0_arm64.whl", hash = "sha256:c08a9a4b7a4ca389232fa8d097aebc20bbd4f61e477abc7065b5c18b8202dede"}, + {file = "safetensors-0.3.3-cp38-cp38-macosx_13_0_x86_64.whl", hash = "sha256:a002868d2e3f49bbe81bee2655a411c24fa1f8e68b703dec6629cb989d6ae42e"}, + {file = "safetensors-0.3.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ab43aeeb9eadbb6b460df3568a662e6f1911ecc39387f8752afcb6a7d96c087"}, + {file = "safetensors-0.3.3-cp38-cp38-win32.whl", hash = "sha256:f2f59fce31dd3429daca7269a6b06f65e6547a0c248f5116976c3f1e9b73f251"}, + {file = "safetensors-0.3.3-cp39-cp39-macosx_10_11_x86_64.whl", hash = "sha256:59a596b3225c96d59af412385981f17dd95314e3fffdf359c7e3f5bb97730a19"}, + {file = "safetensors-0.3.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:82a16e92210a6221edd75ab17acdd468dd958ef5023d9c6c1289606cc30d1479"}, + {file = "safetensors-0.3.3-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:98a929e763a581f516373ef31983ed1257d2d0da912a8e05d5cd12e9e441c93a"}, + {file = "safetensors-0.3.3-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:12b83f1986cd16ea0454c636c37b11e819d60dd952c26978310a0835133480b7"}, + {file = "safetensors-0.3.3-cp39-cp39-macosx_13_0_arm64.whl", hash = "sha256:f439175c827c2f1bbd54df42789c5204a10983a30bc4242bc7deaf854a24f3f0"}, + {file = "safetensors-0.3.3-cp39-cp39-macosx_13_0_x86_64.whl", hash = "sha256:0085be33b8cbcb13079b3a8e131656e05b0bc5e6970530d4c24150f7afd76d70"}, + {file = "safetensors-0.3.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad3cc8006e7a86ee7c88bd2813ec59cd7cc75b03e6fa4af89b9c7b235b438d68"}, + {file = "safetensors-0.3.3-cp39-cp39-win32.whl", hash = "sha256:ab29f54c6b8c301ca05fa014728996bd83aac6e21528f893aaf8945c71f42b6d"}, + {file = "safetensors-0.3.3.tar.gz", hash = "sha256:edb7072d788c4f929d0f5735d3a2fb51e5a27f833587828583b7f5747af1a2b8"}, ] [package.extras] -all = ["black (==22.3)", "click (==8.0.4)", "flake8 (>=3.8.3)", "flax (>=0.6.3)", "h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "isort (>=5.5.4)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "numpy (>=1.21.6)", "paddlepaddle (>=2.4.1)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "setuptools-rust (>=1.5.2)", "tensorflow (>=2.11.0)", "torch (>=1.10)"] -dev = ["black (==22.3)", "click (==8.0.4)", "flake8 (>=3.8.3)", "flax (>=0.6.3)", "h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "isort (>=5.5.4)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "numpy (>=1.21.6)", "paddlepaddle (>=2.4.1)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "setuptools-rust (>=1.5.2)", "tensorflow (>=2.11.0)", "torch (>=1.10)"] -jax = ["flax (>=0.6.3)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)"] +all = ["black (==22.3)", "click (==8.0.4)", "flake8 (>=3.8.3)", "flax (>=0.6.3)", "h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "isort (>=5.5.4)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "numpy (>=1.21.6)", "paddlepaddle (>=2.4.1)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "setuptools-rust (>=1.5.2)", "tensorflow (==2.11.0)", "torch (>=1.10)"] +dev = ["black (==22.3)", "click (==8.0.4)", "flake8 (>=3.8.3)", "flax (>=0.6.3)", "h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "isort (>=5.5.4)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "numpy (>=1.21.6)", "paddlepaddle (>=2.4.1)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "setuptools-rust (>=1.5.2)", "tensorflow (==2.11.0)", "torch (>=1.10)"] +jax = ["flax (>=0.6.3)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "numpy (>=1.21.6)"] numpy = ["numpy (>=1.21.6)"] -paddlepaddle = ["paddlepaddle (>=2.4.1)"] +paddlepaddle = ["numpy (>=1.21.6)", "paddlepaddle (>=2.4.1)"] +pinned-tf = ["tensorflow (==2.11.0)"] quality = ["black (==22.3)", "click (==8.0.4)", "flake8 (>=3.8.3)", "isort (>=5.5.4)"] -tensorflow = ["tensorflow (>=2.11.0)"] +tensorflow = ["numpy (>=1.21.6)", "tensorflow (>=2.11.0)"] testing = ["h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "numpy (>=1.21.6)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "setuptools-rust (>=1.5.2)"] -torch = ["torch (>=1.10)"] +torch = ["numpy (>=1.21.6)", "torch (>=1.10)"] [[package]] name = "scikit-learn" version = "1.3.0" description = "A set of python modules for machine learning and data mining" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -3862,6 +4288,7 @@ tests = ["black (>=23.3.0)", "matplotlib (>=3.1.3)", "mypy (>=1.3)", "numpydoc ( name = "scipy" version = "1.9.3" description = "Fundamental algorithms for scientific computing in Python" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -3900,6 +4327,7 @@ test = ["asv", "gmpy2", "mpmath", "pytest", "pytest-cov", "pytest-xdist", "sciki name = "sentence-transformers" version = "2.2.2" description = "Multilingual text embeddings" +category = "dev" optional = false python-versions = ">=3.6.0" files = [ @@ -3922,6 +4350,7 @@ transformers = ">=4.6.0,<5.0.0" name = "sentencepiece" version = "0.1.99" description = "SentencePiece python wrapper" +category = "dev" optional = false python-versions = "*" files = [ @@ -3974,24 +4403,26 @@ files = [ [[package]] name = "setuptools" -version = "68.0.0" +version = "68.1.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, - {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, + {file = "setuptools-68.1.2-py3-none-any.whl", hash = "sha256:3d8083eed2d13afc9426f227b24fd1659489ec107c0e86cec2ffdde5c92e790b"}, + {file = "setuptools-68.1.2.tar.gz", hash = "sha256:3d4dfa6d95f1b101d695a6160a7626e15583af71a5f52176efa5d39a054d475d"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5,<=7.1.2)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -4003,6 +4434,7 @@ files = [ name = "sniffio" version = "1.3.0" description = "Sniff out which async library your code is running under" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4010,10 +4442,33 @@ files = [ {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] +[[package]] +name = "snoop" +version = "0.4.3" +description = "Powerful debugging tools for Python" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "snoop-0.4.3-py2.py3-none-any.whl", hash = "sha256:b7418581889ff78b29d9dc5ad4625c4c475c74755fb5cba82c693c6e32afadc0"}, + {file = "snoop-0.4.3.tar.gz", hash = "sha256:2e0930bb19ff0dbdaa6f5933f88e89ed5984210ea9f9de0e1d8231fa5c1c1f25"}, +] + +[package.dependencies] +asttokens = "*" +cheap-repr = ">=0.4.0" +executing = "*" +pygments = "*" +six = "*" + +[package.extras] +tests = ["Django", "birdseye", "littleutils", "numpy (>=1.16.5)", "pandas (>=0.24.2)", "pprintpp", "prettyprinter", "pytest", "pytest-order", "pytest-order (<=0.11.0)"] + [[package]] name = "stack-data" version = "0.6.2" description = "Extract data from python stack frames and tracebacks for informative displays" +category = "dev" optional = false python-versions = "*" files = [ @@ -4033,6 +4488,7 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] name = "starlette" version = "0.27.0" description = "The little ASGI library that shines." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4051,6 +4507,7 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyam name = "sympy" version = "1.12" description = "Computer algebra system (CAS) in Python" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -4065,6 +4522,7 @@ mpmath = ">=0.19" name = "threadpoolctl" version = "3.2.0" description = "threadpoolctl" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -4076,6 +4534,7 @@ files = [ name = "tokenizers" version = "0.13.3" description = "Fast and Customizable Tokenizers" +category = "dev" optional = false python-versions = "*" files = [ @@ -4130,6 +4589,7 @@ testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests"] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4141,6 +4601,7 @@ files = [ name = "torch" version = "2.0.0" description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" +category = "dev" optional = false python-versions = ">=3.8.0" files = [ @@ -4196,6 +4657,7 @@ opt-einsum = ["opt-einsum (>=3.3)"] name = "torchvision" version = "0.15.1" description = "image and video datasets and models for torch deep learning" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -4223,7 +4685,7 @@ files = [ [package.dependencies] numpy = "*" -pillow = ">=5.3.0,<8.3.dev0 || >=8.4.dev0" +pillow = ">=5.3.0,<8.3.0 || >=8.4.0" requests = "*" torch = "2.0.0" @@ -4232,40 +4694,42 @@ scipy = ["scipy"] [[package]] name = "tornado" -version = "6.3.2" +version = "6.3.3" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +category = "dev" optional = false python-versions = ">= 3.8" files = [ - {file = "tornado-6.3.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:c367ab6c0393d71171123ca5515c61ff62fe09024fa6bf299cd1339dc9456829"}, - {file = "tornado-6.3.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b46a6ab20f5c7c1cb949c72c1994a4585d2eaa0be4853f50a03b5031e964fc7c"}, - {file = "tornado-6.3.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2de14066c4a38b4ecbbcd55c5cc4b5340eb04f1c5e81da7451ef555859c833f"}, - {file = "tornado-6.3.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05615096845cf50a895026f749195bf0b10b8909f9be672f50b0fe69cba368e4"}, - {file = "tornado-6.3.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b17b1cf5f8354efa3d37c6e28fdfd9c1c1e5122f2cb56dac121ac61baa47cbe"}, - {file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:29e71c847a35f6e10ca3b5c2990a52ce38b233019d8e858b755ea6ce4dcdd19d"}, - {file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:834ae7540ad3a83199a8da8f9f2d383e3c3d5130a328889e4cc991acc81e87a0"}, - {file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6a0848f1aea0d196a7c4f6772197cbe2abc4266f836b0aac76947872cd29b411"}, - {file = "tornado-6.3.2-cp38-abi3-win32.whl", hash = "sha256:7efcbcc30b7c654eb6a8c9c9da787a851c18f8ccd4a5a3a95b05c7accfa068d2"}, - {file = "tornado-6.3.2-cp38-abi3-win_amd64.whl", hash = "sha256:0c325e66c8123c606eea33084976c832aa4e766b7dff8aedd7587ea44a604cdf"}, - {file = "tornado-6.3.2.tar.gz", hash = "sha256:4b927c4f19b71e627b13f3db2324e4ae660527143f9e1f2e2fb404f3a187e2ba"}, + {file = "tornado-6.3.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:502fba735c84450974fec147340016ad928d29f1e91f49be168c0a4c18181e1d"}, + {file = "tornado-6.3.3-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:805d507b1f588320c26f7f097108eb4023bbaa984d63176d1652e184ba24270a"}, + {file = "tornado-6.3.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd19ca6c16882e4d37368e0152f99c099bad93e0950ce55e71daed74045908f"}, + {file = "tornado-6.3.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ac51f42808cca9b3613f51ffe2a965c8525cb1b00b7b2d56828b8045354f76a"}, + {file = "tornado-6.3.3-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71a8db65160a3c55d61839b7302a9a400074c9c753040455494e2af74e2501f2"}, + {file = "tornado-6.3.3-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ceb917a50cd35882b57600709dd5421a418c29ddc852da8bcdab1f0db33406b0"}, + {file = "tornado-6.3.3-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:7d01abc57ea0dbb51ddfed477dfe22719d376119844e33c661d873bf9c0e4a16"}, + {file = "tornado-6.3.3-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:9dc4444c0defcd3929d5c1eb5706cbe1b116e762ff3e0deca8b715d14bf6ec17"}, + {file = "tornado-6.3.3-cp38-abi3-win32.whl", hash = "sha256:65ceca9500383fbdf33a98c0087cb975b2ef3bfb874cb35b8de8740cf7f41bd3"}, + {file = "tornado-6.3.3-cp38-abi3-win_amd64.whl", hash = "sha256:22d3c2fa10b5793da13c807e6fc38ff49a4f6e1e3868b0a6f4164768bb8e20f5"}, + {file = "tornado-6.3.3.tar.gz", hash = "sha256:e7d8db41c0181c80d76c982aacc442c0783a2c54d6400fe028954201a2e032fe"}, ] [[package]] name = "tqdm" -version = "4.65.0" +version = "4.66.1" description = "Fast, Extensible Progress Meter" +category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.65.0-py3-none-any.whl", hash = "sha256:c4f53a17fe37e132815abceec022631be8ffe1b9381c2e6e30aa70edc99e9671"}, - {file = "tqdm-4.65.0.tar.gz", hash = "sha256:1871fb68a86b8fb3b59ca4cdd3dcccbc7e6d613eeed31f4c332531977b89beb5"}, + {file = "tqdm-4.66.1-py3-none-any.whl", hash = "sha256:d302b3c5b53d47bce91fea46679d9c3c6508cf6332229aa1e7d8653723793386"}, + {file = "tqdm-4.66.1.tar.gz", hash = "sha256:d88e651f9db8d8551a62556d3cff9e3034274ca5d66e93197cf2490e2dcb69c7"}, ] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] -dev = ["py-make (>=0.1.0)", "twine", "wheel"] +dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] @@ -4274,6 +4738,7 @@ telegram = ["requests"] name = "traitlets" version = "5.9.0" description = "Traitlets Python configuration system" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4287,18 +4752,19 @@ test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"] [[package]] name = "transformers" -version = "4.31.0" +version = "4.32.0" description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow" +category = "dev" optional = false python-versions = ">=3.8.0" files = [ - {file = "transformers-4.31.0-py3-none-any.whl", hash = "sha256:8487aab0195ce1c2a5ae189305118b9720daddbc7b688edb09ccd79e3b149f6b"}, - {file = "transformers-4.31.0.tar.gz", hash = "sha256:4302fba920a1c24d3a429a29efff6a63eac03f3f3cf55b55927fc795d01cb273"}, + {file = "transformers-4.32.0-py3-none-any.whl", hash = "sha256:32d8adf0ed76285508e7fd66657b4448ec1f882599ae6bf6f9c36bd7bf798402"}, + {file = "transformers-4.32.0.tar.gz", hash = "sha256:ca510f9688d2fe7347abbbfbd13f2f6dcd3c8349870c8d0ed98beed5f579b354"}, ] [package.dependencies] filelock = "*" -huggingface-hub = ">=0.14.1,<1.0" +huggingface-hub = ">=0.15.1,<1.0" numpy = ">=1.17" packaging = ">=20.0" pyyaml = ">=5.1" @@ -4311,18 +4777,18 @@ tqdm = ">=4.27" [package.extras] accelerate = ["accelerate (>=0.20.3)"] agents = ["Pillow (<10.0.0)", "accelerate (>=0.20.3)", "datasets (!=2.5.0)", "diffusers", "opencv-python", "sentencepiece (>=0.1.91,!=0.1.92)", "torch (>=1.9,!=1.12.0)"] -all = ["Pillow (<10.0.0)", "accelerate (>=0.20.3)", "av (==9.2.0)", "codecarbon (==1.2.0)", "decord (==0.6.0)", "flax (>=0.4.1,<=0.7.0)", "jax (>=0.2.8,!=0.3.2,<=0.4.13)", "jaxlib (>=0.1.65,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune]", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>=2.6,<2.14)", "tensorflow-text (<2.14)", "tf2onnx", "timm", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.9,!=1.12.0)", "torchaudio", "torchvision"] +all = ["Pillow (<10.0.0)", "accelerate (>=0.20.3)", "av (==9.2.0)", "codecarbon (==1.2.0)", "decord (==0.6.0)", "flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune]", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>=2.6,<2.14)", "tensorflow-text (<2.14)", "tf2onnx", "timm", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.9,!=1.12.0)", "torchaudio", "torchvision"] audio = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] codecarbon = ["codecarbon (==1.2.0)"] deepspeed = ["accelerate (>=0.20.3)", "deepspeed (>=0.9.3)"] deepspeed-testing = ["GitPython (<3.1.19)", "accelerate (>=0.20.3)", "beautifulsoup4", "black (>=23.1,<24.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "deepspeed (>=0.9.3)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "hf-doc-builder (>=0.3.0)", "nltk", "optuna", "parameterized", "protobuf", "psutil", "pytest (>=7.2.0)", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "timeout-decorator"] -dev = ["GitPython (<3.1.19)", "Pillow (<10.0.0)", "accelerate (>=0.20.3)", "av (==9.2.0)", "beautifulsoup4", "black (>=23.1,<24.0)", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "decord (==0.6.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "flax (>=0.4.1,<=0.7.0)", "fugashi (>=1.0)", "hf-doc-builder", "hf-doc-builder (>=0.3.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "jax (>=0.2.8,!=0.3.2,<=0.4.13)", "jaxlib (>=0.1.65,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "nltk", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pytest (>=7.2.0)", "pytest-timeout", "pytest-xdist", "ray[tune]", "rhoknp (>=1.1.0,<1.3.1)", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (>=0.0.241,<=0.0.259)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorflow (>=2.6,<2.14)", "tensorflow-text (<2.14)", "tf2onnx", "timeout-decorator", "timm", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.9,!=1.12.0)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] +dev = ["GitPython (<3.1.19)", "Pillow (<10.0.0)", "accelerate (>=0.20.3)", "av (==9.2.0)", "beautifulsoup4", "black (>=23.1,<24.0)", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "decord (==0.6.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "flax (>=0.4.1,<=0.7.0)", "fugashi (>=1.0)", "hf-doc-builder", "hf-doc-builder (>=0.3.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "nltk", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pytest (>=7.2.0)", "pytest-timeout", "pytest-xdist", "ray[tune]", "rhoknp (>=1.1.0,<1.3.1)", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (>=0.0.241,<=0.0.259)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorflow (>=2.6,<2.14)", "tensorflow-text (<2.14)", "tf2onnx", "timeout-decorator", "timm", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.9,!=1.12.0)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] dev-tensorflow = ["GitPython (<3.1.19)", "Pillow (<10.0.0)", "beautifulsoup4", "black (>=23.1,<24.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "hf-doc-builder", "hf-doc-builder (>=0.3.0)", "isort (>=5.5.4)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "nltk", "onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pytest (>=7.2.0)", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (>=0.0.241,<=0.0.259)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorflow (>=2.6,<2.14)", "tensorflow-text (<2.14)", "tf2onnx", "timeout-decorator", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "urllib3 (<2.0.0)"] dev-torch = ["GitPython (<3.1.19)", "Pillow (<10.0.0)", "accelerate (>=0.20.3)", "beautifulsoup4", "black (>=23.1,<24.0)", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fugashi (>=1.0)", "hf-doc-builder", "hf-doc-builder (>=0.3.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "kenlm", "librosa", "nltk", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pytest (>=7.2.0)", "pytest-timeout", "pytest-xdist", "ray[tune]", "rhoknp (>=1.1.0,<1.3.1)", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (>=0.0.241,<=0.0.259)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "timeout-decorator", "timm", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.9,!=1.12.0)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] -docs = ["Pillow (<10.0.0)", "accelerate (>=0.20.3)", "av (==9.2.0)", "codecarbon (==1.2.0)", "decord (==0.6.0)", "flax (>=0.4.1,<=0.7.0)", "hf-doc-builder", "jax (>=0.2.8,!=0.3.2,<=0.4.13)", "jaxlib (>=0.1.65,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune]", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>=2.6,<2.14)", "tensorflow-text (<2.14)", "tf2onnx", "timm", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.9,!=1.12.0)", "torchaudio", "torchvision"] +docs = ["Pillow (<10.0.0)", "accelerate (>=0.20.3)", "av (==9.2.0)", "codecarbon (==1.2.0)", "decord (==0.6.0)", "flax (>=0.4.1,<=0.7.0)", "hf-doc-builder", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune]", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>=2.6,<2.14)", "tensorflow-text (<2.14)", "tf2onnx", "timm", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.9,!=1.12.0)", "torchaudio", "torchvision"] docs-specific = ["hf-doc-builder"] fairscale = ["fairscale (>0.3)"] -flax = ["flax (>=0.4.1,<=0.7.0)", "jax (>=0.2.8,!=0.3.2,<=0.4.13)", "jaxlib (>=0.1.65,<=0.4.13)", "optax (>=0.0.8,<=0.1.4)"] +flax = ["flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "optax (>=0.0.8,<=0.1.4)"] flax-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] ftfy = ["ftfy"] integrations = ["optuna", "ray[tune]", "sigopt"] @@ -4350,7 +4816,7 @@ tokenizers = ["tokenizers (>=0.11.1,!=0.11.3,<0.14)"] torch = ["accelerate (>=0.20.3)", "torch (>=1.9,!=1.12.0)"] torch-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] torch-vision = ["Pillow (<10.0.0)", "torchvision"] -torchhub = ["filelock", "huggingface-hub (>=0.14.1,<1.0)", "importlib-metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.9,!=1.12.0)", "tqdm (>=4.27)"] +torchhub = ["filelock", "huggingface-hub (>=0.15.1,<1.0)", "importlib-metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.9,!=1.12.0)", "tqdm (>=4.27)"] video = ["av (==9.2.0)", "decord (==0.6.0)"] vision = ["Pillow (<10.0.0)"] @@ -4358,6 +4824,7 @@ vision = ["Pillow (<10.0.0)"] name = "triton" version = "2.0.0" description = "A language and compiler for custom Deep Learning operations" +category = "dev" optional = false python-versions = "*" files = [ @@ -4395,6 +4862,7 @@ tutorials = ["matplotlib", "pandas", "tabulate"] name = "typing-extensions" version = "4.7.1" description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4406,6 +4874,7 @@ files = [ name = "tzdata" version = "2023.3" description = "Provider of IANA time zone data" +category = "dev" optional = false python-versions = ">=2" files = [ @@ -4413,10 +4882,43 @@ files = [ {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, ] +[[package]] +name = "ucall" +version = "0.5.1" +description = "Up to 100x Faster FastAPI. JSON-RPC with io_uring, SIMD-acceleration, and pure CPython bindings" +category = "dev" +optional = false +python-versions = ">=3.9" +files = [ + {file = "ucall-0.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15cd5ca5b15d198775d0b80532f41579f4ae7bf3693b86b4ac5f5ff1ed0be1d8"}, + {file = "ucall-0.5.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:577018de6f01651ba53ac7c8867ddd9b92cc79f98fbb4c0513fcc22a8d58e007"}, + {file = "ucall-0.5.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:a18ac9f297ef08e928b59c55ad75cba34511e7d4816af2fcb986043a8ecf719d"}, + {file = "ucall-0.5.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef678d300edcb8d1d6d3af65b63034f2b09873b75b9fcb323eaec0d824cadff7"}, + {file = "ucall-0.5.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5f1d3709b4a977c9bdefba3098edd8bc8ae37855f40ccf29f6580f195d7e2b09"}, + {file = "ucall-0.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:fcc1df86e1129bacdbb17662892e02f20189e74f827c0162da56fb2490df87ed"}, + {file = "ucall-0.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:406131c6d0c74035ee7b13131e2e674ca571607bc4c7b3f47c4758f9a5b8724d"}, + {file = "ucall-0.5.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:9a61bf176ce73df006507bdf2a30098b3519e534969886b2caf3d2dc479fda0c"}, + {file = "ucall-0.5.1-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:29bfcd675c458d23c492e86cf703443f1f4ba266a92880bd943de1ead8e27ddd"}, + {file = "ucall-0.5.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:26c79e67dbc7ecf6d925c8ce2f291db281961a8db13d3470f4dafb1c32d72a4b"}, + {file = "ucall-0.5.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fba80f2094597dfa182da47cd5a71a5420da2d171aa5067a7a6efbc196eeb86e"}, + {file = "ucall-0.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:562632065fd36968ec92cc8b51033276449610465ef54916ec05bf9505be6b8b"}, + {file = "ucall-0.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2d9b1cb358b6967023dcdbf1e9744501a687be75b3dc9b5fd5b3f177f18714a0"}, + {file = "ucall-0.5.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:e99eb1c64837b596281a41a641404bcb618c5037d75d652fc1f2a9b8c38aaed1"}, + {file = "ucall-0.5.1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:9b3e346a93c1ff7d2eef9cf03d7f515be4fc6fed195939cfa37cda6ac36a2514"}, + {file = "ucall-0.5.1-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:358ae439f17fd05e70baa8809b38c5ff1146cc3fe77e91d5d50288d9154484af"}, + {file = "ucall-0.5.1-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6012d8d374535b87931dc48cd8ad34529e0e8ae5f30f8a301a8784c39fe7d013"}, + {file = "ucall-0.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:1a96b1df8d1e74afaca42a9b58d6b27a9b4a8444f83e17c0dfd76f9b4c3f3b20"}, +] + +[package.dependencies] +numpy = "*" +pillow = "*" + [[package]] name = "ujson" version = "5.8.0" description = "Ultra fast JSON encoder and decoder for Python" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -4487,6 +4989,7 @@ files = [ name = "urllib3" version = "1.26.16" description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -4499,10 +5002,55 @@ brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +[[package]] +name = "usearch" +version = "1.1.1" +description = "Smaller & Faster Single-File Vector Search Engine from Unum" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "usearch-1.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1a68a223be42573a036c76e516f30c076b16dd38d8cfe9ca79a1cc0e4d60e8a8"}, + {file = "usearch-1.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bf2d8246a23e5a232a9389f4efd25e0bd10624a96f0f95d0cd415945a6be84ee"}, + {file = "usearch-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8692dbd0b66874e6b01e2dd7c50216347f52d175bc7e072712a5e0646ec9295b"}, + {file = "usearch-1.1.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5be4ede1592b056714e3f529cd17e69907364e3c0ee6eee5cf1498f946f0c2ec"}, + {file = "usearch-1.1.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6230b0583779f43dba2da3865dd398f8cf88daa6427d60afff3348bbdea6652f"}, + {file = "usearch-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:eb731f74a7a8208e0fa5b04d9488d1dfc177e253b9c761687cb51d38138d5b93"}, + {file = "usearch-1.1.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7fbb82767109d03c807678664ab02383e31db778adb1d3602da347613fdbf15e"}, + {file = "usearch-1.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7e1f81a92f3fcc091400f2997b7b12b6d53f7abf4edf87e8a17b5eede350431"}, + {file = "usearch-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7027cdc4c733d6926fc2a58e77cb9b14528a3f585b5d738ad6c5f14dc6e027ca"}, + {file = "usearch-1.1.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:e1bb1b238390cc990d051a07fe2a0f173e60bc9e82b7f0f34eb9ddf5bef2b1f8"}, + {file = "usearch-1.1.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:42711bad96f487f5d31ac7be1685797fb4b26904328bc855182e8d6c83b9e538"}, + {file = "usearch-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:ebc34eb7cf0b9f7e52b0f176c48d374f19668ad9653533bdd2e5be1463435d66"}, + {file = "usearch-1.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:36059015e49f9ea303a1d823b80113ce96833330563a54ceac447e4218d63a2c"}, + {file = "usearch-1.1.1-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:918830e1876064227f0a6a17bd4f92c985d8df4856b0370c7729b6e43721b3cc"}, + {file = "usearch-1.1.1-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:a59dfd5d848c484448470e613514f636cf42acac3eab1a9fb9b98d9511de2a97"}, + {file = "usearch-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:70d1f5148a1032da5b0e6651371d29643bf302c0d24a2896d6969d504fccac15"}, + {file = "usearch-1.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5112ebd545ad63b7a63d68838da8a56cfcd313c9ade86bfbe30061c946cbc5dc"}, + {file = "usearch-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0f1e58d11d9dfe1d499e86c108a21f7deb85fe4f40e54b042e057b5df5ead036"}, + {file = "usearch-1.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fbd08ecbf2225f16b9f4b8190cff53de372baddc173e5ba7854425392552013b"}, + {file = "usearch-1.1.1-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:b8040aa9f448ddfaac5528ec1a1c216351cf7a17af35ddf169b239282f7fa4c4"}, + {file = "usearch-1.1.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:479fcf8b884d1a822b83c7cfb853c090f0db4386e86ef790f2c820f96de70140"}, + {file = "usearch-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:b0338b054fde34ab0a42a786bae43ae1793412995f6a87122850fc0318cb5955"}, + {file = "usearch-1.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:12121e7ac82868877ae9e6b513a61c1113afc8a28d793f9350719ef94ac33091"}, + {file = "usearch-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:33255b29bd7fc1feb6584887f6489bf9f896bd9d79b9ce423ff7185b2c2059e5"}, + {file = "usearch-1.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9bb5464473e8ceeef6237285fc0e86a0b77a75304397db3365cb011761fd6abe"}, + {file = "usearch-1.1.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6d84c5771aa37584a335f4b3392185782da785733aab4c3a4ae9949434cbe679"}, + {file = "usearch-1.1.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:02167b0c03062a6d28926535ee862401669b6d6f303e99d2cd1232dc610d2a25"}, + {file = "usearch-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:8300ba31fcc3ace452429781f517273e1297a5881cff629e2f1c6a3a411a48fc"}, +] + +[package.dependencies] +numpy = "*" +pandas = "*" +tqdm = "*" +ucall = {version = "*", markers = "python_version >= \"3.9\""} + [[package]] name = "uvicorn" version = "0.23.2" description = "The lightning-fast ASGI server." +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -4518,7 +5066,7 @@ httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standar python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} -uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""} watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} @@ -4529,6 +5077,7 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", name = "uvloop" version = "0.17.0" description = "Fast implementation of asyncio event loop on top of libuv" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4571,29 +5120,26 @@ test = ["Cython (>=0.29.32,<0.30.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "my [[package]] name = "validators" -version = "0.20.0" -description = "Python Data Validation for Humans™." +version = "0.21.0" +description = "Python Data Validation for Humans™" +category = "dev" optional = false -python-versions = ">=3.4" +python-versions = ">=3.8,<4.0" files = [ - {file = "validators-0.20.0.tar.gz", hash = "sha256:24148ce4e64100a2d5e267233e23e7afeb55316b47d30faae7eb6e7292bc226a"}, + {file = "validators-0.21.0-py3-none-any.whl", hash = "sha256:3470db6f2384c49727ee319afa2e97aec3f8fad736faa6067e0fd7f9eaf2c551"}, + {file = "validators-0.21.0.tar.gz", hash = "sha256:245b98ab778ed9352a7269c6a8f6c2a839bed5b2a7e3e60273ce399d247dd4b3"}, ] -[package.dependencies] -decorator = ">=3.4.0" - -[package.extras] -test = ["flake8 (>=2.4.0)", "isort (>=4.2.2)", "pytest (>=2.2.3)"] - [[package]] name = "virtualenv" -version = "20.24.2" +version = "20.24.3" description = "Virtual Python Environment builder" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.24.2-py3-none-any.whl", hash = "sha256:43a3052be36080548bdee0b42919c88072037d50d56c28bd3f853cbe92b953ff"}, - {file = "virtualenv-20.24.2.tar.gz", hash = "sha256:fd8a78f46f6b99a67b7ec5cf73f92357891a7b3a40fd97637c27f854aae3b9e0"}, + {file = "virtualenv-20.24.3-py3-none-any.whl", hash = "sha256:95a6e9398b4967fbcb5fef2acec5efaf9aa4972049d9ae41f95e0972a683fd02"}, + {file = "virtualenv-20.24.3.tar.gz", hash = "sha256:e5c3b4ce817b0b328af041506a2a299418c98747c4b1e68cb7527e74ced23efc"}, ] [package.dependencies] @@ -4609,6 +5155,7 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess name = "watchfiles" version = "0.19.0" description = "Simple, modern and high performance file watching and code reload in python." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4643,6 +5190,7 @@ anyio = ">=3.0.0" name = "wcwidth" version = "0.2.6" description = "Measures the displayed width of unicode strings in a terminal" +category = "dev" optional = false python-versions = "*" files = [ @@ -4652,13 +5200,14 @@ files = [ [[package]] name = "weaviate-client" -version = "3.22.1" +version = "3.23.0" description = "A python native Weaviate client" +category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "weaviate-client-3.22.1.tar.gz", hash = "sha256:aff61bd3f5d74df20a62328443e3aa9c860d5330fdfb19c4d8ddc44cb604032f"}, - {file = "weaviate_client-3.22.1-py3-none-any.whl", hash = "sha256:01843a4899a227300e570409e77628e9d1b28476313f94943c37aee3f75112e1"}, + {file = "weaviate-client-3.23.0.tar.gz", hash = "sha256:3ffd7f1460c9e32755d84d4f5fc63dfc0bd990dbe2c3dc20d5c68119d467680e"}, + {file = "weaviate_client-3.23.0-py3-none-any.whl", hash = "sha256:3d3bb75c1d96b2b71e213c5eb885ae3e3f42e4304955383c467d100187d9ff8e"}, ] [package.dependencies] @@ -4674,6 +5223,7 @@ grpc = ["grpcio", "grpcio-tools"] name = "websockets" version = "11.0.3" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4751,13 +5301,14 @@ files = [ [[package]] name = "werkzeug" -version = "2.3.6" +version = "2.3.7" description = "The comprehensive WSGI web application library." +category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "Werkzeug-2.3.6-py3-none-any.whl", hash = "sha256:935539fa1413afbb9195b24880778422ed620c0fc09670945185cce4d91a8890"}, - {file = "Werkzeug-2.3.6.tar.gz", hash = "sha256:98c774df2f91b05550078891dee5f0eb0cb797a522c757a2452b9cee5b202330"}, + {file = "werkzeug-2.3.7-py3-none-any.whl", hash = "sha256:effc12dba7f3bd72e605ce49807bbe692bd729c3bb122a3b91747a6ae77df528"}, + {file = "werkzeug-2.3.7.tar.gz", hash = "sha256:2b8c0e447b4b9dbcc85dd97b6eeb4dcbaf6c8b6c3be0bd654e25553e0a2157d8"}, ] [package.dependencies] @@ -4768,13 +5319,14 @@ watchdog = ["watchdog (>=2.3)"] [[package]] name = "wheel" -version = "0.41.0" +version = "0.41.2" description = "A built-package format for Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "wheel-0.41.0-py3-none-any.whl", hash = "sha256:7e9be3bbd0078f6147d82ed9ed957e323e7708f57e134743d2edef3a7b7972a9"}, - {file = "wheel-0.41.0.tar.gz", hash = "sha256:55a0f0a5a84869bce5ba775abfd9c462e3a6b1b7b7ec69d72c0b83d673a5114d"}, + {file = "wheel-0.41.2-py3-none-any.whl", hash = "sha256:75909db2664838d015e3d9139004ee16711748a52c8f336b52882266540215d8"}, + {file = "wheel-0.41.2.tar.gz", hash = "sha256:0c5ac5ff2afb79ac23ab82bab027a0be7b5dbcf2e54dc50efe4bf507de1f7985"}, ] [package.extras] @@ -4784,6 +5336,7 @@ test = ["pytest (>=6.0.0)", "setuptools (>=65)"] name = "win32-setctime" version = "1.1.0" description = "A small Python utility to set file creation time on Windows" +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -4798,6 +5351,7 @@ dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] name = "yarl" version = "1.9.2" description = "Yet another URL library" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4885,6 +5439,7 @@ multidict = ">=4.0" name = "zipp" version = "3.16.2" description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -4899,4 +5454,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "63ba02af3b5d008f0f286564324ddd8d3bef95388b4198fd3970e6b9c86f2128" +content-hash = "36b5293ce1687c9cad6e548c73b691aff0015c2de8e3abeeaa6f6a1edfd92a1b" diff --git a/python/pyproject.toml b/python/pyproject.toml index ab5761c6d732..64c8f4a76fe7 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "semantic-kernel" -version = "0.3.8.dev" +version = "0.3.9.dev" description = "" authors = ["Microsoft "] readme = "pip/README.md" @@ -22,8 +22,13 @@ pre-commit = "3.3.3" black = {version = "23.7.0", allow-prereleases = true} ipykernel = "^6.21.1" pytest = "7.4.0" -ruff = "0.0.283" +ruff = "0.0.285" pytest-asyncio = "0.21.1" +snoop = "0.4.3" + +[tool.poetry.group.google_palm.dependencies] +google-generativeai = { version = "^0.1.0", markers = "python_version >= '3.9'" } +grpcio-status = { version = "^1.53.0", markers = "python_version >= '3.9'" } [tool.poetry.group.hugging_face.dependencies] transformers = "^4.28.1" @@ -52,10 +57,14 @@ psycopg = "^3.1.9" psycopg-binary = "^3.1.9" [tool.poetry.group.azure_cognitive_search.dependencies] -azure-search-documents = {version = "11.4.0b6", allow-prereleases = true} +azure-search-documents = {version = "11.4.0b8", allow-prereleases = true} azure-core = "^1.28.0" azure-identity = "^1.13.0" +[tool.poetry.group.usearch.dependencies] +usearch = "^1.1.1" +pyarrow = "^12.0.1" + [tool.isort] profile = "black" @@ -66,4 +75,3 @@ line-length = 120 [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" - diff --git a/python/samples/kernel-syntax-examples/bing_search_skill.py b/python/samples/kernel-syntax-examples/bing_search_skill.py index f5369c5baa9f..af1107d36e4d 100644 --- a/python/samples/kernel-syntax-examples/bing_search_skill.py +++ b/python/samples/kernel-syntax-examples/bing_search_skill.py @@ -3,7 +3,7 @@ from dotenv import load_dotenv import semantic_kernel as sk -from semantic_kernel.connectors.ai.open_ai import OpenAITextCompletion +from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion from semantic_kernel.connectors.search_engine import BingConnector from semantic_kernel.core_skills import WebSearchEngineSkill @@ -13,8 +13,8 @@ async def main(): kernel = sk.Kernel() api_key, org_id = sk.openai_settings_from_dot_env() - kernel.add_text_completion_service( - "dv", OpenAITextCompletion("text-davinci-003", api_key, org_id) + kernel.add_chat_service( + "chat-gpt", OpenAIChatCompletion("gpt-3.5-turbo", api_key, org_id) ) connector = BingConnector(api_key=os.getenv("BING_API_KEY")) web_skill = kernel.import_skill(WebSearchEngineSkill(connector), "WebSearch") diff --git a/python/samples/kernel-syntax-examples/chat.py b/python/samples/kernel-syntax-examples/chat.py index 179c4d7166e0..ee149b00b383 100644 --- a/python/samples/kernel-syntax-examples/chat.py +++ b/python/samples/kernel-syntax-examples/chat.py @@ -18,8 +18,8 @@ kernel = sk.Kernel() api_key, org_id = sk.openai_settings_from_dot_env() -kernel.add_text_completion_service( - "davinci-003", sk_oai.OpenAITextCompletion("text-davinci-003", api_key, org_id) +kernel.add_chat_service( + "chat-gpt", sk_oai.OpenAIChatCompletion("gpt-3.5-turbo", api_key, org_id) ) prompt_config = sk.PromptTemplateConfig.from_completion_parameters( diff --git a/python/samples/kernel-syntax-examples/google_palm_chat.py b/python/samples/kernel-syntax-examples/google_palm_chat.py new file mode 100644 index 000000000000..36d2752d0682 --- /dev/null +++ b/python/samples/kernel-syntax-examples/google_palm_chat.py @@ -0,0 +1,48 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio + +import semantic_kernel as sk +import semantic_kernel.connectors.ai.google_palm as sk_gp +from semantic_kernel.connectors.ai.chat_request_settings import ChatRequestSettings + + +async def chat_request_example(api_key): + palm_chat_completion = sk_gp.GooglePalmChatCompletion( + "models/chat-bison-001", api_key + ) + settings = ChatRequestSettings() + settings.temperature = 1 + + chat_messages = list() + user_mssg = "I'm planning a vacation. Which are some must-visit places in Europe?" + chat_messages.append(("user", user_mssg)) + answer = await palm_chat_completion.complete_chat_async(chat_messages, settings) + chat_messages.append(("assistant", str(answer))) + user_mssg = "Where should I go in France?" + chat_messages.append(("user", user_mssg)) + answer = await palm_chat_completion.complete_chat_async(chat_messages, settings) + chat_messages.append(("assistant", str(answer))) + + context_vars = sk.ContextVariables() + context_vars["chat_history"] = "" + context_vars["chat_bot_ans"] = "" + for role, mssg in chat_messages: + if role == "user": + context_vars["chat_history"] += f"User:> {mssg}\n" + elif role == "assistant": + context_vars["chat_history"] += f"ChatBot:> {mssg}\n" + context_vars["chat_bot_ans"] += f"{mssg}\n" + + return context_vars + + +async def main() -> None: + api_key = sk.google_palm_settings_from_dot_env() + chat = await chat_request_example(api_key) + print(chat["chat_history"]) + return + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/kernel-syntax-examples/google_palm_chat_with_memory.py b/python/samples/kernel-syntax-examples/google_palm_chat_with_memory.py new file mode 100644 index 000000000000..13e4c71afa3f --- /dev/null +++ b/python/samples/kernel-syntax-examples/google_palm_chat_with_memory.py @@ -0,0 +1,142 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +from typing import Tuple + +import semantic_kernel as sk +import semantic_kernel.connectors.ai.google_palm as sk_gp + +kernel = sk.Kernel() +apikey = sk.google_palm_settings_from_dot_env() +palm_text_embed = sk_gp.GooglePalmTextEmbedding("models/embedding-gecko-001", apikey) +kernel.add_text_embedding_generation_service("gecko", palm_text_embed) +palm_chat_completion = sk_gp.GooglePalmChatCompletion("models/chat-bison-001", apikey) +kernel.add_chat_service("models/chat-bison-001", palm_chat_completion) +kernel.register_memory_store(memory_store=sk.memory.VolatileMemoryStore()) +kernel.import_skill(sk.core_skills.TextMemorySkill()) + + +async def populate_memory(kernel: sk.Kernel) -> None: + # Add some documents to the semantic memory + await kernel.memory.save_information_async( + "aboutMe", id="info1", text="My name is Andrea" + ) + await kernel.memory.save_information_async( + "aboutMe", id="info2", text="I currently work as a tour guide" + ) + await kernel.memory.save_information_async( + "aboutMe", id="info3", text="My favorite hobby is hiking" + ) + await kernel.memory.save_information_async( + "aboutMe", id="info4", text="I visitied Iceland last year." + ) + await kernel.memory.save_information_async( + "aboutMe", id="info5", text="My family is from New York" + ) + + +async def search_memory_examples(kernel: sk.Kernel) -> None: + questions = [ + "what's my name", + "what is my favorite hobby?", + "where's my family from?", + "where did I travel last year?", + "what do I do for work", + ] + + for question in questions: + print(f"Question: {question}") + result = await kernel.memory.search_async("aboutMe", question) + print(f"Answer: {result}\n") + + +async def setup_chat_with_memory( + kernel: sk.Kernel, +) -> Tuple[sk.SKFunctionBase, sk.SKContext]: + """ + When using Google PaLM to chat with memories, a chat prompt template is + essential; otherwise, the kernel will send text prompts to the Google PaLM + chat service. Unfortunately, when a text prompt includes memory, chat + history, and the user's current message, PaLM often struggles to comprehend + the user's message. To address this issue, the prompt containing memory is + incorporated into the chat prompt template as a system message. + Note that this is only an issue for the chat service; the text service + does not require a chat prompt template. + """ + sk_prompt = """ + ChatBot can have a conversation with you about any topic. + It can give explicit instructions or say 'I don't know' if + it does not have an answer. + + Information about me, from previous conversations: + - {{$fact1}} {{recall $fact1}} + - {{$fact2}} {{recall $fact2}} + - {{$fact3}} {{recall $fact3}} + - {{$fact4}} {{recall $fact4}} + - {{$fact5}} {{recall $fact5}} + + """.strip() + + prompt_config = sk.PromptTemplateConfig.from_completion_parameters( + max_tokens=2000, temperature=0.7, top_p=0.8 + ) + prompt_template = sk.ChatPromptTemplate( # Create the chat prompt template + "{{$user_input}}", kernel.prompt_template_engine, prompt_config + ) + prompt_template.add_system_message(sk_prompt) # Add the memory as a system message + function_config = sk.SemanticFunctionConfig(prompt_config, prompt_template) + chat_func = kernel.register_semantic_function( + None, "ChatWithMemory", function_config + ) + + context = kernel.create_new_context() + context["fact1"] = "what is my name?" + context["fact2"] = "what is my favorite hobby?" + context["fact3"] = "where's my family from?" + context["fact4"] = "where did I travel last year?" + context["fact5"] = "what do I do for work?" + + context[sk.core_skills.TextMemorySkill.COLLECTION_PARAM] = "aboutMe" + context[sk.core_skills.TextMemorySkill.RELEVANCE_PARAM] = 0.6 + + context["chat_history"] = "" + + return chat_func, context + + +async def chat( + kernel: sk.Kernel, chat_func: sk.SKFunctionBase, context: sk.SKContext +) -> bool: + try: + user_input = input("User:> ") + context["user_input"] = user_input + except KeyboardInterrupt: + print("\n\nExiting chat...") + return False + except EOFError: + print("\n\nExiting chat...") + return False + + if user_input == "exit": + print("\n\nExiting chat...") + return False + + answer = await kernel.run_async(chat_func, input_vars=context.variables) + context["chat_history"] += f"\nUser:> {user_input}\nChatBot:> {answer}\n" + + print(f"ChatBot:> {answer}") + return True + + +async def main() -> None: + await populate_memory(kernel) + await search_memory_examples(kernel) + chat_func, context = await setup_chat_with_memory(kernel) + print("Begin chatting (type 'exit' to exit):\n") + chatting = True + while chatting: + chatting = await chat(kernel, chat_func, context) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/kernel-syntax-examples/google_palm_chat_with_skill.py b/python/samples/kernel-syntax-examples/google_palm_chat_with_skill.py new file mode 100644 index 000000000000..042495699446 --- /dev/null +++ b/python/samples/kernel-syntax-examples/google_palm_chat_with_skill.py @@ -0,0 +1,79 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio + +import semantic_kernel as sk +import semantic_kernel.connectors.ai.google_palm as sk_gp + +""" +System messages prime the assistant with different personalities or behaviors. +The system message is added to the prompt template, and a chat history can be +added as well to provide further context. +A system message can only be used once at the start of the conversation, and +conversation history persists with the instance of GooglePalmChatCompletion. To +overwrite the system message and start a new conversation, you must create a new +instance of GooglePalmChatCompletion. +Sometimes, PaLM struggles to use the information in the prompt template. In this +case, it is recommended to experiment with the messages in the prompt template +or ask different questions. +""" + +system_message = """ +You are a chat bot. Your name is Blackbeard +and you speak in the style of a swashbuckling +pirate. You reply with brief, to-the-point answers +with no elaboration. Your full name is Captain +Bartholomew "Blackbeard" Thorne. +""" + +kernel = sk.Kernel() +api_key = sk.google_palm_settings_from_dot_env() +palm_chat_completion = sk_gp.GooglePalmChatCompletion("models/chat-bison-001", api_key) +kernel.add_chat_service("models/chat-bison-001", palm_chat_completion) +prompt_config = sk.PromptTemplateConfig.from_completion_parameters( + max_tokens=2000, temperature=0.7, top_p=0.8 +) +prompt_template = sk.ChatPromptTemplate( + "{{$user_input}}", kernel.prompt_template_engine, prompt_config +) +prompt_template.add_system_message(system_message) # Add the system message for context +prompt_template.add_user_message( + "Hi there, my name is Andrea, who are you?" +) # Include a chat history +prompt_template.add_assistant_message("I am Blackbeard.") +function_config = sk.SemanticFunctionConfig(prompt_config, prompt_template) +chat_function = kernel.register_semantic_function( + "PirateSkill", "Chat", function_config +) + + +async def chat() -> bool: + context_vars = sk.ContextVariables() + + try: + user_input = input("User:> ") + context_vars["user_input"] = user_input + except KeyboardInterrupt: + print("\n\nExiting chat...") + return False + except EOFError: + print("\n\nExiting chat...") + return False + + if user_input == "exit": + print("\n\nExiting chat...") + return False + + answer = await kernel.run_async(chat_function, input_vars=context_vars) + print(f"Blackbeard:> {answer}") + return True + + +async def main() -> None: + chatting = True + while chatting: + chatting = await chat() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/kernel-syntax-examples/google_palm_text_completion.py b/python/samples/kernel-syntax-examples/google_palm_text_completion.py new file mode 100644 index 000000000000..664e1194bcf6 --- /dev/null +++ b/python/samples/kernel-syntax-examples/google_palm_text_completion.py @@ -0,0 +1,54 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio + +import semantic_kernel as sk +import semantic_kernel.connectors.ai.google_palm as sk_gp +from semantic_kernel.connectors.ai.complete_request_settings import ( + CompleteRequestSettings, +) + + +async def text_completion_example_complete_async(kernel, api_key, user_mssg, settings): + """ + Complete a text prompt using the Google PaLM model and print the results. + """ + palm_text_completion = sk_gp.GooglePalmTextCompletion( + "models/text-bison-001", api_key + ) + kernel.add_text_completion_service("models/text-bison-001", palm_text_completion) + answer = await palm_text_completion.complete_async(user_mssg, settings) + return answer + + +async def main() -> None: + kernel = sk.Kernel() + apikey = sk.google_palm_settings_from_dot_env() + settings = CompleteRequestSettings() + + user_mssg1 = ( + "Sam has three boxes, each containing a certain number of coins. " + "The first box has twice as many coins as the second box, and the second " + "box has three times as many coins as the third box. Together, the three " + "boxes have 98 coins in total. How many coins are there in each box? " + "Think about it step by step, and show your work." + ) + response = await text_completion_example_complete_async( + kernel, apikey, user_mssg1, settings + ) + print(f"User:> {user_mssg1}\n\nChatBot:> {response}\n") + # Use temperature to influence the variance of the responses + settings.number_of_responses = 3 + settings.temperature = 1 + user_mssg2 = ( + "I need a concise answer. A common method for traversing a binary tree is" + ) + response = await text_completion_example_complete_async( + kernel, apikey, user_mssg2, settings + ) + print(f"User:> {user_mssg2}\n\nChatBot:> {response}") + return + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/kernel-syntax-examples/google_search_skill.py b/python/samples/kernel-syntax-examples/google_search_skill.py index 18416665898e..14ec3542e858 100644 --- a/python/samples/kernel-syntax-examples/google_search_skill.py +++ b/python/samples/kernel-syntax-examples/google_search_skill.py @@ -5,7 +5,7 @@ from dotenv import load_dotenv import semantic_kernel as sk -from semantic_kernel.connectors.ai.open_ai import OpenAITextCompletion +from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion from semantic_kernel.connectors.search_engine import GoogleConnector from semantic_kernel.core_skills import WebSearchEngineSkill @@ -15,8 +15,8 @@ async def main(): kernel = sk.Kernel() api_key, org_id = sk.openai_settings_from_dot_env() - kernel.add_text_completion_service( - "dv", OpenAITextCompletion("text-davinci-003", api_key, org_id) + kernel.add_chat_service( + "chat-gpt", OpenAIChatCompletion("gpt-3.5-turbo", api_key, org_id) ) """ diff --git a/python/samples/kernel-syntax-examples/memory.py b/python/samples/kernel-syntax-examples/memory.py index 110a11767426..b4376a846e0e 100644 --- a/python/samples/kernel-syntax-examples/memory.py +++ b/python/samples/kernel-syntax-examples/memory.py @@ -108,8 +108,8 @@ async def main() -> None: kernel = sk.Kernel() api_key, org_id = sk.openai_settings_from_dot_env() - kernel.add_text_completion_service( - "dv", sk_oai.OpenAITextCompletion("text-davinci-003", api_key, org_id) + kernel.add_chat_service( + "chat-gpt", sk_oai.OpenAIChatCompletion("gpt-3.5-turbo", api_key, org_id) ) kernel.add_text_embedding_generation_service( "ada", sk_oai.OpenAITextEmbedding("text-embedding-ada-002", api_key, org_id) diff --git a/python/semantic_kernel/__init__.py b/python/semantic_kernel/__init__.py index 1fc5d3c2e8bb..c8fcce5676c3 100644 --- a/python/semantic_kernel/__init__.py +++ b/python/semantic_kernel/__init__.py @@ -16,6 +16,8 @@ from semantic_kernel.utils.null_logger import NullLogger from semantic_kernel.utils.settings import ( azure_openai_settings_from_dot_env, + bing_search_settings_from_dot_env, + google_palm_settings_from_dot_env, openai_settings_from_dot_env, pinecone_settings_from_dot_env, postgres_settings_from_dot_env, @@ -28,6 +30,8 @@ "azure_openai_settings_from_dot_env", "postgres_settings_from_dot_env", "pinecone_settings_from_dot_env", + "bing_search_settings_from_dot_env", + "google_palm_settings_from_dot_env", "PromptTemplateConfig", "PromptTemplate", "ChatPromptTemplate", diff --git a/python/semantic_kernel/connectors/ai/chat_request_settings.py b/python/semantic_kernel/connectors/ai/chat_request_settings.py index 65b4fe425316..09bf53715a8f 100644 --- a/python/semantic_kernel/connectors/ai/chat_request_settings.py +++ b/python/semantic_kernel/connectors/ai/chat_request_settings.py @@ -1,7 +1,7 @@ # Copyright (c) Microsoft. All rights reserved. from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Dict +from typing import TYPE_CHECKING, Dict, List if TYPE_CHECKING: from semantic_kernel.semantic_functions.prompt_template_config import ( @@ -18,16 +18,19 @@ class ChatRequestSettings: number_of_responses: int = 1 max_tokens: int = 256 token_selection_biases: Dict[int, int] = field(default_factory=dict) + stop_sequences: List[str] = field(default_factory=list) def update_from_completion_config( self, completion_config: "PromptTemplateConfig.CompletionConfig" ): self.temperature = completion_config.temperature self.top_p = completion_config.top_p - self.presence_penalty = completion_config.presence_penalty - self.frequency_penalty = completion_config.frequency_penalty self.number_of_responses = completion_config.number_of_responses + self.stop_sequences = completion_config.stop_sequences self.max_tokens = completion_config.max_tokens + self.presence_penalty = completion_config.presence_penalty + self.frequency_penalty = completion_config.frequency_penalty + self.token_selection_biases = completion_config.token_selection_biases @staticmethod def from_completion_config( diff --git a/python/semantic_kernel/connectors/ai/google_palm/__init__.py b/python/semantic_kernel/connectors/ai/google_palm/__init__.py new file mode 100644 index 000000000000..249ff10e1e34 --- /dev/null +++ b/python/semantic_kernel/connectors/ai/google_palm/__init__.py @@ -0,0 +1,17 @@ +# Copyright (c) Microsoft. All rights reserved. + +from semantic_kernel.connectors.ai.google_palm.services.gp_chat_completion import ( + GooglePalmChatCompletion, +) +from semantic_kernel.connectors.ai.google_palm.services.gp_text_completion import ( + GooglePalmTextCompletion, +) +from semantic_kernel.connectors.ai.google_palm.services.gp_text_embedding import ( + GooglePalmTextEmbedding, +) + +__all__ = [ + "GooglePalmTextCompletion", + "GooglePalmChatCompletion", + "GooglePalmTextEmbedding", +] diff --git a/python/semantic_kernel/connectors/ai/google_palm/services/gp_chat_completion.py b/python/semantic_kernel/connectors/ai/google_palm/services/gp_chat_completion.py new file mode 100644 index 000000000000..dfeb0c59e06f --- /dev/null +++ b/python/semantic_kernel/connectors/ai/google_palm/services/gp_chat_completion.py @@ -0,0 +1,217 @@ +# Copyright (c) Microsoft. All rights reserved. + +from typing import List, Optional, Tuple, Union + +import google.generativeai as palm +from google.generativeai.types import ChatResponse, ExampleOptions, MessagePromptOptions + +from semantic_kernel.connectors.ai.ai_exception import AIException +from semantic_kernel.connectors.ai.chat_completion_client_base import ( + ChatCompletionClientBase, +) +from semantic_kernel.connectors.ai.chat_request_settings import ChatRequestSettings +from semantic_kernel.connectors.ai.complete_request_settings import ( + CompleteRequestSettings, +) +from semantic_kernel.connectors.ai.text_completion_client_base import ( + TextCompletionClientBase, +) + + +class GooglePalmChatCompletion(ChatCompletionClientBase, TextCompletionClientBase): + _model_id: str + _api_key: str + _message_history: ChatResponse + + def __init__( + self, + model_id: str, + api_key: str, + ) -> None: + """ + Initializes a new instance of the GooglePalmChatCompletion class. + + Arguments: + model_id {str} -- GooglePalm model name, see + https://developers.generativeai.google/models/language + api_key {str} -- GooglePalm API key, see + https://developers.generativeai.google/products/palm + """ + if not api_key: + raise ValueError("The Google PaLM API key cannot be `None` or empty`") + + self._model_id = model_id + self._api_key = api_key + self._message_history = None + + async def complete_chat_async( + self, + messages: List[Tuple[str, str]], + request_settings: ChatRequestSettings, + context: Optional[str] = None, + examples: Optional[ExampleOptions] = None, + prompt: Optional[MessagePromptOptions] = None, + ) -> Union[str, List[str]]: + response = await self._send_chat_request( + messages, request_settings, context, examples, prompt + ) + + if request_settings.number_of_responses > 1: + return [ + candidate["output"] + if candidate["output"] is not None + else "I don't know." + for candidate in response.candidates + ] + else: + if response.last is None: + return "I don't know." # PaLM returns None if it doesn't know + else: + return response.last + + async def complete_chat_stream_async( + self, + messages: List[Tuple[str, str]], + request_settings: ChatRequestSettings, + context: Optional[str] = None, + ): + raise NotImplementedError( + "Google Palm API does not currently support streaming" + ) + + async def complete_async( + self, prompt: str, request_settings: CompleteRequestSettings + ) -> Union[str, List[str]]: + prompt_to_message = [("user", prompt)] + chat_settings = ChatRequestSettings( + temperature=request_settings.temperature, + top_p=request_settings.top_p, + presence_penalty=request_settings.presence_penalty, + frequency_penalty=request_settings.frequency_penalty, + max_tokens=request_settings.max_tokens, + number_of_responses=request_settings.number_of_responses, + token_selection_biases=request_settings.token_selection_biases, + ) + response = await self._send_chat_request(prompt_to_message, chat_settings) + + if chat_settings.number_of_responses > 1: + return [ + candidate["output"] + if candidate["output"] is not None + else "I don't know." + for candidate in response.candidates + ] + else: + if response.last is None: + return "I don't know." # PaLM returns None if it doesn't know + else: + return response.last + + async def complete_stream_async( + self, prompt: str, request_settings: CompleteRequestSettings + ): + raise NotImplementedError( + "Google Palm API does not currently support streaming" + ) + + async def _send_chat_request( + self, + messages: List[Tuple[str, str]], + request_settings: ChatRequestSettings, + context: Optional[str] = None, + examples: Optional[ExampleOptions] = None, + prompt: Optional[MessagePromptOptions] = None, + ): + """ + Completes the given user message. If len(messages) > 1, and a + conversation has not been initiated yet, it is assumed that chat history + is needed for context. All messages preceding the last message will be + utilized for context. This also enables Google PaLM to utilize memory + and skills, which should be stored in the messages parameter as system + messages. + + Arguments: + messages {str} -- The message (from a user) to respond to. + request_settings {ChatRequestSettings} -- The request settings. + context {str} -- Text that should be provided to the model first, + to ground the response. If a system message is provided, it will be + used as context. + examples {ExamplesOptions} -- Examples of what the model should + generate. This includes both the user input and the response that + the model should emulate. These examples are treated identically to + conversation messages except that they take precedence over the + history in messages: If the total input size exceeds the model's + input_token_limit the input will be truncated. Items will be dropped + from messages before examples + See: https://developers.generativeai.google/api/python/google/generativeai/types/ExampleOptions + prompt {MessagePromptOptions} -- You may pass a + types.MessagePromptOptions instead of a setting context/examples/messages, + but not both. + See: https://developers.generativeai.google/api/python/google/generativeai/types/MessagePromptOptions + + Returns: + str -- The completed text. + """ + if request_settings is None: + raise ValueError("The request settings cannot be `None`") + + if request_settings.max_tokens < 1: + raise AIException( + AIException.ErrorCodes.InvalidRequest, + "The max tokens must be greater than 0, " + f"but was {request_settings.max_tokens}", + ) + + if len(messages) <= 0: + raise AIException( + AIException.ErrorCodes.InvalidRequest, + "To complete a chat you need at least one message", + ) + + if messages[-1][0] != "user": + raise AIException( + AIException.ErrorCodes.InvalidRequest, + "The last message must be from the user", + ) + try: + palm.configure(api_key=self._api_key) + except Exception as ex: + raise PermissionError( + "Google PaLM service failed to configure. Invalid API key provided.", + ex, + ) + if ( + self._message_history is None and context is None + ): # If the conversation hasn't started yet and no context is provided + context = "" + if len(messages) > 1: # Check if we need context from messages + for index, (role, message) in enumerate(messages): + if index < len(messages) - 1: + if role == "system": + context += message + "\n" + else: + context += role + ": " + message + "\n" + try: + if self._message_history is None: + response = palm.chat( # Start a new conversation + model=self._model_id, + context=context, + examples=examples, + temperature=request_settings.temperature, + candidate_count=request_settings.number_of_responses, + top_p=request_settings.top_p, + prompt=prompt, + messages=messages[-1][1], + ) + else: + response = self._message_history.reply( # Continue the conversation + messages[-1][1], + ) + self._message_history = response # Store response object for future use + except Exception as ex: + raise AIException( + AIException.ErrorCodes.ServiceError, + "Google PaLM service failed to complete the prompt", + ex, + ) + return response diff --git a/python/semantic_kernel/connectors/ai/google_palm/services/gp_text_completion.py b/python/semantic_kernel/connectors/ai/google_palm/services/gp_text_completion.py new file mode 100644 index 000000000000..287f1f11a7ca --- /dev/null +++ b/python/semantic_kernel/connectors/ai/google_palm/services/gp_text_completion.py @@ -0,0 +1,105 @@ +# Copyright (c) Microsoft. All rights reserved. + +from typing import List, Union + +import google.generativeai as palm + +from semantic_kernel.connectors.ai.ai_exception import AIException +from semantic_kernel.connectors.ai.complete_request_settings import ( + CompleteRequestSettings, +) +from semantic_kernel.connectors.ai.text_completion_client_base import ( + TextCompletionClientBase, +) + + +class GooglePalmTextCompletion(TextCompletionClientBase): + _model_id: str + _api_key: str + + def __init__(self, model_id: str, api_key: str) -> None: + """ + Initializes a new instance of the GooglePalmTextCompletion class. + + Arguments: + model_id {str} -- GooglePalm model name, see + https://developers.generativeai.google/models/language + api_key {str} -- GooglePalm API key, see + https://developers.generativeai.google/products/palm + """ + if not api_key: + raise ValueError("The Google PaLM API key cannot be `None` or empty`") + + self._model_id = model_id + self._api_key = api_key + + async def complete_async( + self, prompt: str, request_settings: CompleteRequestSettings + ) -> Union[str, List[str]]: + response = await self._send_completion_request(prompt, request_settings) + + if request_settings.number_of_responses > 1: + return [candidate["output"] for candidate in response.candidates] + else: + return response.result + + async def complete_stream_async( + self, prompt: str, request_settings: CompleteRequestSettings + ): + raise NotImplementedError( + "Google Palm API does not currently support streaming" + ) + + async def _send_completion_request( + self, prompt: str, request_settings: CompleteRequestSettings + ): + """ + Completes the given prompt. Returns a single string completion. + Cannot return multiple completions. Cannot return logprobs. + + Arguments: + prompt {str} -- The prompt to complete. + request_settings {CompleteRequestSettings} -- The request settings. + + Returns: + str -- The completed text. + """ + if not prompt: + raise ValueError("Prompt cannot be `None` or empty") + if request_settings is None: + raise ValueError("Request settings cannot be `None`") + if request_settings.max_tokens < 1: + raise AIException( + AIException.ErrorCodes.InvalidRequest, + "The max tokens must be greater than 0, " + f"but was {request_settings.max_tokens}", + ) + try: + palm.configure(api_key=self._api_key) + except Exception as ex: + raise PermissionError( + "Google PaLM service failed to configure. Invalid API key provided.", + ex, + ) + try: + response = palm.generate_text( + model=self._model_id, + prompt=prompt, + temperature=request_settings.temperature, + max_output_tokens=request_settings.max_tokens, + stop_sequences=( + request_settings.stop_sequences + if request_settings.stop_sequences is not None + and len(request_settings.stop_sequences) > 0 + else None + ), + candidate_count=request_settings.number_of_responses, + top_p=request_settings.top_p, + ) + except Exception as ex: + raise AIException( + AIException.ErrorCodes.ServiceError, + "Google PaLM service failed to complete the prompt", + ex, + ) + return response diff --git a/python/semantic_kernel/connectors/ai/google_palm/services/gp_text_embedding.py b/python/semantic_kernel/connectors/ai/google_palm/services/gp_text_embedding.py new file mode 100644 index 000000000000..aa1f19a7cf52 --- /dev/null +++ b/python/semantic_kernel/connectors/ai/google_palm/services/gp_text_embedding.py @@ -0,0 +1,66 @@ +# Copyright (c) Microsoft. All rights reserved. + + +from typing import List + +import google.generativeai as palm +from numpy import array, ndarray + +from semantic_kernel.connectors.ai.ai_exception import AIException +from semantic_kernel.connectors.ai.embeddings.embedding_generator_base import ( + EmbeddingGeneratorBase, +) + + +class GooglePalmTextEmbedding(EmbeddingGeneratorBase): + _model_id: str + _api_key: str + + def __init__(self, model_id: str, api_key: str) -> None: + """ + Initializes a new instance of the GooglePalmTextEmbedding class. + + Arguments: + model_id {str} -- GooglePalm model name, see + https://developers.generativeai.google/models/language + api_key {str} -- GooglePalm API key, see + https://developers.generativeai.google/products/palm + """ + if not api_key: + raise ValueError("The Google PaLM API key cannot be `None` or empty`") + + self._model_id = model_id + self._api_key = api_key + + async def generate_embeddings_async(self, texts: List[str]) -> ndarray: + """ + Generates embeddings for a list of texts. + + Arguments: + texts {List[str]} -- Texts to generate embeddings for. + + Returns: + ndarray -- Embeddings for the texts. + """ + try: + palm.configure(api_key=self._api_key) + except Exception as ex: + raise PermissionError( + "Google PaLM service failed to configure. Invalid API key provided.", + ex, + ) + embeddings = [] + for text in texts: + try: + response = palm.generate_embeddings( + model=self._model_id, + text=text, + ) + embeddings.append(array(response["embedding"])) + except Exception as ex: + raise AIException( + AIException.ErrorCodes.ServiceError, + "Google PaLM service failed to generate the embedding.", + ex, + ) + return array(embeddings) diff --git a/python/semantic_kernel/connectors/ai/open_ai/services/open_ai_chat_completion.py b/python/semantic_kernel/connectors/ai/open_ai/services/open_ai_chat_completion.py index 16b1dc2b339c..ae3316a1b8c7 100644 --- a/python/semantic_kernel/connectors/ai/open_ai/services/open_ai_chat_completion.py +++ b/python/semantic_kernel/connectors/ai/open_ai/services/open_ai_chat_completion.py @@ -101,15 +101,8 @@ async def complete_async( str -- The completed text. """ prompt_to_message = [("user", prompt)] - chat_settings = ChatRequestSettings( - temperature=request_settings.temperature, - top_p=request_settings.top_p, - presence_penalty=request_settings.presence_penalty, - frequency_penalty=request_settings.frequency_penalty, - max_tokens=request_settings.max_tokens, - number_of_responses=request_settings.number_of_responses, - token_selection_biases=request_settings.token_selection_biases, - ) + chat_settings = ChatRequestSettings.from_completion_config(request_settings) + response = await self._send_chat_request( prompt_to_message, chat_settings, False ) @@ -131,6 +124,7 @@ async def complete_stream_async( max_tokens=request_settings.max_tokens, number_of_responses=request_settings.number_of_responses, token_selection_biases=request_settings.token_selection_biases, + stop_sequences=request_settings.stop_sequences, ) response = await self._send_chat_request(prompt_to_message, chat_settings, True) @@ -205,11 +199,17 @@ async def _send_chat_request( messages=formatted_messages, temperature=request_settings.temperature, top_p=request_settings.top_p, - presence_penalty=request_settings.presence_penalty, - frequency_penalty=request_settings.frequency_penalty, - max_tokens=request_settings.max_tokens, n=request_settings.number_of_responses, stream=stream, + stop=( + request_settings.stop_sequences + if request_settings.stop_sequences is not None + and len(request_settings.stop_sequences) > 0 + else None + ), + max_tokens=request_settings.max_tokens, + presence_penalty=request_settings.presence_penalty, + frequency_penalty=request_settings.frequency_penalty, logit_bias=( request_settings.token_selection_biases if request_settings.token_selection_biases is not None diff --git a/python/semantic_kernel/connectors/memory/azure_cognitive_search/azure_cognitive_search_memory_store.py b/python/semantic_kernel/connectors/memory/azure_cognitive_search/azure_cognitive_search_memory_store.py index c1a907bca5ab..d5add2b89158 100644 --- a/python/semantic_kernel/connectors/memory/azure_cognitive_search/azure_cognitive_search_memory_store.py +++ b/python/semantic_kernel/connectors/memory/azure_cognitive_search/azure_cognitive_search_memory_store.py @@ -8,10 +8,11 @@ from azure.core.exceptions import ResourceNotFoundError from azure.search.documents.indexes.aio import SearchIndexClient from azure.search.documents.indexes.models import ( + HnswVectorSearchAlgorithmConfiguration, SearchIndex, VectorSearch, - VectorSearchAlgorithmConfiguration, ) +from azure.search.documents.models import Vector from numpy import ndarray from semantic_kernel.connectors.memory.azure_cognitive_search.utils import ( @@ -58,7 +59,6 @@ def __init__( Instantiate using Async Context Manager: async with AzureCognitiveSearchMemoryStore(<...>) as memory: await memory.<...> - """ try: pass @@ -82,14 +82,14 @@ async def close_async(self): async def create_collection_async( self, collection_name: str, - vector_config: Optional[VectorSearchAlgorithmConfiguration] = None, + vector_config: Optional[HnswVectorSearchAlgorithmConfiguration] = None, ) -> None: """Creates a new collection if it does not exist. Arguments: collection_name {str} -- The name of the collection to create. - vector_config {VectorSearchAlgorithmConfiguration} -- Optional search algorithm configuration - (default: {None}). + vector_config {HnswVectorSearchAlgorithmConfiguration} -- Optional search algorithm configuration + (default: {None}). semantic_config {SemanticConfiguration} -- Optional search index configuration (default: {None}). Returns: None @@ -100,7 +100,7 @@ async def create_collection_async( else: vector_search = VectorSearch( algorithm_configurations=[ - VectorSearchAlgorithmConfiguration( + HnswVectorSearchAlgorithmConfiguration( name="az-vector-config", kind="hnsw", hnsw_parameters={ @@ -403,12 +403,14 @@ async def get_nearest_matches_async( collection_name.lower() ) + vector = Vector( + value=embedding.flatten(), k=limit, fields=SEARCH_FIELD_EMBEDDING + ) + search_results = await search_client.search( search_text="*", - vector_fields=SEARCH_FIELD_EMBEDDING, - vector=embedding.tolist(), + vectors=[vector], select=get_field_selection(with_embeddings), - top_k=limit, ) if not search_results or search_results is None: diff --git a/python/semantic_kernel/connectors/memory/usearch/__init__.py b/python/semantic_kernel/connectors/memory/usearch/__init__.py new file mode 100644 index 000000000000..f74403f6441f --- /dev/null +++ b/python/semantic_kernel/connectors/memory/usearch/__init__.py @@ -0,0 +1,7 @@ +# Copyright (c) Microsoft. All rights reserved. + +from semantic_kernel.connectors.memory.usearch.usearch_memory_store import ( + USearchMemoryStore, +) + +__all__ = ["USearchMemoryStore"] diff --git a/python/semantic_kernel/connectors/memory/usearch/usearch_memory_store.py b/python/semantic_kernel/connectors/memory/usearch/usearch_memory_store.py new file mode 100644 index 000000000000..cb59d1c953f8 --- /dev/null +++ b/python/semantic_kernel/connectors/memory/usearch/usearch_memory_store.py @@ -0,0 +1,638 @@ +# Copyright (c) Microsoft. All rights reserved. + +import itertools +import os +from dataclasses import dataclass +from enum import Enum +from logging import Logger +from pathlib import Path +from typing import Dict, List, Optional, Tuple, Union + +import numpy as np +import pandas as pd +import pyarrow as pa +import pyarrow.parquet as pq +from numpy import ndarray + +from semantic_kernel.memory.memory_record import MemoryRecord +from semantic_kernel.memory.memory_store_base import MemoryStoreBase +from semantic_kernel.utils.null_logger import NullLogger +from usearch.index import ( + BatchMatches, + CompiledMetric, + Index, + Matches, + MetricKind, + ScalarKind, +) + + +@dataclass +class _USearchCollection: + """Represents a collection for USearch with embeddings and related data. + + Attributes: + embeddings_index (Index): The index of embeddings. + embeddings_data_table (pa.Table): The PyArrow table holding embeddings data. + embeddings_id_to_label (Dict[str, int]): Mapping of embeddings ID to label. + """ + + embeddings_index: Index + embeddings_data_table: pa.Table + embeddings_id_to_label: Dict[str, int] + + @staticmethod + def create_default(embeddings_index: Index) -> "_USearchCollection": + """Create a default `_USearchCollection` using a given embeddings index. + + Args: + embeddings_index (Index): The index of embeddings to be used for the default collection. + + Returns: + _USearchCollection: A default `_USearchCollection` initialized with the given embeddings index. + """ + return _USearchCollection( + embeddings_index, + pa.Table.from_pandas( + pd.DataFrame(columns=_embeddings_data_schema.names), + schema=_embeddings_data_schema, + ), + {}, + ) + + +# PyArrow Schema definition for the embeddings data from `MemoryRecord`. +_embeddings_data_schema = pa.schema( + [ + pa.field("key", pa.string()), + pa.field("timestamp", pa.timestamp("us")), + pa.field("is_reference", pa.bool_()), + pa.field("external_source_name", pa.string()), + pa.field("id", pa.string()), + pa.field("description", pa.string()), + pa.field("text", pa.string()), + pa.field("additional_metadata", pa.string()), + ] +) + + +class _CollectionFileType(Enum): + """Enumeration of file types used for storing collections.""" + + USEARCH = 0 + PARQUET = 1 + + +# Mapping of collection file types to their file extensions. +_collection_file_extensions: Dict[_CollectionFileType, str] = { + _CollectionFileType.USEARCH: ".usearch", + _CollectionFileType.PARQUET: ".parquet", +} + + +def memoryrecords_to_pyarrow_table(records: List[MemoryRecord]) -> pa.Table: + """Convert a list of `MemoryRecord` to a PyArrow Table""" + records_pylist = [ + {attr: getattr(record, "_" + attr) for attr in _embeddings_data_schema.names} + for record in records + ] + return pa.Table.from_pylist(records_pylist, schema=_embeddings_data_schema) + + +def pyarrow_table_to_memoryrecords( + table: pa.Table, vectors: Optional[ndarray] = None +) -> List[MemoryRecord]: + """Convert a PyArrow Table to a list of MemoryRecords. + + Args: + table (pa.Table): The PyArrow Table to convert. + vectors (Optional[ndarray], optional): An array of vectors to include as embeddings in the MemoryRecords. + The length and order of the vectors should match the rows in the table. Defaults to None. + + Returns: + List[MemoryRecord]: List of MemoryRecords constructed from the table. + """ + result_memory_records = [ + MemoryRecord( + **row.to_dict(), embedding=vectors[index] if vectors is not None else None + ) + for index, row in table.to_pandas().iterrows() + ] + + return result_memory_records + + +class USearchMemoryStore(MemoryStoreBase): + def __init__( + self, + persist_directory: Optional[os.PathLike] = None, + logger: Optional[Logger] = None, + ) -> None: + """ + Create a USearchMemoryStore instance. + + This store helps searching embeddings with USearch, keeping collections in memory. + To save collections to disk, provide the `persist_directory` param. + Collections are saved when `close_async` is called. + + To both save collections and free up memory, call `close_async`. + When `USearchMemoryStore` is used with a context manager, this will happen automatically. + Otherwise, it should be called explicitly. + + Args: + persist_directory (Optional[os.PathLike], default=None): Directory for loading and saving collections. + If None, collections are not loaded nor saved. + logger (Optional[Logger], default=None): Logger for diagnostics. If None, a NullLogger is used. + """ + self._logger = logger or NullLogger() + self._persist_directory = ( + Path(persist_directory) if persist_directory is not None else None + ) + + self._collections: Dict[str, _USearchCollection] = {} + if self._persist_directory: + self._collections = self._read_collections_from_dir() + + def _get_collection_path( + self, collection_name: str, *, file_type: _CollectionFileType + ) -> Path: + """ + Get the path for the given collection name and file type. + + Args: + collection_name (str): Name of the collection. + file_type (_CollectionFileType): The file type. + + Returns: + Path: Path to the collection file. + + Raises: + ValueError: If persist directory path is not set. + """ + collection_name = collection_name.lower() + if self._persist_directory is None: + raise ValueError("Path of persist directory is not set") + + return self._persist_directory / ( + collection_name + _collection_file_extensions[file_type] + ) + + async def create_collection_async( + self, + collection_name: str, + ndim: int = 0, + metric: Union[str, MetricKind, CompiledMetric] = MetricKind.IP, + dtype: Optional[Union[str, ScalarKind]] = None, + connectivity: Optional[int] = None, + expansion_add: Optional[int] = None, + expansion_search: Optional[int] = None, + view: bool = False, + ) -> None: + """Create a new collection. + + Args: + collection_name (str): Name of the collection. Case-insensitive. + Must have name that is valid file name for the current OS environment. + ndim (int, optional): Number of dimensions. Defaults to 0. + metric (Union[str, MetricKind, CompiledMetric], optional): Metric kind. Defaults to MetricKind.IP. + dtype (Optional[Union[str, ScalarKind]], optional): Data type. Defaults to None. + connectivity (int, optional): Connectivity parameter. Defaults to None. + expansion_add (int, optional): Expansion add parameter. Defaults to None. + expansion_search (int, optional): Expansion search parameter. Defaults to None. + view (bool, optional): Viewing flag. Defaults to False. + + Raises: + ValueError: If collection with the given name already exists. + ValueError: If collection name is empty string. + """ + collection_name = collection_name.lower() + if not collection_name: + raise ValueError("Collection name can not be empty.") + if collection_name in self._collections: + raise ValueError(f"Collection with name {collection_name} already exists.") + + embeddings_index_path = ( + self._get_collection_path( + collection_name, file_type=_CollectionFileType.USEARCH + ) + if self._persist_directory + else None + ) + + embeddings_index = Index( + path=embeddings_index_path, + ndim=ndim, + metric=metric, + dtype=dtype, + connectivity=connectivity, + expansion_add=expansion_add, + expansion_search=expansion_search, + view=view, + ) + + self._collections[collection_name] = _USearchCollection.create_default( + embeddings_index + ) + + return None + + def _read_embeddings_table( + self, path: os.PathLike + ) -> Tuple[pa.Table, Dict[str, int]]: + """Read embeddings from the provided path and generate an ID to label mapping. + + Args: + path (os.PathLike): Path to the embeddings. + + Returns: + Tuple of embeddings table and a dictionary mapping from record ID to its label. + """ + embeddings_table = pq.read_table(path, schema=_embeddings_data_schema) + embeddings_id_to_label: Dict[str, int] = { + record_id: idx + for idx, record_id in enumerate(embeddings_table.column("id").to_pylist()) + } + return embeddings_table, embeddings_id_to_label + + def _read_embeddings_index(self, path: Path) -> Index: + """Read embeddings index.""" + # str cast is temporarily fix for https://github.com/unum-cloud/usearch/issues/196 + return Index.restore(str(path), view=False) + + def _read_collections_from_dir(self) -> Dict[str, _USearchCollection]: + """Read all collections from directory to memory. + + Raises: + ValueError: If files for a collection do not match expected amount. + + Returns: + Dict[str, _USearchCollection]: Dictionary with collection names as keys and + their _USearchCollection as values. + """ + collections: Dict[str, _USearchCollection] = {} + + for collection_name, collection_files in self._get_all_storage_files().items(): + expected_storage_files = len(_CollectionFileType) + if len(collection_files) != expected_storage_files: + raise ValueError( + f"Expected {expected_storage_files} files for collection {collection_name}" + ) + parquet_file, usearch_file = collection_files + if ( + parquet_file.suffix + == _collection_file_extensions[_CollectionFileType.USEARCH] + ): + parquet_file, usearch_file = usearch_file, parquet_file + + embeddings_table, embeddings_id_to_label = self._read_embeddings_table( + parquet_file + ) + embeddings_index = self._read_embeddings_index(usearch_file) + + collections[collection_name] = _USearchCollection( + embeddings_index, + embeddings_table, + embeddings_id_to_label, + ) + + return collections + + async def get_collections_async(self) -> List[str]: + """Get list of existing collections. + + Returns: + List[str]: List of collection names. + """ + return list(self._collections.keys()) + + async def delete_collection_async(self, collection_name: str) -> None: + collection_name = collection_name.lower() + collection = self._collections.pop(collection_name, None) + if collection: + collection.embeddings_index.reset() + return None + + async def does_collection_exist_async(self, collection_name: str) -> bool: + collection_name = collection_name.lower() + return collection_name in self._collections + + async def upsert_async(self, collection_name: str, record: MemoryRecord) -> str: + """Upsert single MemoryRecord and return its ID.""" + collection_name = collection_name.lower() + res = await self.upsert_batch_async( + collection_name=collection_name, records=[record] + ) + return res[0] + + async def upsert_batch_async( + self, + collection_name: str, + records: List[MemoryRecord], + *, + compact: bool = False, + copy: bool = True, + threads: int = 0, + log: Union[str, bool] = False, + batch_size: int = 0, + ) -> List[str]: + """Upsert a batch of MemoryRecords and return their IDs. + + Args: + collection_name (str): Name of the collection to search within. + records (List[MemoryRecord]): Records to upsert. + compact (bool, optional): Removes links to removed nodes (expensive). Defaults to False. + copy (bool, optional): Should the index store a copy of vectors. Defaults to True. + threads (int, optional): Optimal number of cores to use. Defaults to 0. + log (Union[str, bool], optional): Whether to print the progress bar. Defaults to False. + batch_size (int, optional): Number of vectors to process at once. Defaults to 0. + + Raises: + KeyError: If collection not exist + + Returns: + List[str]: List of IDs. + """ + collection_name = collection_name.lower() + if collection_name not in self._collections: + raise KeyError( + f"Collection {collection_name} does not exist, cannot insert." + ) + + ucollection = self._collections[collection_name] + all_records_id = [record._id for record in records] + + # Remove vectors from index + remove_labels = [ + ucollection.embeddings_id_to_label[id] + for id in all_records_id + if id in ucollection.embeddings_id_to_label + ] + ucollection.embeddings_index.remove( + remove_labels, compact=compact, threads=threads + ) + + # Determine label insertion points + table_num_rows = ucollection.embeddings_data_table.num_rows + insert_labels = np.arange(table_num_rows, table_num_rows + len(records)) + + # Add embeddings to index + ucollection.embeddings_index.add( + keys=insert_labels, + vectors=np.stack([record.embedding for record in records]), + copy=copy, + threads=threads, + log=log, + batch_size=batch_size, + ) + + # Update embeddings_table + ucollection.embeddings_data_table = pa.concat_tables( + [ucollection.embeddings_data_table, memoryrecords_to_pyarrow_table(records)] + ) + + # Update embeddings_id_to_label + for index, record_id in enumerate(all_records_id): + ucollection.embeddings_id_to_label[record_id] = insert_labels[index] + + return all_records_id + + async def get_async( + self, + collection_name: str, + key: str, + with_embedding: bool, + dtype: ScalarKind = ScalarKind.F32, + ) -> MemoryRecord: + """Retrieve a single MemoryRecord using its key.""" + collection_name = collection_name.lower() + result = await self.get_batch_async( + collection_name=collection_name, + keys=[key], + with_embeddings=with_embedding, + dtype=dtype, + ) + if not result: + raise KeyError(f"Key '{key}' not found in collection '{collection_name}'") + return result[0] + + async def get_batch_async( + self, + collection_name: str, + keys: List[str], + with_embeddings: bool, + dtype: ScalarKind = ScalarKind.F32, + ) -> List[MemoryRecord]: + """Retrieve a batch of MemoryRecords using their keys.""" + collection_name = collection_name.lower() + if collection_name not in self._collections: + raise KeyError(f"Collection {collection_name} does not exist") + + ucollection = self._collections[collection_name] + labels = [ + ucollection.embeddings_id_to_label[key] + for key in keys + if key in ucollection.embeddings_id_to_label + ] + if not labels: + return [] + vectors = ( + ucollection.embeddings_index.get_vectors(labels, dtype) + if with_embeddings + else None + ) + + return pyarrow_table_to_memoryrecords( + ucollection.embeddings_data_table.take(pa.array(labels)), vectors + ) + + async def remove_async(self, collection_name: str, key: str) -> None: + """Remove a single MemoryRecord using its key.""" + collection_name = collection_name.lower() + await self.remove_batch_async(collection_name=collection_name, keys=[key]) + return None + + async def remove_batch_async(self, collection_name: str, keys: List[str]) -> None: + """Remove a batch of MemoryRecords using their keys.""" + collection_name = collection_name.lower() + if collection_name not in self._collections: + raise KeyError( + f"Collection {collection_name} does not exist, cannot insert." + ) + + ucollection = self._collections[collection_name] + + labels = [ucollection.embeddings_id_to_label[key] for key in keys] + ucollection.embeddings_index.remove(labels) + for key in keys: + del ucollection.embeddings_id_to_label[key] + + return None + + async def get_nearest_match_async( + self, + collection_name: str, + embedding: ndarray, + min_relevance_score: float = 0.0, + with_embedding: bool = True, + exact: bool = False, + ) -> Tuple[MemoryRecord, float]: + """Retrieve the nearest matching MemoryRecord for the provided embedding. + + By default it is approximately search, see `exact` param description. + + Measure of similarity between vectors is relevance score. It is from 0 to 1. + USearch returns distances for vectors. Distance is converted to relevance score by inverse function. + + Args: + collection_name (str): Name of the collection to search within. + embedding (ndarray): The embedding vector to search for. + min_relevance_score (float, optional): The minimum relevance score for vectors. Supposed to be from 0 to 1. + Only vectors with greater or equal relevance score are returned. Defaults to 0.0. + with_embedding (bool, optional): If True, include the embedding in the result. Defaults to True. + exact (bool, optional): Perform exhaustive linear-time exact search. Defaults to False. + + Returns: + Tuple[MemoryRecord, float]: The nearest matching record and its relevance score. + """ + collection_name = collection_name.lower() + results = await self.get_nearest_matches_async( + collection_name=collection_name, + embedding=embedding, + limit=1, + min_relevance_score=min_relevance_score, + with_embeddings=with_embedding, + exact=exact, + ) + return results[0] + + async def get_nearest_matches_async( + self, + collection_name: str, + embedding: ndarray, + limit: int, + min_relevance_score: float = 0.0, + with_embeddings: bool = True, + *, + threads: int = 0, + exact: bool = False, + log: Union[str, bool] = False, + batch_size: int = 0, + ) -> List[Tuple[MemoryRecord, float]]: + """Get the nearest matches to a given embedding. + + By default it is approximately search, see `exact` param description. + + Measure of similarity between vectors is relevance score. It is from 0 to 1. + USearch returns distances for vectors. Distance is converted to relevance score by inverse function. + + Args: + collection_name (str): Name of the collection to search within. + embedding (ndarray): The embedding vector to search for. + limit (int): maximum amount of embeddings to search for. + min_relevance_score (float, optional): The minimum relevance score for vectors. Supposed to be from 0 to 1. + Only vectors with greater or equal relevance score are returned. Defaults to 0.0. + with_embedding (bool, optional): If True, include the embedding in the result. Defaults to True. + threads (int, optional): Optimal number of cores to use. Defaults to 0. + exact (bool, optional): Perform exhaustive linear-time exact search. Defaults to False. + log (Union[str, bool], optional): Whether to print the progress bar. Defaults to False. + batch_size (int, optional): Number of vectors to process at once. Defaults to 0. + + Raises: + KeyError: if a collection with specified name does not exist + + Returns: + List[Tuple[MemoryRecord, float]]: The nearest matching records and their relevance score. + """ + collection_name = collection_name.lower() + ucollection = self._collections[collection_name] + + result: Union[Matches, BatchMatches] = ucollection.embeddings_index.search( + vectors=embedding, + k=limit, + threads=threads, + exact=exact, + log=log, + batch_size=batch_size, + ) + + assert isinstance(result, Matches) + + relevance_score = 1 / (result.distances + 1) + filtered_labels = result.keys[ + np.where(relevance_score >= min_relevance_score)[0] + ] + + filtered_vectors: Optional[np.ndarray] = None + if with_embeddings: + filtered_vectors = ucollection.embeddings_index.get_vectors(filtered_labels) + + return [ + (mem_rec, relevance_score[index].item()) + for index, mem_rec in enumerate( + pyarrow_table_to_memoryrecords( + ucollection.embeddings_data_table.take(pa.array(filtered_labels)), + filtered_vectors, + ) + ) + ] + + def _get_all_storage_files(self) -> Dict[str, List[Path]]: + """Return storage files for each collection in `self._persist_directory`. + + Collection name is derived from file name and converted to lowercase. Files with extensions that + do not match storage extensions are discarded. + + Raises: + ValueError: If persist directory is not set. + + Returns: + Dict[str, List[Path]]: Dictionary of collection names mapped to their respective files. + """ + if self._persist_directory is None: + raise ValueError("Persist directory is not set") + + storage_exts = _collection_file_extensions.values() + collection_storage_files: Dict[str, List[Path]] = {} + for path in self._persist_directory.iterdir(): + if path.is_file() and (path.suffix in storage_exts): + collection_name = path.stem.lower() + if collection_name in collection_storage_files: + collection_storage_files[collection_name].append(path) + else: + collection_storage_files[collection_name] = [path] + return collection_storage_files + + def _dump_collections(self) -> None: + collection_storage_files = self._get_all_storage_files() + for file_path in itertools.chain.from_iterable( + collection_storage_files.values() + ): + file_path.unlink() + + for collection_name, ucollection in self._collections.items(): + ucollection.embeddings_index.save( + self._get_collection_path( + collection_name, file_type=_CollectionFileType.USEARCH + ) + ) + pq.write_table( + ucollection.embeddings_data_table, + self._get_collection_path( + collection_name, file_type=_CollectionFileType.PARQUET + ), + ) + + return None + + async def close_async(self) -> None: + """Persist collection, clear. + + Returns: + None + """ + if self._persist_directory: + self._dump_collections() + + for collection_name in await self.get_collections_async(): + await self.delete_collection_async(collection_name) + self._collections = {} diff --git a/python/semantic_kernel/core_skills/text_memory_skill.py b/python/semantic_kernel/core_skills/text_memory_skill.py index 471bb8060b9d..29639690a6cf 100644 --- a/python/semantic_kernel/core_skills/text_memory_skill.py +++ b/python/semantic_kernel/core_skills/text_memory_skill.py @@ -1,4 +1,5 @@ # Copyright (c) Microsoft. All rights reserved. +import json from semantic_kernel.orchestration.sk_context import SKContext from semantic_kernel.sk_pydantic import PydanticField @@ -9,8 +10,10 @@ class TextMemorySkill(PydanticField): COLLECTION_PARAM = "collection" RELEVANCE_PARAM = "relevance" KEY_PARAM = "key" + LIMIT_PARAM = "limit" DEFAULT_COLLECTION = "generic" DEFAULT_RELEVANCE = 0.75 + DEFAULT_LIMIT = 1 # @staticmethod @sk_function( @@ -28,6 +31,11 @@ class TextMemorySkill(PydanticField): description="The relevance score, from 0.0 to 1.0; 1.0 means perfect match", default_value=DEFAULT_RELEVANCE, ) + @sk_function_context_parameter( + name=LIMIT_PARAM, + description="The maximum number of relevant memories to recall.", + default_value=DEFAULT_LIMIT, + ) async def recall_async(self, ask: str, context: SKContext) -> str: """ Recall a fact from the long term memory. @@ -39,10 +47,11 @@ async def recall_async(self, ask: str, context: SKContext) -> str: Args: ask -- The question to ask the memory context -- Contains the 'collection' to search for information - and the 'relevance' score to use when searching + , the 'relevance' score to use when searching + and the 'limit' of relevant memories to retrieve. Returns: - The nearest item from the memory store + The nearest item from the memory store as a string or empty string if not found. """ if context.variables is None: raise ValueError("Context has no variables") @@ -65,15 +74,25 @@ async def recall_async(self, ask: str, context: SKContext) -> str: if relevance is None or str(relevance).strip() == "": relevance = TextMemorySkill.DEFAULT_RELEVANCE + limit = ( + context.variables[TextMemorySkill.LIMIT_PARAM] + if context.variables.contains_key(TextMemorySkill.LIMIT_PARAM) + else TextMemorySkill.DEFAULT_LIMIT + ) + if limit is None or str(limit).strip() == "": + limit = TextMemorySkill.DEFAULT_LIMIT + results = await context.memory.search_async( - collection, ask, min_relevance_score=float(relevance) + collection=collection, + query=ask, + limit=int(limit), + min_relevance_score=float(relevance), ) if results is None or len(results) == 0: if context.log is not None: context.log.warning(f"Memory not found in collection: {collection}") return "" - - return results[0].text if results[0].text is not None else "" + return results[0].text if limit == 1 else json.dumps([r.text for r in results]) @sk_function( description="Save information to semantic memory", diff --git a/python/semantic_kernel/kernel.py b/python/semantic_kernel/kernel.py index abfad1749608..f49b711a88c5 100644 --- a/python/semantic_kernel/kernel.py +++ b/python/semantic_kernel/kernel.py @@ -133,6 +133,39 @@ def register_semantic_function( return function + def register_native_function( + self, + skill_name: Optional[str], + sk_function: Callable, + ) -> SKFunctionBase: + if not hasattr(sk_function, "__sk_function__"): + raise KernelException( + KernelException.ErrorCodes.InvalidFunctionType, + "sk_function argument must be decorated with @sk_function", + ) + function_name = sk_function.__sk_function_name__ + + if skill_name is None or skill_name == "": + skill_name = SkillCollection.GLOBAL_SKILL + assert skill_name is not None # for type checker + + validate_skill_name(skill_name) + validate_function_name(function_name) + + function = SKFunction.from_native_method(sk_function, skill_name, self.logger) + + if self.skills.has_function(skill_name, function_name): + raise KernelException( + KernelException.ErrorCodes.FunctionOverloadNotSupported, + "Overloaded functions are not supported, " + "please differentiate function names.", + ) + + function.set_default_skill_collection(self.skills) + self._skill_collection.add_native_function(function) + + return function + async def run_stream_async( self, *functions: Any, diff --git a/python/semantic_kernel/orchestration/sk_function.py b/python/semantic_kernel/orchestration/sk_function.py index 94d3c0b84d7d..e9cfc87d9a68 100644 --- a/python/semantic_kernel/orchestration/sk_function.py +++ b/python/semantic_kernel/orchestration/sk_function.py @@ -82,6 +82,7 @@ def from_native_method(method, skill_name="", log=None) -> "SKFunction": if ( hasattr(method, "__sk_function_input_description__") and method.__sk_function_input_description__ is not None + and method.__sk_function_input_description__ != "" ): input_param = ParameterView( "input", diff --git a/python/semantic_kernel/planning/__init__.py b/python/semantic_kernel/planning/__init__.py index 7274719ad92f..be5e35f0b72f 100644 --- a/python/semantic_kernel/planning/__init__.py +++ b/python/semantic_kernel/planning/__init__.py @@ -4,10 +4,12 @@ from semantic_kernel.planning.basic_planner import BasicPlanner from semantic_kernel.planning.plan import Plan from semantic_kernel.planning.sequential_planner import SequentialPlanner +from semantic_kernel.planning.stepwise_planner import StepwisePlanner __all__ = [ - "SequentialPlanner", "BasicPlanner", "Plan", + "SequentialPlanner", + "StepwisePlanner", "ActionPlanner", ] diff --git a/python/semantic_kernel/planning/stepwise_planner/Skills/StepwiseStep/config.json b/python/semantic_kernel/planning/stepwise_planner/Skills/StepwiseStep/config.json new file mode 100644 index 000000000000..0802ff14375e --- /dev/null +++ b/python/semantic_kernel/planning/stepwise_planner/Skills/StepwiseStep/config.json @@ -0,0 +1,32 @@ +{ + "schema": 1, + "description": "Given a request or command or goal generate multi-step plan to reach the goal. After each step LLM is called to perform the reasoning for the next step.", + "type": "completion", + "completion": { + "max_tokens": 1024, + "temperature": 0, + "top_p": 0, + "presence_penalty": 0, + "frequency_penalty": 0, + "stop_sequences": ["[OBSERVATION]", "\n[THOUGHT]"] + }, + "input": { + "parameters": [ + { + "name": "question", + "description": "The question to answer", + "defaultValue": "" + }, + { + "name": "agentScratchPad", + "description": "The agent's scratch pad", + "defaultValue": "" + }, + { + "name": "functionDescriptions", + "description": "The manual of the agent's functions", + "defaultValue": "" + } + ] + } + } \ No newline at end of file diff --git a/python/semantic_kernel/planning/stepwise_planner/Skills/StepwiseStep/skprompt.txt b/python/semantic_kernel/planning/stepwise_planner/Skills/StepwiseStep/skprompt.txt new file mode 100644 index 000000000000..2c4576b30056 --- /dev/null +++ b/python/semantic_kernel/planning/stepwise_planner/Skills/StepwiseStep/skprompt.txt @@ -0,0 +1,53 @@ +[INSTRUCTION] +Answer the following questions as accurately as possible using the provided functions. + +[AVAILABLE FUNCTIONS] +The function definitions below are in the following format: +: + inputs: + - : + - ... + +{{$function_descriptions}} +[END AVAILABLE FUNCTIONS] + +[USAGE INSTRUCTIONS] +To use the functions, specify a JSON blob representing an action. The JSON blob should contain an "action" key with the name of the function to use, and an "action_variables" key with a JSON object of string values to use when calling the function. +Do not call functions directly; they must be invoked through an action. +The "action_variables" value should always include an "input" key, even if the input value is empty. Additional keys in the "action_variables" value should match the defined [PARAMETERS] of the named "action" in [AVAILABLE FUNCTIONS]. +Dictionary values in "action_variables" must be strings and represent the actual values to be passed to the function. +Ensure that the $JSON_BLOB contains only a SINGLE action; do NOT return multiple actions. +IMPORTANT: Use only the available functions listed in the [AVAILABLE FUNCTIONS] section. Do not attempt to use any other functions that are not specified. + +Here is an example of a valid $JSON_BLOB: +{ + "action": "functionName", + "action_variables": {"parameterName": "some value", ...} +} +[END USAGE INSTRUCTIONS] +[END INSTRUCTION] + +[THOUGHT PROCESS] +[QUESTION] +the input question I must answer +[THOUGHT] +To solve this problem, I should carefully analyze the given question and identify the necessary steps. Any facts I discover earlier in my thought process should be repeated here to keep them readily available. +[ACTION] +{ + "action": "functionName", + "action_variables": {"parameterName": "some value", ...} +} +[OBSERVATION] +The result of the action will be provided here. +... (These Thought/Action/Observation can repeat until the final answer is reached.) +[FINAL ANSWER] +Once I have gathered all the necessary observations and performed any required actions, I can provide the final answer in a clear and human-readable format. +[END THOUGHT PROCESS] + +Let's break down the problem step by step and think about the best approach. Questions and observations should be followed by a single thought and an optional single action to take. + +Begin! + +[QUESTION] +{{$question}} +{{$agent_scratch_pad}} \ No newline at end of file diff --git a/python/semantic_kernel/planning/stepwise_planner/__init__.py b/python/semantic_kernel/planning/stepwise_planner/__init__.py new file mode 100644 index 000000000000..7a72895111c7 --- /dev/null +++ b/python/semantic_kernel/planning/stepwise_planner/__init__.py @@ -0,0 +1,5 @@ +from semantic_kernel.planning.stepwise_planner.stepwise_planner import StepwisePlanner + +__all__ = [ + "StepwisePlanner", +] diff --git a/python/semantic_kernel/planning/stepwise_planner/stepwise_planner.py b/python/semantic_kernel/planning/stepwise_planner/stepwise_planner.py new file mode 100644 index 000000000000..96490c065cf6 --- /dev/null +++ b/python/semantic_kernel/planning/stepwise_planner/stepwise_planner.py @@ -0,0 +1,461 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +import itertools +import json +import os +import re +from typing import TYPE_CHECKING, Dict, List + +from semantic_kernel.kernel import Kernel +from semantic_kernel.orchestration.sk_context import SKContext +from semantic_kernel.planning.plan import Plan +from semantic_kernel.planning.planning_exception import PlanningException +from semantic_kernel.planning.stepwise_planner.stepwise_planner_config import ( + StepwisePlannerConfig, +) +from semantic_kernel.planning.stepwise_planner.system_step import SystemStep +from semantic_kernel.semantic_functions.prompt_template import PromptTemplate +from semantic_kernel.semantic_functions.prompt_template_config import ( + PromptTemplateConfig, +) +from semantic_kernel.semantic_functions.semantic_function_config import ( + SemanticFunctionConfig, +) +from semantic_kernel.skill_definition.function_view import FunctionView +from semantic_kernel.skill_definition.sk_function_context_parameter_decorator import ( + sk_function_context_parameter, +) +from semantic_kernel.skill_definition.sk_function_decorator import sk_function + +if TYPE_CHECKING: + from semantic_kernel.orchestration.sk_function_base import SKFunctionBase + + +CUR_DIR = os.path.dirname(os.path.realpath(__file__)) +PROMPT_CONFIG_FILE_PATH = os.path.join(CUR_DIR, "Skills/StepwiseStep/config.json") +PROMPT_TEMPLATE_FILE_PATH = os.path.join(CUR_DIR, "Skills/StepwiseStep/skprompt.txt") + + +def read_file(file_path: str) -> str: + with open(file_path, "r") as file: + return file.read() + + +# TODO: Original C# uses "StepwisePlanner_Excluded" for RESTRICTED_SKILL_NAME +RESTRICTED_SKILL_NAME = "StepwisePlanner" +S_FINAL_ANSWER_REGEX = re.compile( + r"\[FINAL[_\s\-]ANSWER\](?P.+)", re.DOTALL +) +S_THOUGHT_REGEX = re.compile( + r"(\[THOUGHT\])?(?P.+?)(?=\[ACTION\]|$)", re.DOTALL +) +S_ACTION_REGEX = re.compile(r"\[ACTION\][^{}]*({(?:[^{}]*{[^{}]*})*[^{}]*})", re.DOTALL) + +ACTION = "[ACTION]" +THOUGHT = "[THOUGHT]" +OBSERVATION = "[OBSERVATION]" +SCRATCH_PAD_PREFIX = ( + "This was my previous work (but they haven't seen any of it!" + " They only see what I return as final answer):" +) + + +def is_null_or_empty(value: str) -> bool: + return value is None or value == "" + + +class StepwisePlanner: + config: StepwisePlannerConfig + _context: "SKContext" + _function_flow_function: "SKFunctionBase" + + def __init__( + self, + kernel: Kernel, + config: StepwisePlannerConfig = None, + prompt: str = None, + prompt_user_config: PromptTemplateConfig = None, + ): + assert isinstance(kernel, Kernel) + self._kernel = kernel + + self.config = config or StepwisePlannerConfig() + self.config.excluded_skills.append(RESTRICTED_SKILL_NAME) + + prompt_config = prompt_user_config or PromptTemplateConfig() + prompt_template = prompt or read_file(PROMPT_TEMPLATE_FILE_PATH) + + if prompt_user_config is None: + prompt_config = PromptTemplateConfig.from_json( + read_file(PROMPT_CONFIG_FILE_PATH) + ) + + prompt_config.completion.max_tokens = self.config.max_tokens + + self._system_step_function = self.import_semantic_function( + kernel, "StepwiseStep", prompt_template, prompt_config + ) + self._native_functions = self._kernel.import_skill(self, RESTRICTED_SKILL_NAME) + + self._context = kernel.create_new_context() + self._logger = self._kernel.logger + + def create_plan(self, goal: str) -> Plan: + if is_null_or_empty(goal): + raise PlanningException( + PlanningException.ErrorCodes.InvalidGoal, "The goal specified is empty" + ) + + function_descriptions = self.get_function_descriptions() + + plan_step: Plan = Plan.from_function(self._native_functions["ExecutePlan"]) + plan_step.parameters.set("function_descriptions", function_descriptions) + plan_step.parameters.set("question", goal) + + plan_step._outputs.append("agent_scratch_pad") + plan_step._outputs.append("step_count") + plan_step._outputs.append("skill_count") + plan_step._outputs.append("steps_taken") + + plan = Plan(goal) + + plan.add_steps([plan_step]) + + return plan + + # TODO: sync C# with https://github.com/microsoft/semantic-kernel/pull/1195 + @sk_function(name="ExecutePlan", description="Execute a plan") + @sk_function_context_parameter( + name="question", description="The question to answer" + ) + @sk_function_context_parameter( + name="function_descriptions", description="List of tool descriptions" + ) + async def execute_plan_async(self, context: SKContext): + question = context["question"] + + steps_taken: List[SystemStep] = [] + if not is_null_or_empty(question): + for i in range(self.config.max_iterations): + scratch_pad = self.create_scratch_pad(question, steps_taken) + + context.variables.set("agent_scratch_pad", scratch_pad) + + llm_response = await self._system_step_function.invoke_async( + context=context + ) + + if llm_response.error_occurred: + raise PlanningException( + PlanningException.ErrorCodes.UnknownError, + f"Error occurred while executing stepwise plan: {llm_response.last_exception}", + llm_response.last_exception, + ) + + action_text = llm_response.result.strip() + self._logger.debug(f"Response: {action_text}") + + next_step = self.parse_result(action_text) + steps_taken.append(next_step) + + if not is_null_or_empty(next_step.final_answer): + self._logger.debug(f"Final Answer: {next_step.final_answer}") + + context.variables.update(next_step.final_answer) + updated_scratch_pad = self.create_scratch_pad(question, steps_taken) + context.variables.set("agent_scratch_pad", updated_scratch_pad) + + # Add additional results to the context + self.add_execution_stats_to_context(steps_taken, context) + + return context + + self._logger.debug("Thoughts: {next_step.thought}") + + if not is_null_or_empty(next_step.action): + self._logger.info(f"Action: {next_step.action}. Iteration: {i+1}.") + self._logger.debug( + f"Action: {next_step.action}({next_step.action_variables}). Iteration: {i+1}.", + ) + + try: + await asyncio.sleep(self.config.min_iteration_time_ms / 1000) + result = await self.invoke_action_async( + next_step.action, next_step.action_variables + ) + + if is_null_or_empty(result): + next_step.observation = "Got no result from action" + else: + next_step.observation = result + + except Exception as e: + next_step.observation = ( + f"Error invoking action {next_step.action}: {str(e)}" + ) + self._logger.warning( + f"Error invoking action {next_step.action}" + ) + + self._logger.debug(f"Observation: {next_step.observation}") + else: + self._logger.info("Action: No action to take") + + # sleep 3 seconds + await asyncio.sleep(self.config.min_iteration_time_ms / 1000) + + steps_taken_str = json.dumps([s.__dict__ for s in steps_taken], indent=4) + context.variables.update( + f"Result not found, review _steps_taken to see what happened.\n{steps_taken_str}" + ) + else: + context.variables.update("Question not found.") + + return context + + def parse_result(self, input: str): + result = SystemStep(original_response=input) + + # Extract final answer + final_answer_match = re.search(S_FINAL_ANSWER_REGEX, input) + + if final_answer_match: + result.final_answer = final_answer_match.group(1).strip() + return result + + # Extract thought + thought_match = re.search(S_THOUGHT_REGEX, input) + + if thought_match: + result.thought = thought_match.group(0).strip() + elif not input.contains(ACTION): + result.thought = input + else: + raise ValueError("Unexpected input format") + + result.thought = result.thought.replace(THOUGHT, "").strip() + + # Extract action + action_match = re.search(S_ACTION_REGEX, input) + + if action_match: + action_json = action_match.group(1).strip() + + try: + system_step_results = json.loads(action_json) + + if system_step_results is None or len(system_step_results) == 0: + result.observation = ( + f"System step parsing error, empty JSON: {action_json}" + ) + else: + result.action = system_step_results["action"] + result.action_variables = system_step_results["action_variables"] + except Exception: + result.observation = ( + f"System step parsing error, invalid JSON: {action_json}" + ) + + if is_null_or_empty(result.thought) and is_null_or_empty(result.action): + result.observation = ( + "System step error, no thought or action found.", + "Please give a valid thought and/or action.", + ) + + return result + + def add_execution_stats_to_context( + self, steps_taken: List[SystemStep], context: SKContext + ): + context.variables.set("step_count", str(len(steps_taken))) + context.variables.set( + "steps_taken", json.dumps([s.__dict__ for s in steps_taken], indent=4) + ) + + action_counts: Dict[str, int] = {} + for step in steps_taken: + if is_null_or_empty(step.action): + continue + + current_count = action_counts.get(step.action, 0) + action_counts[step.action] = current_count + 1 + + skill_call_list_with_counts = [ + f"{skill}({action_counts[skill]})" for skill in action_counts + ] + skill_call_list_with_counts = ", ".join(skill_call_list_with_counts) + skill_call_count_str = str(sum(action_counts.values())) + + context.variables.set( + "skill_count", f"{skill_call_count_str} ({skill_call_list_with_counts})" + ) + + def create_scratch_pad(self, question: str, steps_taken: List[SystemStep]) -> str: + if len(steps_taken) == 0: + return "" + + scratch_pad_lines: List[str] = [] + + # Add the original first thought + scratch_pad_lines.append(SCRATCH_PAD_PREFIX) + scratch_pad_lines.append(f"{THOUGHT}\n{steps_taken[0].thought}") + + # Keep track of where to insert the next step + insert_point = len(scratch_pad_lines) + + for i in reversed(range(len(steps_taken))): + if len(scratch_pad_lines) / 4.0 > (self.config.max_tokens * 0.75): + self._logger.debug( + f"Scratchpad is too long, truncating. Skipping {i + 1} steps." + ) + break + + s = steps_taken[i] + + if not is_null_or_empty(s.observation): + scratch_pad_lines.insert( + insert_point, f"{OBSERVATION}\n{s.observation}" + ) + + if not is_null_or_empty(s.action): + scratch_pad_lines.insert( + insert_point, + f'{ACTION}\n{{"action": "{s.action}", "action_variables": {json.dumps(s.action_variables)}}}', + ) + + if i != 0: + scratch_pad_lines.insert(insert_point, f"{THOUGHT}\n{s.thought}") + + scratch_pad = "\n".join(scratch_pad_lines).strip() + + if not (is_null_or_empty(scratch_pad.strip())): + self._logger.debug(f"Scratchpad: {scratch_pad}") + + return scratch_pad + + async def invoke_action_async( + self, action_name: str, action_variables: Dict[str, str] + ) -> str: + available_functions = self.get_available_functions() + target_function = next( + ( + f + for f in available_functions + if self.to_fully_qualified_name(f) == action_name + ), + None, + ) + + if target_function is None: + raise PlanningException( + PlanningException.ErrorCodes.UnknownError, + f"The function '{action_name}' was not found.", + ) + + try: + function = self._kernel.func( + target_function.skill_name, target_function.name + ) + action_context = self.create_action_context(action_variables) + + result = await function.invoke_async(context=action_context) + + if result.error_occurred: + self._logger.error(f"Error occurred: {result.last_exception}") + return f"Error occurred: {result.last_exception}" + + self._logger.debug( + f"Invoked {target_function.name}. Result: {result.result}" + ) + + return result.result + + except Exception as e: + self._logger.error( + e, + f"Something went wrong in system step: {target_function.skill_name}.{target_function.name}. Error: {e}", + ) + return ( + "Something went wrong in system step: ", + f"{target_function.skill_name}.{target_function.name}. Error: {e}", + ) + + def create_action_context(self, action_variables: Dict[str, str]) -> SKContext: + action_context = self._kernel.create_new_context() + if action_variables is not None: + for k, v in action_variables.items(): + action_context.variables.set(k, v) + + return action_context + + def get_available_functions(self) -> List[FunctionView]: + functions_view = self._context.skills.get_functions_view() + + excluded_skills = self.config.excluded_skills or [] + excluded_functions = self.config.excluded_functions or [] + + available_functions: List[FunctionView] = [ + *functions_view.semantic_functions.values(), + *functions_view.native_functions.values(), + ] + available_functions = itertools.chain.from_iterable(available_functions) + available_functions = [ + func + for func in available_functions + if ( + func.skill_name not in excluded_skills + and func.name not in excluded_functions + ) + ] + available_functions = sorted( + available_functions, key=lambda x: (x.skill_name, x.name) + ) + + return available_functions + + def get_function_descriptions(self) -> str: + available_functions = self.get_available_functions() + + function_descriptions = "\n".join( + [self.to_manual_string(f) for f in available_functions] + ) + return function_descriptions + + def import_semantic_function( + self, + kernel: Kernel, + function_name: str, + prompt_template: str, + config: PromptTemplateConfig = None, + ) -> "SKFunctionBase": + template = PromptTemplate( + prompt_template, kernel.prompt_template_engine, config + ) + function_config = SemanticFunctionConfig(config, template) + + return kernel.register_semantic_function( + RESTRICTED_SKILL_NAME, function_name, function_config + ) + + def to_manual_string(self, function: FunctionView) -> str: + inputs = [ + f" - {parameter.name}: {parameter.description}" + + ( + f" (default value={parameter.default_value})" + if parameter.default_value + else "" + ) + for parameter in function.parameters + ] + inputs = "\n".join(inputs) + + function_description = function.description.strip() + + if is_null_or_empty(inputs): + return f"{self.to_fully_qualified_name(function)}: {function_description}\n inputs: None\n" + + return f"{self.to_fully_qualified_name(function)}: {function_description}\n inputs:\n{inputs}\n" + + def to_fully_qualified_name(self, function: FunctionView): + return f"{function.skill_name}.{function.name}" diff --git a/python/semantic_kernel/planning/stepwise_planner/stepwise_planner_config.py b/python/semantic_kernel/planning/stepwise_planner/stepwise_planner_config.py new file mode 100644 index 000000000000..0654a829dd9c --- /dev/null +++ b/python/semantic_kernel/planning/stepwise_planner/stepwise_planner_config.py @@ -0,0 +1,25 @@ +# Copyright (c) Microsoft. All rights reserved. + +from typing import List, Optional + + +class StepwisePlannerConfig: + def __init__( + self, + relevancy_threshold: Optional[float] = None, + max_relevant_functions: int = 100, + excluded_skills: List[str] = None, + excluded_functions: List[str] = None, + included_functions: List[str] = None, + max_tokens: int = 1024, + max_iterations: int = 100, + min_iteration_time_ms: int = 0, + ): + self.relevancy_threshold: float = relevancy_threshold + self.max_relevant_functions: int = max_relevant_functions + self.excluded_skills: List[str] = excluded_skills or [] + self.excluded_functions: List[str] = excluded_functions or [] + self.included_functions: List[str] = included_functions or [] + self.max_tokens: int = max_tokens + self.max_iterations: int = max_iterations + self.min_iteration_time_ms: int = min_iteration_time_ms diff --git a/python/semantic_kernel/planning/stepwise_planner/system_step.py b/python/semantic_kernel/planning/stepwise_planner/system_step.py new file mode 100644 index 000000000000..6d14bf198f73 --- /dev/null +++ b/python/semantic_kernel/planning/stepwise_planner/system_step.py @@ -0,0 +1,12 @@ +from dataclasses import dataclass, field +from typing import Dict, Optional + + +@dataclass +class SystemStep: + thought: Optional[str] = None + action: Optional[str] = None + action_variables: Optional[Dict[str, str]] = field(default_factory=dict) + observation: Optional[str] = None + final_answer: Optional[str] = None + original_response: Optional[str] = None diff --git a/python/semantic_kernel/utils/settings.py b/python/semantic_kernel/utils/settings.py index d8f0bf918714..e0e55cdb32a0 100644 --- a/python/semantic_kernel/utils/settings.py +++ b/python/semantic_kernel/utils/settings.py @@ -79,3 +79,35 @@ def pinecone_settings_from_dot_env() -> Tuple[str, str]: assert environment, "Pinecone environment not found in .env file" return api_key, environment + + +def bing_search_settings_from_dot_env() -> str: + """Reads the Bing Search API key from the .env file. + + Returns: + Tuple[str, str]: The Bing Search API key, the Bing Search endpoint + """ + + api_key = None + config = dotenv_values(".env") + api_key = config.get("BING_API_KEY", None) + + assert api_key is not None, "Bing Search API key not found in .env file" + + return api_key + + +def google_palm_settings_from_dot_env() -> str: + """ + Reads the Google PaLM API key from the .env file. + + Returns: + str: The Google PaLM API key + """ + + config = dotenv_values(".env") + api_key = config.get("GOOGLE_PALM_API_KEY", None) + + assert api_key is not None, "Google PaLM API key not found in .env file" + + return api_key diff --git a/python/tests/conftest.py b/python/tests/conftest.py index a90ffb90ddf0..be99f5bc2ab3 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -37,3 +37,14 @@ def get_oai_config(): api_key, org_id = sk.openai_settings_from_dot_env() return api_key, org_id + + +@pytest.fixture(scope="session") +def get_gp_config(): + if "Python_Integration_Tests" in os.environ: + api_key = os.environ["GOOGLE_PALM_API_KEY"] + else: + # Load credentials from .env file + api_key = sk.google_palm_settings_from_dot_env() + + return api_key diff --git a/python/tests/integration/completions/conftest.py b/python/tests/integration/completions/conftest.py index 323335ff560e..7b0a48ceca49 100644 --- a/python/tests/integration/completions/conftest.py +++ b/python/tests/integration/completions/conftest.py @@ -1,9 +1,14 @@ # Copyright (c) Microsoft. All rights reserved. +import sys + import pytest import semantic_kernel.connectors.ai.hugging_face as sk_hf +if sys.version_info >= (3, 9): + import semantic_kernel.connectors.ai.google_palm as sk_gp + @pytest.fixture( scope="module", @@ -149,3 +154,27 @@ def setup_summarize_conversation_using_skill(create_kernel): John: Yeah, that's a good idea.""" yield kernel, ChatTranscript + + +@pytest.fixture(scope="module") +def setup_gp_text_completion_function(create_kernel, get_gp_config): + kernel = create_kernel + api_key = get_gp_config + # Configure LLM service + palm_text_completion = sk_gp.GooglePalmTextCompletion( + "models/text-bison-001", api_key + ) + kernel.add_text_completion_service("models/text-bison-001", palm_text_completion) + + # Define semantic function using SK prompt template language + sk_prompt = "Hello, I like {{$input}}{{$input2}}" + + # Create the semantic function + text2text_function = kernel.create_semantic_function( + sk_prompt, max_tokens=25, temperature=0.7, top_p=0.5 + ) + + # User input + simple_input = "sleeping and " + + yield kernel, text2text_function, simple_input diff --git a/python/tests/integration/completions/test_gp_chat_service.py b/python/tests/integration/completions/test_gp_chat_service.py new file mode 100644 index 000000000000..e4701e3eed95 --- /dev/null +++ b/python/tests/integration/completions/test_gp_chat_service.py @@ -0,0 +1,60 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +import os +import sys + +import pytest + +if sys.version_info >= (3, 9): + import semantic_kernel.connectors.ai.google_palm as sk_gp + +pytestmark = [ + pytest.mark.skipif( + sys.version_info < (3, 9), reason="Google Palm requires Python 3.9 or greater" + ), + pytest.mark.skipif( + "Python_Integration_Tests" in os.environ, + reason="Google Palm integration tests are only set up to run locally", + ), +] + + +@pytest.mark.asyncio +async def test_gp_chat_service_with_skills( + setup_tldr_function_for_oai_models, get_gp_config +): + kernel, sk_prompt, text_to_summarize = setup_tldr_function_for_oai_models + api_key = get_gp_config + + print("* Service: Google PaLM Chat Completion") + print("* Model: chat-bison-001") + palm_chat_completion = sk_gp.GooglePalmChatCompletion( + "models/chat-bison-001", api_key + ) + kernel.add_chat_service("models/chat-bison-001", palm_chat_completion) + + # Create the semantic function + tldr_function = kernel.create_semantic_function( + sk_prompt, max_tokens=200, temperature=0, top_p=0.5 + ) + + max_retries = 5 # Adjust the number of retries as per your requirement + retry_delay = 2 # Adjust the delay (in seconds) between retries + + for _ in range(max_retries): + try: + summary = await kernel.run_async(tldr_function, input_str=text_to_summarize) + output = str(summary).strip() + print(f"TLDR using input string: '{output}'") + assert "First Law" not in output and ( + "human" in output or "Human" in output or "preserve" in output + ) + assert len(output) < 100 + break + except Exception as e: + print(f"Error occurred: {e}") + await asyncio.sleep(retry_delay) # Introduce a delay before the next retry + else: + # The loop completed without breaking, meaning all retries failed + raise AssertionError("Test failed after multiple retries") diff --git a/python/tests/integration/completions/test_gp_text_service.py b/python/tests/integration/completions/test_gp_text_service.py new file mode 100644 index 000000000000..125f771fb20b --- /dev/null +++ b/python/tests/integration/completions/test_gp_text_service.py @@ -0,0 +1,118 @@ +# Copyright (c) Microsoft. All rights reserved. + +import os +import sys + +import pytest + +import semantic_kernel as sk + +pytestmark = [ + pytest.mark.skipif( + sys.version_info < (3, 9), reason="Google Palm requires Python 3.9 or greater" + ), + pytest.mark.skipif( + "Python_Integration_Tests" in os.environ, + reason="Google Palm integration tests are only set up to run locally", + ), +] + + +@pytest.mark.asyncio +async def test_text2text_generation_input_str(setup_gp_text_completion_function): + kernel, text2text_function, simple_input = setup_gp_text_completion_function + + # Complete input string and print + summary = await kernel.run_async(text2text_function, input_str=simple_input) + + output = str(summary).strip() + print(f"Completion using input string: '{output}'") + assert len(output) > 0 + + +@pytest.mark.asyncio +async def test_text2text_generation_input_vars(setup_gp_text_completion_function): + kernel, text2text_function, simple_input = setup_gp_text_completion_function + + # Complete input as context variable and print + context_vars = sk.ContextVariables(simple_input) + summary = await kernel.run_async(text2text_function, input_vars=context_vars) + + output = str(summary).strip() + print(f"Completion using context variables: '{output}'") + assert len(output) > 0 + + +@pytest.mark.asyncio +async def test_text2text_generation_input_context(setup_gp_text_completion_function): + kernel, text2text_function, simple_input = setup_gp_text_completion_function + + # Complete input context and print + context = kernel.create_new_context() + context["input"] = simple_input + summary = await kernel.run_async(text2text_function, input_context=context) + + output = str(summary).strip() + print(f"Completion using input context: '{output}'") + assert len(output) > 0 + + +@pytest.mark.asyncio +async def test_text2text_generation_input_context_with_vars( + setup_gp_text_completion_function, +): + kernel, text2text_function, simple_input = setup_gp_text_completion_function + + # Complete input context with additional variables and print + context = kernel.create_new_context() + context["input"] = simple_input + context_vars = sk.ContextVariables("running and") + summary = await kernel.run_async( + text2text_function, input_context=context, input_vars=context_vars + ) + + output = str(summary).strip() + print(f"Completion using context and additional variables: '{output}'") + assert len(output) > 0 + + +@pytest.mark.asyncio +async def test_text2text_generation_input_context_with_str( + setup_gp_text_completion_function, +): + kernel, text2text_function, simple_input = setup_gp_text_completion_function + + # Complete input context with additional input string and print + context = kernel.create_new_context() + context["input"] = simple_input + summary = await kernel.run_async( + text2text_function, input_context=context, input_str="running and" + ) + + output = str(summary).strip() + print(f"Completion using context and additional string: '{output}'") + assert len(output) > 0 + + +@pytest.mark.asyncio +async def test_text2text_generation_input_context_with_vars_and_str( + setup_gp_text_completion_function, +): + kernel, text2text_function, simple_input = setup_gp_text_completion_function + + # Complete input context with additional variables and string and print + context = kernel.create_new_context() + context["input"] = simple_input + context_vars = sk.ContextVariables(variables={"input2": "running and"}) + summary = await kernel.run_async( + text2text_function, + input_context=context, + input_vars=context_vars, + input_str="new text", + ) + + output = str(summary).strip() + print( + f"Completion using context, additional variables, and additional string: '{output}'" + ) + assert len(output) > 0 diff --git a/python/tests/integration/connectors/memory/test_usearch.py b/python/tests/integration/connectors/memory/test_usearch.py new file mode 100644 index 000000000000..30e24295b42e --- /dev/null +++ b/python/tests/integration/connectors/memory/test_usearch.py @@ -0,0 +1,389 @@ +# Copyright (c) Microsoft. All rights reserved. + +from datetime import datetime +from typing import List + +import numpy as np +import pytest + +from semantic_kernel.connectors.memory.usearch import USearchMemoryStore +from semantic_kernel.memory.memory_record import MemoryRecord + +try: + import pyarrow # noqa: F401 + + pyarrow_installed = True +except ImportError: + pyarrow_installed = False + +try: + import usearch # noqa: F401 + + usearch_installed = True +except ImportError: + usearch_installed = False + + +pytestmark = [ + pytest.mark.skipif(not usearch_installed, reason="`USearch` is not installed"), + pytest.mark.skipif( + not pyarrow_installed, + reason="`USearch` dependency `pyarrow` is not installed", + ), +] + + +@pytest.fixture +def memory_record1(): + return MemoryRecord( + id="test_id1", + text="sample text1", + is_reference=False, + embedding=np.array([0.5, 0.5], dtype=np.float32), + description="description", + additional_metadata="additional metadata", + external_source_name="external source", + timestamp=datetime.now(), + ) + + +@pytest.fixture +def memory_record1_with_collision(): + return MemoryRecord( + id="test_id1", + text="sample text2", + is_reference=False, + embedding=np.array([1, 0.6], dtype=np.float32), + description="description_2", + additional_metadata="additional metadata_2", + external_source_name="external source", + timestamp=datetime.now(), + ) + + +@pytest.fixture +def memory_record2(): + return MemoryRecord( + id="test_id2", + text="sample text2", + is_reference=False, + embedding=np.array([0.25, 0.75], dtype=np.float32), + description="description", + additional_metadata="additional metadata", + external_source_name="external source", + timestamp=datetime.now(), + ) + + +@pytest.fixture +def memory_record3(): + return MemoryRecord( + id="test_id3", + text="sample text3", + is_reference=False, + embedding=np.array([0.25, 0.80], dtype=np.float32), + description="description", + additional_metadata="additional metadata", + external_source_name="external source", + timestamp=datetime.now(), + ) + + +def gen_memory_records( + count: int, ndim: int, start_index: int = 0 +) -> List[MemoryRecord]: + return [ + MemoryRecord( + is_reference=False, + text="random text", + additional_metadata="additional", + external_source_name="external_name", + description="something descriptive", + timestamp=datetime.datetime.now(), + id=f":{start_index + index}", + embedding=np.random.uniform(0, 0.3, (ndim)).astype(np.float32), + ) + for index in range(count) + ] + + +def compare_memory_records( + record1: MemoryRecord, record2: MemoryRecord, with_embedding: bool +): + """Compare two MemoryRecord instances and assert they are the same.""" + + assert ( + record1._key == record2._key + ), f"_key mismatch: {record1._key} != {record2._key}" + assert ( + record1._timestamp == record2._timestamp + ), f"_timestamp mismatch: {record1._timestamp} != {record2._timestamp}" + assert ( + record1._is_reference == record2._is_reference + ), f"_is_reference mismatch: {record1._is_reference} != {record2._is_reference}" + assert ( + record1._external_source_name == record2._external_source_name + ), f"_external_source_name mismatch: {record1._external_source_name} != {record2._external_source_name}" + assert record1._id == record2._id, f"_id mismatch: {record1._id} != {record2._id}" + assert ( + record1._description == record2._description + ), f"_description mismatch: {record1._description} != {record2._description}" + assert ( + record1._text == record2._text + ), f"_text mismatch: {record1._text} != {record2._text}" + assert ( + record1._additional_metadata == record2._additional_metadata + ), f"_additional_metadata mismatch: {record1._additional_metadata} != {record2._additional_metadata}" + if with_embedding is True: + assert np.array_equal( + record1._embedding, record2._embedding + ), "_embedding arrays are not equal" + + +@pytest.mark.asyncio +async def test_create_and_get_collection_async(): + memory = USearchMemoryStore() + + await memory.create_collection_async("test_collection1") + await memory.create_collection_async("test_collection2") + await memory.create_collection_async("test_collection3") + result = await memory.get_collections_async() + + assert len(result) == 3 + assert result == ["test_collection1", "test_collection2", "test_collection3"] + + +@pytest.mark.asyncio +async def test_delete_collection_async(): + memory = USearchMemoryStore() + + await memory.create_collection_async("test_collection") + await memory.delete_collection_async("test_collection") + result = await memory.get_collections_async() + assert len(result) == 0 + + await memory.create_collection_async("test_collection") + await memory.delete_collection_async("TEST_COLLECTION") + result = await memory.get_collections_async() + assert len(result) == 0 + + +@pytest.mark.asyncio +async def test_does_collection_exist_async(): + memory = USearchMemoryStore() + await memory.create_collection_async("test_collection") + result = await memory.does_collection_exist_async("test_collection") + assert result is True + + result = await memory.does_collection_exist_async("TEST_COLLECTION") + assert result is True + + +@pytest.mark.asyncio +async def test_upsert_and_get_async_with_no_embedding(memory_record1: MemoryRecord): + memory = USearchMemoryStore() + await memory.create_collection_async("test_collection", ndim=2) + await memory.upsert_async("test_collection", memory_record1) + + result = await memory.get_async("test_collection", "test_id1", False) + compare_memory_records(result, memory_record1, False) + + +@pytest.mark.asyncio +async def test_upsert_and_get_async_with_embedding(memory_record1: MemoryRecord): + memory = USearchMemoryStore() + await memory.create_collection_async("test_collection", ndim=2) + await memory.upsert_async("test_collection", memory_record1) + + result = await memory.get_async("test_collection", "test_id1", True) + compare_memory_records(result, memory_record1, True) + + +@pytest.mark.asyncio +async def test_upsert_and_get_batch_async( + memory_record1: MemoryRecord, memory_record2: MemoryRecord +): + memory = USearchMemoryStore() + await memory.create_collection_async( + "test_collection", ndim=memory_record1.embedding.shape[0] + ) + + await memory.upsert_batch_async("test_collection", [memory_record1, memory_record2]) + + result = await memory.get_batch_async( + "test_collection", ["test_id1", "test_id2"], True + ) + assert len(result) == 2 + + compare_memory_records(result[0], memory_record1, True) + compare_memory_records(result[1], memory_record2, True) + + +@pytest.mark.asyncio +async def test_remove_async(memory_record1): + memory = USearchMemoryStore() + await memory.create_collection_async( + "test_collection", ndim=memory_record1.embedding.shape[0] + ) + + await memory.upsert_async("test_collection", memory_record1) + await memory.remove_async("test_collection", "test_id1") + + # memory.get_async should raise Exception if record is not found + with pytest.raises(KeyError): + await memory.get_async("test_collection", "test_id1", True) + + +@pytest.mark.asyncio +async def test_remove_batch_async( + memory_record1: MemoryRecord, memory_record2: MemoryRecord +): + memory = USearchMemoryStore() + await memory.create_collection_async( + "test_collection", ndim=memory_record1.embedding.shape[0] + ) + + await memory.upsert_batch_async("test_collection", [memory_record1, memory_record2]) + await memory.remove_batch_async("test_collection", ["test_id1", "test_id2"]) + + result = await memory.get_batch_async( + "test_collection", ["test_id1", "test_id2"], True + ) + assert len(result) == 0 + + +@pytest.mark.asyncio +async def test_get_nearest_match_async( + memory_record1: MemoryRecord, memory_record2: MemoryRecord +): + memory = USearchMemoryStore() + + collection_name = "test_collection" + await memory.create_collection_async( + collection_name, ndim=memory_record1.embedding.shape[0], metric="cos" + ) + + await memory.upsert_batch_async(collection_name, [memory_record1, memory_record2]) + + result = await memory.get_nearest_match_async( + collection_name, np.array([0.5, 0.5]), exact=True + ) + + assert len(result) == 2 + assert isinstance(result[0], MemoryRecord) + assert result[1] == pytest.approx(1, abs=1e-5) + + +@pytest.mark.asyncio +async def test_get_nearest_matches_async( + memory_record1: MemoryRecord, memory_record2: MemoryRecord +): + memory = USearchMemoryStore() + + collection_name = "test_collection" + await memory.create_collection_async( + collection_name, ndim=memory_record1.embedding.shape[0], metric="cos" + ) + + await memory.upsert_batch_async(collection_name, [memory_record1, memory_record2]) + + results = await memory.get_nearest_matches_async( + collection_name, np.array([0.5, 0.5]), limit=2, exact=True + ) + + assert len(results) == 2 + assert isinstance(results[0][0], MemoryRecord) + assert results[0][1] == pytest.approx(1, abs=1e-5) + assert results[1][1] == pytest.approx(0.90450, abs=1e-5) + + +@pytest.mark.asyncio +async def test_create_and_save_collection_async( + tmpdir, memory_record1, memory_record2, memory_record3 +): + memory = USearchMemoryStore(tmpdir) + + await memory.create_collection_async("test_collection1", ndim=2) + await memory.create_collection_async("test_collection2", ndim=2) + await memory.create_collection_async("test_collection3", ndim=2) + await memory.upsert_batch_async( + "test_collection1", [memory_record1, memory_record2] + ) + await memory.upsert_batch_async( + "test_collection2", [memory_record2, memory_record3] + ) + await memory.upsert_batch_async( + "test_collection3", [memory_record1, memory_record3] + ) + await memory.close_async() + + assert (tmpdir / "test_collection1.parquet").exists() + assert (tmpdir / "test_collection1.usearch").exists() + assert (tmpdir / "test_collection2.parquet").exists() + assert (tmpdir / "test_collection2.usearch").exists() + assert (tmpdir / "test_collection3.parquet").exists() + assert (tmpdir / "test_collection3.usearch").exists() + + memory = USearchMemoryStore(tmpdir) + result = await memory.get_collections_async() + assert len(result) == 3 + assert set(result) == {"test_collection1", "test_collection2", "test_collection3"} + await memory.delete_collection_async("test_collection1") + await memory.delete_collection_async("test_collection3") + await memory.close_async() + + memory = USearchMemoryStore(tmpdir) + result = await memory.get_collections_async() + assert len(result) == 1 + assert set(result) == {"test_collection2"} + await memory.delete_collection_async("test_collection2") + await memory.close_async() + + memory = USearchMemoryStore(tmpdir) + result = await memory.get_collections_async() + assert len(result) == 0 + + +@pytest.mark.asyncio +async def test_upsert_and_get_async_with_embedding_with_persist( + tmpdir, memory_record1: MemoryRecord, memory_record1_with_collision: MemoryRecord +): + memory = USearchMemoryStore(tmpdir) + assert len(await memory.get_collections_async()) == 0 + await memory.create_collection_async("test_collection", ndim=2) + await memory.upsert_async("test_collection", memory_record1) + await memory.close_async() + + memory = USearchMemoryStore(tmpdir) + assert len(await memory.get_collections_async()) == 1 + result = await memory.get_async("test_collection", "test_id1", True) + compare_memory_records(result, memory_record1, True) + + await memory.upsert_async("test_collection", memory_record1_with_collision) + result = await memory.get_async("test_collection", "test_id1", True) + compare_memory_records(result, memory_record1_with_collision, True) + await memory.close_async() + + memory = USearchMemoryStore(tmpdir) + assert len(await memory.get_collections_async()) == 1 + result = await memory.get_async("test_collection", "test_id1", True) + compare_memory_records(result, memory_record1_with_collision, True) + + +@pytest.mark.asyncio +async def test_remove_get_async( + memory_record1: MemoryRecord, memory_record2: MemoryRecord +): + memory = USearchMemoryStore() + await memory.create_collection_async( + "test_collection", ndim=memory_record1.embedding.shape[0] + ) + + await memory.upsert_batch_async("test_collection", [memory_record1, memory_record2]) + await memory.remove_async("test_collection", "test_id1") + + result = await memory.get_batch_async( + "test_collection", ["test_id1", "test_id2"], True + ) + assert len(result) == 1 + compare_memory_records(result[0], memory_record2, True) diff --git a/python/tests/integration/embeddings/test_gp_embedding_service.py b/python/tests/integration/embeddings/test_gp_embedding_service.py new file mode 100644 index 000000000000..7e851ab3a6e3 --- /dev/null +++ b/python/tests/integration/embeddings/test_gp_embedding_service.py @@ -0,0 +1,44 @@ +# Copyright (c) Microsoft. All rights reserved. + +import os +import sys + +import pytest + +import semantic_kernel as sk + +if sys.version_info >= (3, 9): + import semantic_kernel.connectors.ai.google_palm as sk_gp + +pytestmark = [ + pytest.mark.skipif( + sys.version_info < (3, 9), reason="Google Palm requires Python 3.9 or greater" + ), + pytest.mark.skipif( + "Python_Integration_Tests" in os.environ, + reason="Google Palm integration tests are only set up to run locally", + ), +] + + +@pytest.mark.asyncio +async def test_gp_embedding_service(create_kernel, get_gp_config): + kernel = create_kernel + + api_key = get_gp_config + + palm_text_embed = sk_gp.GooglePalmTextEmbedding( + "models/embedding-gecko-001", api_key + ) + kernel.add_text_embedding_generation_service("gecko", palm_text_embed) + kernel.register_memory_store(memory_store=sk.memory.VolatileMemoryStore()) + + await kernel.memory.save_information_async( + "test", id="info1", text="this is a test" + ) + await kernel.memory.save_reference_async( + "test", + external_id="info1", + text="this is a test", + external_source_name="external source", + ) diff --git a/python/tests/integration/fakes/writer_skill_fake.py b/python/tests/integration/fakes/writer_skill_fake.py index e370c80f4a78..c19f8bf3c855 100644 --- a/python/tests/integration/fakes/writer_skill_fake.py +++ b/python/tests/integration/fakes/writer_skill_fake.py @@ -1,6 +1,6 @@ # Copyright (c) Microsoft. All rights reserved. -from semantic_kernel.skill_definition.sk_function_decorator import sk_function +from semantic_kernel.skill_definition import sk_function, sk_function_context_parameter # TODO: this fake skill is temporal usage. # C# supports import skill from samples dir by using test helper and python should do the same @@ -15,10 +15,11 @@ class WriterSkillFake: def translate(self, language: str) -> str: return f"Translate: {language}" - @sk_function( - description="Write an outline for a novel", - name="NovelOutline", - input_default_value="", + @sk_function(description="Write an outline for a novel", name="NovelOutline") + @sk_function_context_parameter( + name="endMarker", + description="The marker to use to end each chapter.", + default_value="", ) def write_novel_outline(self, input: str) -> str: return f"Novel outline: {input}" diff --git a/python/tests/integration/planning/sequential_planner/test_sequential_planner.py b/python/tests/integration/planning/sequential_planner/test_sequential_planner.py index 292acab0ec50..36f9be9e6156 100644 --- a/python/tests/integration/planning/sequential_planner/test_sequential_planner.py +++ b/python/tests/integration/planning/sequential_planner/test_sequential_planner.py @@ -1,5 +1,7 @@ # Copyright (c) Microsoft. All rights reserved. +import time + import pytest import semantic_kernel @@ -14,6 +16,19 @@ from tests.integration.fakes.writer_skill_fake import WriterSkillFake +async def retry(func, retries=3): + min_delay = 2 + max_delay = 7 + for i in range(retries): + try: + result = await func() + return result + except Exception: + if i == retries - 1: # Last retry + raise + time.sleep(max(min(i, max_delay), min_delay)) + + def initialize_kernel(get_aoai_config, use_embeddings=False, use_chat_model=False): _, api_key, endpoint = get_aoai_config @@ -102,17 +117,13 @@ async def test_create_plan_with_defaults_async( planner = SequentialPlanner(kernel) # Act - plan = await planner.create_plan_async(prompt) + plan = await retry(lambda: planner.create_plan_async(prompt)) # Assert assert any( step.name == expected_function and step.skill_name == expected_skill - and step.parameters["input"] == expected_default - # TODO: current sk_function decorator only support default values ["input"] key - # TODO: current version of fake skills used inline sk_function but actually most of them already in samples dir. - # add test helper for python to import skills from samples dir. C# already has it. - # and step.parameters["endMarker"] == expected_default + and step.parameters["endMarker"] == expected_default for step in plan._steps ) @@ -147,7 +158,7 @@ async def test_create_plan_goal_relevant_async( ) # Act - plan = await planner.create_plan_async(prompt) + plan = await retry(lambda: planner.create_plan_async(prompt)) # Assert assert any( diff --git a/python/tests/integration/planning/stepwise_planner/test_stepwise_planner.py b/python/tests/integration/planning/stepwise_planner/test_stepwise_planner.py new file mode 100644 index 000000000000..da6414273161 --- /dev/null +++ b/python/tests/integration/planning/stepwise_planner/test_stepwise_planner.py @@ -0,0 +1,174 @@ +# Copyright (c) Microsoft. All rights reserved. + +import json +import os + +import pytest + +import semantic_kernel as sk +import semantic_kernel.connectors.ai.open_ai as sk_oai +from semantic_kernel.connectors.search_engine import BingConnector +from semantic_kernel.core_skills.math_skill import MathSkill +from semantic_kernel.core_skills.time_skill import TimeSkill +from semantic_kernel.kernel import Kernel +from semantic_kernel.orchestration.sk_context import SKContext +from semantic_kernel.planning import StepwisePlanner +from semantic_kernel.planning.stepwise_planner.stepwise_planner_config import ( + StepwisePlannerConfig, +) +from semantic_kernel.skill_definition import sk_function, sk_function_context_parameter + + +class TempWebSearchEngineSkill: + """ + TODO: replace this class with semantic_kernel.core_skills.web_search_engine_skill.WebSearchEngineSkill + + SKFunction.describe() does not contains info for arguments. + + so that `query: str` is not shown in the function description, + BUT this argument must be passed to planner to work appropriately. + + This function temporarily add `query` as parameter by using @sk_function_context_parameter. + original file is here: semantic-kernel/python/semantic_kernel/core_skills/web_search_engine_skill.py + """ + + def __init__(self, connector) -> None: + self._connector = connector + + @sk_function( + description="Performs a web search for a given query", name="searchAsync" + ) + @sk_function_context_parameter( + name="query", + description="The search query", + ) + async def search_async(self, query: str, context: SKContext) -> str: + query = query or context.variables.get("query")[1] + result = await self._connector.search_async(query, num_results=5, offset=0) + return str(result) + + +@pytest.fixture(scope="session") +def get_bing_config(): + if "Python_Integration_Tests" in os.environ: + api_key = os.environ["Bing__ApiKey"] + else: + # Load credentials from .env file + api_key = sk.bing_search_settings_from_dot_env() + + return api_key + + +def initialize_kernel(get_aoai_config, use_embeddings=False, use_chat_model=False): + _, api_key, endpoint = get_aoai_config + + kernel = Kernel() + if use_chat_model: + kernel.add_chat_service( + "chat_completion", + sk_oai.AzureChatCompletion("gpt-35-turbo", endpoint, api_key), + ) + else: + kernel.add_text_completion_service( + "text_completion", + sk_oai.AzureChatCompletion("gpt-35-turbo", endpoint, api_key), + ) + + if use_embeddings: + kernel.add_text_embedding_generation_service( + "text_embedding", + sk_oai.AzureTextEmbedding("text-embedding-ada-002", endpoint, api_key), + ) + return kernel + + +@pytest.mark.parametrize( + "use_chat_model, prompt, expected_function, expected_skill", + [ + ( + False, + "What is the tallest mountain on Earth? How tall is it divided by 2?", + "ExecutePlan", + "StepwisePlanner", + ), + ( + True, + "What is the tallest mountain on Earth? How tall is it divided by 2?", + "ExecutePlan", + "StepwisePlanner", + ), + ], +) +@pytest.mark.asyncio +async def test_can_create_stepwise_plan( + get_aoai_config, + get_bing_config, + use_chat_model, + prompt, + expected_function, + expected_skill, +): + # Arrange + use_embeddings = False + kernel = initialize_kernel(get_aoai_config, use_embeddings, use_chat_model) + bing_connector = BingConnector(api_key=get_bing_config) + web_search_engine_skill = TempWebSearchEngineSkill(bing_connector) + kernel.import_skill(web_search_engine_skill, "WebSearch") + kernel.import_skill(TimeSkill(), "time") + + planner = StepwisePlanner( + kernel, StepwisePlannerConfig(max_iterations=10, min_iteration_time_ms=1000) + ) + + # Act + plan = planner.create_plan(prompt) + + # Assert + assert any( + step.name == expected_function and step.skill_name == expected_skill + for step in plan._steps + ) + + +@pytest.mark.parametrize( + "use_chat_model, prompt", + [ + ( + False, + "What is the tallest mountain on Earth? How tall is it divided by 2?", + ) + ], +) +@pytest.mark.asyncio +async def test_can_execute_stepwise_plan( + get_aoai_config, + get_bing_config, + use_chat_model, + prompt, +): + # Arrange + use_embeddings = False + kernel = initialize_kernel(get_aoai_config, use_embeddings, use_chat_model) + bing_connector = BingConnector(api_key=get_bing_config) + web_search_engine_skill = TempWebSearchEngineSkill(bing_connector) + kernel.import_skill(web_search_engine_skill, "WebSearch") + kernel.import_skill(TimeSkill(), "time") + kernel.import_skill(MathSkill(), "math") + + planner = StepwisePlanner( + kernel, StepwisePlannerConfig(max_iterations=10, min_iteration_time_ms=1000) + ) + + # Act + plan = planner.create_plan(prompt) + result = await plan.invoke_async() + + steps_taken_string = result.variables["steps_taken"] + assert steps_taken_string is not None + + steps_taken = json.loads(steps_taken_string) + assert steps_taken is not None and len(steps_taken) > 0 + + assert ( + 3 <= len(steps_taken) <= 10 + ), f"Actual: {len(steps_taken)}. Expected at least 3 steps and at most 10 steps to be taken." diff --git a/python/tests/unit/ai/google_palm/services/test_palm_chat_completion.py b/python/tests/unit/ai/google_palm/services/test_palm_chat_completion.py new file mode 100644 index 000000000000..d22f19e2d992 --- /dev/null +++ b/python/tests/unit/ai/google_palm/services/test_palm_chat_completion.py @@ -0,0 +1,82 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +import sys +from unittest.mock import MagicMock, patch + +import pytest + +from semantic_kernel.connectors.ai.chat_request_settings import ( + ChatRequestSettings, +) + +if sys.version_info >= (3, 9): + from semantic_kernel.connectors.ai.google_palm.services.gp_chat_completion import ( + GooglePalmChatCompletion, + ) + + +pytestmark = pytest.mark.skipif( + sys.version_info < (3, 9), reason="Google Palm requires Python 3.9 or greater" +) + + +def test_google_palm_chat_completion_init() -> None: + model_id = "test_model_id" + api_key = "test_api_key" + + gp_chat_completion = GooglePalmChatCompletion( + model_id=model_id, + api_key=api_key, + ) + + assert gp_chat_completion._model_id == model_id + assert gp_chat_completion._api_key == api_key + assert isinstance(gp_chat_completion, GooglePalmChatCompletion) + + +def test_google_palm_chat_completion_init_with_empty_api_key() -> None: + model_id = "test_model_id" + # api_key = "test_api_key" + + with pytest.raises( + ValueError, match="The Google PaLM API key cannot be `None` or empty" + ): + GooglePalmChatCompletion( + model_id=model_id, + api_key="", + ) + + +@pytest.mark.asyncio +async def test_google_palm_text_completion_complete_chat_async_call_with_parameters() -> None: + mock_response = MagicMock() + mock_response.last = asyncio.Future() + mock_response.last.set_result("Example response") + mock_gp = MagicMock() + mock_gp.chat.return_value = mock_response + with patch( + "semantic_kernel.connectors.ai.google_palm.services.gp_chat_completion.palm", + new=mock_gp, + ): + model_id = "test_model_id" + api_key = "test_api_key" + prompt = [("user", "hello world")] + gp_chat_completion = GooglePalmChatCompletion( + model_id=model_id, + api_key=api_key, + ) + settings = ChatRequestSettings() + response = await gp_chat_completion.complete_chat_async(prompt, settings) + assert isinstance(response.result(), str) and len(response.result()) > 0 + + mock_gp.chat.assert_called_once_with( + model=model_id, + context="", + examples=None, + temperature=settings.temperature, + candidate_count=settings.number_of_responses, + top_p=settings.top_p, + prompt=None, + messages=prompt[-1][1], + ) diff --git a/python/tests/unit/ai/google_palm/services/test_palm_text_completion.py b/python/tests/unit/ai/google_palm/services/test_palm_text_completion.py new file mode 100644 index 000000000000..3a9ede666f58 --- /dev/null +++ b/python/tests/unit/ai/google_palm/services/test_palm_text_completion.py @@ -0,0 +1,81 @@ +# Copyright (c) Microsoft. All rights reserved. +import asyncio +import sys +from unittest.mock import MagicMock, patch + +import pytest + +from semantic_kernel.connectors.ai.complete_request_settings import ( + CompleteRequestSettings, +) + +if sys.version_info >= (3, 9): + from semantic_kernel.connectors.ai.google_palm.services.gp_text_completion import ( + GooglePalmTextCompletion, + ) + + +pytestmark = pytest.mark.skipif( + sys.version_info < (3, 9), reason="Google Palm requires Python 3.9 or greater" +) + + +def test_google_palm_text_completion_init() -> None: + model_id = "test_model_id" + api_key = "test_api_key" + + # Test successful initialization + gp_text_completion = GooglePalmTextCompletion( + model_id=model_id, + api_key=api_key, + ) + + assert gp_text_completion._model_id == model_id + assert gp_text_completion._api_key == api_key + assert isinstance(gp_text_completion, GooglePalmTextCompletion) + + +def test_google_palm_text_completion_init_with_empty_api_key() -> None: + model_id = "test_model_id" + # api_key = "test_api_key" + + with pytest.raises( + ValueError, match="The Google PaLM API key cannot be `None` or empty" + ): + GooglePalmTextCompletion( + model_id=model_id, + api_key="", + ) + + +@pytest.mark.asyncio +async def test_google_palm_text_completion_complete_async_call_with_parameters() -> None: + mock_response = MagicMock() + mock_response.result = asyncio.Future() + mock_response.result.set_result("Example response") + mock_gp = MagicMock() + mock_gp.generate_text.return_value = mock_response + with patch( + "semantic_kernel.connectors.ai.google_palm.services.gp_text_completion.palm", + new=mock_gp, + ): + model_id = "test_model_id" + api_key = "test_api_key" + prompt = "hello world" + gp_text_completion = GooglePalmTextCompletion( + model_id=model_id, + api_key=api_key, + ) + settings = CompleteRequestSettings() + response = await gp_text_completion.complete_async(prompt, settings) + assert isinstance(response.result(), str) and len(response.result()) > 0 + + mock_gp.generate_text.assert_called_once_with( + model=model_id, + prompt=prompt, + temperature=settings.temperature, + max_output_tokens=settings.max_tokens, + stop_sequences=None, + candidate_count=settings.number_of_responses, + top_p=settings.top_p, + ) diff --git a/python/tests/unit/ai/google_palm/services/test_palm_text_embedding.py b/python/tests/unit/ai/google_palm/services/test_palm_text_embedding.py new file mode 100644 index 000000000000..add7507acba1 --- /dev/null +++ b/python/tests/unit/ai/google_palm/services/test_palm_text_embedding.py @@ -0,0 +1,70 @@ +# Copyright (c) Microsoft. All rights reserved. + +import sys +from unittest.mock import MagicMock, patch + +import pytest + +if sys.version_info >= (3, 9): + from semantic_kernel.connectors.ai.google_palm.services.gp_text_embedding import ( + GooglePalmTextEmbedding, + ) + + +pytestmark = pytest.mark.skipif( + sys.version_info < (3, 9), reason="Google Palm requires Python 3.9 or greater" +) + + +def test_google_palm_text_embedding_init() -> None: + model_id = "test_model_id" + api_key = "test_api_key" + + # Test successful initialization + gp_text_embed = GooglePalmTextEmbedding( + model_id=model_id, + api_key=api_key, + ) + + assert gp_text_embed._model_id == model_id + assert gp_text_embed._api_key == api_key + assert isinstance(gp_text_embed, GooglePalmTextEmbedding) + + +def test_google_palm_text_embedding_init_with_empty_api_key() -> None: + model_id = "test_model_id" + # api_key = "test_api_key" + + with pytest.raises( + ValueError, match="The Google PaLM API key cannot be `None` or empty" + ): + GooglePalmTextEmbedding( + model_id=model_id, + api_key="", + ) + + +@pytest.mark.asyncio +async def test_google_palm_text_embedding_calls_with_parameters() -> None: + mock_gp = MagicMock() + mock_gp.generate_embeddings.return_value = {"embedding": [0.1, 0.2, 0.3]} + with patch( + "semantic_kernel.connectors.ai.google_palm.services.gp_text_embedding.palm", + new=mock_gp, + ): + model_id = "test_model_id" + api_key = "test_api_key" + texts = ["hello world"] + text = "hello world" + + gp_text_embedding = GooglePalmTextEmbedding( + model_id=model_id, + api_key=api_key, + ) + + await gp_text_embedding.generate_embeddings_async(texts) + + mock_gp.generate_embeddings.assert_called_once_with( + model=model_id, + text=text, + ) diff --git a/python/tests/unit/ai/open_ai/services/test_azure_chat_completion.py b/python/tests/unit/ai/open_ai/services/test_azure_chat_completion.py index 66161da9569d..fefcb3c432d2 100644 --- a/python/tests/unit/ai/open_ai/services/test_azure_chat_completion.py +++ b/python/tests/unit/ai/open_ai/services/test_azure_chat_completion.py @@ -148,12 +148,13 @@ async def test_azure_chat_completion_call_with_parameters() -> None: organization=None, messages=messages, temperature=complete_request_settings.temperature, - max_tokens=complete_request_settings.max_tokens, top_p=complete_request_settings.top_p, - presence_penalty=complete_request_settings.presence_penalty, - frequency_penalty=complete_request_settings.frequency_penalty, n=complete_request_settings.number_of_responses, stream=False, + stop=None, + max_tokens=complete_request_settings.max_tokens, + presence_penalty=complete_request_settings.presence_penalty, + frequency_penalty=complete_request_settings.frequency_penalty, logit_bias={}, ) @@ -197,11 +198,62 @@ async def test_azure_chat_completion_call_with_parameters_and_Logit_Bias_Defined organization=None, messages=messages, temperature=complete_request_settings.temperature, - max_tokens=complete_request_settings.max_tokens, top_p=complete_request_settings.top_p, + n=complete_request_settings.number_of_responses, + stream=False, + stop=None, + max_tokens=complete_request_settings.max_tokens, presence_penalty=complete_request_settings.presence_penalty, frequency_penalty=complete_request_settings.frequency_penalty, + logit_bias=token_bias, + ) + + +@pytest.mark.asyncio +async def test_azure_chat_completion_call_with_parameters_and_Stop_Defined() -> None: + mock_openai = AsyncMock() + with patch( + "semantic_kernel.connectors.ai.open_ai.services.open_ai_chat_completion.openai", + new=mock_openai, + ): + deployment_name = "test_deployment" + endpoint = "https://test-endpoint.com" + api_key = "test_api_key" + api_type = "azure" + api_version = "2023-03-15-preview" + logger = Logger("test_logger") + prompt = "hello world" + messages = [{"role": "user", "content": prompt}] + complete_request_settings = CompleteRequestSettings() + + stop = ["!"] + complete_request_settings.stop_sequences = stop + + azure_chat_completion = AzureChatCompletion( + deployment_name=deployment_name, + endpoint=endpoint, + api_key=api_key, + api_version=api_version, + logger=logger, + ) + + await azure_chat_completion.complete_async(prompt, complete_request_settings) + + mock_openai.ChatCompletion.acreate.assert_called_once_with( + engine=deployment_name, + api_key=api_key, + api_type=api_type, + api_base=endpoint, + api_version=api_version, + organization=None, + messages=messages, + temperature=complete_request_settings.temperature, + top_p=complete_request_settings.top_p, n=complete_request_settings.number_of_responses, stream=False, - logit_bias=token_bias, + stop=complete_request_settings.stop_sequences, + max_tokens=complete_request_settings.max_tokens, + presence_penalty=complete_request_settings.presence_penalty, + frequency_penalty=complete_request_settings.frequency_penalty, + logit_bias={}, ) diff --git a/python/tests/unit/kernel_extensions/test_register_functions.py b/python/tests/unit/kernel_extensions/test_register_functions.py new file mode 100644 index 000000000000..772f94a8f700 --- /dev/null +++ b/python/tests/unit/kernel_extensions/test_register_functions.py @@ -0,0 +1,61 @@ +# Copyright (c) Microsoft. All rights reserved. + + +import pytest + +from semantic_kernel import Kernel +from semantic_kernel.kernel_exception import KernelException +from semantic_kernel.orchestration.sk_function_base import SKFunctionBase +from semantic_kernel.skill_definition.sk_function_decorator import sk_function +from semantic_kernel.skill_definition.skill_collection import SkillCollection + + +def not_decorated_native_function(arg1: str) -> str: + return "test" + + +@sk_function(name="getLightStatus") +def decorated_native_function(arg1: str) -> str: + return "test" + + +def test_register_valid_native_function(): + kernel = Kernel() + + registered_func = kernel.register_native_function( + "TestSkill", decorated_native_function + ) + + assert isinstance(registered_func, SKFunctionBase) + assert ( + kernel.skills.get_native_function("TestSkill", "getLightStatus") + == registered_func + ) + assert registered_func.invoke("testtest").result == "test" + + +def test_register_undecorated_native_function(): + kernel = Kernel() + + with pytest.raises(KernelException): + kernel.register_native_function("TestSkill", not_decorated_native_function) + + +def test_register_with_none_skill_name(): + kernel = Kernel() + + registered_func = kernel.register_native_function(None, decorated_native_function) + assert registered_func.skill_name == SkillCollection.GLOBAL_SKILL + + +def test_register_overloaded_native_function(): + kernel = Kernel() + + kernel.register_native_function("TestSkill", decorated_native_function) + + with pytest.raises(KernelException): + kernel.register_native_function("TestSkill", decorated_native_function) + + +if __name__ == "__main__": + pytest.main([__file__]) diff --git a/python/tests/unit/orchestration/test_native_function.py b/python/tests/unit/orchestration/test_native_function.py new file mode 100644 index 000000000000..6a133c0aafeb --- /dev/null +++ b/python/tests/unit/orchestration/test_native_function.py @@ -0,0 +1,100 @@ +# Copyright (c) Microsoft. All rights reserved. + +from semantic_kernel.orchestration.sk_function import SKFunction +from semantic_kernel.skill_definition.sk_function_decorator import sk_function + + +def test_init_native_function_with_input_description(): + class MockMethodWithInputDescription: + __sk_function__ = True + __sk_function_name__ = "mock_function" + __sk_function_description__ = "Mock description" + __sk_function_input_description__ = "Mock input description" + __sk_function_input_default_value__ = "default_input_value" + __sk_function_context_parameters__ = [ + { + "name": "param1", + "description": "Param 1 description", + "default_value": "default_param1_value", + } + ] + + mock_method = MockMethodWithInputDescription + + native_function = SKFunction.from_native_method(mock_method, "MockSkill") + + assert native_function._function == mock_method + assert native_function._parameters[0].name == "input" + assert native_function._parameters[0].description == "Mock input description" + assert native_function._parameters[0].default_value == "default_input_value" + assert native_function._parameters[1].name == "param1" + assert native_function._parameters[1].description == "Param 1 description" + assert native_function._parameters[1].default_value == "default_param1_value" + + +def test_init_native_function_without_input_description(): + class MockMethodWithoutInputDescription: + __sk_function__ = True + __sk_function_name__ = "mock_function_no_input_desc" + __sk_function_description__ = "Mock description no input desc" + __sk_function_context_parameters__ = [ + { + "name": "param1", + "description": "Param 1 description", + "default_value": "default_param1_value", + } + ] + + mock_method = MockMethodWithoutInputDescription + + native_function = SKFunction.from_native_method(mock_method, "MockSkill") + + assert native_function._function == mock_method + assert native_function._parameters[0].name == "param1" + assert native_function._parameters[0].description == "Param 1 description" + assert native_function._parameters[0].default_value == "default_param1_value" + + +def test_init_native_function_from_sk_function_decorator(): + @sk_function( + description="Test description", + name="test_function", + input_description="Test input description", + input_default_value="test_default_value", + ) + def decorated_function(): + pass + + assert decorated_function.__sk_function__ is True + assert decorated_function.__sk_function_description__ == "Test description" + assert decorated_function.__sk_function_name__ == "test_function" + assert ( + decorated_function.__sk_function_input_description__ == "Test input description" + ) + assert ( + decorated_function.__sk_function_input_default_value__ == "test_default_value" + ) + + native_function = SKFunction.from_native_method(decorated_function, "MockSkill") + + assert native_function._function == decorated_function + assert native_function._parameters[0].name == "input" + assert native_function._parameters[0].description == "Test input description" + assert native_function._parameters[0].default_value == "test_default_value" + + +def test_init_native_function_from_sk_function_decorator_defaults(): + @sk_function() + def decorated_function(): + pass + + assert decorated_function.__sk_function__ is True + assert decorated_function.__sk_function_description__ == "" + assert decorated_function.__sk_function_name__ == "decorated_function" + assert decorated_function.__sk_function_input_description__ == "" + assert decorated_function.__sk_function_input_default_value__ == "" + + native_function = SKFunction.from_native_method(decorated_function, "MockSkill") + + assert native_function._function == decorated_function + assert len(native_function._parameters) == 0 diff --git a/python/tests/unit/planning/stepwise_planner/test_stepwise_planner_parse_result.py b/python/tests/unit/planning/stepwise_planner/test_stepwise_planner_parse_result.py new file mode 100644 index 000000000000..4686c979f2c9 --- /dev/null +++ b/python/tests/unit/planning/stepwise_planner/test_stepwise_planner_parse_result.py @@ -0,0 +1,34 @@ +# Copyright (c) Microsoft. All rights reserved. + +from unittest.mock import Mock + +import pytest + +from semantic_kernel.kernel import Kernel +from semantic_kernel.planning.stepwise_planner.stepwise_planner import StepwisePlanner + + +@pytest.mark.parametrize( + "input, expected", + [ + ("[FINAL ANSWER] 42", "42"), + ("[FINAL ANSWER]42", "42"), + ("I think I have everything I need.\n[FINAL ANSWER] 42", "42"), + ("I think I have everything I need.\n[FINAL ANSWER] 42\n", "42"), + ("I think I have everything I need.\n[FINAL ANSWER] 42\n\n", "42"), + ("I think I have everything I need.\n[FINAL ANSWER]42\n\n\n", "42"), + ("I think I have everything I need.\n[FINAL ANSWER]\n 42\n\n\n", "42"), + ], +) +def test_when_input_is_final_answer_returns_final_answer(input: str, expected: str): + kernel = Mock(spec=Kernel) + + planner = StepwisePlanner(kernel) + + result = planner.parse_result(input) + + assert result.final_answer == expected + + +if __name__ == "__main__": + pytest.main([__file__]) diff --git a/samples/apps/auth-api-webapp-react/src/components/InteractWithGraph.tsx b/samples/apps/auth-api-webapp-react/src/components/InteractWithGraph.tsx index b02797b11fb4..9f0d7255d890 100644 --- a/samples/apps/auth-api-webapp-react/src/components/InteractWithGraph.tsx +++ b/samples/apps/auth-api-webapp-react/src/components/InteractWithGraph.tsx @@ -35,7 +35,7 @@ const InteractWithGraph: FC = ({ uri, config, onBack }) => { inputs: [{ key: 'filePath', value: path }], }, 'documentskill', - 'appendtextasync', + 'appendtext', ); //upload to onedrive @@ -46,7 +46,7 @@ const InteractWithGraph: FC = ({ uri, config, onBack }) => { inputs: [{ key: 'destinationPath', value: destinationPath }], }, 'clouddriveskill', - 'uploadfileasync', + 'uploadfile', ); } catch (e) { alert('Something went wrong.\n\nDetails:\n' + e); @@ -59,9 +59,9 @@ const InteractWithGraph: FC = ({ uri, config, onBack }) => { config, { value: destinationPath }, 'clouddriveskill', - 'createlinkasync', + 'createlink', ); - var myEmail = await sk.invokeAsync(config, { value: '' }, 'emailskill', 'getmyemailaddressasync'); + var myEmail = await sk.invokeAsync(config, { value: '' }, 'emailskill', 'getmyemailaddress'); await sk.invokeAsync( config, @@ -79,7 +79,7 @@ const InteractWithGraph: FC = ({ uri, config, onBack }) => { ], }, 'emailskill', - 'sendemailasync', + 'sendemail', ); } catch (e) { alert('Something went wrong.\n\nDetails:\n' + e); @@ -103,7 +103,7 @@ const InteractWithGraph: FC = ({ uri, config, onBack }) => { ], }, 'tasklistskill', - 'addtaskasync', + 'addtask', ); } catch (e) { alert('Something went wrong.\n\nDetails:\n' + e); diff --git a/samples/dotnet/Directory.Packages.props b/samples/dotnet/Directory.Packages.props index 7604638fe93f..55447fed4318 100644 --- a/samples/dotnet/Directory.Packages.props +++ b/samples/dotnet/Directory.Packages.props @@ -15,7 +15,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/samples/dotnet/KernelHttpServer/Extensions.cs b/samples/dotnet/KernelHttpServer/Extensions.cs index 9e90ecd9c33d..da3e0037db4d 100644 --- a/samples/dotnet/KernelHttpServer/Extensions.cs +++ b/samples/dotnet/KernelHttpServer/Extensions.cs @@ -135,8 +135,6 @@ internal static void RegisterTextMemory(this IKernel kernel) _ = kernel.ImportSkill(new TextMemorySkill(kernel.Memory), nameof(TextMemorySkill)); } - [SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", - Justification = "The caller invokes native skills during a request and the skill instances must remain alive for those requests to be successful.")] internal static void RegisterNativeSkills(this IKernel kernel, IEnumerable? skillsToLoad = null) { if (ShouldLoad(nameof(DocumentSkill), skillsToLoad)) diff --git a/samples/dotnet/KernelHttpServer/KernelHttpServer.csproj b/samples/dotnet/KernelHttpServer/KernelHttpServer.csproj index 15e463231497..e25cb8f44bec 100644 --- a/samples/dotnet/KernelHttpServer/KernelHttpServer.csproj +++ b/samples/dotnet/KernelHttpServer/KernelHttpServer.csproj @@ -11,9 +11,9 @@ - - - + + + diff --git a/samples/dotnet/KernelHttpServer/Plugins/GitHubPlugin.cs b/samples/dotnet/KernelHttpServer/Plugins/GitHubPlugin.cs index 8797060fd0ed..15970ceba2d3 100644 --- a/samples/dotnet/KernelHttpServer/Plugins/GitHubPlugin.cs +++ b/samples/dotnet/KernelHttpServer/Plugins/GitHubPlugin.cs @@ -35,7 +35,7 @@ public class GitHubPlugin private readonly ISKFunction _summarizeCodeFunction; private readonly IKernel _kernel; - private readonly ILogger _logger; + private readonly ILogger _logger; private static readonly char[] s_trimChars = new char[] { ' ', '/' }; internal const string SummarizeCodeSnippetDefinition = @@ -55,10 +55,10 @@ Do not incorporate other general knowledge. /// /// Kernel instance /// Optional logger - public GitHubPlugin(IKernel kernel, ILogger? logger = null) + public GitHubPlugin(IKernel kernel, ILoggerFactory? loggerFactory = null) { this._kernel = kernel; - this._logger = logger ?? NullLogger.Instance; + this._logger = loggerFactory is not null ? loggerFactory.CreateLogger() : NullLogger.Instance; this._summarizeCodeFunction = kernel.CreateSemanticFunction( SummarizeCodeSnippetDefinition, diff --git a/samples/dotnet/graph-api-skills/Program.cs b/samples/dotnet/graph-api-skills/Program.cs index 2ab745e9110a..01b9964d2c5d 100644 --- a/samples/dotnet/graph-api-skills/Program.cs +++ b/samples/dotnet/graph-api-skills/Program.cs @@ -50,7 +50,7 @@ public static async Task Main() .AddDebug(); }); - ILogger logger = loggerFactory.CreateLogger(); + ILogger logger = loggerFactory.CreateLogger(); MsGraphConfiguration? candidateGraphApiConfig = configuration.GetRequiredSection("MsGraph").Get() ?? throw new InvalidOperationException("Missing configuration for Microsoft Graph API."); diff --git a/samples/dotnet/openapi-skills/Program.cs b/samples/dotnet/openapi-skills/Program.cs index 065fdfe03291..c2efc1bc9b5b 100644 --- a/samples/dotnet/openapi-skills/Program.cs +++ b/samples/dotnet/openapi-skills/Program.cs @@ -46,7 +46,7 @@ private static async Task Main(string[] args) .AddConsole() .AddDebug()); - ILogger logger = loggerFactory.CreateLogger(); + ILogger logger = loggerFactory.CreateLogger(); // Initialize semantic kernel AIServiceOptions aiOptions = configuration.GetRequiredSection(AIServiceOptions.PropertyName).Get()