From 6c3b9d43ed116224ea1bd74a3f2dd8a1f2b44ab5 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Tue, 24 Feb 2026 11:26:12 +0000 Subject: [PATCH 01/19] .NET: Upgrade to XUnit 3 and Microsoft Testing Platform (#4176) --- .github/workflows/dotnet-build-and-test.yml | 19 +++-- dotnet/.github/skills/build-and-test/SKILL.md | 44 ++++++++++-- dotnet/Directory.Packages.props | 10 ++- dotnet/global.json | 3 + .../AgentTests.cs | 10 ++- ...opicChatCompletion.IntegrationTests.csproj | 1 + ...pletionChatClientAgentRunStreamingTests.cs | 12 ++-- ...icChatCompletionChatClientAgentRunTests.cs | 12 ++-- .../AnthropicChatCompletionFixture.cs | 10 ++- ...nthropicChatCompletionRunStreamingTests.cs | 43 ++++++++---- .../AnthropicChatCompletionRunTests.cs | 43 ++++++++---- .../AIProjectClientAgentRunStreamingTests.cs | 8 +-- .../AIProjectClientAgentRunTests.cs | 8 +-- ...jectClientAgentStructuredOutputRunTests.cs | 29 ++++---- ...tClientChatClientAgentRunStreamingTests.cs | 4 +- .../AIProjectClientChatClientAgentRunTests.cs | 4 +- .../AIProjectClientFixture.cs | 10 +-- .../AzureAI.IntegrationTests.csproj | 1 + ...AIAgentsPersistent.IntegrationTests.csproj | 1 + .../AzureAIAgentsPersistentFixture.cs | 11 +-- ...gentsPersistentStructuredOutputRunTests.cs | 24 ++++--- .../CopilotStudio.IntegrationTests.csproj | 1 + .../CopilotStudioFixture.cs | 10 ++- .../CopilotStudioRunStreamingTests.cs | 48 ++++++++----- .../CopilotStudioRunTests.cs | 48 ++++++++----- dotnet/tests/Directory.Build.props | 13 ++-- .../CosmosChatHistoryProviderTests.cs | 70 ++++++++++--------- .../CosmosCheckpointStoreTests.cs | 42 +++++------ ...oft.Agents.AI.CosmosNoSql.UnitTests.csproj | 1 - .../AgentEntityTests.cs | 1 - .../ConsoleAppSamplesValidation.cs | 5 +- .../ExternalClientTests.cs | 1 - .../Logging/TestLogger.cs | 1 - .../Logging/TestLoggerProvider.cs | 1 - .../OrchestrationTests.cs | 1 - .../TestHelper.cs | 1 - .../TimeToLiveTests.cs | 1 - .../ToolCallingTests.cs | 1 - .../SamplesValidation.cs | 5 +- .../AzureAgentProviderTest.cs | 1 - .../DeclarativeCodeGenTest.cs | 1 - .../DeclarativeWorkflowTest.cs | 1 - .../Framework/IntegrationTest.cs | 1 - .../Framework/TestOutputAdapter.cs | 1 - .../Framework/WorkflowTest.cs | 1 - .../FunctionCallingWorkflowTest.cs | 1 - .../InvokeFunctionToolWorkflowTest.cs | 1 - .../MediaInputTest.cs | 1 - .../AddConversationMessageTemplateTest.cs | 1 - .../CodeGen/BreakLoopTemplateTest.cs | 1 - .../CodeGen/ClearAllVariablesTemplateTest.cs | 1 - .../CodeGen/ConditionGroupTemplateTest.cs | 1 - .../CodeGen/ContinueLoopTemplateTest.cs | 1 - .../CopyConversationMessagesTemplateTest.cs | 1 - .../CodeGen/CreateConversationTemplateTest.cs | 1 - .../CodeGen/DeclarativeEjectionTest.cs | 1 - .../CodeGen/EdgeTemplateTest.cs | 1 - .../CodeGen/EndConversationTest.cs | 1 - .../CodeGen/EndDialogTest.cs | 1 - .../CodeGen/ForeachTemplateTest.cs | 1 - .../CodeGen/GotoTemplateTest.cs | 1 - .../CodeGen/InvokeAzureAgentTemplateTest.cs | 1 - .../CodeGen/ProviderTemplateTest.cs | 1 - .../CodeGen/ResetVariableTemplateTest.cs | 1 - ...RetrieveConversationMessageTemplateTest.cs | 1 - ...etrieveConversationMessagesTemplateTest.cs | 1 - .../SetMultipleVariablesTemplateTest.cs | 1 - .../CodeGen/SetTextVariableTemplateTest.cs | 1 - .../CodeGen/SetVariableTemplateTest.cs | 1 - .../CodeGen/WorkflowActionTemplateTest.cs | 1 - .../DeclarativeWorkflowExceptionTest.cs | 1 - .../DeclarativeWorkflowTest.cs | 1 - .../Entities/EntityExtractionResultTest.cs | 1 - .../Entities/EntityExtractorTest.cs | 1 - .../Events/EventTest.cs | 1 - .../Events/ExternalInputRequestTest.cs | 1 - .../Events/ExternalInputResponseTest.cs | 1 - .../Interpreter/WorkflowModelTest.cs | 1 - .../AddConversationMessageExecutorTest.cs | 1 - .../ClearAllVariablesExecutorTest.cs | 1 - .../ObjectModel/ConditionGroupExecutorTest.cs | 1 - .../CopyConversationMessagesExecutorTest.cs | 1 - .../CreateConversationExecutorTest.cs | 1 - .../ObjectModel/DefaultActionExecutorTest.cs | 1 - .../ObjectModel/EditTableExecutorTest.cs | 1 - .../ObjectModel/EditTableV2ExecutorTest.cs | 1 - .../ObjectModel/ForeachExecutorTest.cs | 1 - .../InvokeFunctionToolExecutorTest.cs | 1 - .../ObjectModel/ParseValueExecutorTest.cs | 1 - .../ObjectModel/QuestionExecutorTest.cs | 1 - .../RequestExternalInputExecutorTest.cs | 1 - .../ObjectModel/ResetVariableExecutorTest.cs | 1 - ...RetrieveConversationMessageExecutorTest.cs | 1 - ...etrieveConversationMessagesExecutorTest.cs | 1 - .../ObjectModel/SendActivityExecutorTest.cs | 1 - .../SetMultipleVariablesExecutorTest.cs | 1 - .../SetTextVariableExecutorTest.cs | 1 - .../ObjectModel/SetVariableExecutorTest.cs | 1 - .../ObjectModel/WorkflowActionExecutorTest.cs | 1 - .../PowerFx/RecalcEngineFactoryTests.cs | 1 - .../PowerFx/RecalcEngineTest.cs | 1 - .../PowerFx/TemplateExtensionsTests.cs | 1 - .../PowerFx/WorkflowExpressionEngineTests.cs | 1 - .../TestOutputAdapter.cs | 1 - .../WorkflowTest.cs | 1 - .../OpenAIAssistantFixture.cs | 11 +-- .../OpenAIChatCompletionFixture.cs | 10 ++- ...esponseChatClientAgentRunStreamingTests.cs | 16 +++-- .../OpenAIResponseChatClientAgentRunTests.cs | 16 +++-- .../OpenAIResponseFixture.cs | 8 ++- .../OpenAIResponseRunStreamingTests.cs | 17 +++-- .../OpenAIResponseRunTests.cs | 17 +++-- dotnet/tests/coverage.runsettings | 21 ++++++ 113 files changed, 426 insertions(+), 314 deletions(-) create mode 100644 dotnet/tests/coverage.runsettings diff --git a/.github/workflows/dotnet-build-and-test.yml b/.github/workflows/dotnet-build-and-test.yml index 95842e703a..65a8af8111 100644 --- a/.github/workflows/dotnet-build-and-test.yml +++ b/.github/workflows/dotnet-build-and-test.yml @@ -143,7 +143,8 @@ jobs: - name: Run Unit Tests shell: bash run: | - export UT_PROJECTS=$(find ./dotnet -type f -name "*.UnitTests.csproj" | tr '\n' ' ') + cd dotnet + export UT_PROJECTS=$(find ./ -type f -name "*.UnitTests.csproj" | tr '\n' ' ') for project in $UT_PROJECTS; do # Query the project's target frameworks using MSBuild with the current configuration target_frameworks=$(dotnet msbuild $project -getProperty:TargetFrameworks -p:Configuration=${{ matrix.configuration }} -nologo 2>/dev/null | tr -d '\r') @@ -151,9 +152,10 @@ jobs: # Check if the project supports the target framework if [[ "$target_frameworks" == *"${{ matrix.targetFramework }}"* ]]; then if [[ "${{ matrix.targetFramework }}" == "${{ env.COVERAGE_FRAMEWORK }}" ]]; then - dotnet test -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx --collect:"XPlat Code Coverage" --results-directory:"TestResults/Coverage/" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.ExcludeByAttribute=GeneratedCodeAttribute,CompilerGeneratedAttribute,ExcludeFromCodeCoverageAttribute + # --ignore-exit-code 8: ignore failures due to finding no matching tests to run in a single project. + dotnet test --project $project -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} --no-build -v Normal --report-xunit-trx --ignore-exit-code 8 --coverage --coverage-output-format cobertura --coverage-settings "$(pwd)/tests/coverage.runsettings" --results-directory "$(pwd)/../TestResults/Coverage/" else - dotnet test -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx + dotnet test --project $project -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} --no-build -v Normal --report-xunit-trx --ignore-exit-code 8 fi else echo "Skipping $project - does not support target framework ${{ matrix.targetFramework }} (supports: $target_frameworks)" @@ -188,14 +190,19 @@ jobs: shell: bash if: github.event_name != 'pull_request' && matrix.integration-tests run: | - export INTEGRATION_TEST_PROJECTS=$(find ./dotnet -type f -name "*IntegrationTests.csproj" | tr '\n' ' ') + cd dotnet + export INTEGRATION_TEST_PROJECTS=$(find ./ -type f -name "*IntegrationTests.csproj" | tr '\n' ' ') for project in $INTEGRATION_TEST_PROJECTS; do # Query the project's target frameworks using MSBuild with the current configuration target_frameworks=$(dotnet msbuild $project -getProperty:TargetFrameworks -p:Configuration=${{ matrix.configuration }} -nologo 2>/dev/null | tr -d '\r') # Check if the project supports the target framework if [[ "$target_frameworks" == *"${{ matrix.targetFramework }}"* ]]; then - dotnet test -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx --filter "Category!=IntegrationDisabled" + if [[ "${{ matrix.targetFramework }}" == "${{ env.COVERAGE_FRAMEWORK }}" ]]; then + dotnet test --project $project -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} --no-build -v Normal --report-xunit-trx --ignore-exit-code 8 --filter-not-trait "Category=IntegrationDisabled" --coverage --coverage-output-format cobertura --coverage-settings "$(pwd)/tests/coverage.runsettings" --results-directory "$(pwd)/../TestResults/Coverage/" + else + dotnet test --project $project -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} --no-build -v Normal --report-xunit-trx --ignore-exit-code 8 --filter-not-trait "Category=IntegrationDisabled" + fi else echo "Skipping $project - does not support target framework ${{ matrix.targetFramework }} (supports: $target_frameworks)" fi @@ -225,7 +232,7 @@ jobs: if: matrix.targetFramework == env.COVERAGE_FRAMEWORK uses: danielpalme/ReportGenerator-GitHub-Action@5.5.1 with: - reports: "./TestResults/Coverage/**/coverage.cobertura.xml" + reports: "./TestResults/Coverage/**/*.cobertura.xml" targetdir: "./TestResults/Reports" reporttypes: "HtmlInline;JsonSummary" diff --git a/dotnet/.github/skills/build-and-test/SKILL.md b/dotnet/.github/skills/build-and-test/SKILL.md index 60492fe135..a1d57ee3cd 100644 --- a/dotnet/.github/skills/build-and-test/SKILL.md +++ b/dotnet/.github/skills/build-and-test/SKILL.md @@ -17,14 +17,17 @@ dotnet format # Auto-fix formatting for all projects # Build/test/format a specific project (preferred for isolated/internal changes) dotnet build src/Microsoft.Agents.AI. --tl:off -dotnet test tests/Microsoft.Agents.AI..UnitTests +dotnet test --project tests/Microsoft.Agents.AI..UnitTests dotnet format src/Microsoft.Agents.AI. # Run a single test -dotnet test --filter "FullyQualifiedName~Namespace.TestClassName.TestMethodName" +# Replace the filter values with the appropriate assembly, namespace, class, and method names for the test you want to run and use * as a wildcard elsewhere, e.g. "/*/*/HttpClientTests/GetAsync_ReturnsSuccessStatusCode" +# Use `--ignore-exit-code 8` to avoid failing the build when no tests are found for some projects +dotnet test --filter-query "////" --ignore-exit-code 8 # Run unit tests only -dotnet test --filter FullyQualifiedName\~UnitTests +# Use `--ignore-exit-code 8` to avoid failing the build when no tests are found for integration test projects +dotnet test --filter-query "/*UnitTests*/*/*/*" --ignore-exit-code 8 ``` Use `--tl:off` when building to avoid flickering when running commands in the agent. @@ -56,7 +59,7 @@ Example: Running tests for a single project using .NET 10. ```bash # From dotnet/ directory -dotnet test ./tests/Microsoft.Agents.AI.Abstractions.UnitTests -f net10.0 +dotnet test --project ./tests/Microsoft.Agents.AI.Abstractions.UnitTests -f net10.0 ``` Example: Running a single test in a specific project using .NET 10. @@ -64,7 +67,7 @@ Provide the full namespace, class name, and method name for the test you want to ```bash # From dotnet/ directory -dotnet test ./tests/Microsoft.Agents.AI.Abstractions.UnitTests -f net10.0 --filter "FullyQualifiedName~Microsoft.Agents.AI.Abstractions.UnitTests.AgentRunOptionsTests.CloningConstructorCopiesProperties" +dotnet test --project ./tests/Microsoft.Agents.AI.Abstractions.UnitTests -f net10.0 --filter-query "/*/Microsoft.Agents.AI.Abstractions.UnitTests/AgentRunOptionsTests/CloningConstructorCopiesProperties" ``` ### Multi-target framework tip @@ -83,3 +86,34 @@ Just remember to run `dotnet restore` after pulling changes, making changes to p Unit tests target both .NET Framework as well as .NET Core. When running on Linux, only the .NET Core tests can be run, as .NET Framework is not supported on Linux. To run only the .NET Core tests, use the `-f net10.0` option with `dotnet test`. + +### Microsoft Testing Platform (MTP) + +Tests use the [Microsoft Testing Platform](https://learn.microsoft.com/dotnet/core/testing/unit-testing-platform-intro) via xUnit v3. Key differences from the legacy VSTest runner: + +- **`dotnet test` requires `--project`** to specify a test project directly (positional arguments are no longer supported). +- **Test output** uses the MTP format (e.g., `[✓112/x0/↓0]` progress and `Test run summary: Passed!`). +- **TRX reports** use `--report-xunit-trx` instead of `--logger trx`. +- **Code coverage** uses `Microsoft.Testing.Extensions.CodeCoverage` with `--coverage --coverage-output-format cobertura`. +- **Running a test project directly** is supported via `dotnet run --project `. This bypasses the `dotnet test` infrastructure and runs the test executable directly with the MTP command line. + +- **Running tests across the solution** with a filter may cause some projects to match zero tests, which MTP treats as a failure (exit code 8). Use `--ignore-exit-code 8` to suppress this: + +```bash +# Run all unit tests across the solution, ignoring projects with no matching tests +dotnet test --solution ./agent-framework-dotnet.slnx --no-build -f net10.0 +``` + +```bash +# Run tests via dotnet test (uses MTP under the hood) +dotnet test --project ./tests/Microsoft.Agents.AI.UnitTests -f net10.0 + +# Run tests with code coverage (Cobertura format) +dotnet test --project ./tests/Microsoft.Agents.AI.UnitTests -f net10.0 --coverage --coverage-output-format cobertura --coverage-settings ./tests/coverage.runsettings + +# Run tests directly via dotnet run (MTP native command line) +dotnet run --project ./tests/Microsoft.Agents.AI.UnitTests -f net10.0 + +# Show MTP command line help +dotnet run --project ./tests/Microsoft.Agents.AI.UnitTests -f net10.0 -- -? +``` diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index 0de4409d53..6d73ee4158 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -138,12 +138,10 @@ - - - - - - + + + + diff --git a/dotnet/global.json b/dotnet/global.json index 54533bf771..482aa6b8d3 100644 --- a/dotnet/global.json +++ b/dotnet/global.json @@ -3,5 +3,8 @@ "version": "10.0.100", "rollForward": "minor", "allowPrerelease": false + }, + "test": { + "runner": "Microsoft.Testing.Platform" } } \ No newline at end of file diff --git a/dotnet/tests/AgentConformance.IntegrationTests/AgentTests.cs b/dotnet/tests/AgentConformance.IntegrationTests/AgentTests.cs index 353b4a36ba..1dc8fa2bcd 100644 --- a/dotnet/tests/AgentConformance.IntegrationTests/AgentTests.cs +++ b/dotnet/tests/AgentConformance.IntegrationTests/AgentTests.cs @@ -15,11 +15,15 @@ public abstract class AgentTests(Func createAgentF { protected TAgentFixture Fixture { get; private set; } = default!; - public Task InitializeAsync() + public async ValueTask InitializeAsync() { this.Fixture = createAgentFixture(); - return this.Fixture.InitializeAsync(); + await this.Fixture.InitializeAsync(); } - public Task DisposeAsync() => this.Fixture.DisposeAsync(); + public async ValueTask DisposeAsync() + { + GC.SuppressFinalize(this); + await this.Fixture.DisposeAsync(); + } } diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletion.IntegrationTests.csproj b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletion.IntegrationTests.csproj index 929eafe998..ac59cff3fd 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletion.IntegrationTests.csproj +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletion.IntegrationTests.csproj @@ -1,6 +1,7 @@ + $(NoWarn);CS8793 True diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionChatClientAgentRunStreamingTests.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionChatClientAgentRunStreamingTests.cs index 992db5380b..4a95ace16e 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionChatClientAgentRunStreamingTests.cs +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionChatClientAgentRunStreamingTests.cs @@ -8,13 +8,17 @@ namespace AnthropicChatCompletion.IntegrationTests; public abstract class SkipAllChatClientRunStreaming(Func func) : ChatClientAgentRunStreamingTests(func) { - [Fact(Skip = AnthropicChatCompletionFixture.SkipReason)] public override Task RunWithFunctionsInvokesFunctionsAndReturnsExpectedResultsAsync() - => base.RunWithFunctionsInvokesFunctionsAndReturnsExpectedResultsAsync(); + { + Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); + return base.RunWithFunctionsInvokesFunctionsAndReturnsExpectedResultsAsync(); + } - [Fact(Skip = AnthropicChatCompletionFixture.SkipReason)] public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() - => base.RunWithInstructionsAndNoMessageReturnsExpectedResultAsync(); + { + Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); + return base.RunWithInstructionsAndNoMessageReturnsExpectedResultAsync(); + } } public class AnthropicBetaChatCompletionChatClientAgentReasoningRunStreamingTests() : SkipAllChatClientRunStreaming(() => new(useReasoningChatModel: true, useBeta: true)); diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionChatClientAgentRunTests.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionChatClientAgentRunTests.cs index e2ce6e5d04..98ff9b6448 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionChatClientAgentRunTests.cs +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionChatClientAgentRunTests.cs @@ -8,13 +8,17 @@ namespace AnthropicChatCompletion.IntegrationTests; public abstract class SkipAllChatClientAgentRun(Func func) : ChatClientAgentRunTests(func) { - [Fact(Skip = AnthropicChatCompletionFixture.SkipReason)] public override Task RunWithFunctionsInvokesFunctionsAndReturnsExpectedResultsAsync() - => base.RunWithFunctionsInvokesFunctionsAndReturnsExpectedResultsAsync(); + { + Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); + return base.RunWithFunctionsInvokesFunctionsAndReturnsExpectedResultsAsync(); + } - [Fact(Skip = AnthropicChatCompletionFixture.SkipReason)] public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() - => base.RunWithInstructionsAndNoMessageReturnsExpectedResultAsync(); + { + Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); + return base.RunWithInstructionsAndNoMessageReturnsExpectedResultAsync(); + } } public class AnthropicBetaChatCompletionChatClientAgentRunTests() diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs index f36cb119d9..8048d86dee 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -101,9 +102,12 @@ public Task DeleteSessionAsync(AgentSession session) => // Chat Completion does not require/support deleting sessions, so this is a no-op. Task.CompletedTask; - public async Task InitializeAsync() => + public async ValueTask InitializeAsync() => this._agent = await this.CreateChatClientAgentAsync(); - public Task DisposeAsync() => - Task.CompletedTask; + public ValueTask DisposeAsync() + { + GC.SuppressFinalize(this); + return default; + } } diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunStreamingTests.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunStreamingTests.cs index 4ed6d39edb..7bc187f03f 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunStreamingTests.cs +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunStreamingTests.cs @@ -8,20 +8,35 @@ namespace AnthropicChatCompletion.IntegrationTests; public abstract class SkipAllRunStreaming(Func func) : RunStreamingTests(func) { - [Fact(Skip = AnthropicChatCompletionFixture.SkipReason)] - public override Task RunWithChatMessageReturnsExpectedResultAsync() => base.RunWithChatMessageReturnsExpectedResultAsync(); - - [Fact(Skip = AnthropicChatCompletionFixture.SkipReason)] - public override Task RunWithNoMessageDoesNotFailAsync() => base.RunWithNoMessageDoesNotFailAsync(); - - [Fact(Skip = AnthropicChatCompletionFixture.SkipReason)] - public override Task RunWithChatMessagesReturnsExpectedResultAsync() => base.RunWithChatMessagesReturnsExpectedResultAsync(); - - [Fact(Skip = AnthropicChatCompletionFixture.SkipReason)] - public override Task RunWithStringReturnsExpectedResultAsync() => base.RunWithStringReturnsExpectedResultAsync(); - - [Fact(Skip = AnthropicChatCompletionFixture.SkipReason)] - public override Task SessionMaintainsHistoryAsync() => base.SessionMaintainsHistoryAsync(); + public override Task RunWithChatMessageReturnsExpectedResultAsync() + { + Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); + return base.RunWithChatMessageReturnsExpectedResultAsync(); + } + + public override Task RunWithNoMessageDoesNotFailAsync() + { + Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); + return base.RunWithNoMessageDoesNotFailAsync(); + } + + public override Task RunWithChatMessagesReturnsExpectedResultAsync() + { + Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); + return base.RunWithChatMessagesReturnsExpectedResultAsync(); + } + + public override Task RunWithStringReturnsExpectedResultAsync() + { + Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); + return base.RunWithStringReturnsExpectedResultAsync(); + } + + public override Task SessionMaintainsHistoryAsync() + { + Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); + return base.SessionMaintainsHistoryAsync(); + } } public class AnthropicBetaChatCompletionRunStreamingTests() diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunTests.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunTests.cs index 06f2a15804..6c664d65fc 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunTests.cs +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunTests.cs @@ -8,20 +8,35 @@ namespace AnthropicChatCompletion.IntegrationTests; public abstract class SkipAllRun(Func func) : RunTests(func) { - [Fact(Skip = AnthropicChatCompletionFixture.SkipReason)] - public override Task RunWithChatMessageReturnsExpectedResultAsync() => base.RunWithChatMessageReturnsExpectedResultAsync(); - - [Fact(Skip = AnthropicChatCompletionFixture.SkipReason)] - public override Task RunWithNoMessageDoesNotFailAsync() => base.RunWithNoMessageDoesNotFailAsync(); - - [Fact(Skip = AnthropicChatCompletionFixture.SkipReason)] - public override Task RunWithChatMessagesReturnsExpectedResultAsync() => base.RunWithChatMessagesReturnsExpectedResultAsync(); - - [Fact(Skip = AnthropicChatCompletionFixture.SkipReason)] - public override Task RunWithStringReturnsExpectedResultAsync() => base.RunWithStringReturnsExpectedResultAsync(); - - [Fact(Skip = AnthropicChatCompletionFixture.SkipReason)] - public override Task SessionMaintainsHistoryAsync() => base.SessionMaintainsHistoryAsync(); + public override Task RunWithChatMessageReturnsExpectedResultAsync() + { + Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); + return base.RunWithChatMessageReturnsExpectedResultAsync(); + } + + public override Task RunWithNoMessageDoesNotFailAsync() + { + Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); + return base.RunWithNoMessageDoesNotFailAsync(); + } + + public override Task RunWithChatMessagesReturnsExpectedResultAsync() + { + Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); + return base.RunWithChatMessagesReturnsExpectedResultAsync(); + } + + public override Task RunWithStringReturnsExpectedResultAsync() + { + Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); + return base.RunWithStringReturnsExpectedResultAsync(); + } + + public override Task SessionMaintainsHistoryAsync() + { + Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); + return base.SessionMaintainsHistoryAsync(); + } } public class AnthropicBetaChatCompletionRunTests() diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunStreamingTests.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunStreamingTests.cs index 50ced1e64d..870dda648c 100644 --- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunStreamingTests.cs +++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunStreamingTests.cs @@ -9,10 +9,10 @@ namespace AzureAI.IntegrationTests; public class AIProjectClientAgentRunStreamingPreviousResponseTests() : RunStreamingTests(() => new()) { - [Fact(Skip = "No messages is not supported")] public override Task RunWithNoMessageDoesNotFailAsync() { - return Task.CompletedTask; + Assert.Skip("No messages is not supported"); + return base.RunWithNoMessageDoesNotFailAsync(); } } @@ -24,9 +24,9 @@ public override Task RunWithNoMessageDoesNotFailAsync() return new ChatClientAgentRunOptions(new() { ConversationId = conversationId }); }; - [Fact(Skip = "No messages is not supported")] public override Task RunWithNoMessageDoesNotFailAsync() { - return Task.CompletedTask; + Assert.Skip("No messages is not supported"); + return base.RunWithNoMessageDoesNotFailAsync(); } } diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunTests.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunTests.cs index 0092090401..af4cee82e6 100644 --- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunTests.cs +++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunTests.cs @@ -9,10 +9,10 @@ namespace AzureAI.IntegrationTests; public class AIProjectClientAgentRunPreviousResponseTests() : RunTests(() => new()) { - [Fact(Skip = "No messages is not supported")] public override Task RunWithNoMessageDoesNotFailAsync() { - return Task.CompletedTask; + Assert.Skip("No messages is not supported"); + return base.RunWithNoMessageDoesNotFailAsync(); } } @@ -24,9 +24,9 @@ public override Task RunWithNoMessageDoesNotFailAsync() return new ChatClientAgentRunOptions(new() { ConversationId = conversationId }); }; - [Fact(Skip = "No messages is not supported")] public override Task RunWithNoMessageDoesNotFailAsync() { - return Task.CompletedTask; + Assert.Skip("No messages is not supported"); + return base.RunWithNoMessageDoesNotFailAsync(); } } diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentStructuredOutputRunTests.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentStructuredOutputRunTests.cs index 94ce01e221..9db48f3832 100644 --- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentStructuredOutputRunTests.cs +++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentStructuredOutputRunTests.cs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft. All rights reserved. -using System; using System.Threading.Tasks; using AgentConformance.IntegrationTests; using AgentConformance.IntegrationTests.Support; @@ -66,17 +65,23 @@ public async Task RunGenericWithResponseFormatAtAgentInitializationReturnsExpect Assert.Equal("Paris", response.Result.Name); } - [Fact(Skip = NotSupported)] - public override Task RunWithGenericTypeReturnsExpectedResultAsync() => - base.RunWithGenericTypeReturnsExpectedResultAsync(); + public override Task RunWithGenericTypeReturnsExpectedResultAsync() + { + Assert.Skip(NotSupported); + return base.RunWithGenericTypeReturnsExpectedResultAsync(); + } - [Fact(Skip = NotSupported)] - public override Task RunWithResponseFormatReturnsExpectedResultAsync() => - base.RunWithResponseFormatReturnsExpectedResultAsync(); + public override Task RunWithResponseFormatReturnsExpectedResultAsync() + { + Assert.Skip(NotSupported); + return base.RunWithResponseFormatReturnsExpectedResultAsync(); + } - [Fact(Skip = NotSupported)] - public override Task RunWithPrimitiveTypeReturnsExpectedResultAsync() => - base.RunWithPrimitiveTypeReturnsExpectedResultAsync(); + public override Task RunWithPrimitiveTypeReturnsExpectedResultAsync() + { + Assert.Skip(NotSupported); + return base.RunWithPrimitiveTypeReturnsExpectedResultAsync(); + } } /// @@ -84,7 +89,7 @@ public override Task RunWithPrimitiveTypeReturnsExpectedResultAsync() => /// public class AIProjectClientStructuredOutputFixture : AIProjectClientFixture { - public override Task InitializeAsync() + public override async ValueTask InitializeAsync() { var agentOptions = new ChatClientAgentOptions { @@ -94,6 +99,6 @@ public override Task InitializeAsync() }, }; - return this.InitializeAsync(agentOptions); + await this.InitializeAsync(agentOptions); } } diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunStreamingTests.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunStreamingTests.cs index befa409d80..3b0c1c27b4 100644 --- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunStreamingTests.cs +++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunStreamingTests.cs @@ -7,9 +7,9 @@ namespace AzureAI.IntegrationTests; public class AIProjectClientChatClientAgentRunStreamingTests() : ChatClientAgentRunStreamingTests(() => new()) { - [Fact(Skip = "No messages is not supported")] public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() { - return Task.CompletedTask; + Assert.Skip("No messages is not supported"); + return base.RunWithInstructionsAndNoMessageReturnsExpectedResultAsync(); } } diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunTests.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunTests.cs index 1af12606cb..1e47d0a970 100644 --- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunTests.cs +++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunTests.cs @@ -7,9 +7,9 @@ namespace AzureAI.IntegrationTests; public class AIProjectClientChatClientAgentRunTests() : ChatClientAgentRunTests(() => new()) { - [Fact(Skip = "No messages is not supported")] public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() { - return Task.CompletedTask; + Assert.Skip("No messages is not supported"); + return base.RunWithInstructionsAndNoMessageReturnsExpectedResultAsync(); } } diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs index a13af9c940..3055df899c 100644 --- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs +++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs @@ -158,17 +158,19 @@ private async Task DeleteResponseChainAsync(string lastResponseId) } } - public Task DisposeAsync() + public ValueTask DisposeAsync() { + GC.SuppressFinalize(this); + if (this._client is not null && this._agent is not null) { - return this._client.Agents.DeleteAgentAsync(this._agent.Name); + return new ValueTask(this._client.Agents.DeleteAgentAsync(this._agent.Name)); } - return Task.CompletedTask; + return default; } - public virtual async Task InitializeAsync() + public virtual async ValueTask InitializeAsync() { this._client = new(new Uri(s_config.Endpoint), new AzureCliCredential()); this._agent = await this.CreateChatClientAgentAsync(); diff --git a/dotnet/tests/AzureAI.IntegrationTests/AzureAI.IntegrationTests.csproj b/dotnet/tests/AzureAI.IntegrationTests/AzureAI.IntegrationTests.csproj index 83f65051d2..7ea633e3d4 100644 --- a/dotnet/tests/AzureAI.IntegrationTests/AzureAI.IntegrationTests.csproj +++ b/dotnet/tests/AzureAI.IntegrationTests/AzureAI.IntegrationTests.csproj @@ -1,6 +1,7 @@ + $(NoWarn);CS8793 True diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistent.IntegrationTests.csproj b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistent.IntegrationTests.csproj index 4078342410..2b12fe728a 100644 --- a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistent.IntegrationTests.csproj +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistent.IntegrationTests.csproj @@ -1,6 +1,7 @@ + $(NoWarn);CS8793 True diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs index 3e3272d951..dca9956774 100644 --- a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Threading.Tasks; using AgentConformance.IntegrationTests; @@ -86,17 +87,19 @@ public Task DeleteSessionAsync(AgentSession session) return Task.CompletedTask; } - public Task DisposeAsync() + public ValueTask DisposeAsync() { + GC.SuppressFinalize(this); + if (this._persistentAgentsClient is not null && this._agent is not null) { - return this._persistentAgentsClient.Administration.DeleteAgentAsync(this._agent.Id); + return new ValueTask(this._persistentAgentsClient.Administration.DeleteAgentAsync(this._agent.Id)); } - return Task.CompletedTask; + return default; } - public async Task InitializeAsync() + public async ValueTask InitializeAsync() { this._persistentAgentsClient = new(s_config.Endpoint, new AzureCliCredential()); this._agent = await this.CreateChatClientAgentAsync(); diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentStructuredOutputRunTests.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentStructuredOutputRunTests.cs index a56917c515..7aa6e3251d 100644 --- a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentStructuredOutputRunTests.cs +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentStructuredOutputRunTests.cs @@ -9,15 +9,21 @@ namespace AzureAIAgentsPersistent.IntegrationTests; { private const string SkipReason = "Fails intermittently on the build agent/CI"; - [Fact(Skip = SkipReason)] - public override Task RunWithResponseFormatReturnsExpectedResultAsync() => - base.RunWithResponseFormatReturnsExpectedResultAsync(); + public override Task RunWithResponseFormatReturnsExpectedResultAsync() + { + Assert.SkipWhen(SkipReason is not null, SkipReason!); + return base.RunWithResponseFormatReturnsExpectedResultAsync(); + } - [Fact(Skip = SkipReason)] - public override Task RunWithGenericTypeReturnsExpectedResultAsync() => - base.RunWithGenericTypeReturnsExpectedResultAsync(); + public override Task RunWithGenericTypeReturnsExpectedResultAsync() + { + Assert.SkipWhen(SkipReason is not null, SkipReason!); + return base.RunWithGenericTypeReturnsExpectedResultAsync(); + } - [Fact(Skip = SkipReason)] - public override Task RunWithPrimitiveTypeReturnsExpectedResultAsync() => - base.RunWithPrimitiveTypeReturnsExpectedResultAsync(); + public override Task RunWithPrimitiveTypeReturnsExpectedResultAsync() + { + Assert.SkipWhen(SkipReason is not null, SkipReason!); + return base.RunWithPrimitiveTypeReturnsExpectedResultAsync(); + } } diff --git a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudio.IntegrationTests.csproj b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudio.IntegrationTests.csproj index 5f535eb7bd..312a322989 100644 --- a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudio.IntegrationTests.csproj +++ b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudio.IntegrationTests.csproj @@ -1,6 +1,7 @@ + $(NoWarn);CS8793 True true diff --git a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs index dd5fe46ecc..6018fab2f0 100644 --- a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs +++ b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs @@ -27,7 +27,7 @@ public Task DeleteSessionAsync(AgentSession session) => // Chat Completion does not require/support deleting threads, so this is a no-op. Task.CompletedTask; - public Task InitializeAsync() + public ValueTask InitializeAsync() { const string CopilotStudioHttpClientName = nameof(CopilotStudioAgent); @@ -54,8 +54,12 @@ public Task InitializeAsync() this.Agent = new CopilotStudioAgent(client); - return Task.CompletedTask; + return default; } - public Task DisposeAsync() => Task.CompletedTask; + public ValueTask DisposeAsync() + { + GC.SuppressFinalize(this); + return default; + } } diff --git a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunStreamingTests.cs b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunStreamingTests.cs index 076512252b..d583ddf563 100644 --- a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunStreamingTests.cs +++ b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunStreamingTests.cs @@ -10,23 +10,33 @@ namespace CopilotStudio.IntegrationTests; // Set to null to run the tests. private const string ManualVerification = "For manual verification"; - [Fact(Skip = "Copilot Studio does not support session history retrieval, so this test is not applicable.")] - public override Task SessionMaintainsHistoryAsync() => - Task.CompletedTask; - - [Fact(Skip = ManualVerification)] - public override Task RunWithChatMessageReturnsExpectedResultAsync() => - base.RunWithChatMessageReturnsExpectedResultAsync(); - - [Fact(Skip = ManualVerification)] - public override Task RunWithChatMessagesReturnsExpectedResultAsync() => - base.RunWithChatMessagesReturnsExpectedResultAsync(); - - [Fact(Skip = ManualVerification)] - public override Task RunWithNoMessageDoesNotFailAsync() => - base.RunWithNoMessageDoesNotFailAsync(); - - [Fact(Skip = ManualVerification)] - public override Task RunWithStringReturnsExpectedResultAsync() => - base.RunWithStringReturnsExpectedResultAsync(); + public override Task SessionMaintainsHistoryAsync() + { + Assert.Skip("Copilot Studio does not support session history retrieval, so this test is not applicable."); + return base.SessionMaintainsHistoryAsync(); + } + + public override Task RunWithChatMessageReturnsExpectedResultAsync() + { + Assert.SkipWhen(ManualVerification is not null, ManualVerification!); + return base.RunWithChatMessageReturnsExpectedResultAsync(); + } + + public override Task RunWithChatMessagesReturnsExpectedResultAsync() + { + Assert.SkipWhen(ManualVerification is not null, ManualVerification!); + return base.RunWithChatMessagesReturnsExpectedResultAsync(); + } + + public override Task RunWithNoMessageDoesNotFailAsync() + { + Assert.SkipWhen(ManualVerification is not null, ManualVerification!); + return base.RunWithNoMessageDoesNotFailAsync(); + } + + public override Task RunWithStringReturnsExpectedResultAsync() + { + Assert.SkipWhen(ManualVerification is not null, ManualVerification!); + return base.RunWithStringReturnsExpectedResultAsync(); + } } diff --git a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunTests.cs b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunTests.cs index bf7bcfcd64..612ccd27e7 100644 --- a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunTests.cs +++ b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunTests.cs @@ -10,23 +10,33 @@ namespace CopilotStudio.IntegrationTests; // Set to null to run the tests. private const string ManualVerification = "For manual verification"; - [Fact(Skip = "Copilot Studio does not support session history retrieval, so this test is not applicable.")] - public override Task SessionMaintainsHistoryAsync() => - Task.CompletedTask; - - [Fact(Skip = ManualVerification)] - public override Task RunWithChatMessageReturnsExpectedResultAsync() => base.RunWithChatMessageReturnsExpectedResultAsync(); - - [Fact(Skip = ManualVerification)] - public override Task RunWithChatMessagesReturnsExpectedResultAsync() => - - base.RunWithChatMessagesReturnsExpectedResultAsync(); - - [Fact(Skip = ManualVerification)] - public override Task RunWithNoMessageDoesNotFailAsync() => - base.RunWithNoMessageDoesNotFailAsync(); - - [Fact(Skip = ManualVerification)] - public override Task RunWithStringReturnsExpectedResultAsync() => - base.RunWithStringReturnsExpectedResultAsync(); + public override Task SessionMaintainsHistoryAsync() + { + Assert.Skip("Copilot Studio does not support session history retrieval, so this test is not applicable."); + return base.SessionMaintainsHistoryAsync(); + } + + public override Task RunWithChatMessageReturnsExpectedResultAsync() + { + Assert.SkipWhen(ManualVerification is not null, ManualVerification!); + return base.RunWithChatMessageReturnsExpectedResultAsync(); + } + + public override Task RunWithChatMessagesReturnsExpectedResultAsync() + { + Assert.SkipWhen(ManualVerification is not null, ManualVerification!); + return base.RunWithChatMessagesReturnsExpectedResultAsync(); + } + + public override Task RunWithNoMessageDoesNotFailAsync() + { + Assert.SkipWhen(ManualVerification is not null, ManualVerification!); + return base.RunWithNoMessageDoesNotFailAsync(); + } + + public override Task RunWithStringReturnsExpectedResultAsync() + { + Assert.SkipWhen(ManualVerification is not null, ManualVerification!); + return base.RunWithStringReturnsExpectedResultAsync(); + } } diff --git a/dotnet/tests/Directory.Build.props b/dotnet/tests/Directory.Build.props index e3bdd6745d..c4bfc0b0b5 100644 --- a/dotnet/tests/Directory.Build.props +++ b/dotnet/tests/Directory.Build.props @@ -6,22 +6,25 @@ false true false + Exe net10.0;net472 b7762d10-e29b-4bb1-8b74-b6d69a667dd4 - $(NoWarn);Moq1410;xUnit2023;MAAI001 + true + true + $(NoWarn);Moq1410;xUnit1051;MAAI001 - + - - + + - + diff --git a/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatHistoryProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatHistoryProviderTests.cs index 3cac6ff971..337677419b 100644 --- a/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatHistoryProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatHistoryProviderTests.cs @@ -58,7 +58,7 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable private bool _preserveContainer; private CosmosClient? _setupClient; // Only used for test setup/cleanup - public async Task InitializeAsync() + public async ValueTask InitializeAsync() { // Fail fast if emulator is not available this.SkipIfEmulatorNotAvailable(); @@ -100,8 +100,10 @@ await databaseResponse.Database.CreateContainerIfNotExistsAsync( } } - public async Task DisposeAsync() + public async ValueTask DisposeAsync() { + GC.SuppressFinalize(this); + if (this._setupClient != null && this._emulatorAvailable) { try @@ -143,12 +145,12 @@ private void SkipIfEmulatorNotAvailable() // Locally: Skip if emulator connection check failed var ciEmulatorAvailable = string.Equals(Environment.GetEnvironmentVariable("COSMOS_EMULATOR_AVAILABLE"), "true", StringComparison.OrdinalIgnoreCase); - Xunit.Skip.If(!ciEmulatorAvailable && !this._emulatorAvailable, "Cosmos DB Emulator is not available"); + Assert.SkipWhen(!ciEmulatorAvailable && !this._emulatorAvailable, "Cosmos DB Emulator is not available"); } #region Constructor Tests - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public void StateKey_ReturnsDefaultKey_WhenNoStateKeyProvided() { @@ -162,7 +164,7 @@ public void StateKey_ReturnsDefaultKey_WhenNoStateKeyProvided() Assert.Equal("CosmosChatHistoryProvider", provider.StateKey); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public void StateKey_ReturnsCustomKey_WhenSetViaConstructor() { @@ -177,7 +179,7 @@ public void StateKey_ReturnsCustomKey_WhenSetViaConstructor() Assert.Equal("custom-key", provider.StateKey); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public void Constructor_WithConnectionString_ShouldCreateInstance() { @@ -194,7 +196,7 @@ public void Constructor_WithConnectionString_ShouldCreateInstance() Assert.Equal(TestContainerId, provider.ContainerId); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public void Constructor_WithNullConnectionString_ShouldThrowArgumentException() { @@ -204,7 +206,7 @@ public void Constructor_WithNullConnectionString_ShouldThrowArgumentException() _ => new CosmosChatHistoryProvider.State("test-conversation"))); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public void Constructor_WithNullStateInitializer_ShouldThrowArgumentNullException() { @@ -219,7 +221,7 @@ public void Constructor_WithNullStateInitializer_ShouldThrowArgumentNullExceptio #region InvokedAsync Tests - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public async Task InvokedAsync_WithSingleMessage_ShouldAddMessageAsync() { @@ -284,7 +286,7 @@ public async Task InvokedAsync_WithSingleMessage_ShouldAddMessageAsync() Assert.Equal(ChatRole.User, messageList[0].Role); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public async Task InvokedAsync_WithMultipleMessages_ShouldAddAllMessagesAsync() { @@ -327,7 +329,7 @@ public async Task InvokedAsync_WithMultipleMessages_ShouldAddAllMessagesAsync() #region InvokingAsync Tests - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public async Task InvokingAsync_WithNoMessages_ShouldReturnEmptyAsync() { @@ -345,7 +347,7 @@ public async Task InvokingAsync_WithNoMessages_ShouldReturnEmptyAsync() Assert.Empty(messages); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public async Task InvokingAsync_WithConversationIsolation_ShouldOnlyReturnMessagesForConversationAsync() { @@ -389,7 +391,7 @@ public async Task InvokingAsync_WithConversationIsolation_ShouldOnlyReturnMessag #region Integration Tests - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public async Task FullWorkflow_AddAndGet_ShouldWorkCorrectlyAsync() { @@ -440,7 +442,7 @@ public async Task FullWorkflow_AddAndGet_ShouldWorkCorrectlyAsync() #region Disposal Tests - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public void Dispose_AfterUse_ShouldNotThrow() { @@ -453,7 +455,7 @@ public void Dispose_AfterUse_ShouldNotThrow() provider.Dispose(); // Should not throw } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public void Dispose_MultipleCalls_ShouldNotThrow() { @@ -471,7 +473,7 @@ public void Dispose_MultipleCalls_ShouldNotThrow() #region Hierarchical Partitioning Tests - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public void Constructor_WithHierarchicalConnectionString_ShouldCreateInstance() { @@ -488,7 +490,7 @@ public void Constructor_WithHierarchicalConnectionString_ShouldCreateInstance() Assert.Equal(HierarchicalTestContainerId, provider.ContainerId); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public void Constructor_WithHierarchicalEndpoint_ShouldCreateInstance() { @@ -506,7 +508,7 @@ public void Constructor_WithHierarchicalEndpoint_ShouldCreateInstance() Assert.Equal(HierarchicalTestContainerId, provider.ContainerId); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public void Constructor_WithHierarchicalCosmosClient_ShouldCreateInstance() { @@ -523,7 +525,7 @@ public void Constructor_WithHierarchicalCosmosClient_ShouldCreateInstance() Assert.Equal(HierarchicalTestContainerId, provider.ContainerId); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public void State_WithEmptyConversationId_ShouldThrowArgumentException() { @@ -532,7 +534,7 @@ public void State_WithEmptyConversationId_ShouldThrowArgumentException() new CosmosChatHistoryProvider.State("")); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public void State_WithWhitespaceConversationId_ShouldThrowArgumentException() { @@ -541,7 +543,7 @@ public void State_WithWhitespaceConversationId_ShouldThrowArgumentException() new CosmosChatHistoryProvider.State(" ")); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public async Task InvokedAsync_WithHierarchicalPartitioning_ShouldAddMessageWithMetadataAsync() { @@ -595,7 +597,7 @@ public async Task InvokedAsync_WithHierarchicalPartitioning_ShouldAddMessageWith Assert.Equal(SessionId, (string)document!.sessionId); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public async Task InvokedAsync_WithHierarchicalMultipleMessages_ShouldAddAllMessagesAsync() { @@ -634,7 +636,7 @@ public async Task InvokedAsync_WithHierarchicalMultipleMessages_ShouldAddAllMess Assert.Equal("Third hierarchical message", messageList[2].Text); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public async Task InvokingAsync_WithHierarchicalPartitionIsolation_ShouldIsolateMessagesByUserIdAsync() { @@ -680,7 +682,7 @@ public async Task InvokingAsync_WithHierarchicalPartitionIsolation_ShouldIsolate Assert.Equal("Message from user 2", messageList2[0].Text); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public async Task StateBag_WithHierarchicalPartitioning_ShouldPreserveStateAcrossProviderInstancesAsync() { @@ -715,7 +717,7 @@ public async Task StateBag_WithHierarchicalPartitioning_ShouldPreserveStateAcros Assert.Equal(HierarchicalTestContainerId, newStore.ContainerId); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public async Task HierarchicalAndSimplePartitioning_ShouldCoexistAsync() { @@ -757,7 +759,7 @@ public async Task HierarchicalAndSimplePartitioning_ShouldCoexistAsync() Assert.Equal("Hierarchical partitioning message", hierarchicalMessageList[0].Text); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public async Task MaxMessagesToRetrieve_ShouldLimitAndReturnMostRecentAsync() { @@ -798,7 +800,7 @@ public async Task MaxMessagesToRetrieve_ShouldLimitAndReturnMostRecentAsync() Assert.Equal("Message 10", messageList[4].Text); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public async Task MaxMessagesToRetrieve_Null_ShouldReturnAllMessagesAsync() { @@ -834,7 +836,7 @@ public async Task MaxMessagesToRetrieve_Null_ShouldReturnAllMessagesAsync() Assert.Equal("Message 10", messageList[9].Text); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public async Task GetMessageCountAsync_WithMessages_ShouldReturnCorrectCountAsync() { @@ -866,7 +868,7 @@ public async Task GetMessageCountAsync_WithMessages_ShouldReturnCorrectCountAsyn Assert.Equal(5, count); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public async Task GetMessageCountAsync_WithNoMessages_ShouldReturnZeroAsync() { @@ -885,7 +887,7 @@ public async Task GetMessageCountAsync_WithNoMessages_ShouldReturnZeroAsync() Assert.Equal(0, count); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public async Task ClearMessagesAsync_WithMessages_ShouldDeleteAndReturnCountAsync() { @@ -933,7 +935,7 @@ public async Task ClearMessagesAsync_WithMessages_ShouldDeleteAndReturnCountAsyn Assert.Empty(retrievedMessages); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public async Task ClearMessagesAsync_WithNoMessages_ShouldReturnZeroAsync() { @@ -956,7 +958,7 @@ public async Task ClearMessagesAsync_WithNoMessages_ShouldReturnZeroAsync() #region Message Filter Tests - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public async Task InvokedAsync_DefaultFilter_ExcludesChatHistoryMessagesFromStorageAsync() { @@ -991,7 +993,7 @@ public async Task InvokedAsync_DefaultFilter_ExcludesChatHistoryMessagesFromStor Assert.Equal("Response", messages[2].Text); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public async Task InvokedAsync_CustomStorageInputFilter_OverridesDefaultAsync() { @@ -1029,7 +1031,7 @@ public async Task InvokedAsync_CustomStorageInputFilter_OverridesDefaultAsync() Assert.Equal("Response", messages[1].Text); } - [SkippableFact] + [Fact] [Trait("Category", "CosmosDB")] public async Task InvokingAsync_RetrievalOutputFilter_FiltersRetrievedMessagesAsync() { diff --git a/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosCheckpointStoreTests.cs b/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosCheckpointStoreTests.cs index 0974045a9d..899e037c3d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosCheckpointStoreTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosCheckpointStoreTests.cs @@ -55,7 +55,7 @@ private static JsonSerializerOptions CreateJsonOptions() return options; } - public async Task InitializeAsync() + public async ValueTask InitializeAsync() { // Fail fast if emulator is not available this.SkipIfEmulatorNotAvailable(); @@ -88,8 +88,10 @@ await this._database.CreateContainerIfNotExistsAsync( } } - public async Task DisposeAsync() + public async ValueTask DisposeAsync() { + GC.SuppressFinalize(this); + if (this._cosmosClient != null && this._emulatorAvailable) { try @@ -124,12 +126,12 @@ private void SkipIfEmulatorNotAvailable() // Locally: Skip if emulator connection check failed var ciEmulatorAvailable = string.Equals(Environment.GetEnvironmentVariable("COSMOS_EMULATOR_AVAILABLE"), "true", StringComparison.OrdinalIgnoreCase); - Xunit.Skip.If(!ciEmulatorAvailable && !this._emulatorAvailable, "Cosmos DB Emulator is not available"); + Assert.SkipWhen(!ciEmulatorAvailable && !this._emulatorAvailable, "Cosmos DB Emulator is not available"); } #region Constructor Tests - [SkippableFact] + [Fact] public void Constructor_WithCosmosClient_SetsProperties() { // Arrange @@ -143,7 +145,7 @@ public void Constructor_WithCosmosClient_SetsProperties() Assert.Equal(TestContainerId, store.ContainerId); } - [SkippableFact] + [Fact] public void Constructor_WithConnectionString_SetsProperties() { // Arrange @@ -157,7 +159,7 @@ public void Constructor_WithConnectionString_SetsProperties() Assert.Equal(TestContainerId, store.ContainerId); } - [SkippableFact] + [Fact] public void Constructor_WithNullCosmosClient_ThrowsArgumentNullException() { // Act & Assert @@ -165,7 +167,7 @@ public void Constructor_WithNullCosmosClient_ThrowsArgumentNullException() new CosmosCheckpointStore((CosmosClient)null!, s_testDatabaseId, TestContainerId)); } - [SkippableFact] + [Fact] public void Constructor_WithNullConnectionString_ThrowsArgumentException() { // Act & Assert @@ -177,7 +179,7 @@ public void Constructor_WithNullConnectionString_ThrowsArgumentException() #region Checkpoint Operations Tests - [SkippableFact] + [Fact] public async Task CreateCheckpointAsync_NewCheckpoint_CreatesSuccessfullyAsync() { this.SkipIfEmulatorNotAvailable(); @@ -197,7 +199,7 @@ public async Task CreateCheckpointAsync_NewCheckpoint_CreatesSuccessfullyAsync() Assert.NotEmpty(checkpointInfo.CheckpointId); } - [SkippableFact] + [Fact] public async Task RetrieveCheckpointAsync_ExistingCheckpoint_ReturnsCorrectValueAsync() { this.SkipIfEmulatorNotAvailable(); @@ -218,7 +220,7 @@ public async Task RetrieveCheckpointAsync_ExistingCheckpoint_ReturnsCorrectValue Assert.Equal("Hello, World!", messageProp.GetString()); } - [SkippableFact] + [Fact] public async Task RetrieveCheckpointAsync_NonExistentCheckpoint_ThrowsInvalidOperationExceptionAsync() { this.SkipIfEmulatorNotAvailable(); @@ -233,7 +235,7 @@ await Assert.ThrowsAsync(() => store.RetrieveCheckpointAsync(sessionId, fakeCheckpointInfo).AsTask()); } - [SkippableFact] + [Fact] public async Task RetrieveIndexAsync_EmptyStore_ReturnsEmptyCollectionAsync() { this.SkipIfEmulatorNotAvailable(); @@ -250,7 +252,7 @@ public async Task RetrieveIndexAsync_EmptyStore_ReturnsEmptyCollectionAsync() Assert.Empty(index); } - [SkippableFact] + [Fact] public async Task RetrieveIndexAsync_WithCheckpoints_ReturnsAllCheckpointsAsync() { this.SkipIfEmulatorNotAvailable(); @@ -275,7 +277,7 @@ public async Task RetrieveIndexAsync_WithCheckpoints_ReturnsAllCheckpointsAsync( Assert.Contains(index, c => c.CheckpointId == checkpoint3.CheckpointId); } - [SkippableFact] + [Fact] public async Task CreateCheckpointAsync_WithParent_CreatesHierarchyAsync() { this.SkipIfEmulatorNotAvailable(); @@ -295,7 +297,7 @@ public async Task CreateCheckpointAsync_WithParent_CreatesHierarchyAsync() Assert.Equal(sessionId, childCheckpoint.SessionId); } - [SkippableFact] + [Fact] public async Task RetrieveIndexAsync_WithParentFilter_ReturnsFilteredResultsAsync() { this.SkipIfEmulatorNotAvailable(); @@ -331,7 +333,7 @@ public async Task RetrieveIndexAsync_WithParentFilter_ReturnsFilteredResultsAsyn #region Run Isolation Tests - [SkippableFact] + [Fact] public async Task CheckpointOperations_DifferentRuns_IsolatesDataAsync() { this.SkipIfEmulatorNotAvailable(); @@ -361,7 +363,7 @@ public async Task CheckpointOperations_DifferentRuns_IsolatesDataAsync() #region Error Handling Tests - [SkippableFact] + [Fact] public async Task CreateCheckpointAsync_WithNullSessionId_ThrowsArgumentExceptionAsync() { this.SkipIfEmulatorNotAvailable(); @@ -375,7 +377,7 @@ await Assert.ThrowsAsync(() => store.CreateCheckpointAsync(null!, checkpointValue).AsTask()); } - [SkippableFact] + [Fact] public async Task CreateCheckpointAsync_WithEmptySessionId_ThrowsArgumentExceptionAsync() { this.SkipIfEmulatorNotAvailable(); @@ -389,7 +391,7 @@ await Assert.ThrowsAsync(() => store.CreateCheckpointAsync("", checkpointValue).AsTask()); } - [SkippableFact] + [Fact] public async Task RetrieveCheckpointAsync_WithNullCheckpointInfo_ThrowsArgumentNullExceptionAsync() { this.SkipIfEmulatorNotAvailable(); @@ -407,7 +409,7 @@ await Assert.ThrowsAsync(() => #region Disposal Tests - [SkippableFact] + [Fact] public async Task Dispose_AfterDisposal_ThrowsObjectDisposedExceptionAsync() { this.SkipIfEmulatorNotAvailable(); @@ -424,7 +426,7 @@ await Assert.ThrowsAsync(() => store.CreateCheckpointAsync("test-run", checkpointValue).AsTask()); } - [SkippableFact] + [Fact] public void Dispose_MultipleCalls_DoesNotThrow() { this.SkipIfEmulatorNotAvailable(); diff --git a/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/Microsoft.Agents.AI.CosmosNoSql.UnitTests.csproj b/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/Microsoft.Agents.AI.CosmosNoSql.UnitTests.csproj index 78072b8b6a..0103c23028 100644 --- a/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/Microsoft.Agents.AI.CosmosNoSql.UnitTests.csproj +++ b/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/Microsoft.Agents.AI.CosmosNoSql.UnitTests.csproj @@ -17,7 +17,6 @@ - diff --git a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/AgentEntityTests.cs b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/AgentEntityTests.cs index fe20b2e843..e8c17cdfc9 100644 --- a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/AgentEntityTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/AgentEntityTests.cs @@ -9,7 +9,6 @@ using Microsoft.DurableTask.Entities; using Microsoft.Extensions.Configuration; using OpenAI.Chat; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.DurableTask.IntegrationTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ConsoleAppSamplesValidation.cs b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ConsoleAppSamplesValidation.cs index 254006f94d..91a87021dd 100644 --- a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ConsoleAppSamplesValidation.cs +++ b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ConsoleAppSamplesValidation.cs @@ -6,7 +6,6 @@ using System.Text; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.DurableTask.IntegrationTests; @@ -30,7 +29,7 @@ public sealed class ConsoleAppSamplesValidation(ITestOutputHelper outputHelper) private readonly ITestOutputHelper _outputHelper = outputHelper; - async Task IAsyncLifetime.InitializeAsync() + async ValueTask IAsyncLifetime.InitializeAsync() { if (!s_infrastructureStarted) { @@ -39,7 +38,7 @@ async Task IAsyncLifetime.InitializeAsync() } } - async Task IAsyncLifetime.DisposeAsync() + async ValueTask IAsyncDisposable.DisposeAsync() { // Nothing to clean up await Task.CompletedTask; diff --git a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ExternalClientTests.cs b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ExternalClientTests.cs index d48e8c0c28..0e35d29750 100644 --- a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ExternalClientTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ExternalClientTests.cs @@ -9,7 +9,6 @@ using Microsoft.Extensions.AI; using Microsoft.Extensions.Configuration; using OpenAI.Chat; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.DurableTask.IntegrationTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/Logging/TestLogger.cs b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/Logging/TestLogger.cs index ca80b8cf7b..764d9cb24c 100644 --- a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/Logging/TestLogger.cs +++ b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/Logging/TestLogger.cs @@ -2,7 +2,6 @@ using System.Collections.Concurrent; using Microsoft.Extensions.Logging; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.DurableTask.IntegrationTests.Logging; diff --git a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/Logging/TestLoggerProvider.cs b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/Logging/TestLoggerProvider.cs index 7019852e5e..57fbc4e4db 100644 --- a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/Logging/TestLoggerProvider.cs +++ b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/Logging/TestLoggerProvider.cs @@ -2,7 +2,6 @@ using System.Collections.Concurrent; using Microsoft.Extensions.Logging; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.DurableTask.IntegrationTests.Logging; diff --git a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/OrchestrationTests.cs b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/OrchestrationTests.cs index 641cb57dc8..753d57f160 100644 --- a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/OrchestrationTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/OrchestrationTests.cs @@ -7,7 +7,6 @@ using Microsoft.Extensions.AI; using Microsoft.Extensions.Configuration; using OpenAI.Chat; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.DurableTask.IntegrationTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/TestHelper.cs b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/TestHelper.cs index 8022e71119..171560934b 100644 --- a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/TestHelper.cs +++ b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/TestHelper.cs @@ -14,7 +14,6 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using OpenAI.Chat; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.DurableTask.IntegrationTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/TimeToLiveTests.cs b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/TimeToLiveTests.cs index f9f008c1c2..4c21817a6d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/TimeToLiveTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/TimeToLiveTests.cs @@ -7,7 +7,6 @@ using Microsoft.DurableTask.Client.Entities; using Microsoft.Extensions.Configuration; using OpenAI.Chat; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.DurableTask.IntegrationTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ToolCallingTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ToolCallingTests.cs index d512af28cd..3da741851d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ToolCallingTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ToolCallingTests.cs @@ -16,7 +16,6 @@ using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.AI; using Microsoft.Extensions.DependencyInjection; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs index 80eda23613..19e75467c8 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs @@ -8,7 +8,6 @@ using Microsoft.Extensions.Logging; using ModelContextProtocol.Client; using ModelContextProtocol.Protocol; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests; @@ -36,7 +35,7 @@ public sealed class SamplesValidation(ITestOutputHelper outputHelper) : IAsyncLi private readonly ITestOutputHelper _outputHelper = outputHelper; - async Task IAsyncLifetime.InitializeAsync() + async ValueTask IAsyncLifetime.InitializeAsync() { if (!s_infrastructureStarted) { @@ -45,7 +44,7 @@ async Task IAsyncLifetime.InitializeAsync() } } - async Task IAsyncLifetime.DisposeAsync() + async ValueTask IAsyncDisposable.DisposeAsync() { // Nothing to clean up await Task.CompletedTask; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/AzureAgentProviderTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/AzureAgentProviderTest.cs index da3f6f2fd5..562ce8abda 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/AzureAgentProviderTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/AzureAgentProviderTest.cs @@ -5,7 +5,6 @@ using Azure.Identity; using Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Framework; using Microsoft.Extensions.AI; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/DeclarativeCodeGenTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/DeclarativeCodeGenTest.cs index 93623d40ca..459144a514 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/DeclarativeCodeGenTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/DeclarativeCodeGenTest.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Framework; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/DeclarativeWorkflowTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/DeclarativeWorkflowTest.cs index 8757ff1f3f..c6a328d73d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/DeclarativeWorkflowTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/DeclarativeWorkflowTest.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Agents; using Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Framework; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/IntegrationTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/IntegrationTest.cs index 662575cdf4..f5b56fac49 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/IntegrationTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/IntegrationTest.cs @@ -10,7 +10,6 @@ using Microsoft.Agents.ObjectModel; using Microsoft.Extensions.AI; using Microsoft.Extensions.Configuration; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Framework; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/TestOutputAdapter.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/TestOutputAdapter.cs index e1a0857c85..5acc3e5c02 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/TestOutputAdapter.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/TestOutputAdapter.cs @@ -5,7 +5,6 @@ using System.IO; using System.Text; using Microsoft.Extensions.Logging; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Framework; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/WorkflowTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/WorkflowTest.cs index 151e9fc70c..0333bf4d1c 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/WorkflowTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/WorkflowTest.cs @@ -8,7 +8,6 @@ using System.Text.Json.Serialization; using System.Threading.Tasks; using Microsoft.Extensions.AI; -using Xunit.Abstractions; using Xunit.Sdk; namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Framework; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/FunctionCallingWorkflowTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/FunctionCallingWorkflowTest.cs index 63e052481a..17b9514ee4 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/FunctionCallingWorkflowTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/FunctionCallingWorkflowTest.cs @@ -11,7 +11,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Framework; using Microsoft.Agents.AI.Workflows.Declarative.Kit; using Microsoft.Extensions.AI; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/InvokeFunctionToolWorkflowTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/InvokeFunctionToolWorkflowTest.cs index 289fbe2faa..2c5ccbe010 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/InvokeFunctionToolWorkflowTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/InvokeFunctionToolWorkflowTest.cs @@ -11,7 +11,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Framework; using Microsoft.Agents.AI.Workflows.Declarative.Kit; using Microsoft.Extensions.AI; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/MediaInputTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/MediaInputTest.cs index da30db6f98..60c4915579 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/MediaInputTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/MediaInputTest.cs @@ -9,7 +9,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Framework; using Microsoft.Extensions.AI; using OpenAI.Files; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/AddConversationMessageTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/AddConversationMessageTemplateTest.cs index d62bb8556c..786563d688 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/AddConversationMessageTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/AddConversationMessageTemplateTest.cs @@ -5,7 +5,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.CodeGen; using Microsoft.Agents.AI.Workflows.Declarative.Kit; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/BreakLoopTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/BreakLoopTemplateTest.cs index a3e202b60a..2960718256 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/BreakLoopTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/BreakLoopTemplateTest.cs @@ -2,7 +2,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.CodeGen; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ClearAllVariablesTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ClearAllVariablesTemplateTest.cs index a7abb63ee4..be7ea25eab 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ClearAllVariablesTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ClearAllVariablesTemplateTest.cs @@ -3,7 +3,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.CodeGen; using Microsoft.Agents.AI.Workflows.Declarative.Kit; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ConditionGroupTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ConditionGroupTemplateTest.cs index 0d3c47089e..af0166c44e 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ConditionGroupTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ConditionGroupTemplateTest.cs @@ -3,7 +3,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.CodeGen; using Microsoft.Agents.AI.Workflows.Declarative.Kit; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ContinueLoopTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ContinueLoopTemplateTest.cs index 19e4a41d2c..9210460701 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ContinueLoopTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ContinueLoopTemplateTest.cs @@ -2,7 +2,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.CodeGen; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/CopyConversationMessagesTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/CopyConversationMessagesTemplateTest.cs index 438f793b0e..5f005b6b3b 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/CopyConversationMessagesTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/CopyConversationMessagesTemplateTest.cs @@ -3,7 +3,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.CodeGen; using Microsoft.Agents.AI.Workflows.Declarative.Kit; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/CreateConversationTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/CreateConversationTemplateTest.cs index 9991a1a827..c4c0fd4458 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/CreateConversationTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/CreateConversationTemplateTest.cs @@ -5,7 +5,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.Extensions; using Microsoft.Agents.AI.Workflows.Declarative.Kit; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/DeclarativeEjectionTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/DeclarativeEjectionTest.cs index 6f87f77fb4..0c6ac9efe7 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/DeclarativeEjectionTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/DeclarativeEjectionTest.cs @@ -4,7 +4,6 @@ using System.IO; using System.Threading.Tasks; using Shared.Code; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/EdgeTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/EdgeTemplateTest.cs index ead2ca742a..10633f4581 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/EdgeTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/EdgeTemplateTest.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. using Microsoft.Agents.AI.Workflows.Declarative.CodeGen; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/EndConversationTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/EndConversationTest.cs index c38036e777..75d2cc7b80 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/EndConversationTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/EndConversationTest.cs @@ -2,7 +2,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.CodeGen; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/EndDialogTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/EndDialogTest.cs index 59065665c3..aea9b76833 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/EndDialogTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/EndDialogTest.cs @@ -2,7 +2,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.CodeGen; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ForeachTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ForeachTemplateTest.cs index aaafa5bfb3..d6e924c262 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ForeachTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ForeachTemplateTest.cs @@ -4,7 +4,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.Kit; using Microsoft.Agents.AI.Workflows.Declarative.ObjectModel; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/GotoTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/GotoTemplateTest.cs index b4aefadb68..1c9c2c26ad 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/GotoTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/GotoTemplateTest.cs @@ -2,7 +2,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.CodeGen; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/InvokeAzureAgentTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/InvokeAzureAgentTemplateTest.cs index 34acf37702..8642270726 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/InvokeAzureAgentTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/InvokeAzureAgentTemplateTest.cs @@ -3,7 +3,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.CodeGen; using Microsoft.Agents.AI.Workflows.Declarative.Kit; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ProviderTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ProviderTemplateTest.cs index fcaabcb4a1..28ae9a0314 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ProviderTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ProviderTemplateTest.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using Microsoft.Agents.AI.Workflows.Declarative.CodeGen; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ResetVariableTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ResetVariableTemplateTest.cs index 1ffd3e16ef..b34126c5be 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ResetVariableTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/ResetVariableTemplateTest.cs @@ -3,7 +3,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.CodeGen; using Microsoft.Agents.AI.Workflows.Declarative.Kit; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/RetrieveConversationMessageTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/RetrieveConversationMessageTemplateTest.cs index 093a43ffa5..153cb95ea4 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/RetrieveConversationMessageTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/RetrieveConversationMessageTemplateTest.cs @@ -3,7 +3,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.CodeGen; using Microsoft.Agents.AI.Workflows.Declarative.Kit; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/RetrieveConversationMessagesTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/RetrieveConversationMessagesTemplateTest.cs index 1c3f5c20f5..30988ef019 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/RetrieveConversationMessagesTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/RetrieveConversationMessagesTemplateTest.cs @@ -3,7 +3,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.CodeGen; using Microsoft.Agents.AI.Workflows.Declarative.Kit; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/SetMultipleVariablesTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/SetMultipleVariablesTemplateTest.cs index 5dd05c8bac..91387705e0 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/SetMultipleVariablesTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/SetMultipleVariablesTemplateTest.cs @@ -4,7 +4,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.Kit; using Microsoft.Agents.ObjectModel; using Microsoft.PowerFx.Types; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/SetTextVariableTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/SetTextVariableTemplateTest.cs index 4638ee0c8b..9a503394de 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/SetTextVariableTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/SetTextVariableTemplateTest.cs @@ -3,7 +3,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.CodeGen; using Microsoft.Agents.AI.Workflows.Declarative.Kit; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/SetVariableTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/SetVariableTemplateTest.cs index c71c57486e..64f8a1b6a8 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/SetVariableTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/SetVariableTemplateTest.cs @@ -4,7 +4,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.Kit; using Microsoft.Agents.ObjectModel; using Microsoft.PowerFx.Types; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/WorkflowActionTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/WorkflowActionTemplateTest.cs index 2f6cedb6dd..6ae2a4b45e 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/WorkflowActionTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/WorkflowActionTemplateTest.cs @@ -3,7 +3,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.Extensions; using Microsoft.Agents.AI.Workflows.Declarative.Kit; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/DeclarativeWorkflowExceptionTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/DeclarativeWorkflowExceptionTest.cs index cbe3ac0a81..099c09c27d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/DeclarativeWorkflowExceptionTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/DeclarativeWorkflowExceptionTest.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. using System; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/DeclarativeWorkflowTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/DeclarativeWorkflowTest.cs index 09c984ca05..6c61d6cb7d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/DeclarativeWorkflowTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/DeclarativeWorkflowTest.cs @@ -12,7 +12,6 @@ using Microsoft.Agents.ObjectModel; using Microsoft.Extensions.AI; using Moq; -using Xunit.Abstractions; using Xunit.Sdk; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Entities/EntityExtractionResultTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Entities/EntityExtractionResultTest.cs index 50cff90b3e..d2c545516e 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Entities/EntityExtractionResultTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Entities/EntityExtractionResultTest.cs @@ -2,7 +2,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.Entities; using Microsoft.PowerFx.Types; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.Entities; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Entities/EntityExtractorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Entities/EntityExtractorTest.cs index b03700d215..4a677eb362 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Entities/EntityExtractorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Entities/EntityExtractorTest.cs @@ -4,7 +4,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.Entities; using Microsoft.Agents.ObjectModel; using Microsoft.PowerFx.Types; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.Entities; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/EventTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/EventTest.cs index a4965ebc61..9133471553 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/EventTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/EventTest.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Text.Json; using Microsoft.Extensions.AI; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/ExternalInputRequestTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/ExternalInputRequestTest.cs index d1165d84d4..cebdc60cb9 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/ExternalInputRequestTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/ExternalInputRequestTest.cs @@ -2,7 +2,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.Events; using Microsoft.Extensions.AI; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.Events; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/ExternalInputResponseTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/ExternalInputResponseTest.cs index b1fb358727..384664a68c 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/ExternalInputResponseTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/ExternalInputResponseTest.cs @@ -2,7 +2,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.Events; using Microsoft.Extensions.AI; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.Events; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Interpreter/WorkflowModelTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Interpreter/WorkflowModelTest.cs index 95d738f8f0..03a5bb670f 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Interpreter/WorkflowModelTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Interpreter/WorkflowModelTest.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. using Microsoft.Agents.AI.Workflows.Declarative.Interpreter; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.Interpreter; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/AddConversationMessageExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/AddConversationMessageExecutorTest.cs index a7f2ba48f6..2f89de4dee 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/AddConversationMessageExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/AddConversationMessageExecutorTest.cs @@ -9,7 +9,6 @@ using Microsoft.Agents.ObjectModel; using Microsoft.Extensions.AI; using Microsoft.PowerFx.Types; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ClearAllVariablesExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ClearAllVariablesExecutorTest.cs index 70e4ac0a02..cc18bcb463 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ClearAllVariablesExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ClearAllVariablesExecutorTest.cs @@ -4,7 +4,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.ObjectModel; using Microsoft.Agents.ObjectModel; using Microsoft.PowerFx.Types; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ConditionGroupExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ConditionGroupExecutorTest.cs index caf7344467..910af1ca64 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ConditionGroupExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ConditionGroupExecutorTest.cs @@ -4,7 +4,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.Kit; using Microsoft.Agents.AI.Workflows.Declarative.ObjectModel; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CopyConversationMessagesExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CopyConversationMessagesExecutorTest.cs index cb818fec15..c0a2fdf659 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CopyConversationMessagesExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CopyConversationMessagesExecutorTest.cs @@ -9,7 +9,6 @@ using Microsoft.Agents.ObjectModel; using Microsoft.Extensions.AI; using Microsoft.PowerFx.Types; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CreateConversationExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CreateConversationExecutorTest.cs index a8c8f799b2..5c00fbcdda 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CreateConversationExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/CreateConversationExecutorTest.cs @@ -6,7 +6,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.PowerFx; using Microsoft.Agents.ObjectModel; using Microsoft.PowerFx.Types; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/DefaultActionExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/DefaultActionExecutorTest.cs index 0e7f0a4558..e10f0b0d92 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/DefaultActionExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/DefaultActionExecutorTest.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using Microsoft.Agents.AI.Workflows.Declarative.ObjectModel; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/EditTableExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/EditTableExecutorTest.cs index 6c422247f1..ad9d51c2fe 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/EditTableExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/EditTableExecutorTest.cs @@ -7,7 +7,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.ObjectModel; using Microsoft.Agents.ObjectModel; using Microsoft.PowerFx.Types; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/EditTableV2ExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/EditTableV2ExecutorTest.cs index 5eb723ae0e..bb4442507c 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/EditTableV2ExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/EditTableV2ExecutorTest.cs @@ -5,7 +5,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.ObjectModel; using Microsoft.Agents.ObjectModel; using Microsoft.PowerFx.Types; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ForeachExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ForeachExecutorTest.cs index 44989ad8a1..7840910d5b 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ForeachExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ForeachExecutorTest.cs @@ -5,7 +5,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.ObjectModel; using Microsoft.Agents.ObjectModel; using Microsoft.PowerFx.Types; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/InvokeFunctionToolExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/InvokeFunctionToolExecutorTest.cs index 4a07ba3002..b00339ea3b 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/InvokeFunctionToolExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/InvokeFunctionToolExecutorTest.cs @@ -6,7 +6,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.PowerFx; using Microsoft.Agents.ObjectModel; using Microsoft.Extensions.AI; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ParseValueExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ParseValueExecutorTest.cs index 22854c90e8..01c6944654 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ParseValueExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ParseValueExecutorTest.cs @@ -4,7 +4,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.ObjectModel; using Microsoft.Agents.ObjectModel; using Microsoft.PowerFx.Types; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/QuestionExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/QuestionExecutorTest.cs index b2713037bc..dbe056f891 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/QuestionExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/QuestionExecutorTest.cs @@ -12,7 +12,6 @@ using Microsoft.Extensions.AI; using Microsoft.PowerFx.Types; using Moq; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/RequestExternalInputExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/RequestExternalInputExecutorTest.cs index 778a6dd7b7..1e11f1a0ae 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/RequestExternalInputExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/RequestExternalInputExecutorTest.cs @@ -12,7 +12,6 @@ using Microsoft.Extensions.AI; using Microsoft.PowerFx.Types; using Moq; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ResetVariableExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ResetVariableExecutorTest.cs index 9059780751..022d84bbfe 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ResetVariableExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ResetVariableExecutorTest.cs @@ -4,7 +4,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.ObjectModel; using Microsoft.Agents.ObjectModel; using Microsoft.PowerFx.Types; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/RetrieveConversationMessageExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/RetrieveConversationMessageExecutorTest.cs index e3812100ee..622b54d1b2 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/RetrieveConversationMessageExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/RetrieveConversationMessageExecutorTest.cs @@ -6,7 +6,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.ObjectModel; using Microsoft.Agents.ObjectModel; using Microsoft.Extensions.AI; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/RetrieveConversationMessagesExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/RetrieveConversationMessagesExecutorTest.cs index cbdfc2056d..7b726ccb23 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/RetrieveConversationMessagesExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/RetrieveConversationMessagesExecutorTest.cs @@ -4,7 +4,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.Extensions; using Microsoft.Agents.AI.Workflows.Declarative.ObjectModel; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/SendActivityExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/SendActivityExecutorTest.cs index 32cadc6c4e..8ae95d0eb5 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/SendActivityExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/SendActivityExecutorTest.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using Microsoft.Agents.AI.Workflows.Declarative.ObjectModel; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/SetMultipleVariablesExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/SetMultipleVariablesExecutorTest.cs index 037ee5b94a..467a20044e 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/SetMultipleVariablesExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/SetMultipleVariablesExecutorTest.cs @@ -5,7 +5,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.ObjectModel; using Microsoft.Agents.ObjectModel; using Microsoft.PowerFx.Types; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/SetTextVariableExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/SetTextVariableExecutorTest.cs index 0bc850e9ce..f15a315eab 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/SetTextVariableExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/SetTextVariableExecutorTest.cs @@ -4,7 +4,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.ObjectModel; using Microsoft.Agents.ObjectModel; using Microsoft.PowerFx.Types; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/SetVariableExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/SetVariableExecutorTest.cs index dddfab6365..4f4bb39856 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/SetVariableExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/SetVariableExecutorTest.cs @@ -4,7 +4,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.ObjectModel; using Microsoft.Agents.ObjectModel; using Microsoft.PowerFx.Types; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/WorkflowActionExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/WorkflowActionExecutorTest.cs index 6c87668bbf..de5487c79b 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/WorkflowActionExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/WorkflowActionExecutorTest.cs @@ -10,7 +10,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.PowerFx; using Microsoft.Agents.ObjectModel; using Microsoft.PowerFx.Types; -using Xunit.Abstractions; using Xunit.Sdk; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/PowerFx/RecalcEngineFactoryTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/PowerFx/RecalcEngineFactoryTests.cs index 976ad796b9..d158ca552b 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/PowerFx/RecalcEngineFactoryTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/PowerFx/RecalcEngineFactoryTests.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using Microsoft.Agents.AI.Workflows.Declarative.PowerFx; using Microsoft.PowerFx; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.PowerFx; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/PowerFx/RecalcEngineTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/PowerFx/RecalcEngineTest.cs index eeaefaf669..c509259fe1 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/PowerFx/RecalcEngineTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/PowerFx/RecalcEngineTest.cs @@ -2,7 +2,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.PowerFx; using Microsoft.PowerFx; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.PowerFx; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/PowerFx/TemplateExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/PowerFx/TemplateExtensionsTests.cs index 9bbbc39f42..de7f045052 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/PowerFx/TemplateExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/PowerFx/TemplateExtensionsTests.cs @@ -4,7 +4,6 @@ using Microsoft.Agents.AI.Workflows.Declarative.Extensions; using Microsoft.Agents.ObjectModel; using Microsoft.PowerFx.Types; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.PowerFx; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/PowerFx/WorkflowExpressionEngineTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/PowerFx/WorkflowExpressionEngineTests.cs index 2aaa016141..ebaaf5d046 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/PowerFx/WorkflowExpressionEngineTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/PowerFx/WorkflowExpressionEngineTests.cs @@ -8,7 +8,6 @@ using Microsoft.Agents.ObjectModel.Abstractions; using Microsoft.Agents.ObjectModel.Exceptions; using Microsoft.PowerFx.Types; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.PowerFx; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/TestOutputAdapter.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/TestOutputAdapter.cs index 72da232da9..e4d756a24a 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/TestOutputAdapter.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/TestOutputAdapter.cs @@ -5,7 +5,6 @@ using System.IO; using System.Text; using Microsoft.Extensions.Logging; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/WorkflowTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/WorkflowTest.cs index c8805b606c..1e6704b1f6 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/WorkflowTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/WorkflowTest.cs @@ -3,7 +3,6 @@ using System; using Microsoft.Agents.AI.Workflows.Declarative.PowerFx; using Microsoft.Agents.ObjectModel; -using Xunit.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests; diff --git a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs index bb58f09fb4..c59d3cf068 100644 --- a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs +++ b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Threading.Tasks; using AgentConformance.IntegrationTests; @@ -79,7 +80,7 @@ public Task DeleteSessionAsync(AgentSession session) return Task.CompletedTask; } - public async Task InitializeAsync() + public async ValueTask InitializeAsync() { var client = new OpenAIClient(s_config.ApiKey); this._assistantClient = client.GetAssistantClient(); @@ -87,13 +88,15 @@ public async Task InitializeAsync() this._agent = await this.CreateChatClientAgentAsync(); } - public Task DisposeAsync() + public ValueTask DisposeAsync() { + GC.SuppressFinalize(this); + if (this._assistantClient is not null && this._agent is not null) { - return this._assistantClient.DeleteAssistantAsync(this._agent.Id); + return new ValueTask(this._assistantClient.DeleteAssistantAsync(this._agent.Id)); } - return Task.CompletedTask; + return default; } } diff --git a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs index 96a9a17ae8..36ff48dee7 100644 --- a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs +++ b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -64,9 +65,12 @@ public Task DeleteSessionAsync(AgentSession session) => // Chat Completion does not require/support deleting threads, so this is a no-op. Task.CompletedTask; - public async Task InitializeAsync() => + public async ValueTask InitializeAsync() => this._agent = await this.CreateChatClientAgentAsync(); - public Task DisposeAsync() => - Task.CompletedTask; + public ValueTask DisposeAsync() + { + GC.SuppressFinalize(this); + return default; + } } diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunStreamingTests.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunStreamingTests.cs index 80a148d7fc..737abd2561 100644 --- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunStreamingTests.cs +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunStreamingTests.cs @@ -9,16 +9,20 @@ namespace ResponseResult.IntegrationTests; { private const string SkipReason = "ResponseResult does not support empty messages"; - [Fact(Skip = SkipReason)] - public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() => - Task.CompletedTask; + public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() + { + Assert.Skip(SkipReason); + return base.RunWithInstructionsAndNoMessageReturnsExpectedResultAsync(); + } } public class OpenAIResponseStoreFalseChatClientAgentRunStreamingTests() : ChatClientAgentRunStreamingTests(() => new(store: false)) { private const string SkipReason = "ResponseResult does not support empty messages"; - [Fact(Skip = SkipReason)] - public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() => - Task.CompletedTask; + public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() + { + Assert.Skip(SkipReason); + return base.RunWithInstructionsAndNoMessageReturnsExpectedResultAsync(); + } } diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunTests.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunTests.cs index 8b742e2964..58463212bd 100644 --- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunTests.cs +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunTests.cs @@ -9,16 +9,20 @@ namespace ResponseResult.IntegrationTests; { private const string SkipReason = "ResponseResult does not support empty messages"; - [Fact(Skip = SkipReason)] - public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() => - Task.CompletedTask; + public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() + { + Assert.Skip(SkipReason); + return base.RunWithInstructionsAndNoMessageReturnsExpectedResultAsync(); + } } public class OpenAIResponseStoreFalseChatClientAgentRunTests() : ChatClientAgentRunTests(() => new(store: false)) { private const string SkipReason = "ResponseResult does not support empty messages"; - [Fact(Skip = SkipReason)] - public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() => - Task.CompletedTask; + public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() + { + Assert.Skip(SkipReason); + return base.RunWithInstructionsAndNoMessageReturnsExpectedResultAsync(); + } } diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs index e36f8990f6..1ab7cb5112 100644 --- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs @@ -96,7 +96,7 @@ public Task DeleteSessionAsync(AgentSession session) => // Chat Completion does not require/support deleting threads, so this is a no-op. Task.CompletedTask; - public async Task InitializeAsync() + public async ValueTask InitializeAsync() { this._openAIResponseClient = new OpenAIClient(s_config.ApiKey) .GetResponsesClient(s_config.ChatModelId); @@ -104,5 +104,9 @@ public async Task InitializeAsync() this._agent = await this.CreateChatClientAgentAsync(); } - public Task DisposeAsync() => Task.CompletedTask; + public ValueTask DisposeAsync() + { + GC.SuppressFinalize(this); + return default; + } } diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunStreamingTests.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunStreamingTests.cs index c12f8f2db5..75c337bd5a 100644 --- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunStreamingTests.cs +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunStreamingTests.cs @@ -8,16 +8,21 @@ namespace ResponseResult.IntegrationTests; public class OpenAIResponseStoreTrueRunStreamingTests() : RunStreamingTests(() => new(store: true)) { private const string SkipReason = "ResponseResult does not support empty messages"; - [Fact(Skip = SkipReason)] - public override Task RunWithNoMessageDoesNotFailAsync() => - Task.CompletedTask; + + public override Task RunWithNoMessageDoesNotFailAsync() + { + Assert.Skip(SkipReason); + return base.RunWithNoMessageDoesNotFailAsync(); + } } public class OpenAIResponseStoreFalseRunStreamingTests() : RunStreamingTests(() => new(store: false)) { private const string SkipReason = "ResponseResult does not support empty messages"; - [Fact(Skip = SkipReason)] - public override Task RunWithNoMessageDoesNotFailAsync() => - Task.CompletedTask; + public override Task RunWithNoMessageDoesNotFailAsync() + { + Assert.Skip(SkipReason); + return base.RunWithNoMessageDoesNotFailAsync(); + } } diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunTests.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunTests.cs index 423ac583c7..df4962b640 100644 --- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunTests.cs +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunTests.cs @@ -8,16 +8,21 @@ namespace ResponseResult.IntegrationTests; public class OpenAIResponseStoreTrueRunTests() : RunTests(() => new(store: true)) { private const string SkipReason = "ResponseResult does not support empty messages"; - [Fact(Skip = SkipReason)] - public override Task RunWithNoMessageDoesNotFailAsync() => - Task.CompletedTask; + + public override Task RunWithNoMessageDoesNotFailAsync() + { + Assert.Skip(SkipReason); + return base.RunWithNoMessageDoesNotFailAsync(); + } } public class OpenAIResponseStoreFalseRunTests() : RunTests(() => new(store: false)) { private const string SkipReason = "ResponseResult does not support empty messages"; - [Fact(Skip = SkipReason)] - public override Task RunWithNoMessageDoesNotFailAsync() => - Task.CompletedTask; + public override Task RunWithNoMessageDoesNotFailAsync() + { + Assert.Skip(SkipReason); + return base.RunWithNoMessageDoesNotFailAsync(); + } } diff --git a/dotnet/tests/coverage.runsettings b/dotnet/tests/coverage.runsettings new file mode 100644 index 0000000000..c59039e263 --- /dev/null +++ b/dotnet/tests/coverage.runsettings @@ -0,0 +1,21 @@ + + + + + + + + + + + ^System\.CodeDom\.Compiler\.GeneratedCodeAttribute$ + ^System\.Runtime\.CompilerServices\.CompilerGeneratedAttribute$ + ^System\.Diagnostics\.CodeAnalysis\.ExcludeFromCodeCoverageAttribute$ + + + + + + + + From d04228da3949db1581ef77324d59f74b2fa80241 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Tue, 24 Feb 2026 12:07:17 +0000 Subject: [PATCH 02/19] Fix copilot studio integration tests failure (#4209) --- .../CopilotStudioFixture.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs index 6018fab2f0..af97b6bcca 100644 --- a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs +++ b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs @@ -31,8 +31,17 @@ public ValueTask InitializeAsync() { const string CopilotStudioHttpClientName = nameof(CopilotStudioAgent); - var config = TestConfiguration.LoadSection(); - var settings = new CopilotStudioConnectionSettings(config.TenantId, config.AppClientId) + CopilotStudioAgentConfiguration? config = null; + try + { + config = TestConfiguration.LoadSection(); + } + catch (InvalidOperationException ex) + { + Assert.Skip("CopilotStudio configuration could not be loaded. Error:" + ex.Message); + } + + var settings = new CopilotStudioConnectionSettings(config!.TenantId, config.AppClientId) { DirectConnectUrl = config.DirectConnectUrl, }; From fb732311acd9617c6ac8d7e2f0f23c156b01f881 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Tue, 24 Feb 2026 14:48:12 +0000 Subject: [PATCH 03/19] Fix anthropic integration tests and skip reason (#4211) --- ...pletionChatClientAgentRunStreamingTests.cs | 25 ++--------- ...icChatCompletionChatClientAgentRunTests.cs | 25 ++--------- .../AnthropicChatCompletionFixture.cs | 13 +++--- ...nthropicChatCompletionRunStreamingTests.cs | 43 ++----------------- .../AnthropicChatCompletionRunTests.cs | 43 ++----------------- .../AnthropicSkillsIntegrationTests.cs | 18 +++++--- ...gentsPersistentStructuredOutputRunTests.cs | 6 +-- .../CopilotStudioRunStreamingTests.cs | 8 ++-- .../CopilotStudioRunTests.cs | 8 ++-- 9 files changed, 46 insertions(+), 143 deletions(-) diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionChatClientAgentRunStreamingTests.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionChatClientAgentRunStreamingTests.cs index 4a95ace16e..86b07a30f9 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionChatClientAgentRunStreamingTests.cs +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionChatClientAgentRunStreamingTests.cs @@ -1,30 +1,13 @@ // Copyright (c) Microsoft. All rights reserved. -using System; -using System.Threading.Tasks; using AgentConformance.IntegrationTests; namespace AnthropicChatCompletion.IntegrationTests; -public abstract class SkipAllChatClientRunStreaming(Func func) : ChatClientAgentRunStreamingTests(func) -{ - public override Task RunWithFunctionsInvokesFunctionsAndReturnsExpectedResultsAsync() - { - Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); - return base.RunWithFunctionsInvokesFunctionsAndReturnsExpectedResultsAsync(); - } +public class AnthropicBetaChatCompletionChatClientAgentReasoningRunStreamingTests() : ChatClientAgentRunStreamingTests(() => new(useReasoningChatModel: true, useBeta: true)); - public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() - { - Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); - return base.RunWithInstructionsAndNoMessageReturnsExpectedResultAsync(); - } -} +public class AnthropicBetaChatCompletionChatClientAgentRunStreamingTests() : ChatClientAgentRunStreamingTests(() => new(useReasoningChatModel: false, useBeta: true)); -public class AnthropicBetaChatCompletionChatClientAgentReasoningRunStreamingTests() : SkipAllChatClientRunStreaming(() => new(useReasoningChatModel: true, useBeta: true)); +public class AnthropicChatCompletionChatClientAgentRunStreamingTests() : ChatClientAgentRunStreamingTests(() => new(useReasoningChatModel: false, useBeta: false)); -public class AnthropicBetaChatCompletionChatClientAgentRunStreamingTests() : SkipAllChatClientRunStreaming(() => new(useReasoningChatModel: false, useBeta: true)); - -public class AnthropicChatCompletionChatClientAgentRunStreamingTests() : SkipAllChatClientRunStreaming(() => new(useReasoningChatModel: false, useBeta: false)); - -public class AnthropicChatCompletionChatClientAgentReasoningRunStreamingTests() : SkipAllChatClientRunStreaming(() => new(useReasoningChatModel: true, useBeta: false)); +public class AnthropicChatCompletionChatClientAgentReasoningRunStreamingTests() : ChatClientAgentRunStreamingTests(() => new(useReasoningChatModel: true, useBeta: false)); diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionChatClientAgentRunTests.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionChatClientAgentRunTests.cs index 98ff9b6448..db150a2605 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionChatClientAgentRunTests.cs +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionChatClientAgentRunTests.cs @@ -1,34 +1,17 @@ // Copyright (c) Microsoft. All rights reserved. -using System; -using System.Threading.Tasks; using AgentConformance.IntegrationTests; namespace AnthropicChatCompletion.IntegrationTests; -public abstract class SkipAllChatClientAgentRun(Func func) : ChatClientAgentRunTests(func) -{ - public override Task RunWithFunctionsInvokesFunctionsAndReturnsExpectedResultsAsync() - { - Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); - return base.RunWithFunctionsInvokesFunctionsAndReturnsExpectedResultsAsync(); - } - - public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() - { - Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); - return base.RunWithInstructionsAndNoMessageReturnsExpectedResultAsync(); - } -} - public class AnthropicBetaChatCompletionChatClientAgentRunTests() - : SkipAllChatClientAgentRun(() => new(useReasoningChatModel: false, useBeta: true)); + : ChatClientAgentRunTests(() => new(useReasoningChatModel: false, useBeta: true)); public class AnthropicBetaChatCompletionChatClientAgentReasoningRunTests() - : SkipAllChatClientAgentRun(() => new(useReasoningChatModel: true, useBeta: true)); + : ChatClientAgentRunTests(() => new(useReasoningChatModel: true, useBeta: true)); public class AnthropicChatCompletionChatClientAgentRunTests() - : SkipAllChatClientAgentRun(() => new(useReasoningChatModel: false, useBeta: false)); + : ChatClientAgentRunTests(() => new(useReasoningChatModel: false, useBeta: false)); public class AnthropicChatCompletionChatClientAgentReasoningRunTests() - : SkipAllChatClientAgentRun(() => new(useReasoningChatModel: true, useBeta: false)); + : ChatClientAgentRunTests(() => new(useReasoningChatModel: true, useBeta: false)); diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs index 8048d86dee..4cf232f7d8 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs @@ -20,7 +20,6 @@ public class AnthropicChatCompletionFixture : IChatClientAgentFixture // All tests for Anthropic are intended to be ran locally as the CI pipeline for Anthropic is not setup. internal const string SkipReason = "Integrations tests for local execution only"; - private static readonly AnthropicConfiguration s_config = TestConfiguration.LoadSection(); private readonly bool _useReasoningModel; private readonly bool _useBeta; @@ -53,7 +52,8 @@ public Task CreateChatClientAgentAsync( string instructions = "You are a helpful assistant.", IList? aiTools = null) { - var anthropicClient = new AnthropicClient() { ApiKey = s_config.ApiKey }; + AnthropicConfiguration config = TestConfiguration.LoadSection(); + var anthropicClient = new AnthropicClient() { ApiKey = config.ApiKey }; IChatClient? chatClient = this._useBeta ? anthropicClient @@ -64,7 +64,7 @@ public Task CreateChatClientAgentAsync( => options.RawRepresentationFactory = _ => new Anthropic.Models.Beta.Messages.MessageCreateParams() { - Model = options.ModelId ?? (this._useReasoningModel ? s_config.ChatReasoningModelId : s_config.ChatModelId), + Model = options.ModelId ?? (this._useReasoningModel ? config.ChatReasoningModelId : config.ChatModelId), MaxTokens = options.MaxOutputTokens ?? 4096, Messages = [], Thinking = this._useReasoningModel @@ -79,7 +79,7 @@ public Task CreateChatClientAgentAsync( => options.RawRepresentationFactory = _ => new Anthropic.Models.Messages.MessageCreateParams() { - Model = options.ModelId ?? (this._useReasoningModel ? s_config.ChatReasoningModelId : s_config.ChatModelId), + Model = options.ModelId ?? (this._useReasoningModel ? config.ChatReasoningModelId : config.ChatModelId), MaxTokens = options.MaxOutputTokens ?? 4096, Messages = [], Thinking = this._useReasoningModel @@ -102,8 +102,11 @@ public Task DeleteSessionAsync(AgentSession session) => // Chat Completion does not require/support deleting sessions, so this is a no-op. Task.CompletedTask; - public async ValueTask InitializeAsync() => + public async ValueTask InitializeAsync() + { + Assert.SkipWhen(SkipReason is not null, SkipReason ?? string.Empty); this._agent = await this.CreateChatClientAgentAsync(); + } public ValueTask DisposeAsync() { diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunStreamingTests.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunStreamingTests.cs index 7bc187f03f..ee39281ba6 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunStreamingTests.cs +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunStreamingTests.cs @@ -1,52 +1,17 @@ // Copyright (c) Microsoft. All rights reserved. -using System; -using System.Threading.Tasks; using AgentConformance.IntegrationTests; namespace AnthropicChatCompletion.IntegrationTests; -public abstract class SkipAllRunStreaming(Func func) : RunStreamingTests(func) -{ - public override Task RunWithChatMessageReturnsExpectedResultAsync() - { - Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); - return base.RunWithChatMessageReturnsExpectedResultAsync(); - } - - public override Task RunWithNoMessageDoesNotFailAsync() - { - Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); - return base.RunWithNoMessageDoesNotFailAsync(); - } - - public override Task RunWithChatMessagesReturnsExpectedResultAsync() - { - Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); - return base.RunWithChatMessagesReturnsExpectedResultAsync(); - } - - public override Task RunWithStringReturnsExpectedResultAsync() - { - Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); - return base.RunWithStringReturnsExpectedResultAsync(); - } - - public override Task SessionMaintainsHistoryAsync() - { - Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); - return base.SessionMaintainsHistoryAsync(); - } -} - public class AnthropicBetaChatCompletionRunStreamingTests() - : SkipAllRunStreaming(() => new(useReasoningChatModel: false, useBeta: true)); + : RunStreamingTests(() => new(useReasoningChatModel: false, useBeta: true)); public class AnthropicBetaChatCompletionReasoningRunStreamingTests() - : SkipAllRunStreaming(() => new(useReasoningChatModel: true, useBeta: true)); + : RunStreamingTests(() => new(useReasoningChatModel: true, useBeta: true)); public class AnthropicChatCompletionRunStreamingTests() - : SkipAllRunStreaming(() => new(useReasoningChatModel: false, useBeta: false)); + : RunStreamingTests(() => new(useReasoningChatModel: false, useBeta: false)); public class AnthropicChatCompletionReasoningRunStreamingTests() - : SkipAllRunStreaming(() => new(useReasoningChatModel: true, useBeta: false)); + : RunStreamingTests(() => new(useReasoningChatModel: true, useBeta: false)); diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunTests.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunTests.cs index 6c664d65fc..6cf514e695 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunTests.cs +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionRunTests.cs @@ -1,52 +1,17 @@ // Copyright (c) Microsoft. All rights reserved. -using System; -using System.Threading.Tasks; using AgentConformance.IntegrationTests; namespace AnthropicChatCompletion.IntegrationTests; -public abstract class SkipAllRun(Func func) : RunTests(func) -{ - public override Task RunWithChatMessageReturnsExpectedResultAsync() - { - Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); - return base.RunWithChatMessageReturnsExpectedResultAsync(); - } - - public override Task RunWithNoMessageDoesNotFailAsync() - { - Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); - return base.RunWithNoMessageDoesNotFailAsync(); - } - - public override Task RunWithChatMessagesReturnsExpectedResultAsync() - { - Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); - return base.RunWithChatMessagesReturnsExpectedResultAsync(); - } - - public override Task RunWithStringReturnsExpectedResultAsync() - { - Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); - return base.RunWithStringReturnsExpectedResultAsync(); - } - - public override Task SessionMaintainsHistoryAsync() - { - Assert.SkipWhen(AnthropicChatCompletionFixture.SkipReason is not null, AnthropicChatCompletionFixture.SkipReason!); - return base.SessionMaintainsHistoryAsync(); - } -} - public class AnthropicBetaChatCompletionRunTests() - : SkipAllRun(() => new(useReasoningChatModel: false, useBeta: true)); + : RunTests(() => new(useReasoningChatModel: false, useBeta: true)); public class AnthropicBetaChatCompletionReasoningRunTests() - : SkipAllRun(() => new(useReasoningChatModel: true, useBeta: true)); + : RunTests(() => new(useReasoningChatModel: true, useBeta: true)); public class AnthropicChatCompletionRunTests() - : SkipAllRun(() => new(useReasoningChatModel: false, useBeta: false)); + : RunTests(() => new(useReasoningChatModel: false, useBeta: false)); public class AnthropicChatCompletionReasoningRunTests() - : SkipAllRun(() => new(useReasoningChatModel: true, useBeta: false)); + : RunTests(() => new(useReasoningChatModel: true, useBeta: false)); diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicSkillsIntegrationTests.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicSkillsIntegrationTests.cs index a6a96bd234..105793c709 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicSkillsIntegrationTests.cs +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicSkillsIntegrationTests.cs @@ -22,14 +22,15 @@ public sealed class AnthropicSkillsIntegrationTests // All tests for Anthropic are intended to be ran locally as the CI pipeline for Anthropic is not setup. private const string SkipReason = "Integrations tests for local execution only"; - private static readonly AnthropicConfiguration s_config = TestConfiguration.LoadSection(); - - [Fact(Skip = SkipReason)] + [Fact] public async Task CreateAgentWithPptxSkillAsync() { + Assert.SkipWhen(SkipReason is not null, SkipReason ?? string.Empty); + // Arrange - AnthropicClient anthropicClient = new() { ApiKey = s_config.ApiKey }; - string model = s_config.ChatModelId; + AnthropicConfiguration config = TestConfiguration.LoadSection(); + AnthropicClient anthropicClient = new() { ApiKey = config.ApiKey }; + string model = config.ChatModelId; BetaSkillParams pptxSkill = new() { @@ -53,11 +54,14 @@ public async Task CreateAgentWithPptxSkillAsync() Assert.NotEmpty(response.Text); } - [Fact(Skip = SkipReason)] + [Fact] public async Task ListAnthropicManagedSkillsAsync() { + Assert.SkipWhen(SkipReason is not null, SkipReason ?? string.Empty); + // Arrange - AnthropicClient anthropicClient = new() { ApiKey = s_config.ApiKey }; + AnthropicConfiguration config = TestConfiguration.LoadSection(); + AnthropicClient anthropicClient = new() { ApiKey = config.ApiKey }; // Act SkillListPage skills = await anthropicClient.Beta.Skills.List( diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentStructuredOutputRunTests.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentStructuredOutputRunTests.cs index 7aa6e3251d..0fa20f18ac 100644 --- a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentStructuredOutputRunTests.cs +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentStructuredOutputRunTests.cs @@ -11,19 +11,19 @@ namespace AzureAIAgentsPersistent.IntegrationTests; public override Task RunWithResponseFormatReturnsExpectedResultAsync() { - Assert.SkipWhen(SkipReason is not null, SkipReason!); + Assert.SkipWhen(SkipReason is not null, SkipReason ?? string.Empty); return base.RunWithResponseFormatReturnsExpectedResultAsync(); } public override Task RunWithGenericTypeReturnsExpectedResultAsync() { - Assert.SkipWhen(SkipReason is not null, SkipReason!); + Assert.SkipWhen(SkipReason is not null, SkipReason ?? string.Empty); return base.RunWithGenericTypeReturnsExpectedResultAsync(); } public override Task RunWithPrimitiveTypeReturnsExpectedResultAsync() { - Assert.SkipWhen(SkipReason is not null, SkipReason!); + Assert.SkipWhen(SkipReason is not null, SkipReason ?? string.Empty); return base.RunWithPrimitiveTypeReturnsExpectedResultAsync(); } } diff --git a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunStreamingTests.cs b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunStreamingTests.cs index d583ddf563..cd482ee748 100644 --- a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunStreamingTests.cs +++ b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunStreamingTests.cs @@ -18,25 +18,25 @@ public override Task SessionMaintainsHistoryAsync() public override Task RunWithChatMessageReturnsExpectedResultAsync() { - Assert.SkipWhen(ManualVerification is not null, ManualVerification!); + Assert.SkipWhen(ManualVerification is not null, ManualVerification ?? string.Empty); return base.RunWithChatMessageReturnsExpectedResultAsync(); } public override Task RunWithChatMessagesReturnsExpectedResultAsync() { - Assert.SkipWhen(ManualVerification is not null, ManualVerification!); + Assert.SkipWhen(ManualVerification is not null, ManualVerification ?? string.Empty); return base.RunWithChatMessagesReturnsExpectedResultAsync(); } public override Task RunWithNoMessageDoesNotFailAsync() { - Assert.SkipWhen(ManualVerification is not null, ManualVerification!); + Assert.SkipWhen(ManualVerification is not null, ManualVerification ?? string.Empty); return base.RunWithNoMessageDoesNotFailAsync(); } public override Task RunWithStringReturnsExpectedResultAsync() { - Assert.SkipWhen(ManualVerification is not null, ManualVerification!); + Assert.SkipWhen(ManualVerification is not null, ManualVerification ?? string.Empty); return base.RunWithStringReturnsExpectedResultAsync(); } } diff --git a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunTests.cs b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunTests.cs index 612ccd27e7..b927b1bfc5 100644 --- a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunTests.cs +++ b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioRunTests.cs @@ -18,25 +18,25 @@ public override Task SessionMaintainsHistoryAsync() public override Task RunWithChatMessageReturnsExpectedResultAsync() { - Assert.SkipWhen(ManualVerification is not null, ManualVerification!); + Assert.SkipWhen(ManualVerification is not null, ManualVerification ?? string.Empty); return base.RunWithChatMessageReturnsExpectedResultAsync(); } public override Task RunWithChatMessagesReturnsExpectedResultAsync() { - Assert.SkipWhen(ManualVerification is not null, ManualVerification!); + Assert.SkipWhen(ManualVerification is not null, ManualVerification ?? string.Empty); return base.RunWithChatMessagesReturnsExpectedResultAsync(); } public override Task RunWithNoMessageDoesNotFailAsync() { - Assert.SkipWhen(ManualVerification is not null, ManualVerification!); + Assert.SkipWhen(ManualVerification is not null, ManualVerification ?? string.Empty); return base.RunWithNoMessageDoesNotFailAsync(); } public override Task RunWithStringReturnsExpectedResultAsync() { - Assert.SkipWhen(ManualVerification is not null, ManualVerification!); + Assert.SkipWhen(ManualVerification is not null, ManualVerification ?? string.Empty); return base.RunWithStringReturnsExpectedResultAsync(); } } From be5ed93b9976f57df4f63b9a283547681e028a55 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Tue, 24 Feb 2026 17:48:48 +0000 Subject: [PATCH 04/19] Remove accidental add of code coverage for integration tests (#4219) --- .github/workflows/dotnet-build-and-test.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/dotnet-build-and-test.yml b/.github/workflows/dotnet-build-and-test.yml index 65a8af8111..f8bb79b8c0 100644 --- a/.github/workflows/dotnet-build-and-test.yml +++ b/.github/workflows/dotnet-build-and-test.yml @@ -198,11 +198,7 @@ jobs: # Check if the project supports the target framework if [[ "$target_frameworks" == *"${{ matrix.targetFramework }}"* ]]; then - if [[ "${{ matrix.targetFramework }}" == "${{ env.COVERAGE_FRAMEWORK }}" ]]; then - dotnet test --project $project -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} --no-build -v Normal --report-xunit-trx --ignore-exit-code 8 --filter-not-trait "Category=IntegrationDisabled" --coverage --coverage-output-format cobertura --coverage-settings "$(pwd)/tests/coverage.runsettings" --results-directory "$(pwd)/../TestResults/Coverage/" - else dotnet test --project $project -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} --no-build -v Normal --report-xunit-trx --ignore-exit-code 8 --filter-not-trait "Category=IntegrationDisabled" - fi else echo "Skipping $project - does not support target framework ${{ matrix.targetFramework }} (supports: $target_frameworks)" fi From a39e324a43a8babaabda323fe9ddcda1a1102778 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Tue, 24 Feb 2026 21:21:33 +0000 Subject: [PATCH 05/19] Add solution filtered parallel test run (#4226) --- .../New-FrameworkFilteredSolution.ps1 | 109 ++++++++++++++++++ .github/workflows/dotnet-build-and-test.yml | 71 ++++++------ 2 files changed, 147 insertions(+), 33 deletions(-) create mode 100644 .github/workflows/New-FrameworkFilteredSolution.ps1 diff --git a/.github/workflows/New-FrameworkFilteredSolution.ps1 b/.github/workflows/New-FrameworkFilteredSolution.ps1 new file mode 100644 index 0000000000..c8dba126e6 --- /dev/null +++ b/.github/workflows/New-FrameworkFilteredSolution.ps1 @@ -0,0 +1,109 @@ +#!/usr/bin/env pwsh +# Copyright (c) Microsoft. All rights reserved. + +<# +.SYNOPSIS + Generates a filtered .slnx solution file that only includes test projects supporting a given target framework. + +.DESCRIPTION + Parses a .slnx solution file and queries each test project's TargetFrameworks using MSBuild. + Removes test projects that don't support the specified target framework, writes the result + to a temporary or specified output path, and prints the output path. + + This is useful for running `dotnet test --solution` with MTP (Microsoft Testing Platform), + which requires all test projects in the solution to support the requested target framework. + +.PARAMETER Solution + Path to the source .slnx solution file. + +.PARAMETER TargetFramework + The target framework to filter by (e.g., net10.0, net472). + +.PARAMETER Configuration + Optional MSBuild configuration used when querying TargetFrameworks. Defaults to Debug. + +.PARAMETER OutputPath + Optional output path for the filtered .slnx file. If not specified, a temp file is created. + +.EXAMPLE + # Generate a filtered solution and run tests + $filtered = ./eng/New-FilteredSolution.ps1 -Solution ./agent-framework-dotnet.slnx -TargetFramework net472 + dotnet test --solution $filtered --no-build -f net472 + +.EXAMPLE + # Inline usage with dotnet test (PowerShell) + dotnet test --solution (./eng/New-FilteredSolution.ps1 -Solution ./agent-framework-dotnet.slnx -TargetFramework net472) --no-build -f net472 +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory)] + [string]$Solution, + + [Parameter(Mandatory)] + [string]$TargetFramework, + + [string]$Configuration = "Debug", + + [string]$OutputPath +) + +$ErrorActionPreference = "Stop" + +# Resolve the solution path +$solutionPath = Resolve-Path $Solution +$solutionDir = Split-Path $solutionPath -Parent + +if (-not $OutputPath) { + $OutputPath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "filtered-$(Split-Path $solutionPath -Leaf)") +} + +# Parse the .slnx XML +[xml]$slnx = Get-Content $solutionPath -Raw + +# Find all Project elements with paths containing "tests/" +$testProjects = $slnx.SelectNodes("//Project[contains(@Path, 'tests/')]") + +$removed = @() +$kept = @() + +foreach ($proj in $testProjects) { + $projRelPath = $proj.GetAttribute("Path") + $projFullPath = Join-Path $solutionDir $projRelPath + + if (-not (Test-Path $projFullPath)) { + Write-Verbose "Project not found, keeping in solution: $projRelPath" + $kept += $projRelPath + continue + } + + # Query the project's target frameworks using MSBuild + $targetFrameworks = & dotnet msbuild $projFullPath -getProperty:TargetFrameworks -p:Configuration=$Configuration -nologo 2>$null + $targetFrameworks = $targetFrameworks.Trim() + + if ($targetFrameworks -like "*$TargetFramework*") { + Write-Verbose "Keeping: $projRelPath (targets: $targetFrameworks)" + $kept += $projRelPath + } + else { + Write-Verbose "Removing: $projRelPath (targets: $targetFrameworks, missing: $TargetFramework)" + $removed += $projRelPath + $proj.ParentNode.RemoveChild($proj) | Out-Null + } +} + +# Write the filtered solution +$slnx.Save($OutputPath) + +# Report results to stderr so stdout is clean for piping +Write-Host "Filtered solution written to: $OutputPath" -ForegroundColor Green +if ($removed.Count -gt 0) { + Write-Host "Removed $($removed.Count) test project(s) not targeting ${TargetFramework}:" -ForegroundColor Yellow + foreach ($r in $removed) { + Write-Host " - $r" -ForegroundColor Yellow + } +} +Write-Host "Kept $($kept.Count) test project(s)." -ForegroundColor Green + +# Output the path for piping +Write-Output $OutputPath diff --git a/.github/workflows/dotnet-build-and-test.yml b/.github/workflows/dotnet-build-and-test.yml index f8bb79b8c0..6428e87f5e 100644 --- a/.github/workflows/dotnet-build-and-test.yml +++ b/.github/workflows/dotnet-build-and-test.yml @@ -140,27 +140,37 @@ jobs: popd rm -rf "$TEMP_DIR" + - name: Generate filtered solution + shell: pwsh + run: | + .github/workflows/New-FrameworkFilteredSolution.ps1 ` + -Solution dotnet/agent-framework-dotnet.slnx ` + -TargetFramework ${{ matrix.targetFramework }} ` + -Configuration ${{ matrix.configuration }} ` + -OutputPath dotnet/filtered.slnx ` + -Verbose + - name: Run Unit Tests - shell: bash + shell: pwsh run: | - cd dotnet - export UT_PROJECTS=$(find ./ -type f -name "*.UnitTests.csproj" | tr '\n' ' ') - for project in $UT_PROJECTS; do - # Query the project's target frameworks using MSBuild with the current configuration - target_frameworks=$(dotnet msbuild $project -getProperty:TargetFrameworks -p:Configuration=${{ matrix.configuration }} -nologo 2>/dev/null | tr -d '\r') - - # Check if the project supports the target framework - if [[ "$target_frameworks" == *"${{ matrix.targetFramework }}"* ]]; then - if [[ "${{ matrix.targetFramework }}" == "${{ env.COVERAGE_FRAMEWORK }}" ]]; then - # --ignore-exit-code 8: ignore failures due to finding no matching tests to run in a single project. - dotnet test --project $project -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} --no-build -v Normal --report-xunit-trx --ignore-exit-code 8 --coverage --coverage-output-format cobertura --coverage-settings "$(pwd)/tests/coverage.runsettings" --results-directory "$(pwd)/../TestResults/Coverage/" - else - dotnet test --project $project -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} --no-build -v Normal --report-xunit-trx --ignore-exit-code 8 - fi - else - echo "Skipping $project - does not support target framework ${{ matrix.targetFramework }} (supports: $target_frameworks)" - fi - done + $coverageArgs = @() + if ("${{ matrix.targetFramework }}" -eq "${{ env.COVERAGE_FRAMEWORK }}") { + $coverageArgs = @( + "--coverage", + "--coverage-output-format", "cobertura", + "--coverage-settings", "dotnet/tests/coverage.runsettings", + "--results-directory", "TestResults/Coverage/" + ) + } + + dotnet test --solution dotnet/filtered.slnx ` + -f ${{ matrix.targetFramework }} ` + -c ${{ matrix.configuration }} ` + --no-build -v Normal ` + --report-xunit-trx ` + --ignore-exit-code 8 ` + --filter-query "/*UnitTests*/*/*/*" ` + @coverageArgs env: # Cosmos DB Emulator connection settings COSMOSDB_ENDPOINT: https://localhost:8081 @@ -187,22 +197,17 @@ jobs: id: azure-functions-setup - name: Run Integration Tests - shell: bash + shell: pwsh if: github.event_name != 'pull_request' && matrix.integration-tests run: | - cd dotnet - export INTEGRATION_TEST_PROJECTS=$(find ./ -type f -name "*IntegrationTests.csproj" | tr '\n' ' ') - for project in $INTEGRATION_TEST_PROJECTS; do - # Query the project's target frameworks using MSBuild with the current configuration - target_frameworks=$(dotnet msbuild $project -getProperty:TargetFrameworks -p:Configuration=${{ matrix.configuration }} -nologo 2>/dev/null | tr -d '\r') - - # Check if the project supports the target framework - if [[ "$target_frameworks" == *"${{ matrix.targetFramework }}"* ]]; then - dotnet test --project $project -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} --no-build -v Normal --report-xunit-trx --ignore-exit-code 8 --filter-not-trait "Category=IntegrationDisabled" - else - echo "Skipping $project - does not support target framework ${{ matrix.targetFramework }} (supports: $target_frameworks)" - fi - done + dotnet test --solution dotnet/filtered.slnx ` + -f ${{ matrix.targetFramework }} ` + -c ${{ matrix.configuration }} ` + --no-build -v Normal ` + --report-xunit-trx ` + --ignore-exit-code 8 ` + --filter-query "/*IntegrationTests*/*/*/*" ` + --filter-not-trait "Category=IntegrationDisabled" env: # Cosmos DB Emulator connection settings COSMOSDB_ENDPOINT: https://localhost:8081 From f1585b188082ffa3b8d44a4a2207082ea59aa886 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Tue, 24 Feb 2026 21:45:53 +0000 Subject: [PATCH 06/19] Fix build paths (#4228) --- .github/workflows/dotnet-build-and-test.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dotnet-build-and-test.yml b/.github/workflows/dotnet-build-and-test.yml index 6428e87f5e..c02b05e856 100644 --- a/.github/workflows/dotnet-build-and-test.yml +++ b/.github/workflows/dotnet-build-and-test.yml @@ -152,18 +152,19 @@ jobs: - name: Run Unit Tests shell: pwsh + working-directory: dotnet run: | $coverageArgs = @() if ("${{ matrix.targetFramework }}" -eq "${{ env.COVERAGE_FRAMEWORK }}") { $coverageArgs = @( "--coverage", "--coverage-output-format", "cobertura", - "--coverage-settings", "dotnet/tests/coverage.runsettings", - "--results-directory", "TestResults/Coverage/" + "--coverage-settings", "tests/coverage.runsettings", + "--results-directory", "../TestResults/Coverage/" ) } - dotnet test --solution dotnet/filtered.slnx ` + dotnet test --solution ./filtered.slnx ` -f ${{ matrix.targetFramework }} ` -c ${{ matrix.configuration }} ` --no-build -v Normal ` @@ -198,9 +199,10 @@ jobs: - name: Run Integration Tests shell: pwsh + working-directory: dotnet if: github.event_name != 'pull_request' && matrix.integration-tests run: | - dotnet test --solution dotnet/filtered.slnx ` + dotnet test --solution ./filtered.slnx ` -f ${{ matrix.targetFramework }} ` -c ${{ matrix.configuration }} ` --no-build -v Normal ` From 9e3d6575eeccc8867f2a6d8e3551955157bb9a60 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Tue, 24 Feb 2026 22:10:21 +0000 Subject: [PATCH 07/19] Fix coverage settings path and trait filter (#4229) --- .github/workflows/dotnet-build-and-test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dotnet-build-and-test.yml b/.github/workflows/dotnet-build-and-test.yml index c02b05e856..71128add9b 100644 --- a/.github/workflows/dotnet-build-and-test.yml +++ b/.github/workflows/dotnet-build-and-test.yml @@ -154,12 +154,13 @@ jobs: shell: pwsh working-directory: dotnet run: | + $coverageSettings = Join-Path $PWD "tests/coverage.runsettings" $coverageArgs = @() if ("${{ matrix.targetFramework }}" -eq "${{ env.COVERAGE_FRAMEWORK }}") { $coverageArgs = @( "--coverage", "--coverage-output-format", "cobertura", - "--coverage-settings", "tests/coverage.runsettings", + "--coverage-settings", $coverageSettings, "--results-directory", "../TestResults/Coverage/" ) } @@ -208,8 +209,7 @@ jobs: --no-build -v Normal ` --report-xunit-trx ` --ignore-exit-code 8 ` - --filter-query "/*IntegrationTests*/*/*/*" ` - --filter-not-trait "Category=IntegrationDisabled" + --filter-query "/*IntegrationTests*/*/*/*[Category!=IntegrationDisabled]" env: # Cosmos DB Emulator connection settings COSMOSDB_ENDPOINT: https://localhost:8081 From b8a59129de9bd59d6d1b66231ae00aaa55b4fee7 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Tue, 24 Feb 2026 22:35:29 +0000 Subject: [PATCH 08/19] Add project name filter to solution (#4231) --- .../New-FrameworkFilteredSolution.ps1 | 19 +++++++++++++ .github/workflows/dotnet-build-and-test.yml | 27 +++++++++++-------- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/.github/workflows/New-FrameworkFilteredSolution.ps1 b/.github/workflows/New-FrameworkFilteredSolution.ps1 index c8dba126e6..aa05099612 100644 --- a/.github/workflows/New-FrameworkFilteredSolution.ps1 +++ b/.github/workflows/New-FrameworkFilteredSolution.ps1 @@ -22,6 +22,10 @@ .PARAMETER Configuration Optional MSBuild configuration used when querying TargetFrameworks. Defaults to Debug. +.PARAMETER ProjectNameFilter + Optional wildcard pattern to filter test project names (e.g., *UnitTests*, *IntegrationTests*). + When specified, only test projects whose filename matches this pattern are kept. + .PARAMETER OutputPath Optional output path for the filtered .slnx file. If not specified, a temp file is created. @@ -30,6 +34,10 @@ $filtered = ./eng/New-FilteredSolution.ps1 -Solution ./agent-framework-dotnet.slnx -TargetFramework net472 dotnet test --solution $filtered --no-build -f net472 +.EXAMPLE + # Generate a solution with only unit test projects + ./eng/New-FilteredSolution.ps1 -Solution ./agent-framework-dotnet.slnx -TargetFramework net10.0 -ProjectNameFilter "*UnitTests*" -OutputPath filtered-unit.slnx + .EXAMPLE # Inline usage with dotnet test (PowerShell) dotnet test --solution (./eng/New-FilteredSolution.ps1 -Solution ./agent-framework-dotnet.slnx -TargetFramework net472) --no-build -f net472 @@ -45,6 +53,8 @@ param( [string]$Configuration = "Debug", + [string]$ProjectNameFilter, + [string]$OutputPath ) @@ -70,6 +80,15 @@ $kept = @() foreach ($proj in $testProjects) { $projRelPath = $proj.GetAttribute("Path") $projFullPath = Join-Path $solutionDir $projRelPath + $projFileName = Split-Path $projRelPath -Leaf + + # Filter by project name pattern if specified + if ($ProjectNameFilter -and ($projFileName -notlike $ProjectNameFilter)) { + Write-Verbose "Removing (name filter): $projRelPath" + $removed += $projRelPath + $proj.ParentNode.RemoveChild($proj) | Out-Null + continue + } if (-not (Test-Path $projFullPath)) { Write-Verbose "Project not found, keeping in solution: $projRelPath" diff --git a/.github/workflows/dotnet-build-and-test.yml b/.github/workflows/dotnet-build-and-test.yml index 71128add9b..6bb91c211f 100644 --- a/.github/workflows/dotnet-build-and-test.yml +++ b/.github/workflows/dotnet-build-and-test.yml @@ -140,15 +140,21 @@ jobs: popd rm -rf "$TEMP_DIR" - - name: Generate filtered solution + - name: Generate filtered solutions shell: pwsh run: | - .github/workflows/New-FrameworkFilteredSolution.ps1 ` - -Solution dotnet/agent-framework-dotnet.slnx ` - -TargetFramework ${{ matrix.targetFramework }} ` - -Configuration ${{ matrix.configuration }} ` - -OutputPath dotnet/filtered.slnx ` - -Verbose + $commonArgs = @{ + Solution = "dotnet/agent-framework-dotnet.slnx" + TargetFramework = "${{ matrix.targetFramework }}" + Configuration = "${{ matrix.configuration }}" + Verbose = $true + } + .github/workflows/New-FrameworkFilteredSolution.ps1 @commonArgs ` + -ProjectNameFilter "*UnitTests*" ` + -OutputPath dotnet/filtered-unit.slnx + .github/workflows/New-FrameworkFilteredSolution.ps1 @commonArgs ` + -ProjectNameFilter "*IntegrationTests*" ` + -OutputPath dotnet/filtered-integration.slnx - name: Run Unit Tests shell: pwsh @@ -165,13 +171,12 @@ jobs: ) } - dotnet test --solution ./filtered.slnx ` + dotnet test --solution ./filtered-unit.slnx ` -f ${{ matrix.targetFramework }} ` -c ${{ matrix.configuration }} ` --no-build -v Normal ` --report-xunit-trx ` --ignore-exit-code 8 ` - --filter-query "/*UnitTests*/*/*/*" ` @coverageArgs env: # Cosmos DB Emulator connection settings @@ -203,13 +208,13 @@ jobs: working-directory: dotnet if: github.event_name != 'pull_request' && matrix.integration-tests run: | - dotnet test --solution ./filtered.slnx ` + dotnet test --solution ./filtered-integration.slnx ` -f ${{ matrix.targetFramework }} ` -c ${{ matrix.configuration }} ` --no-build -v Normal ` --report-xunit-trx ` --ignore-exit-code 8 ` - --filter-query "/*IntegrationTests*/*/*/*[Category!=IntegrationDisabled]" + --filter-not-trait "Category=IntegrationDisabled" env: # Cosmos DB Emulator connection settings COSMOSDB_ENDPOINT: https://localhost:8081 From 059398c80d652af05619f98de7d6438ee461e32c Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Wed, 25 Feb 2026 10:21:15 +0000 Subject: [PATCH 09/19] Increase Integration Test Parallelism (#4241) --- .github/workflows/dotnet-build-and-test.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dotnet-build-and-test.yml b/.github/workflows/dotnet-build-and-test.yml index 6bb91c211f..599fbc23bd 100644 --- a/.github/workflows/dotnet-build-and-test.yml +++ b/.github/workflows/dotnet-build-and-test.yml @@ -214,7 +214,9 @@ jobs: --no-build -v Normal ` --report-xunit-trx ` --ignore-exit-code 8 ` - --filter-not-trait "Category=IntegrationDisabled" + --filter-not-trait "Category=IntegrationDisabled" ` + --parallel-algorithm aggressive ` + --max-threads 2.0x env: # Cosmos DB Emulator connection settings COSMOSDB_ENDPOINT: https://localhost:8081 From 0ee09d37a830cf314a5d329108543456c01c9487 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Wed, 25 Feb 2026 11:14:55 +0000 Subject: [PATCH 10/19] Increase integration tests threads to 4x (#4242) --- .github/workflows/dotnet-build-and-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet-build-and-test.yml b/.github/workflows/dotnet-build-and-test.yml index 599fbc23bd..f62055f7ff 100644 --- a/.github/workflows/dotnet-build-and-test.yml +++ b/.github/workflows/dotnet-build-and-test.yml @@ -216,7 +216,7 @@ jobs: --ignore-exit-code 8 ` --filter-not-trait "Category=IntegrationDisabled" ` --parallel-algorithm aggressive ` - --max-threads 2.0x + --max-threads 4.0x env: # Cosmos DB Emulator connection settings COSMOSDB_ENDPOINT: https://localhost:8081 From 9059721788a858c1e251c71ee7f9562a2fa6e37a Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Wed, 25 Feb 2026 11:32:07 +0000 Subject: [PATCH 11/19] Separate build and test into parallel jobs (#4243) --- .../New-FrameworkFilteredSolution.ps1 | 33 ++++++-- .github/workflows/dotnet-build-and-test.yml | 82 ++++++++++++++----- 2 files changed, 88 insertions(+), 27 deletions(-) diff --git a/.github/workflows/New-FrameworkFilteredSolution.ps1 b/.github/workflows/New-FrameworkFilteredSolution.ps1 index aa05099612..3302a0391d 100644 --- a/.github/workflows/New-FrameworkFilteredSolution.ps1 +++ b/.github/workflows/New-FrameworkFilteredSolution.ps1 @@ -22,10 +22,13 @@ .PARAMETER Configuration Optional MSBuild configuration used when querying TargetFrameworks. Defaults to Debug. -.PARAMETER ProjectNameFilter +.PARAMETER TestProjectNameFilter Optional wildcard pattern to filter test project names (e.g., *UnitTests*, *IntegrationTests*). When specified, only test projects whose filename matches this pattern are kept. +.PARAMETER ExcludeSamples + When specified, removes all projects under the samples/ directory from the solution. + .PARAMETER OutputPath Optional output path for the filtered .slnx file. If not specified, a temp file is created. @@ -36,7 +39,7 @@ .EXAMPLE # Generate a solution with only unit test projects - ./eng/New-FilteredSolution.ps1 -Solution ./agent-framework-dotnet.slnx -TargetFramework net10.0 -ProjectNameFilter "*UnitTests*" -OutputPath filtered-unit.slnx + ./eng/New-FilteredSolution.ps1 -Solution ./agent-framework-dotnet.slnx -TargetFramework net10.0 -TestProjectNameFilter "*UnitTests*" -OutputPath filtered-unit.slnx .EXAMPLE # Inline usage with dotnet test (PowerShell) @@ -53,7 +56,9 @@ param( [string]$Configuration = "Debug", - [string]$ProjectNameFilter, + [string]$TestProjectNameFilter, + + [switch]$ExcludeSamples, [string]$OutputPath ) @@ -71,19 +76,31 @@ if (-not $OutputPath) { # Parse the .slnx XML [xml]$slnx = Get-Content $solutionPath -Raw -# Find all Project elements with paths containing "tests/" -$testProjects = $slnx.SelectNodes("//Project[contains(@Path, 'tests/')]") - $removed = @() $kept = @() +# Remove sample projects if requested +if ($ExcludeSamples) { + $sampleProjects = $slnx.SelectNodes("//Project[contains(@Path, 'samples/')]") + foreach ($proj in $sampleProjects) { + $projRelPath = $proj.GetAttribute("Path") + Write-Verbose "Removing (sample): $projRelPath" + $removed += $projRelPath + $proj.ParentNode.RemoveChild($proj) | Out-Null + } + Write-Host "Removed $($sampleProjects.Count) sample project(s)." -ForegroundColor Yellow +} + +# Find all Project elements with paths containing "tests/" +$testProjects = $slnx.SelectNodes("//Project[contains(@Path, 'tests/')]") + foreach ($proj in $testProjects) { $projRelPath = $proj.GetAttribute("Path") $projFullPath = Join-Path $solutionDir $projRelPath $projFileName = Split-Path $projRelPath -Leaf # Filter by project name pattern if specified - if ($ProjectNameFilter -and ($projFileName -notlike $ProjectNameFilter)) { + if ($TestProjectNameFilter -and ($projFileName -notlike $TestProjectNameFilter)) { Write-Verbose "Removing (name filter): $projRelPath" $removed += $projRelPath $proj.ParentNode.RemoveChild($proj) | Out-Null @@ -117,7 +134,7 @@ $slnx.Save($OutputPath) # Report results to stderr so stdout is clean for piping Write-Host "Filtered solution written to: $OutputPath" -ForegroundColor Green if ($removed.Count -gt 0) { - Write-Host "Removed $($removed.Count) test project(s) not targeting ${TargetFramework}:" -ForegroundColor Yellow + Write-Host "Removed $($removed.Count) project(s):" -ForegroundColor Yellow foreach ($r in $removed) { Write-Host " - $r" -ForegroundColor Yellow } diff --git a/.github/workflows/dotnet-build-and-test.yml b/.github/workflows/dotnet-build-and-test.yml index f62055f7ff..9899b6ee2e 100644 --- a/.github/workflows/dotnet-build-and-test.yml +++ b/.github/workflows/dotnet-build-and-test.yml @@ -59,20 +59,20 @@ jobs: if: steps.filter.outputs.dotnet != 'true' run: echo "NOT dotnet file" - dotnet-build-and-test: + # Build the full solution (including samples) on all TFMs. No tests. + dotnet-build: needs: paths-filter if: needs.paths-filter.outputs.dotnetChanges == 'true' strategy: fail-fast: false matrix: include: - - { targetFramework: "net10.0", os: "ubuntu-latest", configuration: Release, integration-tests: true, environment: "integration" } + - { targetFramework: "net10.0", os: "ubuntu-latest", configuration: Release } - { targetFramework: "net9.0", os: "windows-latest", configuration: Debug } - { targetFramework: "net8.0", os: "ubuntu-latest", configuration: Release } - - { targetFramework: "net472", os: "windows-latest", configuration: Release, integration-tests: true, environment: "integration" } + - { targetFramework: "net472", os: "windows-latest", configuration: Release } runs-on: ${{ matrix.os }} - environment: ${{ matrix.environment }} steps: - uses: actions/checkout@v6 with: @@ -84,16 +84,6 @@ jobs: python workflow-samples - # Start Cosmos DB Emulator for all integration tests and only for unit tests when CosmosDB changes happened) - - name: Start Azure Cosmos DB Emulator - if: ${{ runner.os == 'Windows' && (needs.paths-filter.outputs.cosmosDbChanges == 'true' || (github.event_name != 'pull_request' && matrix.integration-tests)) }} - shell: pwsh - run: | - Write-Host "Launching Azure Cosmos DB Emulator" - Import-Module "$env:ProgramFiles\Azure Cosmos DB Emulator\PSModules\Microsoft.Azure.CosmosDB.Emulator" - Start-CosmosDbEmulator -NoUI -Key "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==" - echo "COSMOS_EMULATOR_AVAILABLE=true" >> $env:GITHUB_ENV - - name: Setup dotnet uses: actions/setup-dotnet@v5.1.0 with: @@ -140,20 +130,74 @@ jobs: popd rm -rf "$TEMP_DIR" - - name: Generate filtered solutions + # Build src+tests only (no samples) for a single TFM and run tests. + dotnet-test: + needs: paths-filter + if: needs.paths-filter.outputs.dotnetChanges == 'true' + strategy: + fail-fast: false + matrix: + include: + - { targetFramework: "net10.0", os: "ubuntu-latest", configuration: Release, integration-tests: true, environment: "integration" } + - { targetFramework: "net472", os: "windows-latest", configuration: Release, integration-tests: true, environment: "integration" } + + runs-on: ${{ matrix.os }} + environment: ${{ matrix.environment }} + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + sparse-checkout: | + . + .github + dotnet + python + workflow-samples + + # Start Cosmos DB Emulator for all integration tests and only for unit tests when CosmosDB changes happened) + - name: Start Azure Cosmos DB Emulator + if: ${{ runner.os == 'Windows' && (needs.paths-filter.outputs.cosmosDbChanges == 'true' || (github.event_name != 'pull_request' && matrix.integration-tests)) }} + shell: pwsh + run: | + Write-Host "Launching Azure Cosmos DB Emulator" + Import-Module "$env:ProgramFiles\Azure Cosmos DB Emulator\PSModules\Microsoft.Azure.CosmosDB.Emulator" + Start-CosmosDbEmulator -NoUI -Key "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==" + echo "COSMOS_EMULATOR_AVAILABLE=true" >> $env:GITHUB_ENV + + - name: Setup dotnet + uses: actions/setup-dotnet@v5.1.0 + with: + global-json-file: ${{ github.workspace }}/dotnet/global.json + + - name: Generate test solution (no samples) + shell: pwsh + run: | + .github/workflows/New-FrameworkFilteredSolution.ps1 ` + -Solution dotnet/agent-framework-dotnet.slnx ` + -TargetFramework ${{ matrix.targetFramework }} ` + -Configuration ${{ matrix.configuration }} ` + -ExcludeSamples ` + -OutputPath dotnet/filtered.slnx ` + -Verbose + + - name: Build src and tests + shell: bash + run: dotnet build dotnet/filtered.slnx -c ${{ matrix.configuration }} -f ${{ matrix.targetFramework }} --warnaserror + + - name: Generate test-type filtered solutions shell: pwsh run: | $commonArgs = @{ - Solution = "dotnet/agent-framework-dotnet.slnx" + Solution = "dotnet/filtered.slnx" TargetFramework = "${{ matrix.targetFramework }}" Configuration = "${{ matrix.configuration }}" Verbose = $true } .github/workflows/New-FrameworkFilteredSolution.ps1 @commonArgs ` - -ProjectNameFilter "*UnitTests*" ` + -TestProjectNameFilter "*UnitTests*" ` -OutputPath dotnet/filtered-unit.slnx .github/workflows/New-FrameworkFilteredSolution.ps1 @commonArgs ` - -ProjectNameFilter "*IntegrationTests*" ` + -TestProjectNameFilter "*IntegrationTests*" ` -OutputPath dotnet/filtered-integration.slnx - name: Run Unit Tests @@ -262,7 +306,7 @@ jobs: dotnet-build-and-test-check: if: always() runs-on: ubuntu-latest - needs: [dotnet-build-and-test] + needs: [dotnet-build, dotnet-test] steps: - name: Get Date shell: bash From ce9f2cdbb1fec9f6d13f4f99fa08a6fe99649b63 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Wed, 25 Feb 2026 11:52:43 +0000 Subject: [PATCH 12/19] Filter src by framework for tests build (#4244) * Separate build and test into parallel jobs * Filter source projects by framework for tests build --- ...dSolution.ps1 => New-FilteredSolution.ps1} | 32 +++++++++---------- .github/workflows/dotnet-build-and-test.yml | 6 ++-- dotnet/.github/skills/build-and-test/SKILL.md | 13 +++++++- 3 files changed, 31 insertions(+), 20 deletions(-) rename .github/workflows/{New-FrameworkFilteredSolution.ps1 => New-FilteredSolution.ps1} (73%) diff --git a/.github/workflows/New-FrameworkFilteredSolution.ps1 b/.github/workflows/New-FilteredSolution.ps1 similarity index 73% rename from .github/workflows/New-FrameworkFilteredSolution.ps1 rename to .github/workflows/New-FilteredSolution.ps1 index 3302a0391d..2871f5f36b 100644 --- a/.github/workflows/New-FrameworkFilteredSolution.ps1 +++ b/.github/workflows/New-FilteredSolution.ps1 @@ -3,15 +3,14 @@ <# .SYNOPSIS - Generates a filtered .slnx solution file that only includes test projects supporting a given target framework. + Generates a filtered .slnx solution file by removing projects that don't match the specified criteria. .DESCRIPTION - Parses a .slnx solution file and queries each test project's TargetFrameworks using MSBuild. - Removes test projects that don't support the specified target framework, writes the result - to a temporary or specified output path, and prints the output path. - - This is useful for running `dotnet test --solution` with MTP (Microsoft Testing Platform), - which requires all test projects in the solution to support the requested target framework. + Parses a .slnx solution file and applies one or more filters: + - Removes projects that don't support the specified target framework (via MSBuild query). + - Optionally removes all sample projects (under samples/). + - Optionally filters test projects by name pattern (e.g., only *UnitTests*). + Writes the filtered solution to the specified output path and prints the path. .PARAMETER Solution Path to the source .slnx solution file. @@ -34,16 +33,16 @@ .EXAMPLE # Generate a filtered solution and run tests - $filtered = ./eng/New-FilteredSolution.ps1 -Solution ./agent-framework-dotnet.slnx -TargetFramework net472 + $filtered = .github/workflows/New-FilteredSolution.ps1 -Solution dotnet/agent-framework-dotnet.slnx -TargetFramework net472 dotnet test --solution $filtered --no-build -f net472 .EXAMPLE # Generate a solution with only unit test projects - ./eng/New-FilteredSolution.ps1 -Solution ./agent-framework-dotnet.slnx -TargetFramework net10.0 -TestProjectNameFilter "*UnitTests*" -OutputPath filtered-unit.slnx + .github/workflows/New-FilteredSolution.ps1 -Solution dotnet/agent-framework-dotnet.slnx -TargetFramework net10.0 -TestProjectNameFilter "*UnitTests*" -OutputPath filtered-unit.slnx .EXAMPLE # Inline usage with dotnet test (PowerShell) - dotnet test --solution (./eng/New-FilteredSolution.ps1 -Solution ./agent-framework-dotnet.slnx -TargetFramework net472) --no-build -f net472 + dotnet test --solution (.github/workflows/New-FilteredSolution.ps1 -Solution dotnet/agent-framework-dotnet.slnx -TargetFramework net472) --no-build -f net472 #> [CmdletBinding()] @@ -91,16 +90,17 @@ if ($ExcludeSamples) { Write-Host "Removed $($sampleProjects.Count) sample project(s)." -ForegroundColor Yellow } -# Find all Project elements with paths containing "tests/" -$testProjects = $slnx.SelectNodes("//Project[contains(@Path, 'tests/')]") +# Filter all remaining projects by target framework +$allProjects = $slnx.SelectNodes("//Project") -foreach ($proj in $testProjects) { +foreach ($proj in $allProjects) { $projRelPath = $proj.GetAttribute("Path") $projFullPath = Join-Path $solutionDir $projRelPath $projFileName = Split-Path $projRelPath -Leaf + $isTestProject = $projRelPath -like "*tests/*" - # Filter by project name pattern if specified - if ($TestProjectNameFilter -and ($projFileName -notlike $TestProjectNameFilter)) { + # Filter test projects by name pattern if specified + if ($isTestProject -and $TestProjectNameFilter -and ($projFileName -notlike $TestProjectNameFilter)) { Write-Verbose "Removing (name filter): $projRelPath" $removed += $projRelPath $proj.ParentNode.RemoveChild($proj) | Out-Null @@ -139,7 +139,7 @@ if ($removed.Count -gt 0) { Write-Host " - $r" -ForegroundColor Yellow } } -Write-Host "Kept $($kept.Count) test project(s)." -ForegroundColor Green +Write-Host "Kept $($kept.Count) project(s)." -ForegroundColor Green # Output the path for piping Write-Output $OutputPath diff --git a/.github/workflows/dotnet-build-and-test.yml b/.github/workflows/dotnet-build-and-test.yml index 9899b6ee2e..e79ff15f6d 100644 --- a/.github/workflows/dotnet-build-and-test.yml +++ b/.github/workflows/dotnet-build-and-test.yml @@ -172,7 +172,7 @@ jobs: - name: Generate test solution (no samples) shell: pwsh run: | - .github/workflows/New-FrameworkFilteredSolution.ps1 ` + .github/workflows/New-FilteredSolution.ps1 ` -Solution dotnet/agent-framework-dotnet.slnx ` -TargetFramework ${{ matrix.targetFramework }} ` -Configuration ${{ matrix.configuration }} ` @@ -193,10 +193,10 @@ jobs: Configuration = "${{ matrix.configuration }}" Verbose = $true } - .github/workflows/New-FrameworkFilteredSolution.ps1 @commonArgs ` + .github/workflows/New-FilteredSolution.ps1 @commonArgs ` -TestProjectNameFilter "*UnitTests*" ` -OutputPath dotnet/filtered-unit.slnx - .github/workflows/New-FrameworkFilteredSolution.ps1 @commonArgs ` + .github/workflows/New-FilteredSolution.ps1 @commonArgs ` -TestProjectNameFilter "*IntegrationTests*" ` -OutputPath dotnet/filtered-integration.slnx diff --git a/dotnet/.github/skills/build-and-test/SKILL.md b/dotnet/.github/skills/build-and-test/SKILL.md index a1d57ee3cd..ef04bb96d6 100644 --- a/dotnet/.github/skills/build-and-test/SKILL.md +++ b/dotnet/.github/skills/build-and-test/SKILL.md @@ -101,7 +101,18 @@ Tests use the [Microsoft Testing Platform](https://learn.microsoft.com/dotnet/co ```bash # Run all unit tests across the solution, ignoring projects with no matching tests -dotnet test --solution ./agent-framework-dotnet.slnx --no-build -f net10.0 +dotnet test --solution ./agent-framework-dotnet.slnx --no-build -f net10.0 --ignore-exit-code 8 +``` + +- **Running tests with `--solution` for a specific TFM** requires all projects in the solution to support that TFM. Not all projects target every framework (e.g., some are `net10.0`-only). Use `.github/workflows/New-FilteredSolution.ps1` to generate a filtered solution: + +```powershell +# Generate a filtered solution for net472 and run tests +$filtered = .github/workflows/New-FilteredSolution.ps1 -Solution dotnet/agent-framework-dotnet.slnx -TargetFramework net472 +dotnet test --solution $filtered --no-build -f net472 --ignore-exit-code 8 + +# Exclude samples and keep only unit test projects +.github/workflows/New-FilteredSolution.ps1 -Solution dotnet/agent-framework-dotnet.slnx -TargetFramework net10.0 -ExcludeSamples -TestProjectNameFilter "*UnitTests*" -OutputPath dotnet/filtered-unit.slnx ``` ```bash From ce0b374c2762880be59a5e527f103776a5f02c10 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Wed, 25 Feb 2026 12:20:34 +0000 Subject: [PATCH 13/19] Pre-build samples via tests to avoid timeouts (#4245) --- .github/workflows/dotnet-build-and-test.yml | 2 +- .../SamplesValidation.cs | 32 ++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet-build-and-test.yml b/.github/workflows/dotnet-build-and-test.yml index e79ff15f6d..8e3582517f 100644 --- a/.github/workflows/dotnet-build-and-test.yml +++ b/.github/workflows/dotnet-build-and-test.yml @@ -260,7 +260,7 @@ jobs: --ignore-exit-code 8 ` --filter-not-trait "Category=IntegrationDisabled" ` --parallel-algorithm aggressive ` - --max-threads 4.0x + --max-threads 2.0x env: # Cosmos DB Emulator connection settings COSMOSDB_ENDPOINT: https://localhost:8081 diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs index 19e75467c8..5b1e46ebf0 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs @@ -792,6 +792,9 @@ private async Task WaitForConditionAsync(Func> condition, string mess private async Task RunSampleTestAsync(string samplePath, Func, Task> testAction) { + // Build the sample project first (it may not have been built as part of the solution) + await this.BuildSampleAsync(samplePath); + // Start the Azure Functions app List logsContainer = []; using Process funcProcess = this.StartFunctionApp(samplePath, logsContainer); @@ -811,12 +814,39 @@ private async Task RunSampleTestAsync(string samplePath, Func logs) { ProcessStartInfo startInfo = new() { FileName = "dotnet", - Arguments = $"run -f {s_dotnetTargetFramework} --port {AzureFunctionsPort}", + Arguments = $"run --no-build -f {s_dotnetTargetFramework} --port {AzureFunctionsPort}", WorkingDirectory = samplePath, UseShellExecute = false, RedirectStandardOutput = true, From 17f75c325e67b0ddec7557daf28d5e404d0d7b86 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Wed, 25 Feb 2026 13:49:59 +0000 Subject: [PATCH 14/19] Separate build from run for console sample validation (#4251) --- .../ConsoleAppSamplesValidation.cs | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ConsoleAppSamplesValidation.cs b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ConsoleAppSamplesValidation.cs index 91a87021dd..2f509962eb 100644 --- a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ConsoleAppSamplesValidation.cs +++ b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ConsoleAppSamplesValidation.cs @@ -735,6 +735,9 @@ private async Task IsRedisRunningAsync() private async Task RunSampleTestAsync(string samplePath, Func, Task> testAction) { + // Build the sample project first (it may not have been built as part of the solution) + await this.BuildSampleAsync(samplePath); + // Generate a unique TaskHub name for this sample test to prevent cross-test interference // when multiple tests run together and share the same DTS emulator. string uniqueTaskHubName = $"sample-{Guid.NewGuid().ToString("N").Substring(0, 6)}"; @@ -813,12 +816,39 @@ private async Task WriteInputAsync(Process process, string input, CancellationTo return null; } + private async Task BuildSampleAsync(string samplePath) + { + this._outputHelper.WriteLine($"Building sample at {samplePath}..."); + + ProcessStartInfo buildInfo = new() + { + FileName = "dotnet", + Arguments = $"build --framework {s_dotnetTargetFramework}", + WorkingDirectory = samplePath, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + }; + + using Process buildProcess = new() { StartInfo = buildInfo }; + buildProcess.Start(); + await buildProcess.WaitForExitAsync(); + + if (buildProcess.ExitCode != 0) + { + string stderr = await buildProcess.StandardError.ReadToEndAsync(); + throw new InvalidOperationException($"Failed to build sample at {samplePath}: {stderr}"); + } + + this._outputHelper.WriteLine($"Build completed for {samplePath}."); + } + private Process StartConsoleApp(string samplePath, BlockingCollection logs, string taskHubName) { ProcessStartInfo startInfo = new() { FileName = "dotnet", - Arguments = $"run --framework {s_dotnetTargetFramework}", + Arguments = $"run --no-build --framework {s_dotnetTargetFramework}", WorkingDirectory = samplePath, UseShellExecute = false, RedirectStandardOutput = true, From d4f95798c2415a059f0b944e5f5cbce23db1b972 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Wed, 25 Feb 2026 14:54:27 +0000 Subject: [PATCH 15/19] Address PR comments (#4255) --- .../ConsoleAppSamplesValidation.cs | 9 +++++++-- .../SamplesValidation.cs | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ConsoleAppSamplesValidation.cs b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ConsoleAppSamplesValidation.cs index 2f509962eb..44c0fb116d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ConsoleAppSamplesValidation.cs +++ b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ConsoleAppSamplesValidation.cs @@ -832,12 +832,17 @@ private async Task BuildSampleAsync(string samplePath) using Process buildProcess = new() { StartInfo = buildInfo }; buildProcess.Start(); + + // Read both streams asynchronously to avoid deadlocks from filled pipe buffers + Task stdoutTask = buildProcess.StandardOutput.ReadToEndAsync(); + Task stderrTask = buildProcess.StandardError.ReadToEndAsync(); await buildProcess.WaitForExitAsync(); + string stderr = await stderrTask; if (buildProcess.ExitCode != 0) { - string stderr = await buildProcess.StandardError.ReadToEndAsync(); - throw new InvalidOperationException($"Failed to build sample at {samplePath}: {stderr}"); + string stdout = await stdoutTask; + throw new InvalidOperationException($"Failed to build sample at {samplePath}:\n{stdout}\n{stderr}"); } this._outputHelper.WriteLine($"Build completed for {samplePath}."); diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs index 5b1e46ebf0..919f66f30b 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs @@ -830,12 +830,17 @@ private async Task BuildSampleAsync(string samplePath) using Process buildProcess = new() { StartInfo = buildInfo }; buildProcess.Start(); + + // Read both streams asynchronously to avoid deadlocks from filled pipe buffers + Task stdoutTask = buildProcess.StandardOutput.ReadToEndAsync(); + Task stderrTask = buildProcess.StandardError.ReadToEndAsync(); await buildProcess.WaitForExitAsync(); + string stderr = await stderrTask; if (buildProcess.ExitCode != 0) { - string stderr = await buildProcess.StandardError.ReadToEndAsync(); - throw new InvalidOperationException($"Failed to build sample at {samplePath}: {stderr}"); + string stdout = await stdoutTask; + throw new InvalidOperationException($"Failed to build sample at {samplePath}:\n{stdout}\n{stderr}"); } this._outputHelper.WriteLine($"Build completed for {samplePath}."); From 8b191de936ceb61caf42992ad4ff74a7ee21b8c0 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Thu, 26 Feb 2026 10:49:07 +0000 Subject: [PATCH 16/19] Merge and move scripts (#4308) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * .NET: Add Microsoft Fabric sample #3674 (#4230) Co-authored-by: Chris <66376200+crickman@users.noreply.github.com> * Python: Phase 2: Embedding clients for Ollama, Bedrock, and Azure AI Inference (#4207) * Phase 2: Embedding clients for Ollama, Bedrock, and Azure AI Inference Add embedding client implementations to existing provider packages: - OllamaEmbeddingClient: Text embeddings via Ollama's embed API - BedrockEmbeddingClient: Text embeddings via Amazon Titan on Bedrock - AzureAIInferenceEmbeddingClient: Text and image embeddings via Azure AI Inference, supporting Content | str input with separate model IDs for text (AZURE_AI_INFERENCE_EMBEDDING_MODEL_ID) and image (AZURE_AI_INFERENCE_IMAGE_EMBEDDING_MODEL_ID) endpoints Additional changes: - Rename EmbeddingCoT -> EmbeddingT, EmbeddingOptionsCoT -> EmbeddingOptionsT - Add otel_provider_name passthrough to all embedding clients - Register integration pytest marker in all packages - Add lazy-loading namespace exports for Ollama and Bedrock embeddings - Add image embedding sample using Cohere-embed-v3-english - Add azure-ai-inference dependency to azure-ai package Part of #1188 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix mypy duplicate name and ruff lint issues - Rename second 'vector' variable to 'img_vector' in image embedding loop - Combine nested with statements in tests - Remove unused result assignments in tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * updates from feedback * Fix CI failures in embedding usage handling - Fix Azure AI embedding mypy issues by normalizing vectors to list[float], safely accumulating optional usage token fields, and filtering None entries before constructing GeneratedEmbeddings - Avoid Bandit false positive by initializing usage details as an empty dict - Update OpenAI embedding tests to assert canonical usage keys (input_token_count/total_token_count) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * [Purview] Mark responses as responses and fix epoch bug for python long overflow (#4225) * .NET: Support InvokeMcpTool for declarative workflows (#4204) * Initial implementation of InvokeMcpTool in declarative workflow * Cleaned up sample implementation * Updated sample comments. * Added missing executor routing attribute * Fix PR comments. * Updated based on PR comments. * Updated based on PR comments. * Removed unnecessary using statement. * Update Python package versions to rc2 (#4258) - Bump core and azure-ai to 1.0.0rc2 - Bump preview packages to 1.0.0b260225 - Update dependencies to >=1.0.0rc2 - Add CHANGELOG entries for changes since rc1 - Update uv.lock Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * .NET: Fixing issue where OpenTelemetry span is never exported in .NET in-process workflow execution (#4196) * 1. Add reproduction test for issue #4155: workflow.run Activity never stopped in streaming OffThread path The WorkflowRunActivity_IsStopped_Streaming_OffThread test demonstrates that the workflow.run OpenTelemetry Activity created in StreamingRunEventStream.RunLoopAsync is started but never stopped when using the OffThread/Default streaming execution. The background run loop keeps running after event consumption completes, so the using Activity? declaration never disposes until explicit StopAsync() is called. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> 2. Fix workflow.run Activity never stopped in streaming OffThread execution (#4155) The workflow.run OpenTelemetry Activity in StreamingRunEventStream.RunLoopAsync was scoped to the method lifetime via 'using'. Since the run loop only exits on cancellation, the Activity was never stopped/exported until explicit disposal. Fix: Remove 'using' and explicitly dispose the Activity when the workflow reaches Idle status (all supersteps complete). A safety-net disposal in the finally block handles cancellation and error paths. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add root-level workflow.session activity spanning run loop lifetime\n\nImplements two-level telemetry hierarchy per PR feedback from lokitoth:\n- workflow.session: spans the entire run loop / stream lifetime\n- workflow_invoke: per input-to-halt cycle, nested within the session\n\nThis ensures the session activity stays open across multiple turns,\nwhile individual run activities are created and disposed per cycle.\n\nAlso fixes linkedSource CancellationTokenSource disposal leak in\nStreamingRunEventStream (added using declaration)." * Address Copilot review: fix Activity/CTS disposal, rename activity, add error tag\n\n1. LockstepRunEventStream: Remove 'using' from Activity in async iterator\n and manually dispose in finally block (fixes #4155 pattern). Also dispose\n linkedSource CTS in finally to prevent leak.\n2. Tags.cs: Add ErrorMessage (\"error.message\") tag for runtime errors,\n distinct from BuildErrorMessage (\"build.error.message\").\n3. ActivityNames: Rename WorkflowRun from \"workflow_invoke\" to \"workflow.run\"\n for cross-language consistency.\n4. WorkflowTelemetryContext: Fix XML doc to say \"outer/parent span\" instead\n of \"root-level span\".\n5. ObservabilityTests: Assert WorkflowSession absence when DisableWorkflowRun\n is true.\n6. WorkflowRunActivityStopTests: Fix streaming test race by disposing\n StreamingRun before asserting activities are stopped.\n7. StreamingRunEventStream/LockstepRunEventStream: Use Tags.ErrorMessage\n instead of Tags.BuildErrorMessage for runtime error events." * Review fixes: revert workflow_invoke rename, use 'using' for linkedSource, move SessionStarted earlier\n\n- Revert ActivityNames.WorkflowRun back to \"workflow_invoke\" (OTEL semantic convention contract)\n- Use 'using' declaration for linkedSource CTS in LockstepRunEventStream (no timing sensitivity)\n- Move SessionStarted event before WaitForInputAsync in StreamingRunEventStream to match Lockstep behavior" * Improve naming and comments in WorkflowRunActivityStopTests" * Prevent session Activity.Current leak in lockstep mode, add nesting test Save and restore Activity.Current in LockstepRunEventStream.Start() so the session activity doesn't leak into caller code via AsyncLocal. Re-establish Activity.Current = sessionActivity before creating the run activity in TakeEventStreamAsync to preserve parent-child nesting. Add test verifying app activities after RunAsync are not parented under the session, and that the workflow_invoke activity nests under the session." * Fix stale XML doc: WorkflowRun -> WorkflowInvoke in ObservabilityTests --------- Co-authored-by: alliscode Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python / .NET Samples - Restructure and Improve Samples (Feature Branc… (#4092) * Python: .NET Samples - Restructure and Improve Samples (Feature Branch) (#4091) * Moved by agent (#4094) * Fix readme links * .NET Samples - Create `04-hosting` learning path step (#4098) * Agent move * Agent reorderd * Remove A2A section from README Removed A2A section from the Getting Started README. * Agent fixed links * Fix broken sample links in durable-agents README (#4101) * Initial plan * Fix broken internal links in documentation Co-authored-by: crickman <66376200+crickman@users.noreply.github.com> * Revert template link changes; keep only durable-agents README fix Co-authored-by: crickman <66376200+crickman@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: crickman <66376200+crickman@users.noreply.github.com> * .NET Samples - Create `03-workflows` learning path step (#4102) * Fix solution project path * Python: Fix broken markdown links to repo resources (outside /docs) (#4105) * Initial plan * Fix broken markdown links to repo resources Co-authored-by: crickman <66376200+crickman@users.noreply.github.com> * Update README to rename .NET Workflows Samples section --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: crickman <66376200+crickman@users.noreply.github.com> * .NET Samples - Create `02-agents` learning path step (#4107) * .NET: Fix broken relative link in GroupChatToolApproval README (#4108) * Initial plan * Fix broken link in GroupChatToolApproval README Co-authored-by: crickman <66376200+crickman@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: crickman <66376200+crickman@users.noreply.github.com> * Update labeler configuration for workflow samples * .NET - Reorder Agents samples to start from Step01 instead of Step04 (#4110) * Fix solution * Resolve new sample paths * Move new AgentSkills and AgentWithMemory_Step04 samples * Fix link * Fix readme path * fix: update stale dotnet/samples/Durable path reference in AGENTS.md Co-authored-by: crickman <66376200+crickman@users.noreply.github.com> * Moved new sample * Update solution * Resolve merge (new sample) * Sync to new sample - FoundryAgents_Step21_BingCustomSearch * Updated README * .NET Samples - Configuration Naming Update (#4149) * .NET: Restore AzureFunctions index parity with ConsoleApps under DurableAgents samples (#4221) * Clean-up `05_host_your_agent` * Config setting consistency * Refine samples * AGENTS.md * Move new samples * Re-order samples * Move new project and fixup solution * Fixup model config * Fix up new UT project --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> * Python: Fix Bedrock embedding test stub missing meta attribute (#4287) * Fix Bedrock embedding test stub missing meta attribute * Increase test coverage so gate passes * Python: (ag-ui): fix approval payloads being re-processed on subsequent conversation turns (#4232) * Fix ag-ui tool call issue * Safe json fix * Python: Update workflow orchestration samples to use AzureOpenAIResponsesClient (#4285) * Update workflow orchestration samples to use AzureOpenAIResponsesClient * Fix broken link * Move scripts to scripts folder --------- Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> Co-authored-by: Chris <66376200+crickman@users.noreply.github.com> Co-authored-by: Eduard van Valkenburg Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Rishabh Chawla Co-authored-by: Peter Ibekwe <109177538+peibekwe@users.noreply.github.com> Co-authored-by: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Co-authored-by: Ben Thomas Co-authored-by: alliscode Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com> --- .github/labeler.yml | 2 +- .github/workflows/dotnet-build-and-test.yml | 31 +- README.md | 14 +- docs/decisions/0001-agent-run-response.md | 2 +- docs/features/durable-agents/AGENTS.md | 2 +- docs/features/durable-agents/README.md | 2 +- .../vector-stores-and-embeddings/README.md | 3 +- dotnet/.github/skills/build-and-test/SKILL.md | 6 +- dotnet/Directory.Packages.props | 6 +- dotnet/README.md | 12 +- dotnet/agent-framework-dotnet.slnx | 554 ++++++------ .../eng/scripts}/New-FilteredSolution.ps1 | 6 +- .../eng/scripts}/dotnet-check-coverage.ps1 | 0 .../01_hello_agent/01_hello_agent.csproj} | 2 +- .../01_hello_agent}/Program.cs | 0 .../02_add_tools/02_add_tools.csproj} | 2 +- .../02_add_tools}/Program.cs | 0 .../03_multi_turn/03_multi_turn.csproj} | 2 +- .../03_multi_turn}/Program.cs | 0 .../01-get-started/04_memory/04_memory.csproj | 21 + .../04_memory}/Program.cs | 0 .../05_first_workflow.csproj | 14 + .../05_first_workflow}/Program.cs | 0 .../06_host_your_agent.csproj | 31 + .../06_host_your_agent/Program.cs | 45 + .../AGUI/README.md | 0 .../Client/Client.csproj | 0 .../Step01_GettingStarted/Client/Program.cs | 0 .../Step01_GettingStarted/Server/Program.cs | 0 .../Server/Properties/launchSettings.json | 0 .../Server/Server.csproj | 0 .../Server}/appsettings.Development.json | 0 .../Server}/appsettings.json | 0 .../Step02_BackendTools/Client/Client.csproj | 0 .../Step02_BackendTools/Client/Program.cs | 0 .../Step02_BackendTools/Server/Program.cs | 0 .../Server/Properties/launchSettings.json | 0 .../Step02_BackendTools/Server/Server.csproj | 0 .../Server}/appsettings.Development.json | 0 .../Server}/appsettings.json | 0 .../Step03_FrontendTools/Client/Client.csproj | 0 .../Step03_FrontendTools/Client/Program.cs | 0 .../Step03_FrontendTools/Server/Program.cs | 0 .../Server/Properties/launchSettings.json | 0 .../Step03_FrontendTools/Server/Server.csproj | 0 .../Server}/appsettings.Development.json | 0 .../Server/appsettings.json | 0 .../Step04_HumanInLoop/Client/Client.csproj | 0 .../AGUI/Step04_HumanInLoop/Client/Program.cs | 0 .../ServerFunctionApprovalClientAgent.cs | 0 .../AGUI/Step04_HumanInLoop/Server/Program.cs | 0 .../Server/Properties/launchSettings.json | 0 .../Step04_HumanInLoop/Server/Server.csproj | 0 .../ServerFunctionApprovalServerAgent.cs | 0 .../Server}/appsettings.Development.json | 0 .../Server/appsettings.json | 0 .../Client/Client.csproj | 0 .../Step05_StateManagement/Client/Program.cs | 0 .../Client/StatefulAgent.cs | 0 .../Step05_StateManagement/Server/Program.cs | 0 .../Server/Properties/launchSettings.json | 0 .../Server/RecipeModels.cs | 0 .../Server/Server.csproj | 0 .../Server/SharedStateAgent.cs | 0 .../Server/appsettings.Development.json | 0 .../Server/appsettings.json | 0 .../AgentOpenTelemetry.csproj | 0 .../AgentOpenTelemetry/Program.cs | 0 .../AgentOpenTelemetry/README.md | 0 .../AgentOpenTelemetry/start-demo.ps1 | 0 .../Agent_With_A2A/Agent_With_A2A.csproj | 0 .../AgentProviders/Agent_With_A2A/Program.cs | 0 .../AgentProviders/Agent_With_A2A/README.md | 0 .../Agent_With_Anthropic.csproj | 0 .../Agent_With_Anthropic/Program.cs | 2 +- .../Agent_With_Anthropic/README.md | 6 +- .../Agent_With_AzureAIAgentsPersistent.csproj | 0 .../Program.cs | 4 +- .../README.md | 4 +- .../Agent_With_AzureAIProject.csproj | 0 .../Agent_With_AzureAIProject/Program.cs | 4 +- .../Agent_With_AzureAIProject/README.md | 4 +- .../Agent_With_AzureFoundryModel.csproj | 0 .../Agent_With_AzureFoundryModel/Program.cs | 6 +- .../Agent_With_AzureFoundryModel/README.md | 8 +- ...gent_With_AzureOpenAIChatCompletion.csproj | 0 .../Program.cs | 0 .../README.md | 0 .../Agent_With_AzureOpenAIResponses.csproj | 0 .../Program.cs | 0 .../Agent_With_AzureOpenAIResponses/README.md | 0 .../Agent_With_CustomImplementation.csproj | 0 .../Program.cs | 0 .../Agent_With_CustomImplementation/README.md | 0 .../Agent_With_GitHubCopilot.csproj | 0 .../Agent_With_GitHubCopilot/Program.cs | 0 .../Agent_With_GitHubCopilot/README.md | 0 .../Agent_With_GoogleGemini.csproj | 0 .../Agent_With_GoogleGemini/Program.cs | 0 .../Agent_With_GoogleGemini/README.md | 0 .../Agent_With_ONNX/Agent_With_ONNX.csproj | 0 .../AgentProviders/Agent_With_ONNX/Program.cs | 0 .../AgentProviders/Agent_With_ONNX/README.md | 0 .../Agent_With_Ollama.csproj | 0 .../Agent_With_Ollama/Program.cs | 0 .../Agent_With_Ollama/README.md | 0 .../Agent_With_OpenAIAssistants.csproj | 0 .../Agent_With_OpenAIAssistants/Program.cs | 2 +- .../Agent_With_OpenAIAssistants/README.md | 2 +- .../Agent_With_OpenAIChatCompletion.csproj | 0 .../Program.cs | 2 +- .../README.md | 2 +- .../Agent_With_OpenAIResponses.csproj | 0 .../Agent_With_OpenAIResponses/Program.cs | 2 +- .../Agent_With_OpenAIResponses}/README.md | 2 +- .../AgentProviders/README.md | 0 .../Agent_Step01_BasicSkills.csproj | 0 .../Agent_Step01_BasicSkills/Program.cs | 0 .../Agent_Step01_BasicSkills/README.md | 0 .../skills/expense-report/SKILL.md | 0 .../assets/expense-report-template.md | 0 .../expense-report/references/POLICY_FAQ.md | 0 .../AgentSkills/README.md | 0 .../Agent_Anthropic_Step01_Running.csproj | 0 .../Agent_Anthropic_Step01_Running/Program.cs | 2 +- .../Agent_Anthropic_Step01_Running/README.md | 4 +- .../Agent_Anthropic_Step02_Reasoning.csproj | 0 .../Program.cs | 2 +- .../README.md | 4 +- ...Anthropic_Step03_UsingFunctionTools.csproj | 0 .../Program.cs | 2 +- .../README.md | 4 +- .../Agent_Anthropic_Step04_UsingSkills.csproj | 0 .../Program.cs | 2 +- .../README.md | 4 +- .../AgentWithAnthropic/README.md | 0 ...WithMemory_Step01_ChatHistoryMemory.csproj | 0 .../Program.cs | 0 ...ntWithMemory_Step02_MemoryUsingMem0.csproj | 0 .../Program.cs | 2 +- ...ithMemory_Step04_MemoryUsingFoundry.csproj | 0 .../Program.cs | 8 +- .../README.md | 8 +- .../AgentWithMemory/README.md | 4 +- .../Agent_OpenAI_Step01_Running.csproj | 0 .../Agent_OpenAI_Step01_Running/Program.cs | 2 +- .../Agent_OpenAI_Step02_Reasoning.csproj | 0 .../Agent_OpenAI_Step02_Reasoning/Program.cs | 2 +- ..._OpenAI_Step03_CreateFromChatClient.csproj | 0 .../OpenAIChatClientAgent.cs | 0 .../Program.cs | 2 +- .../README.md | 2 +- ...ep04_CreateFromOpenAIResponseClient.csproj | 0 .../OpenAIResponseClientAgent.cs | 0 .../Program.cs | 2 +- .../README.md | 2 +- .../Agent_OpenAI_Step05_Conversation.csproj | 0 .../Program.cs | 2 +- .../README.md | 2 +- .../AgentWithOpenAI/README.md | 0 .../AgentWithRAG_Step01_BasicTextRAG.csproj | 0 .../Program.cs | 0 .../TextSearchStore/TextSearchDocument.cs | 0 .../TextSearchStore/TextSearchStore.cs | 0 .../TextSearchStore/TextSearchStoreOptions.cs | 0 .../TextSearchStoreUpsertOptions.cs | 0 ...WithRAG_Step02_CustomVectorStoreRAG.csproj | 0 .../Program.cs | 0 .../README.md | 0 ...tWithRAG_Step03_CustomRAGDataSource.csproj | 0 .../Program.cs | 0 ...entWithRAG_Step04_FoundryServiceRAG.csproj | 0 .../Program.cs | 4 +- .../contoso-outdoors-knowledge-base.md | 0 .../AgentWithRAG/README.md | 0 ...01_UsingFunctionToolsWithApprovals.csproj} | 0 .../Program.cs | 0 .../AIAgentBuilderExtensions.cs | 0 .../Agent_Step02_StructuredOutput.csproj} | 0 .../Agent_Step02_StructuredOutput}/Program.cs | 0 .../Agent_Step02_StructuredOutput}/README.md | 2 +- .../StructuredOutputAgent.cs | 0 .../StructuredOutputAgentOptions.cs | 0 .../StructuredOutputAgentResponse.cs | 0 ...gent_Step03_PersistedConversations.csproj} | 0 .../Program.cs | 0 ..._Step04_3rdPartyChatHistoryStorage.csproj} | 0 .../Program.cs | 0 .../Agent_Step05_Observability.csproj} | 0 .../Agent_Step05_Observability}/Program.cs | 0 .../Agent_Step06_DependencyInjection.csproj} | 0 .../Program.cs | 0 .../Agent_Step07_AsMcpTool.csproj} | 0 .../Agents/Agent_Step07_AsMcpTool}/Program.cs | 4 +- .../Agents/Agent_Step07_AsMcpTool}/README.md | 6 +- .../Agent_Step08_UsingImages.csproj} | 0 .../Agent_Step08_UsingImages}/Program.cs | 0 .../Agent_Step08_UsingImages}/README.md | 2 +- .../Agent_Step09_AsFunctionTool.csproj} | 0 .../Agent_Step09_AsFunctionTool}/Program.cs | 0 ...ndResponsesWithToolsAndPersistence.csproj} | 0 .../Program.cs | 0 .../README.md | 0 .../Agent_Step11_Middleware.csproj} | 0 .../Agent_Step11_Middleware}/Program.cs | 0 .../Agents/Agent_Step11_Middleware}/README.md | 2 +- .../Agent_Step12_Plugins.csproj} | 2 +- .../Agents/Agent_Step12_Plugins}/Program.cs | 0 .../Agent_Step13_ChatReduction.csproj} | 0 .../Agent_Step13_ChatReduction}/Program.cs | 0 .../Agent_Step14_BackgroundResponses.csproj} | 0 .../Program.cs | 0 .../README.md | 0 .../Agent_Step15_DeepResearch.csproj} | 0 .../Agent_Step15_DeepResearch}/Program.cs | 8 +- .../Agent_Step15_DeepResearch}/README.md | 8 +- .../Agent_Step16_Declarative.csproj} | 0 .../Agent_Step16_Declarative}/Program.cs | 0 .../Agent_Step17_AdditionalAIContext.csproj} | 0 .../Program.cs | 0 .../Agents/README.md | 39 +- .../DeclarativeChatClientAgents.csproj | 0 .../DeclarativeAgents/ChatClient/Program.cs | 0 .../ChatClient/Properties/launchSettings.json | 0 .../DevUI_Step01_BasicUsage.csproj | 0 .../DevUI/DevUI_Step01_BasicUsage/Program.cs | 0 .../Properties/launchSettings.json | 0 .../DevUI/DevUI_Step01_BasicUsage/README.md | 0 .../DevUI/README.md | 0 ...gents_Evaluations_Step01_RedTeaming.csproj | 0 .../Program.cs | 4 +- .../README.md | 6 +- ...s_Evaluations_Step02_SelfReflection.csproj | 0 .../Program.cs | 4 +- .../README.md | 6 +- .../FoundryAgents_Step01.1_Basics.csproj | 0 .../FoundryAgents_Step01.1_Basics/Program.cs | 4 +- .../FoundryAgents_Step01.1_Basics/README.md | 6 +- .../FoundryAgents_Step01.2_Running.csproj | 0 .../FoundryAgents_Step01.2_Running/Program.cs | 4 +- .../FoundryAgents_Step01.2_Running/README.md | 6 +- ...Agents_Step02_MultiturnConversation.csproj | 0 .../Program.cs | 4 +- .../README.md | 6 +- ...dryAgents_Step03_UsingFunctionTools.csproj | 0 .../Program.cs | 4 +- .../README.md | 6 +- ...p04_UsingFunctionToolsWithApprovals.csproj | 0 .../Program.cs | 4 +- .../README.md | 6 +- ...undryAgents_Step05_StructuredOutput.csproj | 0 .../Program.cs | 4 +- .../README.md | 6 +- ...gents_Step06_PersistedConversations.csproj | 0 .../Program.cs | 4 +- .../README.md | 6 +- .../FoundryAgents_Step07_Observability.csproj | 0 .../Program.cs | 4 +- .../README.md | 6 +- ...ryAgents_Step08_DependencyInjection.csproj | 0 .../Program.cs | 4 +- .../README.md | 6 +- ...Agents_Step09_UsingMcpClientAsTools.csproj | 0 .../Program.cs | 4 +- .../README.md | 6 +- .../Assets/walkway.jpg | Bin .../FoundryAgents_Step10_UsingImages.csproj | 0 .../Program.cs | 4 +- .../README.md | 6 +- ...FoundryAgents_Step11_AsFunctionTool.csproj | 0 .../Program.cs | 4 +- .../README.md | 6 +- .../FoundryAgents_Step12_Middleware.csproj | 0 .../Program.cs | 4 +- .../FoundryAgents_Step12_Middleware/README.md | 6 +- .../FoundryAgents_Step13_Plugins.csproj | 0 .../FoundryAgents_Step13_Plugins/Program.cs | 4 +- .../FoundryAgents_Step13_Plugins/README.md | 6 +- ...oundryAgents_Step14_CodeInterpreter.csproj | 0 .../Program.cs | 4 +- .../README.md | 6 +- .../Assets/cua_browser_search.png | Bin .../Assets/cua_search_results.png | Bin .../Assets/cua_search_typed.png | Bin .../ComputerUseUtil.cs | 0 .../FoundryAgents_Step15_ComputerUse.csproj | 0 .../Program.cs | 4 +- .../README.md | 6 +- .../FoundryAgents_Step16_FileSearch.csproj} | 0 .../Program.cs | 4 +- .../README.md | 8 +- .../FoundryAgents_Step17_OpenAPITools.csproj} | 0 .../Program.cs | 4 +- .../README.md | 10 +- ...ndryAgents_Step18_BingCustomSearch.csproj} | 0 .../Program.cs | 8 +- .../README.md | 6 +- .../FoundryAgents_Step19_SharePoint.csproj} | 0 .../Program.cs | 0 .../README.md | 4 +- ...undryAgents_Step20_MicrosoftFabric.csproj} | 0 .../Program.cs | 72 ++ .../README.md | 57 ++ .../FoundryAgents_Step21_WebSearch.csproj | 22 + .../Program.cs | 4 +- .../FoundryAgents_Step21_WebSearch}/README.md | 4 +- .../FoundryAgents_Step22_MemorySearch.csproj} | 0 .../Program.cs | 6 +- .../README.md | 4 +- .../FoundryAgents_Step23_LocalMCP.csproj} | 0 .../FoundryAgents_Step23_LocalMCP}/Program.cs | 4 +- .../FoundryAgents_Step23_LocalMCP}/README.md | 8 +- .../FoundryAgents/README.md | 18 +- .../Agent_MCP_Server/Agent_MCP_Server.csproj | 0 .../Agent_MCP_Server/Program.cs | 0 .../Agent_MCP_Server/README.md | 0 .../Agent_MCP_Server_Auth.csproj | 0 .../Agent_MCP_Server_Auth/Program.cs | 0 .../Agent_MCP_Server_Auth/README.md | 0 .../FoundryAgent_Hosted_MCP.csproj | 0 .../FoundryAgent_Hosted_MCP/Program.cs | 4 +- .../FoundryAgent_Hosted_MCP/README.md | 4 +- .../ModelContextProtocol/README.md | 0 .../ResponseAgent_Hosted_MCP/Program.cs | 0 .../ResponseAgent_Hosted_MCP/README.md | 0 .../ResponseAgent_Hosted_MCP.csproj | 0 .../{GettingStarted => 02-agents}/README.md | 5 +- .../CustomAgentExecutors.csproj | 6 +- .../Agents/CustomAgentExecutors/Program.cs | 0 .../Agents/FoundryAgent/FoundryAgent.csproj | 22 + .../Agents/FoundryAgent/Program.cs | 6 +- .../DeploymentGroupChatManager.cs | 0 .../GroupChatToolApproval.csproj | 22 + .../Agents/GroupChatToolApproval/Program.cs | 0 .../Agents/GroupChatToolApproval/README.md | 4 +- .../Agents/WorkflowAsAnAgent/Program.cs | 0 .../WorkflowAsAnAgent.csproj | 22 + .../WorkflowAsAnAgent/WorkflowFactory.cs | 0 .../CheckpointAndRehydrate.csproj} | 0 .../CheckpointAndRehydrate/Program.cs | 0 .../CheckpointAndRehydrate/WorkflowFactory.cs | 0 .../CheckpointAndResume.csproj} | 3 +- .../Checkpoint/CheckpointAndResume/Program.cs | 0 .../CheckpointAndResume/WorkflowFactory.cs | 0 .../CheckpointWithHumanInTheLoop.csproj | 2 +- .../CheckpointWithHumanInTheLoop/Program.cs | 0 .../WorkflowFactory.cs | 0 .../Concurrent/Concurrent/Concurrent.csproj | 27 + .../Concurrent/Concurrent/Program.cs | 0 .../Concurrent/MapReduce/MapReduce.csproj} | 13 +- .../Concurrent/MapReduce/Program.cs | 0 .../01_EdgeCondition/01_EdgeCondition.csproj | 4 +- .../01_EdgeCondition/Program.cs | 0 .../01_EdgeCondition/Resources.cs | 0 .../02_SwitchCase/02_SwitchCase.csproj | 4 +- .../ConditionalEdges/02_SwitchCase/Program.cs | 0 .../02_SwitchCase/Resources.cs | 0 .../03_MultiSelection.csproj | 4 +- .../03_MultiSelection/Program.cs | 0 .../03_MultiSelection/Resources.cs | 0 .../ConfirmInput/ConfirmInput.csproj | 6 +- .../ConfirmInput/ConfirmInput.yaml | 0 .../Declarative/ConfirmInput/Program.cs | 0 .../CustomerSupport/CustomerSupport.csproj | 8 +- .../Declarative/CustomerSupport/Program.cs | 12 +- .../Properties/launchSettings.json | 19 + .../CustomerSupport/TicketingPlugin.cs | 0 .../DeepResearch/DeepResearch.csproj | 8 +- .../Declarative/DeepResearch/Program.cs | 14 +- .../Properties/launchSettings.json | 11 + .../Declarative/DeepResearch/wttr.json | 0 .../ExecuteCode/ExecuteCode.csproj | 6 +- .../Declarative/ExecuteCode/Generated.cs | 0 .../Declarative/ExecuteCode/Program.cs | 2 +- .../ExecuteWorkflow/ExecuteWorkflow.csproj | 4 +- .../Declarative/ExecuteWorkflow/Program.cs | 2 +- .../Properties/launchSettings.json | 32 + .../FunctionTools/FunctionTools.csproj | 6 +- .../FunctionTools/FunctionTools.yaml | 0 .../Declarative/FunctionTools/MenuPlugin.cs | 0 .../Declarative/FunctionTools/Program.cs | 2 +- .../Properties/launchSettings.json | 11 + .../GenerateCode/GenerateCode.csproj | 4 +- .../Declarative/GenerateCode/Program.cs | 0 .../Properties/launchSettings.json | 28 + .../HostedWorkflow/HostedWorkflow.csproj | 8 +- .../Declarative/HostedWorkflow/Program.cs | 6 +- .../InputArguments/InputArguments.csproj | 6 +- .../InputArguments/InputArguments.yaml | 0 .../Declarative/InputArguments/Program.cs | 6 +- .../Properties/launchSettings.json | 11 + .../InvokeFunctionTool.csproj | 4 +- .../InvokeFunctionTool.yaml | 0 .../InvokeFunctionTool/MenuPlugin.cs | 0 .../Declarative/InvokeFunctionTool/Program.cs | 2 +- .../InvokeMcpTool/InvokeMcpTool.csproj | 39 + .../InvokeMcpTool/InvokeMcpTool.yaml | 63 ++ .../Declarative/InvokeMcpTool/Program.cs | 141 +++ .../Declarative/Marketing/Marketing.csproj | 8 +- .../Declarative/Marketing/Program.cs | 6 +- .../Marketing/Properties/launchSettings.json | 11 + .../Properties/launchSettings.json | 32 + .../Properties/launchSettings.json | 32 + .../Declarative/README.md | 22 +- .../Declarative/StudentTeacher/Program.cs | 4 +- .../Properties/launchSettings.json | 11 + .../StudentTeacher/StudentTeacher.csproj | 8 +- .../Declarative/ToolApproval/Program.cs | 2 +- .../Properties/launchSettings.json | 11 + .../ToolApproval/ToolApproval.csproj | 6 +- .../ToolApproval/ToolApproval.yaml | 0 .../HumanInTheLoopBasic.csproj | 2 +- .../HumanInTheLoopBasic/Program.cs | 0 .../HumanInTheLoopBasic/WorkflowFactory.cs | 0 .../Loop/Loop.csproj} | 2 +- .../Loop/Program.cs | 0 .../ApplicationInsights.csproj | 2 +- .../ApplicationInsights/Program.cs | 0 .../AspireDashboard/AspireDashboard.csproj | 2 +- .../Observability/AspireDashboard/Program.cs | 2 +- .../WorkflowAsAnAgent/Program.cs | 2 +- .../WorkflowAsAnAgentObservability.csproj | 6 +- .../WorkflowAsAnAgent/WorkflowHelper.cs | 0 .../Workflows => 03-workflows}/README.md | 19 +- .../Resources/Lorem_Ipsum.txt | 0 .../Resources/ambiguous_email.txt | 0 .../Resources/email.txt | 0 .../Resources/spam.txt | 0 .../SharedStates/Program.cs | 0 .../SharedStates/Resources.cs | 0 .../SharedStates/SharedStates.csproj | 2 +- .../Visualization/Program.cs | 0 .../Visualization/README.md | 0 .../Resources/graphviz_render.png | Bin .../Resources/mermaid_render.png | Bin .../Visualization/Visualization.csproj | 16 + .../01_Streaming/01_Streaming.csproj} | 2 +- .../_StartHere/01_Streaming}/Program.cs | 0 .../02_AgentsInWorkflows.csproj | 22 + .../02_AgentsInWorkflows}/Program.cs | 0 .../03_AgentWorkflowPatterns.csproj | 22 + .../03_AgentWorkflowPatterns}/Program.cs | 0 .../04_MultiModelService.csproj} | 4 +- .../04_MultiModelService}/Program.cs | 6 +- .../05_SubWorkflows/05_SubWorkflows.csproj | 16 + .../_StartHere/05_SubWorkflows}/Program.cs | 0 .../06_MixedWorkflowAgentsAndExecutors.csproj | 22 + .../Program.cs | 0 .../README.md | 2 +- .../07_WriterCriticWorkflow.csproj} | 8 +- .../07_WriterCriticWorkflow}/Program.cs | 0 .../A2AAgent_AsFunctionTools.csproj | 0 .../A2A/A2AAgent_AsFunctionTools/Program.cs | 0 .../A2A/A2AAgent_AsFunctionTools/README.md | 0 .../A2AAgent_PollingForTaskCompletion.csproj | 0 .../Program.cs | 0 .../README.md | 0 .../A2A/README.md | 2 +- .../AzureFunctions/.editorconfig | 0 .../01_SingleAgent/01_SingleAgent.csproj | 0 .../AzureFunctions/01_SingleAgent/Program.cs | 4 +- .../AzureFunctions/01_SingleAgent/README.md | 0 .../AzureFunctions/01_SingleAgent/demo.http | 0 .../AzureFunctions/01_SingleAgent/host.json | 0 .../01_SingleAgent}/local.settings.json | 2 +- .../02_AgentOrchestration_Chaining.csproj | 0 .../FunctionTriggers.cs | 0 .../02_AgentOrchestration_Chaining/Program.cs | 6 +- .../02_AgentOrchestration_Chaining/README.md | 0 .../02_AgentOrchestration_Chaining/demo.http | 0 .../02_AgentOrchestration_Chaining/host.json | 0 .../local.settings.json | 2 +- .../03_AgentOrchestration_Concurrency.csproj | 0 .../FunctionTriggers.cs | 0 .../Program.cs | 6 +- .../README.md | 0 .../demo.http | 0 .../host.json | 0 .../local.settings.json | 2 +- .../04_AgentOrchestration_Conditionals.csproj | 0 .../FunctionTriggers.cs | 0 .../Models.cs | 0 .../Program.cs | 6 +- .../README.md | 0 .../demo.http | 0 .../host.json | 0 .../local.settings.json | 2 +- .../05_AgentOrchestration_HITL.csproj | 0 .../FunctionTriggers.cs | 0 .../05_AgentOrchestration_HITL/Models.cs | 0 .../05_AgentOrchestration_HITL/Program.cs | 6 +- .../05_AgentOrchestration_HITL/README.md | 0 .../05_AgentOrchestration_HITL/demo.http | 0 .../05_AgentOrchestration_HITL/host.json | 0 .../local.settings.json | 10 + .../06_LongRunningTools.csproj | 0 .../06_LongRunningTools/FunctionTriggers.cs | 0 .../06_LongRunningTools/Models.cs | 0 .../06_LongRunningTools/Program.cs | 6 +- .../06_LongRunningTools/README.md | 0 .../06_LongRunningTools/Tools.cs | 0 .../06_LongRunningTools/demo.http | 0 .../06_LongRunningTools/host.json | 0 .../06_LongRunningTools/local.settings.json | 10 + .../07_AgentAsMcpTool.csproj | 0 .../07_AgentAsMcpTool/Program.cs | 6 +- .../07_AgentAsMcpTool/README.md | 6 +- .../07_AgentAsMcpTool/host.json | 0 .../07_AgentAsMcpTool/local.settings.json | 10 + .../08_ReliableStreaming.csproj | 0 .../08_ReliableStreaming/FunctionTriggers.cs | 0 .../08_ReliableStreaming/Program.cs | 6 +- .../08_ReliableStreaming/README.md | 4 +- .../RedisStreamResponseHandler.cs | 0 .../08_ReliableStreaming/Tools.cs | 0 .../08_ReliableStreaming/host.json | 0 .../08_ReliableStreaming/local.settings.json | 2 +- .../DurableAgents}/AzureFunctions/README.md | 14 +- .../01_SingleAgent/01_SingleAgent.csproj | 0 .../ConsoleApps/01_SingleAgent/Program.cs | 6 +- .../ConsoleApps/01_SingleAgent/README.md | 2 +- .../02_AgentOrchestration_Chaining.csproj | 0 .../02_AgentOrchestration_Chaining/Models.cs | 0 .../02_AgentOrchestration_Chaining/Program.cs | 6 +- .../02_AgentOrchestration_Chaining/README.md | 2 +- .../03_AgentOrchestration_Concurrency.csproj | 0 .../Models.cs | 0 .../Program.cs | 6 +- .../README.md | 2 +- .../04_AgentOrchestration_Conditionals.csproj | 0 .../Models.cs | 0 .../Program.cs | 6 +- .../README.md | 2 +- .../05_AgentOrchestration_HITL.csproj | 0 .../05_AgentOrchestration_HITL/Models.cs | 0 .../05_AgentOrchestration_HITL/Program.cs | 6 +- .../05_AgentOrchestration_HITL/README.md | 2 +- .../06_LongRunningTools.csproj | 0 .../ConsoleApps/06_LongRunningTools/Models.cs | 0 .../06_LongRunningTools/Program.cs | 6 +- .../ConsoleApps/06_LongRunningTools/README.md | 2 +- .../07_ReliableStreaming.csproj | 0 .../07_ReliableStreaming/Program.cs | 6 +- .../07_ReliableStreaming/README.md | 6 +- .../RedisStreamResponseHandler.cs | 0 .../DurableAgents}/ConsoleApps/README.md | 10 +- .../DurableAgents}/Directory.Build.props | 2 +- .../A2AClient/A2AClient.csproj | 6 +- .../A2AClient/HostClientAgent.cs | 0 .../A2AClientServer/A2AClient/Program.cs | 0 .../A2AClientServer/A2AClient/README.md | 4 +- .../A2AServer/A2AServer.csproj | 10 +- .../A2AClientServer/A2AServer/A2AServer.http | 0 .../A2AServer/HostAgentFactory.cs | 0 .../A2AServer/Models/InvoiceQuery.cs | 0 .../A2AClientServer/A2AServer/Program.cs | 4 +- .../A2AClientServer/README.md | 2 +- .../a2a-inspector-agent-card.png | Bin .../a2a-inspector-raw-json-response.png | Bin .../a2a-inspector-send-message.png | Bin .../A2AClientServer/demo-architecture.png | Bin .../rest-client-agent-card.png | Bin .../rest-client-send-message.png | Bin .../AGUIClient/AGUIClient.csproj | 4 +- .../AGUIClient/AGUIClientSerializerContext.cs | 0 .../AGUIClientServer/AGUIClient/Program.cs | 0 .../AGUIClientServer/AGUIClient/README.md | 0 .../AGUIClient/SensorRequest.cs | 0 .../AGUIClient/SensorResponse.cs | 0 .../AGUIDojoServer/AGUIDojoServer.csproj | 22 + .../AGUIDojoServerSerializerContext.cs | 0 .../AgenticUI/AgenticPlanningTools.cs | 0 .../AgenticUI/AgenticUIAgent.cs | 0 .../AgenticUI/JsonPatchOperation.cs | 0 .../AGUIDojoServer/AgenticUI/Plan.cs | 0 .../AGUIDojoServer/AgenticUI/Step.cs | 0 .../AGUIDojoServer/AgenticUI/StepStatus.cs | 0 .../BackendToolRendering/WeatherInfo.cs | 0 .../AGUIDojoServer/ChatClientAgentFactory.cs | 0 .../PredictiveStateUpdates/DocumentState.cs | 0 .../PredictiveStateUpdatesAgent.cs | 0 .../AGUIDojoServer/Program.cs | 0 .../Properties/launchSettings.json | 0 .../AGUIDojoServer/SharedState/Ingredient.cs | 0 .../AGUIDojoServer/SharedState/Recipe.cs | 0 .../SharedState/RecipeResponse.cs | 0 .../SharedState/SharedStateAgent.cs | 0 .../appsettings.Development.json | 0 .../AGUIDojoServer/appsettings.json | 0 .../AGUIServer/AGUIServer.csproj | 22 + .../AGUIServer/AGUIServer.http | 0 .../AGUIServer/AGUIServerSerializerContext.cs | 0 .../AGUIClientServer/AGUIServer/Program.cs | 0 .../AGUIServer/Properties/launchSettings.json | 0 .../ServerWeatherForecastRequest.cs | 0 .../ServerWeatherForecastResponse.cs | 0 .../AGUIClientServer/README.md | 0 .../Client/AGUIWebChatClient.csproj | 2 +- .../AGUIWebChat/Client/Components/App.razor | 0 .../Components/Layout/LoadingSpinner.razor | 0 .../Layout/LoadingSpinner.razor.css | 0 .../Client/Components/Layout/MainLayout.razor | 0 .../Components/Layout/MainLayout.razor.css | 0 .../Client/Components/Pages/Chat/Chat.razor | 0 .../Components/Pages/Chat/Chat.razor.css | 0 .../Components/Pages/Chat/ChatCitation.razor | 0 .../Pages/Chat/ChatCitation.razor.css | 0 .../Components/Pages/Chat/ChatHeader.razor | 0 .../Pages/Chat/ChatHeader.razor.css | 0 .../Components/Pages/Chat/ChatInput.razor | 0 .../Components/Pages/Chat/ChatInput.razor.css | 0 .../Components/Pages/Chat/ChatInput.razor.js | 0 .../Pages/Chat/ChatMessageItem.razor | 0 .../Pages/Chat/ChatMessageItem.razor.css | 0 .../Pages/Chat/ChatMessageList.razor | 0 .../Pages/Chat/ChatMessageList.razor.css | 0 .../Pages/Chat/ChatMessageList.razor.js | 0 .../Pages/Chat/ChatSuggestions.razor | 0 .../Pages/Chat/ChatSuggestions.razor.css | 0 .../Client/Components/Routes.razor | 0 .../Client/Components/_Imports.razor | 0 .../AGUIWebChat/Client/Program.cs | 2 +- .../Client/Properties/launchSettings.json | 2 +- .../AGUIWebChat/Client/wwwroot/app.css | 0 .../AGUIWebChat/Client/wwwroot/favicon.png | Bin .../{ => 05-end-to-end}/AGUIWebChat/README.md | 8 +- .../Server/AGUIWebChatServer.csproj | 21 + .../AGUIWebChat/Server/Program.cs | 0 .../Server/Properties/launchSettings.json | 0 .../ActorFrameworkWebApplicationExtensions.cs | 0 .../AgentWebChat.AgentHost.csproj | 14 +- .../Custom/CustomAITools.cs | 0 .../AgentWebChat.AgentHost/Program.cs | 0 .../Properties/launchSettings.json | 0 .../Utilities/ChatClientConnectionInfo.cs | 0 .../Utilities/ChatClientExtensions.cs | 0 .../appsettings.Development.json | 0 .../AgentWebChat.AgentHost}/appsettings.json | 0 .../AgentWebChat.AppHost.csproj | 0 .../AgentWebChat.AppHost/ModelExtensions.cs | 0 .../AgentWebChat.AppHost/Program.cs | 0 .../Properties/launchSettings.json | 0 .../appsettings.Development.json | 0 .../AgentWebChat.AppHost/appsettings.json | 0 .../AgentWebChat.ServiceDefaults.csproj | 0 .../ServiceDefaultsExtensions.cs | 0 .../AgentWebChat.Web/A2AAgentClient.cs | 0 .../AgentWebChat.Web/AgentDiscoveryClient.cs | 0 .../AgentWebChat.Web/AgentWebChat.Web.csproj | 18 + .../AgentWebChat.Web/Components/App.razor | 0 .../Components/Layout/MainLayout.razor | 0 .../Components/Layout/MainLayout.razor.css | 0 .../Components/Pages/Error.razor | 0 .../Components/Pages/Home.razor | 0 .../AgentWebChat.Web/Components/Routes.razor | 0 .../Components/_Imports.razor | 0 .../AgentWebChat.Web/IAgentClient.cs | 0 .../OpenAIChatCompletionsAgentClient.cs | 0 .../OpenAIResponsesAgentClient.cs | 0 .../AgentWebChat/AgentWebChat.Web/Program.cs | 0 .../Properties/launchSettings.json | 0 .../appsettings.Development.json | 0 .../AgentWebChat.Web}/appsettings.json | 0 .../AgentWebChat.Web/wwwroot/app.css | 0 .../AgentWebChat.Web/wwwroot/favicon.png | Bin .../AgentWithPurview/AgentWithPurview.csproj | 0 .../AgentWithPurview/Program.cs | 0 .../AgentWithHostedMCP.csproj | 0 .../AgentWithHostedMCP/Dockerfile | 0 .../AgentWithHostedMCP/Program.cs | 0 .../HostedAgents/AgentWithHostedMCP/README.md | 0 .../AgentWithHostedMCP/agent.yaml | 0 .../AgentWithHostedMCP/run-requests.http | 0 .../AgentWithTextSearchRag.csproj | 0 .../AgentWithTextSearchRag/Dockerfile | 0 .../AgentWithTextSearchRag/Program.cs | 0 .../AgentWithTextSearchRag/README.md | 0 .../AgentWithTextSearchRag/agent.yaml | 0 .../AgentWithTextSearchRag/run-requests.http | 0 .../AgentsInWorkflows.csproj | 0 .../HostedAgents/AgentsInWorkflows/Dockerfile | 0 .../HostedAgents/AgentsInWorkflows/Program.cs | 0 .../HostedAgents/AgentsInWorkflows/README.md | 0 .../HostedAgents/AgentsInWorkflows/agent.yaml | 0 .../AgentsInWorkflows/run-requests.http | 0 .../M365Agent/AFAgentApplication.cs | 0 .../M365Agent/Agents/AdaptiveCardAIContent.cs | 0 .../M365Agent/Agents/WeatherForecastAgent.cs | 0 .../Agents/WeatherForecastAgentResponse.cs | 0 ...WeatherForecastAgentResponseContentType.cs | 0 .../M365Agent/Auth/AspNetExtensions.cs | 0 .../M365Agent/Auth/TokenValidationOptions.cs | 0 .../M365Agent/JsonUtilities.cs | 0 .../M365Agent/M365Agent.csproj | 2 +- .../{ => 05-end-to-end}/M365Agent/Program.cs | 0 .../M365Agent/Properties/launchSettings.json | 0 .../{ => 05-end-to-end}/M365Agent/README.md | 0 .../M365Agent/appManifest/color.png | Bin .../M365Agent/appManifest/manifest.json | 0 .../M365Agent/appManifest/outline.png | Bin .../M365Agent/appsettings.json.template | 0 dotnet/samples/AGENTS.md | 130 +++ .../AGUIDojoServer/AGUIDojoServer.csproj | 22 - .../AGUIServer/AGUIServer.csproj | 22 - .../Server/AGUIWebChatServer.csproj | 21 - .../AgentWebChat.Web/AgentWebChat.Web.csproj | 18 - .../local.settings.json | 10 - .../06_LongRunningTools/local.settings.json | 10 - .../07_AgentAsMcpTool/local.settings.json | 10 - .../Agents/FoundryAgent/FoundryAgent.csproj | 22 - .../GroupChatToolApproval.csproj | 22 - .../WorkflowAsAnAgent.csproj | 22 - .../Concurrent/Concurrent/Concurrent.csproj | 22 - .../Concurrent/MapReduce/MapReduce.csproj | 20 - .../Workflows/Directory.Build.props | 13 - .../01_ExecutorsAndEdges.csproj | 15 - .../02_Streaming/02_Streaming.csproj | 15 - .../03_AgentsInWorkflows.csproj | 22 - .../04_AgentWorkflowPatterns.csproj | 22 - .../06_SubWorkflows/06_SubWorkflows.csproj | 16 - .../07_MixedWorkflowAgentsAndExecutors.csproj | 22 - dotnet/samples/README.md | 43 +- .../PurviewWrapper.cs | 4 +- .../DefaultMcpToolHandler.cs | 252 ++++++ ...Agents.AI.Workflows.Declarative.Mcp.csproj | 33 + .../DeclarativeWorkflowOptions.cs | 6 + .../Extensions/JsonDocumentExtensions.cs | 44 +- .../IMcpToolHandler.cs | 41 + .../Interpreter/WorkflowActionVisitor.cs | 36 + .../Interpreter/WorkflowTemplateVisitor.cs | 2 + .../ObjectModel/InvokeFunctionToolExecutor.cs | 36 +- .../ObjectModel/InvokeMcpToolExecutor.cs | 367 ++++++++ .../README.md | 4 +- .../Execution/LockstepRunEventStream.cs | 48 +- .../Execution/StreamingRunEventStream.cs | 64 +- .../Observability/ActivityNames.cs | 3 +- .../Observability/EventNames.cs | 3 + .../Observability/Tags.cs | 1 + .../Observability/WorkflowTelemetryContext.cs | 22 +- .../AnthropicConfiguration.cs | 17 - .../IntegrationTests/AzureAIConfiguration.cs | 15 - .../FoundryMemoryConfiguration.cs | 13 - .../IntegrationTests/Mem0Configuration.cs | 12 - .../IntegrationTests/OpenAIConfiguration.cs | 17 - dotnet/src/Shared/IntegrationTests/README.md | 27 + .../Shared/IntegrationTests/TestSettings.cs | 37 + .../Workflows/Execution/WorkflowFactory.cs | 4 + .../Shared/Workflows/Settings/Application.cs | 7 +- .../Support/TestConfiguration.cs | 30 +- .../AnthropicChatCompletionFixture.cs | 11 +- .../AnthropicSkillsIntegrationTests.cs | 10 +- .../AIProjectClientCreateTests.cs | 17 +- .../AIProjectClientFixture.cs | 10 +- .../AzureAIAgentsPersistentCreateTests.cs | 17 +- .../AzureAIAgentsPersistentFixture.cs | 6 +- .../CopilotStudioFixture.cs | 17 +- .../CopilotStudioAgentConfiguration.cs | 15 - .../CosmosChatHistoryProviderTests.cs | 20 +- .../CosmosCheckpointStoreTests.cs | 12 +- .../ConsoleAppSamplesValidation.cs | 8 +- ...nts.AI.DurableTask.IntegrationTests.csproj | 1 - .../TestHelper.cs | 8 +- ...oft.Agents.AI.DurableTask.UnitTests.csproj | 1 - .../FoundryMemoryProviderTests.cs | 16 +- ...s.AI.FoundryMemory.IntegrationTests.csproj | 1 - ...ing.AzureFunctions.IntegrationTests.csproj | 1 - .../SamplesValidation.cs | 10 +- .../Mem0ProviderTests.cs | 11 +- ...oft.Agents.AI.Mem0.IntegrationTests.csproj | 1 - .../PurviewWrapperTests.cs | 62 +- .../Agents/AgentProvider.cs | 11 +- .../Agents/FunctionToolAgentProvider.cs | 3 +- .../Agents/MarketingAgentProvider.cs | 7 +- .../Agents/MathChatAgentProvider.cs | 5 +- .../Agents/PoemAgentProvider.cs | 3 +- .../Agents/TestAgentProvider.cs | 3 +- .../Agents/VisionAgentProvider.cs | 3 +- .../Framework/IntegrationTest.cs | 14 +- ...kflowTest.cs => InvokeToolWorkflowTest.cs} | 162 +++- ...kflows.Declarative.IntegrationTests.csproj | 2 +- .../Workflows/InvokeMcpTool.yaml | 35 + .../Workflows/InvokeMcpToolWithApproval.yaml | 35 + .../DefaultMcpToolHandlerTests.cs | 345 +++++++ ...Workflows.Declarative.Mcp.UnitTests.csproj | 15 + .../Extensions/JsonDocumentExtensionsTests.cs | 204 +++++ .../ObjectModel/InvokeMcpToolExecutorTest.cs | 844 ++++++++++++++++++ .../ObservabilityTests.cs | 31 +- .../WorkflowRunActivityStopTests.cs | 344 +++++++ .../OpenAIAssistantClientExtensionsTests.cs | 25 +- .../OpenAIAssistantFixture.cs | 6 +- .../OpenAIChatCompletionFixture.cs | 5 +- .../OpenAIResponseFixture.cs | 6 +- python/CHANGELOG.md | 28 +- python/packages/a2a/pyproject.toml | 9 +- .../ag-ui/agent_framework_ag_ui/_agent_run.py | 73 +- .../agent_framework_ag_ui_examples/README.md | 4 - python/packages/ag-ui/pyproject.toml | 7 +- .../tests/ag_ui/test_message_adapters.py | 42 + .../ag-ui/tests/ag_ui/test_message_hygiene.py | 138 +++ python/packages/anthropic/pyproject.toml | 9 +- .../packages/azure-ai-search/pyproject.toml | 8 +- .../tests/test_aisearch_context_provider.py | 10 +- .../agent_framework_azure_ai/__init__.py | 10 + .../_embedding_client.py | 396 ++++++++ python/packages/azure-ai/pyproject.toml | 10 +- ...est_azure_ai_inference_embedding_client.py | 316 +++++++ python/packages/azurefunctions/pyproject.toml | 6 +- .../agent_framework_bedrock/__init__.py | 4 + .../_embedding_client.py | 292 ++++++ python/packages/bedrock/pyproject.toml | 8 +- .../bedrock/test_bedrock_embedding_client.py | 169 ++++ python/packages/chatkit/pyproject.toml | 9 +- python/packages/claude/pyproject.toml | 9 +- python/packages/copilotstudio/pyproject.toml | 10 +- python/packages/core/README.md | 2 +- .../packages/core/agent_framework/_clients.py | 18 +- .../packages/core/agent_framework/_skills.py | 11 +- .../packages/core/agent_framework/_types.py | 8 +- .../core/agent_framework/amazon/__init__.py | 13 +- .../core/agent_framework/amazon/__init__.pyi | 6 + .../azure/_embedding_client.py | 2 + .../core/agent_framework/observability.py | 8 +- .../core/agent_framework/ollama/__init__.py | 13 +- .../core/agent_framework/ollama/__init__.pyi | 8 + .../openai/_embedding_client.py | 11 +- python/packages/core/pyproject.toml | 6 +- .../core/test_function_invocation_logic.py | 6 +- .../core/tests/core/test_observability.py | 186 ++++ .../packages/core/tests/core/test_skills.py | 1 + .../openai/test_openai_embedding_client.py | 8 +- python/packages/declarative/pyproject.toml | 10 +- python/packages/devui/pyproject.toml | 9 +- python/packages/durabletask/pyproject.toml | 6 +- python/packages/foundry_local/pyproject.toml | 9 +- python/packages/github_copilot/pyproject.toml | 10 +- python/packages/lab/pyproject.toml | 4 +- python/packages/mem0/pyproject.toml | 10 +- .../ollama/agent_framework_ollama/__init__.py | 4 + .../_embedding_client.py | 230 +++++ python/packages/ollama/pyproject.toml | 9 +- .../ollama/test_ollama_embedding_client.py | 150 ++++ python/packages/orchestrations/pyproject.toml | 8 +- .../agent_framework_purview/_middleware.py | 4 +- .../agent_framework_purview/_processor.py | 3 +- python/packages/purview/pyproject.toml | 10 +- .../purview/tests/purview/test_middleware.py | 6 +- python/packages/redis/pyproject.toml | 9 +- python/pyproject.toml | 4 +- .../azure_ai_inference_embeddings.py | 87 ++ python/samples/03-workflows/README.md | 8 + .../agent_to_function_tool/main.py | 11 +- .../declarative/function_tools/README.md | 8 +- .../03-workflows/orchestrations/README.md | 4 + .../orchestrations/concurrent_agents.py | 15 +- .../concurrent_custom_agent_executors.py | 21 +- .../concurrent_custom_aggregator.py | 15 +- .../group_chat_agent_manager.py | 15 +- .../group_chat_philosophical_debate.py | 15 +- .../group_chat_simple_selector.py | 15 +- .../orchestrations/handoff_autonomous.py | 16 +- .../orchestrations/handoff_simple.py | 20 +- .../03-workflows/orchestrations/magentic.py | 24 +- .../orchestrations/magentic_checkpoint.py | 27 +- .../magentic_human_plan_review.py | 20 +- .../orchestrations/sequential_agents.py | 13 +- .../sequential_custom_executors.py | 13 +- python/samples/04-hosting/a2a/README.md | 2 +- python/samples/README.md | 2 +- .../shared/sample_assets/sample_image.jpg | Bin 0 -> 182161 bytes python/uv.lock | 78 +- workflow-samples/README.md | 4 +- 871 files changed, 7721 insertions(+), 1729 deletions(-) rename {.github/workflows => dotnet/eng/scripts}/New-FilteredSolution.ps1 (90%) rename {.github/workflows => dotnet/eng/scripts}/dotnet-check-coverage.ps1 (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step05_StructuredOutput/Agent_Step05_StructuredOutput.csproj => 01-get-started/01_hello_agent/01_hello_agent.csproj} (80%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step01_Running => 01-get-started/01_hello_agent}/Program.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step04_UsingFunctionToolsWithApprovals/Agent_Step04_UsingFunctionToolsWithApprovals.csproj => 01-get-started/02_add_tools/02_add_tools.csproj} (80%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step03_UsingFunctionTools => 01-get-started/02_add_tools}/Program.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step06_PersistedConversations/Agent_Step06_PersistedConversations.csproj => 01-get-started/03_multi_turn/03_multi_turn.csproj} (80%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step02_MultiturnConversation => 01-get-started/03_multi_turn}/Program.cs (100%) create mode 100644 dotnet/samples/01-get-started/04_memory/04_memory.csproj rename dotnet/samples/{GettingStarted/AgentWithMemory/AgentWithMemory_Step03_CustomMemory => 01-get-started/04_memory}/Program.cs (100%) create mode 100644 dotnet/samples/01-get-started/05_first_workflow/05_first_workflow.csproj rename dotnet/samples/{GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges => 01-get-started/05_first_workflow}/Program.cs (100%) create mode 100644 dotnet/samples/01-get-started/06_host_your_agent/06_host_your_agent.csproj create mode 100644 dotnet/samples/01-get-started/06_host_your_agent/Program.cs rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step01_GettingStarted/Client/Client.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step01_GettingStarted/Client/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step01_GettingStarted/Server/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step01_GettingStarted/Server/Properties/launchSettings.json (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step01_GettingStarted/Server/Server.csproj (100%) rename dotnet/samples/{AgentWebChat/AgentWebChat.AgentHost => 02-agents/AGUI/Step01_GettingStarted/Server}/appsettings.Development.json (100%) rename dotnet/samples/{AgentWebChat/AgentWebChat.AgentHost => 02-agents/AGUI/Step01_GettingStarted/Server}/appsettings.json (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step02_BackendTools/Client/Client.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step02_BackendTools/Client/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step02_BackendTools/Server/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step02_BackendTools/Server/Properties/launchSettings.json (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step02_BackendTools/Server/Server.csproj (100%) rename dotnet/samples/{AgentWebChat/AgentWebChat.AppHost => 02-agents/AGUI/Step02_BackendTools/Server}/appsettings.Development.json (100%) rename dotnet/samples/{AgentWebChat/AgentWebChat.Web => 02-agents/AGUI/Step02_BackendTools/Server}/appsettings.json (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step03_FrontendTools/Client/Client.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step03_FrontendTools/Client/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step03_FrontendTools/Server/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step03_FrontendTools/Server/Properties/launchSettings.json (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step03_FrontendTools/Server/Server.csproj (100%) rename dotnet/samples/{AgentWebChat/AgentWebChat.Web => 02-agents/AGUI/Step03_FrontendTools/Server}/appsettings.Development.json (100%) rename dotnet/samples/{GettingStarted/AGUI/Step01_GettingStarted => 02-agents/AGUI/Step03_FrontendTools}/Server/appsettings.json (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step04_HumanInLoop/Client/Client.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step04_HumanInLoop/Client/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step04_HumanInLoop/Server/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step04_HumanInLoop/Server/Properties/launchSettings.json (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step04_HumanInLoop/Server/Server.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs (100%) rename dotnet/samples/{AGUIClientServer/AGUIDojoServer => 02-agents/AGUI/Step04_HumanInLoop/Server}/appsettings.Development.json (100%) rename dotnet/samples/{GettingStarted/AGUI/Step02_BackendTools => 02-agents/AGUI/Step04_HumanInLoop}/Server/appsettings.json (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step05_StateManagement/Client/Client.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step05_StateManagement/Client/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step05_StateManagement/Client/StatefulAgent.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step05_StateManagement/Server/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step05_StateManagement/Server/Properties/launchSettings.json (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step05_StateManagement/Server/RecipeModels.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step05_StateManagement/Server/Server.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AGUI/Step05_StateManagement/Server/SharedStateAgent.cs (100%) rename dotnet/samples/{GettingStarted/AGUI/Step01_GettingStarted => 02-agents/AGUI/Step05_StateManagement}/Server/appsettings.Development.json (100%) rename dotnet/samples/{GettingStarted/AGUI/Step03_FrontendTools => 02-agents/AGUI/Step05_StateManagement}/Server/appsettings.json (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentOpenTelemetry/AgentOpenTelemetry.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentOpenTelemetry/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentOpenTelemetry/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentOpenTelemetry/start-demo.ps1 (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_A2A/Agent_With_A2A.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_A2A/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_A2A/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_Anthropic/Agent_With_Anthropic.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_Anthropic/Program.cs (97%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_Anthropic/README.md (91%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_AzureAIAgentsPersistent/Agent_With_AzureAIAgentsPersistent.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_AzureAIAgentsPersistent/Program.cs (86%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_AzureAIAgentsPersistent/README.md (77%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_AzureAIProject/Agent_With_AzureAIProject.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_AzureAIProject/Program.cs (90%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_AzureAIProject/README.md (77%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_AzureFoundryModel/Agent_With_AzureFoundryModel.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_AzureFoundryModel/Program.cs (83%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_AzureFoundryModel/README.md (84%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_AzureOpenAIChatCompletion/Agent_With_AzureOpenAIChatCompletion.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_AzureOpenAIChatCompletion/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_AzureOpenAIChatCompletion/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_AzureOpenAIResponses/Agent_With_AzureOpenAIResponses.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_AzureOpenAIResponses/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_AzureOpenAIResponses/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_CustomImplementation/Agent_With_CustomImplementation.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_CustomImplementation/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_CustomImplementation/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_GitHubCopilot/Agent_With_GitHubCopilot.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_GitHubCopilot/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_GitHubCopilot/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_GoogleGemini/Agent_With_GoogleGemini.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_GoogleGemini/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_GoogleGemini/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_ONNX/Agent_With_ONNX.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_ONNX/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_ONNX/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_Ollama/Agent_With_Ollama.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_Ollama/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_Ollama/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_OpenAIAssistants/Agent_With_OpenAIAssistants.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_OpenAIAssistants/Program.cs (95%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_OpenAIAssistants/README.md (84%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_OpenAIChatCompletion/Agent_With_OpenAIChatCompletion.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_OpenAIChatCompletion/Program.cs (87%) rename dotnet/samples/{GettingStarted/AgentProviders/Agent_With_OpenAIResponses => 02-agents/AgentProviders/Agent_With_OpenAIChatCompletion}/README.md (75%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_OpenAIResponses/Agent_With_OpenAIResponses.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/Agent_With_OpenAIResponses/Program.cs (87%) rename dotnet/samples/{GettingStarted/AgentProviders/Agent_With_OpenAIChatCompletion => 02-agents/AgentProviders/Agent_With_OpenAIResponses}/README.md (75%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentProviders/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentSkills/Agent_Step01_BasicSkills/Agent_Step01_BasicSkills.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentSkills/Agent_Step01_BasicSkills/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentSkills/Agent_Step01_BasicSkills/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentSkills/Agent_Step01_BasicSkills/skills/expense-report/SKILL.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentSkills/Agent_Step01_BasicSkills/skills/expense-report/assets/expense-report-template.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentSkills/Agent_Step01_BasicSkills/skills/expense-report/references/POLICY_FAQ.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentSkills/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithAnthropic/Agent_Anthropic_Step01_Running/Agent_Anthropic_Step01_Running.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithAnthropic/Agent_Anthropic_Step01_Running/Program.cs (89%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithAnthropic/Agent_Anthropic_Step01_Running/README.md (87%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/Agent_Anthropic_Step02_Reasoning.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/Program.cs (95%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/README.md (90%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/Agent_Anthropic_Step03_UsingFunctionTools.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/Program.cs (94%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/README.md (91%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/Agent_Anthropic_Step04_UsingSkills.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/Program.cs (97%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/README.md (95%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithAnthropic/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/AgentWithMemory_Step01_ChatHistoryMemory.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/AgentWithMemory_Step02_MemoryUsingMem0.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/Program.cs (96%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/AgentWithMemory_Step04_MemoryUsingFoundry.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/Program.cs (89%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/README.md (88%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithMemory/README.md (80%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Agent_OpenAI_Step01_Running.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Program.cs (92%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Agent_OpenAI_Step02_Reasoning.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Program.cs (95%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/Agent_OpenAI_Step03_CreateFromChatClient.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/OpenAIChatClientAgent.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/Program.cs (93%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/README.md (94%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/OpenAIResponseClientAgent.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Program.cs (93%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/README.md (95%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Agent_OpenAI_Step05_Conversation.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Program.cs (98%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/README.md (98%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithOpenAI/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/AgentWithRAG_Step01_BasicTextRAG.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchDocument.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchStore.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchStoreOptions.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchStoreUpsertOptions.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/AgentWithRAG_Step02_CustomVectorStoreRAG.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/AgentWithRAG_Step03_CustomRAGDataSource.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/AgentWithRAG_Step04_FoundryServiceRAG.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/Program.cs (90%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/contoso-outdoors-knowledge-base.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/AgentWithRAG/README.md (100%) rename dotnet/samples/{GettingStarted/AgentWithMemory/AgentWithMemory_Step03_CustomMemory/AgentWithMemory_Step03_CustomMemory.csproj => 02-agents/Agents/Agent_Step01_UsingFunctionToolsWithApprovals/Agent_Step01_UsingFunctionToolsWithApprovals.csproj} (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step04_UsingFunctionToolsWithApprovals => 02-agents/Agents/Agent_Step01_UsingFunctionToolsWithApprovals}/Program.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step05_StructuredOutput => 02-agents/Agents/Agent_Step02_StructuredOutput}/AIAgentBuilderExtensions.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step01_Running/Agent_Step01_Running.csproj => 02-agents/Agents/Agent_Step02_StructuredOutput/Agent_Step02_StructuredOutput.csproj} (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step05_StructuredOutput => 02-agents/Agents/Agent_Step02_StructuredOutput}/Program.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step05_StructuredOutput => 02-agents/Agents/Agent_Step02_StructuredOutput}/README.md (97%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step05_StructuredOutput => 02-agents/Agents/Agent_Step02_StructuredOutput}/StructuredOutputAgent.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step05_StructuredOutput => 02-agents/Agents/Agent_Step02_StructuredOutput}/StructuredOutputAgentOptions.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step05_StructuredOutput => 02-agents/Agents/Agent_Step02_StructuredOutput}/StructuredOutputAgentResponse.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step02_MultiturnConversation/Agent_Step02_MultiturnConversation.csproj => 02-agents/Agents/Agent_Step03_PersistedConversations/Agent_Step03_PersistedConversations.csproj} (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step06_PersistedConversations => 02-agents/Agents/Agent_Step03_PersistedConversations}/Program.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step07_3rdPartyChatHistoryStorage/Agent_Step07_3rdPartyChatHistoryStorage.csproj => 02-agents/Agents/Agent_Step04_3rdPartyChatHistoryStorage/Agent_Step04_3rdPartyChatHistoryStorage.csproj} (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step07_3rdPartyChatHistoryStorage => 02-agents/Agents/Agent_Step04_3rdPartyChatHistoryStorage}/Program.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step08_Observability/Agent_Step08_Observability.csproj => 02-agents/Agents/Agent_Step05_Observability/Agent_Step05_Observability.csproj} (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step08_Observability => 02-agents/Agents/Agent_Step05_Observability}/Program.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step09_DependencyInjection/Agent_Step09_DependencyInjection.csproj => 02-agents/Agents/Agent_Step06_DependencyInjection/Agent_Step06_DependencyInjection.csproj} (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step09_DependencyInjection => 02-agents/Agents/Agent_Step06_DependencyInjection}/Program.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step10_AsMcpTool/Agent_Step10_AsMcpTool.csproj => 02-agents/Agents/Agent_Step07_AsMcpTool/Agent_Step07_AsMcpTool.csproj} (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step10_AsMcpTool => 02-agents/Agents/Agent_Step07_AsMcpTool}/Program.cs (85%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step10_AsMcpTool => 02-agents/Agents/Agent_Step07_AsMcpTool}/README.md (85%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step11_UsingImages/Agent_Step11_UsingImages.csproj => 02-agents/Agents/Agent_Step08_UsingImages/Agent_Step08_UsingImages.csproj} (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step11_UsingImages => 02-agents/Agents/Agent_Step08_UsingImages}/Program.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step11_UsingImages => 02-agents/Agents/Agent_Step08_UsingImages}/README.md (98%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step12_AsFunctionTool/Agent_Step12_AsFunctionTool.csproj => 02-agents/Agents/Agent_Step09_AsFunctionTool/Agent_Step09_AsFunctionTool.csproj} (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step12_AsFunctionTool => 02-agents/Agents/Agent_Step09_AsFunctionTool}/Program.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step13_BackgroundResponsesWithToolsAndPersistence/Agent_Step13_BackgroundResponsesWithToolsAndPersistence.csproj => 02-agents/Agents/Agent_Step10_BackgroundResponsesWithToolsAndPersistence/Agent_Step10_BackgroundResponsesWithToolsAndPersistence.csproj} (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step13_BackgroundResponsesWithToolsAndPersistence => 02-agents/Agents/Agent_Step10_BackgroundResponsesWithToolsAndPersistence}/Program.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step13_BackgroundResponsesWithToolsAndPersistence => 02-agents/Agents/Agent_Step10_BackgroundResponsesWithToolsAndPersistence}/README.md (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step14_Middleware/Agent_Step14_Middleware.csproj => 02-agents/Agents/Agent_Step11_Middleware/Agent_Step11_Middleware.csproj} (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step14_Middleware => 02-agents/Agents/Agent_Step11_Middleware}/Program.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step14_Middleware => 02-agents/Agents/Agent_Step11_Middleware}/README.md (95%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step15_Plugins/Agent_Step15_Plugins.csproj => 02-agents/Agents/Agent_Step12_Plugins/Agent_Step12_Plugins.csproj} (92%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step15_Plugins => 02-agents/Agents/Agent_Step12_Plugins}/Program.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step03_UsingFunctionTools/Agent_Step03_UsingFunctionTools.csproj => 02-agents/Agents/Agent_Step13_ChatReduction/Agent_Step13_ChatReduction.csproj} (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step16_ChatReduction => 02-agents/Agents/Agent_Step13_ChatReduction}/Program.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step17_BackgroundResponses/Agent_Step17_BackgroundResponses.csproj => 02-agents/Agents/Agent_Step14_BackgroundResponses/Agent_Step14_BackgroundResponses.csproj} (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step17_BackgroundResponses => 02-agents/Agents/Agent_Step14_BackgroundResponses}/Program.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step17_BackgroundResponses => 02-agents/Agents/Agent_Step14_BackgroundResponses}/README.md (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step18_DeepResearch/Agent_Step18_DeepResearch.csproj => 02-agents/Agents/Agent_Step15_DeepResearch/Agent_Step15_DeepResearch.csproj} (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step18_DeepResearch => 02-agents/Agents/Agent_Step15_DeepResearch}/Program.cs (82%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step18_DeepResearch => 02-agents/Agents/Agent_Step15_DeepResearch}/README.md (85%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step19_Declarative/Agent_Step19_Declarative.csproj => 02-agents/Agents/Agent_Step16_Declarative/Agent_Step16_Declarative.csproj} (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step19_Declarative => 02-agents/Agents/Agent_Step16_Declarative}/Program.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step20_AdditionalAIContext/Agent_Step20_AdditionalAIContext.csproj => 02-agents/Agents/Agent_Step17_AdditionalAIContext/Agent_Step17_AdditionalAIContext.csproj} (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step20_AdditionalAIContext => 02-agents/Agents/Agent_Step17_AdditionalAIContext}/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/Agents/README.md (75%) rename dotnet/samples/{GettingStarted => 02-agents}/DeclarativeAgents/ChatClient/DeclarativeChatClientAgents.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/DeclarativeAgents/ChatClient/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/DeclarativeAgents/ChatClient/Properties/launchSettings.json (100%) rename dotnet/samples/{GettingStarted => 02-agents}/DevUI/DevUI_Step01_BasicUsage/DevUI_Step01_BasicUsage.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/DevUI/DevUI_Step01_BasicUsage/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/DevUI/DevUI_Step01_BasicUsage/Properties/launchSettings.json (100%) rename dotnet/samples/{GettingStarted => 02-agents}/DevUI/DevUI_Step01_BasicUsage/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/DevUI/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/FoundryAgents_Evaluations_Step01_RedTeaming.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/Program.cs (94%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/README.md (92%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/FoundryAgents_Evaluations_Step02_SelfReflection.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/Program.cs (98%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/README.md (94%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step01.1_Basics/FoundryAgents_Step01.1_Basics.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step01.1_Basics/Program.cs (91%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step01.1_Basics/README.md (82%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step01.2_Running/FoundryAgents_Step01.2_Running.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step01.2_Running/Program.cs (89%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step01.2_Running/README.md (81%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/FoundryAgents_Step02_MultiturnConversation.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/Program.cs (92%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/README.md (88%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/FoundryAgents_Step03_UsingFunctionTools.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/Program.cs (92%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/README.md (83%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/FoundryAgents_Step04_UsingFunctionToolsWithApprovals.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/Program.cs (93%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/README.md (85%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step05_StructuredOutput/FoundryAgents_Step05_StructuredOutput.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step05_StructuredOutput/Program.cs (94%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step05_StructuredOutput/README.md (83%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step06_PersistedConversations/FoundryAgents_Step06_PersistedConversations.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step06_PersistedConversations/Program.cs (91%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step06_PersistedConversations/README.md (83%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step07_Observability/FoundryAgents_Step07_Observability.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step07_Observability/Program.cs (91%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step07_Observability/README.md (85%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step08_DependencyInjection/FoundryAgents_Step08_DependencyInjection.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step08_DependencyInjection/Program.cs (94%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step08_DependencyInjection/README.md (84%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/FoundryAgents_Step09_UsingMcpClientAsTools.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/Program.cs (90%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/README.md (85%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step10_UsingImages/Assets/walkway.jpg (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step10_UsingImages/FoundryAgents_Step10_UsingImages.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step10_UsingImages/Program.cs (87%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step10_UsingImages/README.md (84%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/FoundryAgents_Step11_AsFunctionTool.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/Program.cs (90%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/README.md (83%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step12_Middleware/FoundryAgents_Step12_Middleware.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step12_Middleware/Program.cs (97%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step12_Middleware/README.md (89%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step13_Plugins/FoundryAgents_Step13_Plugins.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step13_Plugins/Program.cs (96%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step13_Plugins/README.md (83%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/FoundryAgents_Step14_CodeInterpreter.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/Program.cs (94%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/README.md (86%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_browser_search.png (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_search_results.png (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_search_typed.png (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step15_ComputerUse/ComputerUseUtil.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step15_ComputerUse/FoundryAgents_Step15_ComputerUse.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step15_ComputerUse/Program.cs (97%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/FoundryAgents_Step15_ComputerUse/README.md (89%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch/FoundryAgents_Step18_FileSearch.csproj => 02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/FoundryAgents_Step16_FileSearch.csproj} (100%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch => 02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch}/Program.cs (95%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch => 02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch}/README.md (83%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step19_OpenAPITools/FoundryAgents_Step19_OpenAPITools.csproj => 02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/FoundryAgents_Step17_OpenAPITools.csproj} (100%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step19_OpenAPITools => 02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools}/Program.cs (94%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step19_OpenAPITools => 02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools}/README.md (79%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step21_BingCustomSearch/FoundryAgents_Step21_BingCustomSearch.csproj => 02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/FoundryAgents_Step18_BingCustomSearch.csproj} (100%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step21_BingCustomSearch => 02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch}/Program.cs (82%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step21_BingCustomSearch => 02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch}/README.md (95%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step22_SharePoint/FoundryAgents_Step22_SharePoint.csproj => 02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/FoundryAgents_Step19_SharePoint.csproj} (100%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step22_SharePoint => 02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint}/Program.cs (100%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step22_SharePoint => 02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint}/README.md (96%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step25_WebSearch/FoundryAgents_Step25_WebSearch.csproj => 02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/FoundryAgents_Step20_MicrosoftFabric.csproj} (100%) create mode 100644 dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/Program.cs create mode 100644 dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/README.md create mode 100644 dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/FoundryAgents_Step21_WebSearch.csproj rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step25_WebSearch => 02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch}/Program.cs (91%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step25_WebSearch => 02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch}/README.md (95%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step26_MemorySearch/FoundryAgents_Step26_MemorySearch.csproj => 02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/FoundryAgents_Step22_MemorySearch.csproj} (100%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step26_MemorySearch => 02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch}/Program.cs (93%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step26_MemorySearch => 02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch}/README.md (97%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step27_LocalMCP/FoundryAgents_Step27_LocalMCP.csproj => 02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP/FoundryAgents_Step23_LocalMCP.csproj} (100%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step27_LocalMCP => 02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP}/Program.cs (94%) rename dotnet/samples/{GettingStarted/FoundryAgents/FoundryAgents_Step27_LocalMCP => 02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP}/README.md (83%) rename dotnet/samples/{GettingStarted => 02-agents}/FoundryAgents/README.md (89%) rename dotnet/samples/{GettingStarted => 02-agents}/ModelContextProtocol/Agent_MCP_Server/Agent_MCP_Server.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/ModelContextProtocol/Agent_MCP_Server/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/ModelContextProtocol/Agent_MCP_Server/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/ModelContextProtocol/Agent_MCP_Server_Auth/Agent_MCP_Server_Auth.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/ModelContextProtocol/Agent_MCP_Server_Auth/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/ModelContextProtocol/Agent_MCP_Server_Auth/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/ModelContextProtocol/FoundryAgent_Hosted_MCP/FoundryAgent_Hosted_MCP.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs (94%) rename dotnet/samples/{GettingStarted => 02-agents}/ModelContextProtocol/FoundryAgent_Hosted_MCP/README.md (69%) rename dotnet/samples/{GettingStarted => 02-agents}/ModelContextProtocol/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs (100%) rename dotnet/samples/{GettingStarted => 02-agents}/ModelContextProtocol/ResponseAgent_Hosted_MCP/README.md (100%) rename dotnet/samples/{GettingStarted => 02-agents}/ModelContextProtocol/ResponseAgent_Hosted_MCP/ResponseAgent_Hosted_MCP.csproj (100%) rename dotnet/samples/{GettingStarted => 02-agents}/README.md (74%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Agents/CustomAgentExecutors/CustomAgentExecutors.csproj (61%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Agents/CustomAgentExecutors/Program.cs (100%) create mode 100644 dotnet/samples/03-workflows/Agents/FoundryAgent/FoundryAgent.csproj rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Agents/FoundryAgent/Program.cs (95%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Agents/GroupChatToolApproval/DeploymentGroupChatManager.cs (100%) create mode 100644 dotnet/samples/03-workflows/Agents/GroupChatToolApproval/GroupChatToolApproval.csproj rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Agents/GroupChatToolApproval/Program.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Agents/GroupChatToolApproval/README.md (91%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Agents/WorkflowAsAnAgent/Program.cs (100%) create mode 100644 dotnet/samples/03-workflows/Agents/WorkflowAsAnAgent/WorkflowAsAnAgent.csproj rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Agents/WorkflowAsAnAgent/WorkflowFactory.cs (100%) rename dotnet/samples/{GettingStarted/Workflows/Loop/Loop.csproj => 03-workflows/Checkpoint/CheckpointAndRehydrate/CheckpointAndRehydrate.csproj} (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Checkpoint/CheckpointAndRehydrate/Program.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Checkpoint/CheckpointAndRehydrate/WorkflowFactory.cs (100%) rename dotnet/samples/{GettingStarted/Workflows/Visualization/Visualization.csproj => 03-workflows/Checkpoint/CheckpointAndResume/CheckpointAndResume.csproj} (75%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Checkpoint/CheckpointAndResume/Program.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Checkpoint/CheckpointAndResume/WorkflowFactory.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Checkpoint/CheckpointWithHumanInTheLoop/CheckpointWithHumanInTheLoop.csproj (69%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Checkpoint/CheckpointWithHumanInTheLoop/Program.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Checkpoint/CheckpointWithHumanInTheLoop/WorkflowFactory.cs (100%) create mode 100644 dotnet/samples/03-workflows/Concurrent/Concurrent/Concurrent.csproj rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Concurrent/Concurrent/Program.cs (100%) rename dotnet/samples/{GettingStarted/Agents/Agent_Step16_ChatReduction/Agent_Step16_ChatReduction.csproj => 03-workflows/Concurrent/MapReduce/MapReduce.csproj} (82%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Concurrent/MapReduce/Program.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/ConditionalEdges/01_EdgeCondition/01_EdgeCondition.csproj (74%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/ConditionalEdges/01_EdgeCondition/Program.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/ConditionalEdges/01_EdgeCondition/Resources.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/ConditionalEdges/02_SwitchCase/02_SwitchCase.csproj (74%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/ConditionalEdges/02_SwitchCase/Program.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/ConditionalEdges/02_SwitchCase/Resources.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/ConditionalEdges/03_MultiSelection/03_MultiSelection.csproj (74%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/ConditionalEdges/03_MultiSelection/Program.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/ConditionalEdges/03_MultiSelection/Resources.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/ConfirmInput/ConfirmInput.csproj (78%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/ConfirmInput/ConfirmInput.yaml (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/ConfirmInput/Program.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/CustomerSupport/CustomerSupport.csproj (73%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/CustomerSupport/Program.cs (99%) create mode 100644 dotnet/samples/03-workflows/Declarative/CustomerSupport/Properties/launchSettings.json rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/CustomerSupport/TicketingPlugin.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/DeepResearch/DeepResearch.csproj (74%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/DeepResearch/Program.cs (99%) create mode 100644 dotnet/samples/03-workflows/Declarative/DeepResearch/Properties/launchSettings.json rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/DeepResearch/wttr.json (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/ExecuteCode/ExecuteCode.csproj (77%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/ExecuteCode/Generated.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/ExecuteCode/Program.cs (98%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/ExecuteWorkflow/ExecuteWorkflow.csproj (78%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/ExecuteWorkflow/Program.cs (98%) create mode 100644 dotnet/samples/03-workflows/Declarative/ExecuteWorkflow/Properties/launchSettings.json rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/FunctionTools/FunctionTools.csproj (78%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/FunctionTools/FunctionTools.yaml (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/FunctionTools/MenuPlugin.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/FunctionTools/Program.cs (99%) create mode 100644 dotnet/samples/03-workflows/Declarative/FunctionTools/Properties/launchSettings.json rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/GenerateCode/GenerateCode.csproj (84%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/GenerateCode/Program.cs (100%) create mode 100644 dotnet/samples/03-workflows/Declarative/GenerateCode/Properties/launchSettings.json rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/HostedWorkflow/HostedWorkflow.csproj (74%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/HostedWorkflow/Program.cs (98%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/InputArguments/InputArguments.csproj (78%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/InputArguments/InputArguments.yaml (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/InputArguments/Program.cs (99%) create mode 100644 dotnet/samples/03-workflows/Declarative/InputArguments/Properties/launchSettings.json rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/InvokeFunctionTool/InvokeFunctionTool.csproj (80%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/InvokeFunctionTool/InvokeFunctionTool.yaml (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/InvokeFunctionTool/MenuPlugin.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/InvokeFunctionTool/Program.cs (99%) create mode 100644 dotnet/samples/03-workflows/Declarative/InvokeMcpTool/InvokeMcpTool.csproj create mode 100644 dotnet/samples/03-workflows/Declarative/InvokeMcpTool/InvokeMcpTool.yaml create mode 100644 dotnet/samples/03-workflows/Declarative/InvokeMcpTool/Program.cs rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/Marketing/Marketing.csproj (73%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/Marketing/Program.cs (99%) create mode 100644 dotnet/samples/03-workflows/Declarative/Marketing/Properties/launchSettings.json create mode 100644 dotnet/samples/03-workflows/Declarative/OpenAIChatAgent/Properties/launchSettings.json create mode 100644 dotnet/samples/03-workflows/Declarative/OpenAIResponseAgent/Properties/launchSettings.json rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/README.md (73%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/StudentTeacher/Program.cs (99%) create mode 100644 dotnet/samples/03-workflows/Declarative/StudentTeacher/Properties/launchSettings.json rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/StudentTeacher/StudentTeacher.csproj (73%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/ToolApproval/Program.cs (99%) create mode 100644 dotnet/samples/03-workflows/Declarative/ToolApproval/Properties/launchSettings.json rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/ToolApproval/ToolApproval.csproj (78%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Declarative/ToolApproval/ToolApproval.yaml (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/HumanInTheLoop/HumanInTheLoopBasic/HumanInTheLoopBasic.csproj (69%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/HumanInTheLoop/HumanInTheLoopBasic/Program.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/HumanInTheLoop/HumanInTheLoopBasic/WorkflowFactory.cs (100%) rename dotnet/samples/{GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/CheckpointAndRehydrate.csproj => 03-workflows/Loop/Loop.csproj} (69%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Loop/Program.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Observability/ApplicationInsights/ApplicationInsights.csproj (83%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Observability/ApplicationInsights/Program.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Observability/AspireDashboard/AspireDashboard.csproj (84%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Observability/AspireDashboard/Program.cs (98%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Observability/WorkflowAsAnAgent/Program.cs (99%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Observability/WorkflowAsAnAgent/WorkflowAsAnAgentObservability.csproj (72%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Observability/WorkflowAsAnAgent/WorkflowHelper.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/README.md (68%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Resources/Lorem_Ipsum.txt (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Resources/ambiguous_email.txt (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Resources/email.txt (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Resources/spam.txt (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/SharedStates/Program.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/SharedStates/Resources.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/SharedStates/SharedStates.csproj (79%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Visualization/Program.cs (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Visualization/README.md (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Visualization/Resources/graphviz_render.png (100%) rename dotnet/samples/{GettingStarted/Workflows => 03-workflows}/Visualization/Resources/mermaid_render.png (100%) create mode 100644 dotnet/samples/03-workflows/Visualization/Visualization.csproj rename dotnet/samples/{GettingStarted/Workflows/Checkpoint/CheckpointAndResume/CheckpointAndResume.csproj => 03-workflows/_StartHere/01_Streaming/01_Streaming.csproj} (69%) rename dotnet/samples/{GettingStarted/Workflows/_Foundational/02_Streaming => 03-workflows/_StartHere/01_Streaming}/Program.cs (100%) create mode 100644 dotnet/samples/03-workflows/_StartHere/02_AgentsInWorkflows/02_AgentsInWorkflows.csproj rename dotnet/samples/{GettingStarted/Workflows/_Foundational/03_AgentsInWorkflows => 03-workflows/_StartHere/02_AgentsInWorkflows}/Program.cs (100%) create mode 100644 dotnet/samples/03-workflows/_StartHere/03_AgentWorkflowPatterns/03_AgentWorkflowPatterns.csproj rename dotnet/samples/{GettingStarted/Workflows/_Foundational/04_AgentWorkflowPatterns => 03-workflows/_StartHere/03_AgentWorkflowPatterns}/Program.cs (100%) rename dotnet/samples/{GettingStarted/Workflows/_Foundational/05_MultiModelService/05_MultiModelService.csproj => 03-workflows/_StartHere/04_MultiModelService/04_MultiModelService.csproj} (68%) rename dotnet/samples/{GettingStarted/Workflows/_Foundational/05_MultiModelService => 03-workflows/_StartHere/04_MultiModelService}/Program.cs (95%) create mode 100644 dotnet/samples/03-workflows/_StartHere/05_SubWorkflows/05_SubWorkflows.csproj rename dotnet/samples/{GettingStarted/Workflows/_Foundational/06_SubWorkflows => 03-workflows/_StartHere/05_SubWorkflows}/Program.cs (100%) create mode 100644 dotnet/samples/03-workflows/_StartHere/06_MixedWorkflowAgentsAndExecutors/06_MixedWorkflowAgentsAndExecutors.csproj rename dotnet/samples/{GettingStarted/Workflows/_Foundational/07_MixedWorkflowAgentsAndExecutors => 03-workflows/_StartHere/06_MixedWorkflowAgentsAndExecutors}/Program.cs (100%) rename dotnet/samples/{GettingStarted/Workflows/_Foundational/07_MixedWorkflowAgentsAndExecutors => 03-workflows/_StartHere/06_MixedWorkflowAgentsAndExecutors}/README.md (99%) rename dotnet/samples/{GettingStarted/Workflows/_Foundational/08_WriterCriticWorkflow/08_WriterCriticWorkflow.csproj => 03-workflows/_StartHere/07_WriterCriticWorkflow/07_WriterCriticWorkflow.csproj} (61%) rename dotnet/samples/{GettingStarted/Workflows/_Foundational/08_WriterCriticWorkflow => 03-workflows/_StartHere/07_WriterCriticWorkflow}/Program.cs (100%) rename dotnet/samples/{GettingStarted => 04-hosting}/A2A/A2AAgent_AsFunctionTools/A2AAgent_AsFunctionTools.csproj (100%) rename dotnet/samples/{GettingStarted => 04-hosting}/A2A/A2AAgent_AsFunctionTools/Program.cs (100%) rename dotnet/samples/{GettingStarted => 04-hosting}/A2A/A2AAgent_AsFunctionTools/README.md (100%) rename dotnet/samples/{GettingStarted => 04-hosting}/A2A/A2AAgent_PollingForTaskCompletion/A2AAgent_PollingForTaskCompletion.csproj (100%) rename dotnet/samples/{GettingStarted => 04-hosting}/A2A/A2AAgent_PollingForTaskCompletion/Program.cs (100%) rename dotnet/samples/{GettingStarted => 04-hosting}/A2A/A2AAgent_PollingForTaskCompletion/README.md (100%) rename dotnet/samples/{GettingStarted => 04-hosting}/A2A/README.md (95%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/.editorconfig (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/01_SingleAgent/01_SingleAgent.csproj (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/01_SingleAgent/Program.cs (97%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/01_SingleAgent/README.md (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/01_SingleAgent/demo.http (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/01_SingleAgent/host.json (100%) rename dotnet/samples/{Durable/Agents/AzureFunctions/04_AgentOrchestration_Conditionals => 04-hosting/DurableAgents/AzureFunctions/01_SingleAgent}/local.settings.json (82%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/02_AgentOrchestration_Chaining/02_AgentOrchestration_Chaining.csproj (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/02_AgentOrchestration_Chaining/FunctionTriggers.cs (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/02_AgentOrchestration_Chaining/Program.cs (96%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/02_AgentOrchestration_Chaining/README.md (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/02_AgentOrchestration_Chaining/demo.http (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/02_AgentOrchestration_Chaining/host.json (100%) rename dotnet/samples/{Durable/Agents/AzureFunctions/01_SingleAgent => 04-hosting/DurableAgents/AzureFunctions/02_AgentOrchestration_Chaining}/local.settings.json (82%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/03_AgentOrchestration_Concurrency/03_AgentOrchestration_Concurrency.csproj (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/03_AgentOrchestration_Concurrency/FunctionTriggers.cs (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/03_AgentOrchestration_Concurrency/Program.cs (96%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/03_AgentOrchestration_Concurrency/README.md (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/03_AgentOrchestration_Concurrency/demo.http (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/03_AgentOrchestration_Concurrency/host.json (100%) rename dotnet/samples/{Durable/Agents/AzureFunctions/02_AgentOrchestration_Chaining => 04-hosting/DurableAgents/AzureFunctions/03_AgentOrchestration_Concurrency}/local.settings.json (82%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/04_AgentOrchestration_Conditionals/04_AgentOrchestration_Conditionals.csproj (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/04_AgentOrchestration_Conditionals/FunctionTriggers.cs (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/04_AgentOrchestration_Conditionals/Models.cs (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/04_AgentOrchestration_Conditionals/Program.cs (96%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/04_AgentOrchestration_Conditionals/README.md (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/04_AgentOrchestration_Conditionals/demo.http (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/04_AgentOrchestration_Conditionals/host.json (100%) rename dotnet/samples/{Durable/Agents/AzureFunctions/03_AgentOrchestration_Concurrency => 04-hosting/DurableAgents/AzureFunctions/04_AgentOrchestration_Conditionals}/local.settings.json (82%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/05_AgentOrchestration_HITL/05_AgentOrchestration_HITL.csproj (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/05_AgentOrchestration_HITL/FunctionTriggers.cs (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/05_AgentOrchestration_HITL/Models.cs (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/05_AgentOrchestration_HITL/Program.cs (96%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/05_AgentOrchestration_HITL/README.md (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/05_AgentOrchestration_HITL/demo.http (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/05_AgentOrchestration_HITL/host.json (100%) create mode 100644 dotnet/samples/04-hosting/DurableAgents/AzureFunctions/05_AgentOrchestration_HITL/local.settings.json rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/06_LongRunningTools/06_LongRunningTools.csproj (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/06_LongRunningTools/FunctionTriggers.cs (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/06_LongRunningTools/Models.cs (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/06_LongRunningTools/Program.cs (97%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/06_LongRunningTools/README.md (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/06_LongRunningTools/Tools.cs (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/06_LongRunningTools/demo.http (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/06_LongRunningTools/host.json (100%) create mode 100644 dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/local.settings.json rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/07_AgentAsMcpTool/07_AgentAsMcpTool.csproj (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/07_AgentAsMcpTool/Program.cs (97%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/07_AgentAsMcpTool/README.md (94%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/07_AgentAsMcpTool/host.json (100%) create mode 100644 dotnet/samples/04-hosting/DurableAgents/AzureFunctions/07_AgentAsMcpTool/local.settings.json rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/08_ReliableStreaming/08_ReliableStreaming.csproj (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/08_ReliableStreaming/FunctionTriggers.cs (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/08_ReliableStreaming/Program.cs (98%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/08_ReliableStreaming/README.md (98%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/08_ReliableStreaming/RedisStreamResponseHandler.cs (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/08_ReliableStreaming/Tools.cs (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/08_ReliableStreaming/host.json (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/08_ReliableStreaming/local.settings.json (85%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/AzureFunctions/README.md (94%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/01_SingleAgent/01_SingleAgent.csproj (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/01_SingleAgent/Program.cs (98%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/01_SingleAgent/README.md (96%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/02_AgentOrchestration_Chaining/02_AgentOrchestration_Chaining.csproj (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/02_AgentOrchestration_Chaining/Models.cs (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/02_AgentOrchestration_Chaining/Program.cs (98%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/02_AgentOrchestration_Chaining/README.md (96%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/03_AgentOrchestration_Concurrency/03_AgentOrchestration_Concurrency.csproj (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/03_AgentOrchestration_Concurrency/Models.cs (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/03_AgentOrchestration_Concurrency/Program.cs (99%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/03_AgentOrchestration_Concurrency/README.md (96%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/04_AgentOrchestration_Conditionals/04_AgentOrchestration_Conditionals.csproj (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/04_AgentOrchestration_Conditionals/Models.cs (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/04_AgentOrchestration_Conditionals/Program.cs (99%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/04_AgentOrchestration_Conditionals/README.md (97%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/05_AgentOrchestration_HITL/05_AgentOrchestration_HITL.csproj (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/05_AgentOrchestration_HITL/Models.cs (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/05_AgentOrchestration_HITL/Program.cs (99%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/05_AgentOrchestration_HITL/README.md (96%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/06_LongRunningTools/06_LongRunningTools.csproj (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/06_LongRunningTools/Models.cs (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/06_LongRunningTools/Program.cs (99%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/06_LongRunningTools/README.md (98%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/07_ReliableStreaming/07_ReliableStreaming.csproj (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/07_ReliableStreaming/Program.cs (99%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/07_ReliableStreaming/README.md (97%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/07_ReliableStreaming/RedisStreamResponseHandler.cs (100%) rename dotnet/samples/{Durable/Agents => 04-hosting/DurableAgents}/ConsoleApps/README.md (95%) rename dotnet/samples/{Durable => 04-hosting/DurableAgents}/Directory.Build.props (80%) rename dotnet/samples/{ => 05-end-to-end}/A2AClientServer/A2AClient/A2AClient.csproj (60%) rename dotnet/samples/{ => 05-end-to-end}/A2AClientServer/A2AClient/HostClientAgent.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/A2AClientServer/A2AClient/Program.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/A2AClientServer/A2AClient/README.md (85%) rename dotnet/samples/{ => 05-end-to-end}/A2AClientServer/A2AServer/A2AServer.csproj (54%) rename dotnet/samples/{ => 05-end-to-end}/A2AClientServer/A2AServer/A2AServer.http (100%) rename dotnet/samples/{ => 05-end-to-end}/A2AClientServer/A2AServer/HostAgentFactory.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/A2AClientServer/A2AServer/Models/InvoiceQuery.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/A2AClientServer/A2AServer/Program.cs (96%) rename dotnet/samples/{ => 05-end-to-end}/A2AClientServer/README.md (98%) rename dotnet/samples/{ => 05-end-to-end}/A2AClientServer/a2a-inspector-agent-card.png (100%) rename dotnet/samples/{ => 05-end-to-end}/A2AClientServer/a2a-inspector-raw-json-response.png (100%) rename dotnet/samples/{ => 05-end-to-end}/A2AClientServer/a2a-inspector-send-message.png (100%) rename dotnet/samples/{ => 05-end-to-end}/A2AClientServer/demo-architecture.png (100%) rename dotnet/samples/{ => 05-end-to-end}/A2AClientServer/rest-client-agent-card.png (100%) rename dotnet/samples/{ => 05-end-to-end}/A2AClientServer/rest-client-send-message.png (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIClient/AGUIClient.csproj (70%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIClient/AGUIClientSerializerContext.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIClient/Program.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIClient/README.md (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIClient/SensorRequest.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIClient/SensorResponse.cs (100%) create mode 100644 dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/AGUIDojoServer.csproj rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIDojoServer/AGUIDojoServerSerializerContext.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIDojoServer/AgenticUI/AgenticPlanningTools.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIDojoServer/AgenticUI/AgenticUIAgent.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIDojoServer/AgenticUI/JsonPatchOperation.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIDojoServer/AgenticUI/Plan.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIDojoServer/AgenticUI/Step.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIDojoServer/AgenticUI/StepStatus.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIDojoServer/BackendToolRendering/WeatherInfo.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIDojoServer/ChatClientAgentFactory.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIDojoServer/PredictiveStateUpdates/DocumentState.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIDojoServer/PredictiveStateUpdates/PredictiveStateUpdatesAgent.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIDojoServer/Program.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIDojoServer/Properties/launchSettings.json (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIDojoServer/SharedState/Ingredient.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIDojoServer/SharedState/Recipe.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIDojoServer/SharedState/RecipeResponse.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIDojoServer/SharedState/SharedStateAgent.cs (100%) rename dotnet/samples/{GettingStarted/AGUI/Step04_HumanInLoop/Server => 05-end-to-end/AGUIClientServer/AGUIDojoServer}/appsettings.Development.json (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIDojoServer/appsettings.json (100%) create mode 100644 dotnet/samples/05-end-to-end/AGUIClientServer/AGUIServer/AGUIServer.csproj rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIServer/AGUIServer.http (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIServer/AGUIServerSerializerContext.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIServer/Program.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIServer/Properties/launchSettings.json (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIServer/ServerWeatherForecastRequest.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/AGUIServer/ServerWeatherForecastResponse.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIClientServer/README.md (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/AGUIWebChatClient.csproj (75%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/App.razor (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Layout/LoadingSpinner.razor (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Layout/LoadingSpinner.razor.css (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Layout/MainLayout.razor (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Layout/MainLayout.razor.css (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Pages/Chat/Chat.razor (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Pages/Chat/Chat.razor.css (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Pages/Chat/ChatCitation.razor (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Pages/Chat/ChatCitation.razor.css (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Pages/Chat/ChatHeader.razor (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Pages/Chat/ChatHeader.razor.css (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Pages/Chat/ChatInput.razor (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Pages/Chat/ChatInput.razor.css (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Pages/Chat/ChatInput.razor.js (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageItem.razor (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageItem.razor.css (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageList.razor (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageList.razor.css (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageList.razor.js (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Pages/Chat/ChatSuggestions.razor (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Pages/Chat/ChatSuggestions.razor.css (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/Routes.razor (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Components/_Imports.razor (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Program.cs (91%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/Properties/launchSettings.json (86%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/wwwroot/app.css (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Client/wwwroot/favicon.png (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/README.md (94%) create mode 100644 dotnet/samples/05-end-to-end/AGUIWebChat/Server/AGUIWebChatServer.csproj rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Server/Program.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AGUIWebChat/Server/Properties/launchSettings.json (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.AgentHost/ActorFrameworkWebApplicationExtensions.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.AgentHost/AgentWebChat.AgentHost.csproj (55%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.AgentHost/Custom/CustomAITools.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.AgentHost/Program.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.AgentHost/Properties/launchSettings.json (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientConnectionInfo.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientExtensions.cs (100%) rename dotnet/samples/{GettingStarted/AGUI/Step02_BackendTools/Server => 05-end-to-end/AgentWebChat/AgentWebChat.AgentHost}/appsettings.Development.json (100%) rename dotnet/samples/{GettingStarted/AGUI/Step04_HumanInLoop/Server => 05-end-to-end/AgentWebChat/AgentWebChat.AgentHost}/appsettings.json (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.AppHost/AgentWebChat.AppHost.csproj (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.AppHost/ModelExtensions.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.AppHost/Program.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.AppHost/Properties/launchSettings.json (100%) rename dotnet/samples/{GettingStarted/AGUI/Step03_FrontendTools/Server => 05-end-to-end/AgentWebChat/AgentWebChat.AppHost}/appsettings.Development.json (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.AppHost/appsettings.json (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.ServiceDefaults/AgentWebChat.ServiceDefaults.csproj (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.ServiceDefaults/ServiceDefaultsExtensions.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.Web/A2AAgentClient.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.Web/AgentDiscoveryClient.cs (100%) create mode 100644 dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/AgentWebChat.Web.csproj rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.Web/Components/App.razor (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.Web/Components/Layout/MainLayout.razor (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.Web/Components/Layout/MainLayout.razor.css (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.Web/Components/Pages/Error.razor (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.Web/Components/Pages/Home.razor (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.Web/Components/Routes.razor (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.Web/Components/_Imports.razor (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.Web/IAgentClient.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.Web/OpenAIChatCompletionsAgentClient.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.Web/OpenAIResponsesAgentClient.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.Web/Program.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.Web/Properties/launchSettings.json (100%) rename dotnet/samples/{GettingStarted/AGUI/Step05_StateManagement/Server => 05-end-to-end/AgentWebChat/AgentWebChat.Web}/appsettings.Development.json (100%) rename dotnet/samples/{GettingStarted/AGUI/Step05_StateManagement/Server => 05-end-to-end/AgentWebChat/AgentWebChat.Web}/appsettings.json (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.Web/wwwroot/app.css (100%) rename dotnet/samples/{ => 05-end-to-end}/AgentWebChat/AgentWebChat.Web/wwwroot/favicon.png (100%) rename dotnet/samples/{Purview => 05-end-to-end}/AgentWithPurview/AgentWithPurview.csproj (100%) rename dotnet/samples/{Purview => 05-end-to-end}/AgentWithPurview/Program.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/HostedAgents/AgentWithHostedMCP/AgentWithHostedMCP.csproj (100%) rename dotnet/samples/{ => 05-end-to-end}/HostedAgents/AgentWithHostedMCP/Dockerfile (100%) rename dotnet/samples/{ => 05-end-to-end}/HostedAgents/AgentWithHostedMCP/Program.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/HostedAgents/AgentWithHostedMCP/README.md (100%) rename dotnet/samples/{ => 05-end-to-end}/HostedAgents/AgentWithHostedMCP/agent.yaml (100%) rename dotnet/samples/{ => 05-end-to-end}/HostedAgents/AgentWithHostedMCP/run-requests.http (100%) rename dotnet/samples/{ => 05-end-to-end}/HostedAgents/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj (100%) rename dotnet/samples/{ => 05-end-to-end}/HostedAgents/AgentWithTextSearchRag/Dockerfile (100%) rename dotnet/samples/{ => 05-end-to-end}/HostedAgents/AgentWithTextSearchRag/Program.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/HostedAgents/AgentWithTextSearchRag/README.md (100%) rename dotnet/samples/{ => 05-end-to-end}/HostedAgents/AgentWithTextSearchRag/agent.yaml (100%) rename dotnet/samples/{ => 05-end-to-end}/HostedAgents/AgentWithTextSearchRag/run-requests.http (100%) rename dotnet/samples/{ => 05-end-to-end}/HostedAgents/AgentsInWorkflows/AgentsInWorkflows.csproj (100%) rename dotnet/samples/{ => 05-end-to-end}/HostedAgents/AgentsInWorkflows/Dockerfile (100%) rename dotnet/samples/{ => 05-end-to-end}/HostedAgents/AgentsInWorkflows/Program.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/HostedAgents/AgentsInWorkflows/README.md (100%) rename dotnet/samples/{ => 05-end-to-end}/HostedAgents/AgentsInWorkflows/agent.yaml (100%) rename dotnet/samples/{ => 05-end-to-end}/HostedAgents/AgentsInWorkflows/run-requests.http (100%) rename dotnet/samples/{ => 05-end-to-end}/M365Agent/AFAgentApplication.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/M365Agent/Agents/AdaptiveCardAIContent.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/M365Agent/Agents/WeatherForecastAgent.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/M365Agent/Agents/WeatherForecastAgentResponse.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/M365Agent/Agents/WeatherForecastAgentResponseContentType.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/M365Agent/Auth/AspNetExtensions.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/M365Agent/Auth/TokenValidationOptions.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/M365Agent/JsonUtilities.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/M365Agent/M365Agent.csproj (88%) rename dotnet/samples/{ => 05-end-to-end}/M365Agent/Program.cs (100%) rename dotnet/samples/{ => 05-end-to-end}/M365Agent/Properties/launchSettings.json (100%) rename dotnet/samples/{ => 05-end-to-end}/M365Agent/README.md (100%) rename dotnet/samples/{ => 05-end-to-end}/M365Agent/appManifest/color.png (100%) rename dotnet/samples/{ => 05-end-to-end}/M365Agent/appManifest/manifest.json (100%) rename dotnet/samples/{ => 05-end-to-end}/M365Agent/appManifest/outline.png (100%) rename dotnet/samples/{ => 05-end-to-end}/M365Agent/appsettings.json.template (100%) create mode 100644 dotnet/samples/AGENTS.md delete mode 100644 dotnet/samples/AGUIClientServer/AGUIDojoServer/AGUIDojoServer.csproj delete mode 100644 dotnet/samples/AGUIClientServer/AGUIServer/AGUIServer.csproj delete mode 100644 dotnet/samples/AGUIWebChat/Server/AGUIWebChatServer.csproj delete mode 100644 dotnet/samples/AgentWebChat/AgentWebChat.Web/AgentWebChat.Web.csproj delete mode 100644 dotnet/samples/Durable/Agents/AzureFunctions/05_AgentOrchestration_HITL/local.settings.json delete mode 100644 dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/local.settings.json delete mode 100644 dotnet/samples/Durable/Agents/AzureFunctions/07_AgentAsMcpTool/local.settings.json delete mode 100644 dotnet/samples/GettingStarted/Workflows/Agents/FoundryAgent/FoundryAgent.csproj delete mode 100644 dotnet/samples/GettingStarted/Workflows/Agents/GroupChatToolApproval/GroupChatToolApproval.csproj delete mode 100644 dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/WorkflowAsAnAgent.csproj delete mode 100644 dotnet/samples/GettingStarted/Workflows/Concurrent/Concurrent/Concurrent.csproj delete mode 100644 dotnet/samples/GettingStarted/Workflows/Concurrent/MapReduce/MapReduce.csproj delete mode 100644 dotnet/samples/GettingStarted/Workflows/Directory.Build.props delete mode 100644 dotnet/samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/01_ExecutorsAndEdges.csproj delete mode 100644 dotnet/samples/GettingStarted/Workflows/_Foundational/02_Streaming/02_Streaming.csproj delete mode 100644 dotnet/samples/GettingStarted/Workflows/_Foundational/03_AgentsInWorkflows/03_AgentsInWorkflows.csproj delete mode 100644 dotnet/samples/GettingStarted/Workflows/_Foundational/04_AgentWorkflowPatterns/04_AgentWorkflowPatterns.csproj delete mode 100644 dotnet/samples/GettingStarted/Workflows/_Foundational/06_SubWorkflows/06_SubWorkflows.csproj delete mode 100644 dotnet/samples/GettingStarted/Workflows/_Foundational/07_MixedWorkflowAgentsAndExecutors/07_MixedWorkflowAgentsAndExecutors.csproj create mode 100644 dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/DefaultMcpToolHandler.cs create mode 100644 dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/Microsoft.Agents.AI.Workflows.Declarative.Mcp.csproj create mode 100644 dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/IMcpToolHandler.cs create mode 100644 dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeMcpToolExecutor.cs delete mode 100644 dotnet/src/Shared/IntegrationTests/AnthropicConfiguration.cs delete mode 100644 dotnet/src/Shared/IntegrationTests/AzureAIConfiguration.cs delete mode 100644 dotnet/src/Shared/IntegrationTests/FoundryMemoryConfiguration.cs delete mode 100644 dotnet/src/Shared/IntegrationTests/Mem0Configuration.cs delete mode 100644 dotnet/src/Shared/IntegrationTests/OpenAIConfiguration.cs create mode 100644 dotnet/src/Shared/IntegrationTests/TestSettings.cs delete mode 100644 dotnet/tests/CopilotStudio.IntegrationTests/Support/CopilotStudioAgentConfiguration.cs rename dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/{InvokeFunctionToolWorkflowTest.cs => InvokeToolWorkflowTest.cs} (51%) create mode 100644 dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Workflows/InvokeMcpTool.yaml create mode 100644 dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Workflows/InvokeMcpToolWithApproval.yaml create mode 100644 dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests/DefaultMcpToolHandlerTests.cs create mode 100644 dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests/Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests.csproj create mode 100644 dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/InvokeMcpToolExecutorTest.cs create mode 100644 dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowRunActivityStopTests.cs create mode 100644 python/packages/azure-ai/agent_framework_azure_ai/_embedding_client.py create mode 100644 python/packages/azure-ai/tests/azure_ai/test_azure_ai_inference_embedding_client.py create mode 100644 python/packages/bedrock/agent_framework_bedrock/_embedding_client.py create mode 100644 python/packages/bedrock/tests/bedrock/test_bedrock_embedding_client.py create mode 100644 python/packages/ollama/agent_framework_ollama/_embedding_client.py create mode 100644 python/packages/ollama/tests/ollama/test_ollama_embedding_client.py create mode 100644 python/samples/02-agents/embeddings/azure_ai_inference_embeddings.py create mode 100644 python/samples/shared/sample_assets/sample_image.jpg diff --git a/.github/labeler.yml b/.github/labeler.yml index 5663961c58..fb55fcf926 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -23,7 +23,7 @@ workflows: - any-glob-to-any-file: - dotnet/src/Microsoft.Agents.AI.Workflows/** - dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/** - - dotnet/samples/GettingStarted/Workflow/** + - dotnet/samples/03-workflows/** - python/packages/main/agent_framework/_workflow/** - python/samples/getting_started/workflow/** diff --git a/.github/workflows/dotnet-build-and-test.yml b/.github/workflows/dotnet-build-and-test.yml index 8e3582517f..3bdb43dabf 100644 --- a/.github/workflows/dotnet-build-and-test.yml +++ b/.github/workflows/dotnet-build-and-test.yml @@ -162,7 +162,7 @@ jobs: Write-Host "Launching Azure Cosmos DB Emulator" Import-Module "$env:ProgramFiles\Azure Cosmos DB Emulator\PSModules\Microsoft.Azure.CosmosDB.Emulator" Start-CosmosDbEmulator -NoUI -Key "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==" - echo "COSMOS_EMULATOR_AVAILABLE=true" >> $env:GITHUB_ENV + echo "COSMOSDB_EMULATOR_AVAILABLE=true" >> $env:GITHUB_ENV - name: Setup dotnet uses: actions/setup-dotnet@v5.1.0 @@ -172,7 +172,7 @@ jobs: - name: Generate test solution (no samples) shell: pwsh run: | - .github/workflows/New-FilteredSolution.ps1 ` + ./dotnet/eng/scripts/New-FilteredSolution.ps1 ` -Solution dotnet/agent-framework-dotnet.slnx ` -TargetFramework ${{ matrix.targetFramework }} ` -Configuration ${{ matrix.configuration }} ` @@ -193,10 +193,10 @@ jobs: Configuration = "${{ matrix.configuration }}" Verbose = $true } - .github/workflows/New-FilteredSolution.ps1 @commonArgs ` + ./dotnet/eng/scripts/New-FilteredSolution.ps1 @commonArgs ` -TestProjectNameFilter "*UnitTests*" ` -OutputPath dotnet/filtered-unit.slnx - .github/workflows/New-FilteredSolution.ps1 @commonArgs ` + ./dotnet/eng/scripts/New-FilteredSolution.ps1 @commonArgs ` -TestProjectNameFilter "*IntegrationTests*" ` -OutputPath dotnet/filtered-integration.slnx @@ -266,20 +266,17 @@ jobs: COSMOSDB_ENDPOINT: https://localhost:8081 COSMOSDB_KEY: C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw== # OpenAI Models - OpenAI__ApiKey: ${{ secrets.OPENAI__APIKEY }} - OpenAI__ChatModelId: ${{ vars.OPENAI__CHATMODELID }} - OpenAI__ChatReasoningModelId: ${{ vars.OPENAI__CHATREASONINGMODELID }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + OPENAI_CHAT_MODEL_NAME: ${{ vars.OPENAI_CHAT_MODEL_NAME }} + OPENAI_REASONING_MODEL_NAME: ${{ vars.OPENAI_REASONING_MODEL_NAME }} # Azure OpenAI Models - AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }} - AZURE_OPENAI_ENDPOINT: ${{ vars.AZUREOPENAI__ENDPOINT }} + AZURE_OPENAI_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_DEPLOYMENT_NAME }} + AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_DEPLOYMENT_NAME }} + AZURE_OPENAI_ENDPOINT: ${{ vars.AZURE_OPENAI_ENDPOINT }} # Azure AI Foundry - AzureAI__Endpoint: ${{ secrets.AZUREAI__ENDPOINT }} - AzureAI__DeploymentName: ${{ vars.AZUREAI__DEPLOYMENTNAME }} - AzureAI__BingConnectionId: ${{ vars.AZUREAI__BINGCONECTIONID }} - FOUNDRY_PROJECT_ENDPOINT: ${{ vars.FOUNDRY_PROJECT_ENDPOINT }} - FOUNDRY_MEDIA_DEPLOYMENT_NAME: ${{ vars.FOUNDRY_MEDIA_DEPLOYMENT_NAME }} - FOUNDRY_MODEL_DEPLOYMENT_NAME: ${{ vars.FOUNDRY_MODEL_DEPLOYMENT_NAME }} - FOUNDRY_CONNECTION_GROUNDING_TOOL: ${{ vars.FOUNDRY_CONNECTION_GROUNDING_TOOL }} + AZURE_AI_PROJECT_ENDPOINT: ${{ vars.AZURE_AI_PROJECT_ENDPOINT }} + AZURE_AI_MODEL_DEPLOYMENT_NAME: ${{ vars.AZURE_AI_MODEL_DEPLOYMENT_NAME }} + AZURE_AI_BING_CONNECTION_ID: ${{ vars.AZURE_AI_BING_CONNECTION_ID }} # Generate test reports and check coverage - name: Generate test reports @@ -300,7 +297,7 @@ jobs: - name: Check coverage if: matrix.targetFramework == env.COVERAGE_FRAMEWORK shell: pwsh - run: .github/workflows/dotnet-check-coverage.ps1 -JsonReportPath "TestResults/Reports/Summary.json" -CoverageThreshold $env:COVERAGE_THRESHOLD + run: ./dotnet/eng/scripts/dotnet-check-coverage.ps1 -JsonReportPath "TestResults/Reports/Summary.json" -CoverageThreshold $env:COVERAGE_THRESHOLD # This final job is required to satisfy the merge queue. It must only run (or succeed) if no tests failed dotnet-build-and-test-check: diff --git a/README.md b/README.md index 2b40333063..1c41003080 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Still have questions? Join our [weekly office hours](./COMMUNITY.md#public-commu ### ✨ **Highlights** - **Graph-based Workflows**: Connect agents and deterministic functions using data flows with streaming, checkpointing, human-in-the-loop, and time-travel capabilities - - [Python workflows](./python/samples/03-workflows/) | [.NET workflows](./dotnet/samples/GettingStarted/Workflows/) + - [Python workflows](./python/samples/03-workflows/) | [.NET workflows](./dotnet/samples/03-workflows/) - **AF Labs**: Experimental packages for cutting-edge features including benchmarking, reinforcement learning, and research initiatives - [Labs directory](./python/packages/lab/) - **DevUI**: Interactive developer UI for agent development, testing, and debugging workflows @@ -73,11 +73,11 @@ Still have questions? Join our [weekly office hours](./COMMUNITY.md#public-commu - **Python and C#/.NET Support**: Full framework support for both Python and C#/.NET implementations with consistent APIs - [Python packages](./python/packages/) | [.NET source](./dotnet/src/) - **Observability**: Built-in OpenTelemetry integration for distributed tracing, monitoring, and debugging - - [Python observability](./python/samples/02-agents/observability/) | [.NET telemetry](./dotnet/samples/GettingStarted/AgentOpenTelemetry/) + - [Python observability](./python/samples/02-agents/observability/) | [.NET telemetry](./dotnet/samples/02-agents/AgentOpenTelemetry/) - **Multiple Agent Provider Support**: Support for various LLM providers with more being added continuously - - [Python examples](./python/samples/02-agents/providers/) | [.NET examples](./dotnet/samples/GettingStarted/AgentProviders/) + - [Python examples](./python/samples/02-agents/providers/) | [.NET examples](./dotnet/samples/02-agents/AgentProviders/) - **Middleware**: Flexible middleware system for request/response processing, exception handling, and custom pipelines - - [Python middleware](./python/samples/02-agents/middleware/) | [.NET middleware](./dotnet/samples/GettingStarted/Agents/Agent_Step14_Middleware/) + - [Python middleware](./python/samples/02-agents/middleware/) | [.NET middleware](./dotnet/samples/02-agents/Agents/Agent_Step11_Middleware/) ### 💬 **We want your feedback!** @@ -169,9 +169,9 @@ Console.WriteLine(await agent.RunAsync("Write a haiku about Microsoft Agent Fram ### .NET -- [Getting Started with Agents](./dotnet/samples/GettingStarted/Agents): basic agent creation and tool usage -- [Agent Provider Samples](./dotnet/samples/GettingStarted/AgentProviders): samples showing different agent providers -- [Workflow Samples](./dotnet/samples/GettingStarted/Workflows): advanced multi-agent patterns and workflow orchestration +- [Getting Started with Agents](./dotnet/samples/02-agents/Agents): basic agent creation and tool usage +- [Agent Provider Samples](./dotnet/samples/02-agents/AgentProviders): samples showing different agent providers +- [Workflow Samples](./dotnet/samples/03-workflows): advanced multi-agent patterns and workflow orchestration ## Contributor Resources diff --git a/docs/decisions/0001-agent-run-response.md b/docs/decisions/0001-agent-run-response.md index 6f3385e1a1..fb4a962802 100644 --- a/docs/decisions/0001-agent-run-response.md +++ b/docs/decisions/0001-agent-run-response.md @@ -498,7 +498,7 @@ We need to decide what AIContent types, each agent response type will be mapped | Google ADK | **Approach 1** Both [input and output schemas can be specified for LLM Agents](https://google.github.io/adk-docs/agents/llm-agents/#structuring-data-input_schema-output_schema-output_key) at construction time. This option is specific to this agent type and other agent types do not necessarily support | | AWS (Strands) | **Approach 2** Supports a special invocation method called [structured_output](https://strandsagents.com/latest/documentation/docs/api-reference/python/agent/agent/#strands.agent.agent.Agent.structured_output) | | LangGraph | **Approach 1** Supports [configuring an agent](https://langchain-ai.github.io/langgraph/agents/agents/?h=structured#6-configure-structured-output) at agent construction time, and a [structured response](https://langchain-ai.github.io/langgraph/agents/run_agents/#output-format) can be retrieved as a special property on the agent response | -| Agno | **Approach 1** Supports [configuring an agent](https://docs.agno.com/examples/getting-started/structured-output) at agent construction time | +| Agno | **Approach 1** Supports [configuring an agent](https://docs.agno.com/input-output/structured-output/agent) at agent construction time | | A2A | **Informal Approach 2** Doesn't formally support schema negotiation, but [hints can be provided via metadata](https://a2a-protocol.org/latest/specification/#97-structured-data-exchange-requesting-and-providing-json) at invocation time | | Protocol Activity | Supports returning [Complex types](https://github.com/microsoft/Agents/blob/main/specs/activity/protocol-activity.md#complex-types) but no support for requesting a type | diff --git a/docs/features/durable-agents/AGENTS.md b/docs/features/durable-agents/AGENTS.md index 53c055a216..db6c06df73 100644 --- a/docs/features/durable-agents/AGENTS.md +++ b/docs/features/durable-agents/AGENTS.md @@ -8,7 +8,7 @@ This directory contains feature documentation for the durable agents integration - .NET implementation: `dotnet/src/Microsoft.Agents.AI.DurableTask/` and `dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/` - Python implementation: `python/packages/durabletask/` and `python/packages/azurefunctions/` (package `agent-framework-azurefunctions`) -- .NET samples: `dotnet/samples/Durable/Agents/` +- .NET samples: `dotnet/samples/04-hosting/DurableAgents/` - Python samples: `python/samples/04-hosting/durabletask/` - Official docs (Microsoft Learn): diff --git a/docs/features/durable-agents/README.md b/docs/features/durable-agents/README.md index 326e66743b..525c447ebc 100644 --- a/docs/features/durable-agents/README.md +++ b/docs/features/durable-agents/README.md @@ -219,7 +219,7 @@ When using the [Durable Task Scheduler](https://learn.microsoft.com/azure/azure- ## Samples -- **.NET** – [Console app samples](../../../dotnet/samples/Durable/Agents/ConsoleApps/) and [Azure Functions samples](../../../dotnet/samples/Durable/Agents/AzureFunctions/) covering single-agent, chaining, concurrency, conditionals, human-in-the-loop, long-running tools, MCP tool exposure, and reliable streaming. +- **.NET** – [Console app samples](../../../dotnet/samples/04-hosting/DurableAgents/ConsoleApps/) and [Azure Functions samples](../../../dotnet/samples/04-hosting/DurableAgents/AzureFunctions/) covering single-agent, chaining, concurrency, conditionals, human-in-the-loop, long-running tools, MCP tool exposure, and reliable streaming. - **Python** – [Durable Task samples](../../../python/samples/04-hosting/durabletask/) covering single-agent, multi-agent, streaming, chaining, concurrency, conditionals, and human-in-the-loop. ## Packages diff --git a/docs/features/vector-stores-and-embeddings/README.md b/docs/features/vector-stores-and-embeddings/README.md index 02e71ab028..560fdd86d6 100644 --- a/docs/features/vector-stores-and-embeddings/README.md +++ b/docs/features/vector-stores-and-embeddings/README.md @@ -140,9 +140,10 @@ This feature ports the vector store abstractions, embedding generator abstractio ## Implementation Phases -### Phase 1: Core Embedding Abstractions & OpenAI Implementation +### Phase 1: Core Embedding Abstractions & OpenAI Implementation ✅ DONE **Goal:** Establish the embedding generator abstraction and ship one working implementation. **Mergeable:** Yes — adds new types/protocols, no breaking changes. +**Status:** Merged via PR #4153. Closes sub-issue #4163. #### 1.1 — Embedding types in `_types.py` - `EmbeddingInputT` TypeVar (default `str`) — generic input type for embedding generation diff --git a/dotnet/.github/skills/build-and-test/SKILL.md b/dotnet/.github/skills/build-and-test/SKILL.md index ef04bb96d6..1009e2c5b7 100644 --- a/dotnet/.github/skills/build-and-test/SKILL.md +++ b/dotnet/.github/skills/build-and-test/SKILL.md @@ -104,15 +104,15 @@ Tests use the [Microsoft Testing Platform](https://learn.microsoft.com/dotnet/co dotnet test --solution ./agent-framework-dotnet.slnx --no-build -f net10.0 --ignore-exit-code 8 ``` -- **Running tests with `--solution` for a specific TFM** requires all projects in the solution to support that TFM. Not all projects target every framework (e.g., some are `net10.0`-only). Use `.github/workflows/New-FilteredSolution.ps1` to generate a filtered solution: +- **Running tests with `--solution` for a specific TFM** requires all projects in the solution to support that TFM. Not all projects target every framework (e.g., some are `net10.0`-only). Use `./dotnet/eng/scripts/New-FilteredSolution.ps1` to generate a filtered solution: ```powershell # Generate a filtered solution for net472 and run tests -$filtered = .github/workflows/New-FilteredSolution.ps1 -Solution dotnet/agent-framework-dotnet.slnx -TargetFramework net472 +$filtered = ./dotnet/eng/scripts/New-FilteredSolution.ps1 -Solution dotnet/agent-framework-dotnet.slnx -TargetFramework net472 dotnet test --solution $filtered --no-build -f net472 --ignore-exit-code 8 # Exclude samples and keep only unit test projects -.github/workflows/New-FilteredSolution.ps1 -Solution dotnet/agent-framework-dotnet.slnx -TargetFramework net10.0 -ExcludeSamples -TestProjectNameFilter "*UnitTests*" -OutputPath dotnet/filtered-unit.slnx +./dotnet/eng/scripts/New-FilteredSolution.ps1 -Solution dotnet/agent-framework-dotnet.slnx -TargetFramework net10.0 -ExcludeSamples -TestProjectNameFilter "*UnitTests*" -OutputPath dotnet/filtered-unit.slnx ``` ```bash diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index 6d73ee4158..e3b3cbbc42 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -111,9 +111,9 @@ - - - + + + diff --git a/dotnet/README.md b/dotnet/README.md index c6f3750286..328dfdf684 100644 --- a/dotnet/README.md +++ b/dotnet/README.md @@ -1,11 +1,5 @@ # Get Started with Microsoft Agent Framework for C# Developers -## Samples - -- [Getting Started with Agents](./samples/GettingStarted/Agents): basic agent creation and tool usage -- [Agent Provider Samples](./samples/GettingStarted/AgentProviders): samples showing different agent providers -- [Workflow Samples](./samples/GettingStarted/Workflows): advanced multi-agent patterns and workflow orchestration - ## Quickstart ### Basic Agent - .NET @@ -28,9 +22,9 @@ Console.WriteLine(await agent.RunAsync("Write a haiku about Microsoft Agent Fram ## Examples & Samples -- [Getting Started with Agents](./samples/GettingStarted/Agents): basic agent creation and tool usage -- [Agent Provider Samples](./samples/GettingStarted/AgentProviders): samples showing different agent providers -- [Workflow Samples](./samples/GettingStarted/Workflows): advanced multi-agent patterns and workflow orchestration +- [Getting Started with Agents](./samples/02-agents/Agents): basic agent creation and tool usage +- [Agent Provider Samples](./samples/02-agents/AgentProviders): samples showing different agent providers +- [Workflow Samples](./samples/03-workflows): advanced multi-agent patterns and workflow orchestration ## Agent Framework Documentation diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx index 041da4a6f6..39aee03b4a 100644 --- a/dotnet/agent-framework-dotnet.slnx +++ b/dotnet/agent-framework-dotnet.slnx @@ -5,230 +5,189 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -236,60 +195,108 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -298,7 +305,6 @@ - @@ -331,6 +337,10 @@ + + + + @@ -426,6 +436,7 @@ + @@ -433,11 +444,11 @@ - + @@ -451,11 +462,11 @@ + - @@ -472,17 +483,18 @@ + - + diff --git a/.github/workflows/New-FilteredSolution.ps1 b/dotnet/eng/scripts/New-FilteredSolution.ps1 similarity index 90% rename from .github/workflows/New-FilteredSolution.ps1 rename to dotnet/eng/scripts/New-FilteredSolution.ps1 index 2871f5f36b..de6a8f9d1d 100644 --- a/.github/workflows/New-FilteredSolution.ps1 +++ b/dotnet/eng/scripts/New-FilteredSolution.ps1 @@ -33,16 +33,16 @@ .EXAMPLE # Generate a filtered solution and run tests - $filtered = .github/workflows/New-FilteredSolution.ps1 -Solution dotnet/agent-framework-dotnet.slnx -TargetFramework net472 + $filtered = ./dotnet/eng/scripts/New-FilteredSolution.ps1 -Solution dotnet/agent-framework-dotnet.slnx -TargetFramework net472 dotnet test --solution $filtered --no-build -f net472 .EXAMPLE # Generate a solution with only unit test projects - .github/workflows/New-FilteredSolution.ps1 -Solution dotnet/agent-framework-dotnet.slnx -TargetFramework net10.0 -TestProjectNameFilter "*UnitTests*" -OutputPath filtered-unit.slnx + ./dotnet/eng/scripts/New-FilteredSolution.ps1 -Solution dotnet/agent-framework-dotnet.slnx -TargetFramework net10.0 -TestProjectNameFilter "*UnitTests*" -OutputPath filtered-unit.slnx .EXAMPLE # Inline usage with dotnet test (PowerShell) - dotnet test --solution (.github/workflows/New-FilteredSolution.ps1 -Solution dotnet/agent-framework-dotnet.slnx -TargetFramework net472) --no-build -f net472 + dotnet test --solution (./dotnet/eng/scripts/New-FilteredSolution.ps1 -Solution dotnet/agent-framework-dotnet.slnx -TargetFramework net472) --no-build -f net472 #> [CmdletBinding()] diff --git a/.github/workflows/dotnet-check-coverage.ps1 b/dotnet/eng/scripts/dotnet-check-coverage.ps1 similarity index 100% rename from .github/workflows/dotnet-check-coverage.ps1 rename to dotnet/eng/scripts/dotnet-check-coverage.ps1 diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/Agent_Step05_StructuredOutput.csproj b/dotnet/samples/01-get-started/01_hello_agent/01_hello_agent.csproj similarity index 80% rename from dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/Agent_Step05_StructuredOutput.csproj rename to dotnet/samples/01-get-started/01_hello_agent/01_hello_agent.csproj index 0f9de7c359..b32de63906 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/Agent_Step05_StructuredOutput.csproj +++ b/dotnet/samples/01-get-started/01_hello_agent/01_hello_agent.csproj @@ -15,7 +15,7 @@ - + diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step01_Running/Program.cs b/dotnet/samples/01-get-started/01_hello_agent/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step01_Running/Program.cs rename to dotnet/samples/01-get-started/01_hello_agent/Program.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step04_UsingFunctionToolsWithApprovals/Agent_Step04_UsingFunctionToolsWithApprovals.csproj b/dotnet/samples/01-get-started/02_add_tools/02_add_tools.csproj similarity index 80% rename from dotnet/samples/GettingStarted/Agents/Agent_Step04_UsingFunctionToolsWithApprovals/Agent_Step04_UsingFunctionToolsWithApprovals.csproj rename to dotnet/samples/01-get-started/02_add_tools/02_add_tools.csproj index 0f9de7c359..b32de63906 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step04_UsingFunctionToolsWithApprovals/Agent_Step04_UsingFunctionToolsWithApprovals.csproj +++ b/dotnet/samples/01-get-started/02_add_tools/02_add_tools.csproj @@ -15,7 +15,7 @@ - + diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step03_UsingFunctionTools/Program.cs b/dotnet/samples/01-get-started/02_add_tools/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step03_UsingFunctionTools/Program.cs rename to dotnet/samples/01-get-started/02_add_tools/Program.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step06_PersistedConversations/Agent_Step06_PersistedConversations.csproj b/dotnet/samples/01-get-started/03_multi_turn/03_multi_turn.csproj similarity index 80% rename from dotnet/samples/GettingStarted/Agents/Agent_Step06_PersistedConversations/Agent_Step06_PersistedConversations.csproj rename to dotnet/samples/01-get-started/03_multi_turn/03_multi_turn.csproj index 0f9de7c359..b32de63906 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step06_PersistedConversations/Agent_Step06_PersistedConversations.csproj +++ b/dotnet/samples/01-get-started/03_multi_turn/03_multi_turn.csproj @@ -15,7 +15,7 @@ - + diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step02_MultiturnConversation/Program.cs b/dotnet/samples/01-get-started/03_multi_turn/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step02_MultiturnConversation/Program.cs rename to dotnet/samples/01-get-started/03_multi_turn/Program.cs diff --git a/dotnet/samples/01-get-started/04_memory/04_memory.csproj b/dotnet/samples/01-get-started/04_memory/04_memory.csproj new file mode 100644 index 0000000000..b32de63906 --- /dev/null +++ b/dotnet/samples/01-get-started/04_memory/04_memory.csproj @@ -0,0 +1,21 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + + + diff --git a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step03_CustomMemory/Program.cs b/dotnet/samples/01-get-started/04_memory/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step03_CustomMemory/Program.cs rename to dotnet/samples/01-get-started/04_memory/Program.cs diff --git a/dotnet/samples/01-get-started/05_first_workflow/05_first_workflow.csproj b/dotnet/samples/01-get-started/05_first_workflow/05_first_workflow.csproj new file mode 100644 index 0000000000..b15906ba18 --- /dev/null +++ b/dotnet/samples/01-get-started/05_first_workflow/05_first_workflow.csproj @@ -0,0 +1,14 @@ + + + + Exe + net10.0 + enable + enable + + + + + + + diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/Program.cs b/dotnet/samples/01-get-started/05_first_workflow/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/Program.cs rename to dotnet/samples/01-get-started/05_first_workflow/Program.cs diff --git a/dotnet/samples/01-get-started/06_host_your_agent/06_host_your_agent.csproj b/dotnet/samples/01-get-started/06_host_your_agent/06_host_your_agent.csproj new file mode 100644 index 0000000000..2f0efd7b3a --- /dev/null +++ b/dotnet/samples/01-get-started/06_host_your_agent/06_host_your_agent.csproj @@ -0,0 +1,31 @@ + + + Exe + net10.0 + v4 + enable + enable + + HostedAgent + HostedAgent + + + + + + + + + + + + + + + + + + + + + diff --git a/dotnet/samples/01-get-started/06_host_your_agent/Program.cs b/dotnet/samples/01-get-started/06_host_your_agent/Program.cs new file mode 100644 index 0000000000..6012119b25 --- /dev/null +++ b/dotnet/samples/01-get-started/06_host_your_agent/Program.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to host an AI agent with Azure Functions (DurableAgents). +// +// Prerequisites: +// - Azure Functions Core Tools +// - Azure OpenAI resource +// +// Environment variables: +// AZURE_OPENAI_ENDPOINT +// AZURE_OPENAI_DEPLOYMENT_NAME (defaults to "gpt-4o-mini") +// +// Run with: func start +// Then call: POST http://localhost:7071/api/agents/HostedAgent/run + +using Azure.AI.OpenAI; +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.Hosting.AzureFunctions; +using Microsoft.Azure.Functions.Worker.Builder; +using Microsoft.Extensions.Hosting; +using OpenAI.Chat; + +var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") + ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); +var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +// Set up an AI agent following the standard Microsoft Agent Framework pattern. +// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. +// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid +// latency issues, unintended credential probing, and potential security risks from fallback mechanisms. +AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential()) + .GetChatClient(deploymentName) + .AsAIAgent( + instructions: "You are a helpful assistant hosted in Azure Functions.", + name: "HostedAgent"); + +// Configure the function app to host the AI agent. +// This will automatically generate HTTP API endpoints for the agent. +using IHost app = FunctionsApplication + .CreateBuilder(args) + .ConfigureFunctionsWebApplication() + .ConfigureDurableAgents(options => options.AddAIAgent(agent, timeToLive: TimeSpan.FromHours(1))) + .Build(); +app.Run(); diff --git a/dotnet/samples/GettingStarted/AGUI/README.md b/dotnet/samples/02-agents/AGUI/README.md similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/README.md rename to dotnet/samples/02-agents/AGUI/README.md diff --git a/dotnet/samples/GettingStarted/AGUI/Step01_GettingStarted/Client/Client.csproj b/dotnet/samples/02-agents/AGUI/Step01_GettingStarted/Client/Client.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step01_GettingStarted/Client/Client.csproj rename to dotnet/samples/02-agents/AGUI/Step01_GettingStarted/Client/Client.csproj diff --git a/dotnet/samples/GettingStarted/AGUI/Step01_GettingStarted/Client/Program.cs b/dotnet/samples/02-agents/AGUI/Step01_GettingStarted/Client/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step01_GettingStarted/Client/Program.cs rename to dotnet/samples/02-agents/AGUI/Step01_GettingStarted/Client/Program.cs diff --git a/dotnet/samples/GettingStarted/AGUI/Step01_GettingStarted/Server/Program.cs b/dotnet/samples/02-agents/AGUI/Step01_GettingStarted/Server/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step01_GettingStarted/Server/Program.cs rename to dotnet/samples/02-agents/AGUI/Step01_GettingStarted/Server/Program.cs diff --git a/dotnet/samples/GettingStarted/AGUI/Step01_GettingStarted/Server/Properties/launchSettings.json b/dotnet/samples/02-agents/AGUI/Step01_GettingStarted/Server/Properties/launchSettings.json similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step01_GettingStarted/Server/Properties/launchSettings.json rename to dotnet/samples/02-agents/AGUI/Step01_GettingStarted/Server/Properties/launchSettings.json diff --git a/dotnet/samples/GettingStarted/AGUI/Step01_GettingStarted/Server/Server.csproj b/dotnet/samples/02-agents/AGUI/Step01_GettingStarted/Server/Server.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step01_GettingStarted/Server/Server.csproj rename to dotnet/samples/02-agents/AGUI/Step01_GettingStarted/Server/Server.csproj diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/appsettings.Development.json b/dotnet/samples/02-agents/AGUI/Step01_GettingStarted/Server/appsettings.Development.json similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/appsettings.Development.json rename to dotnet/samples/02-agents/AGUI/Step01_GettingStarted/Server/appsettings.Development.json diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/appsettings.json b/dotnet/samples/02-agents/AGUI/Step01_GettingStarted/Server/appsettings.json similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/appsettings.json rename to dotnet/samples/02-agents/AGUI/Step01_GettingStarted/Server/appsettings.json diff --git a/dotnet/samples/GettingStarted/AGUI/Step02_BackendTools/Client/Client.csproj b/dotnet/samples/02-agents/AGUI/Step02_BackendTools/Client/Client.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step02_BackendTools/Client/Client.csproj rename to dotnet/samples/02-agents/AGUI/Step02_BackendTools/Client/Client.csproj diff --git a/dotnet/samples/GettingStarted/AGUI/Step02_BackendTools/Client/Program.cs b/dotnet/samples/02-agents/AGUI/Step02_BackendTools/Client/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step02_BackendTools/Client/Program.cs rename to dotnet/samples/02-agents/AGUI/Step02_BackendTools/Client/Program.cs diff --git a/dotnet/samples/GettingStarted/AGUI/Step02_BackendTools/Server/Program.cs b/dotnet/samples/02-agents/AGUI/Step02_BackendTools/Server/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step02_BackendTools/Server/Program.cs rename to dotnet/samples/02-agents/AGUI/Step02_BackendTools/Server/Program.cs diff --git a/dotnet/samples/GettingStarted/AGUI/Step02_BackendTools/Server/Properties/launchSettings.json b/dotnet/samples/02-agents/AGUI/Step02_BackendTools/Server/Properties/launchSettings.json similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step02_BackendTools/Server/Properties/launchSettings.json rename to dotnet/samples/02-agents/AGUI/Step02_BackendTools/Server/Properties/launchSettings.json diff --git a/dotnet/samples/GettingStarted/AGUI/Step02_BackendTools/Server/Server.csproj b/dotnet/samples/02-agents/AGUI/Step02_BackendTools/Server/Server.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step02_BackendTools/Server/Server.csproj rename to dotnet/samples/02-agents/AGUI/Step02_BackendTools/Server/Server.csproj diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AppHost/appsettings.Development.json b/dotnet/samples/02-agents/AGUI/Step02_BackendTools/Server/appsettings.Development.json similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.AppHost/appsettings.Development.json rename to dotnet/samples/02-agents/AGUI/Step02_BackendTools/Server/appsettings.Development.json diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/appsettings.json b/dotnet/samples/02-agents/AGUI/Step02_BackendTools/Server/appsettings.json similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.Web/appsettings.json rename to dotnet/samples/02-agents/AGUI/Step02_BackendTools/Server/appsettings.json diff --git a/dotnet/samples/GettingStarted/AGUI/Step03_FrontendTools/Client/Client.csproj b/dotnet/samples/02-agents/AGUI/Step03_FrontendTools/Client/Client.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step03_FrontendTools/Client/Client.csproj rename to dotnet/samples/02-agents/AGUI/Step03_FrontendTools/Client/Client.csproj diff --git a/dotnet/samples/GettingStarted/AGUI/Step03_FrontendTools/Client/Program.cs b/dotnet/samples/02-agents/AGUI/Step03_FrontendTools/Client/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step03_FrontendTools/Client/Program.cs rename to dotnet/samples/02-agents/AGUI/Step03_FrontendTools/Client/Program.cs diff --git a/dotnet/samples/GettingStarted/AGUI/Step03_FrontendTools/Server/Program.cs b/dotnet/samples/02-agents/AGUI/Step03_FrontendTools/Server/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step03_FrontendTools/Server/Program.cs rename to dotnet/samples/02-agents/AGUI/Step03_FrontendTools/Server/Program.cs diff --git a/dotnet/samples/GettingStarted/AGUI/Step03_FrontendTools/Server/Properties/launchSettings.json b/dotnet/samples/02-agents/AGUI/Step03_FrontendTools/Server/Properties/launchSettings.json similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step03_FrontendTools/Server/Properties/launchSettings.json rename to dotnet/samples/02-agents/AGUI/Step03_FrontendTools/Server/Properties/launchSettings.json diff --git a/dotnet/samples/GettingStarted/AGUI/Step03_FrontendTools/Server/Server.csproj b/dotnet/samples/02-agents/AGUI/Step03_FrontendTools/Server/Server.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step03_FrontendTools/Server/Server.csproj rename to dotnet/samples/02-agents/AGUI/Step03_FrontendTools/Server/Server.csproj diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/appsettings.Development.json b/dotnet/samples/02-agents/AGUI/Step03_FrontendTools/Server/appsettings.Development.json similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.Web/appsettings.Development.json rename to dotnet/samples/02-agents/AGUI/Step03_FrontendTools/Server/appsettings.Development.json diff --git a/dotnet/samples/GettingStarted/AGUI/Step01_GettingStarted/Server/appsettings.json b/dotnet/samples/02-agents/AGUI/Step03_FrontendTools/Server/appsettings.json similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step01_GettingStarted/Server/appsettings.json rename to dotnet/samples/02-agents/AGUI/Step03_FrontendTools/Server/appsettings.json diff --git a/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Client/Client.csproj b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/Client.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Client/Client.csproj rename to dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/Client.csproj diff --git a/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Client/Program.cs b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Client/Program.cs rename to dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/Program.cs diff --git a/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs rename to dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs diff --git a/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/Program.cs b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/Program.cs rename to dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/Program.cs diff --git a/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/Properties/launchSettings.json b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/Properties/launchSettings.json similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/Properties/launchSettings.json rename to dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/Properties/launchSettings.json diff --git a/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/Server.csproj b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/Server.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/Server.csproj rename to dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/Server.csproj diff --git a/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs rename to dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/appsettings.Development.json b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/appsettings.Development.json similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIDojoServer/appsettings.Development.json rename to dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/appsettings.Development.json diff --git a/dotnet/samples/GettingStarted/AGUI/Step02_BackendTools/Server/appsettings.json b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/appsettings.json similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step02_BackendTools/Server/appsettings.json rename to dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/appsettings.json diff --git a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Client/Client.csproj b/dotnet/samples/02-agents/AGUI/Step05_StateManagement/Client/Client.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Client/Client.csproj rename to dotnet/samples/02-agents/AGUI/Step05_StateManagement/Client/Client.csproj diff --git a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Client/Program.cs b/dotnet/samples/02-agents/AGUI/Step05_StateManagement/Client/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Client/Program.cs rename to dotnet/samples/02-agents/AGUI/Step05_StateManagement/Client/Program.cs diff --git a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Client/StatefulAgent.cs b/dotnet/samples/02-agents/AGUI/Step05_StateManagement/Client/StatefulAgent.cs similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Client/StatefulAgent.cs rename to dotnet/samples/02-agents/AGUI/Step05_StateManagement/Client/StatefulAgent.cs diff --git a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/Program.cs b/dotnet/samples/02-agents/AGUI/Step05_StateManagement/Server/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/Program.cs rename to dotnet/samples/02-agents/AGUI/Step05_StateManagement/Server/Program.cs diff --git a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/Properties/launchSettings.json b/dotnet/samples/02-agents/AGUI/Step05_StateManagement/Server/Properties/launchSettings.json similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/Properties/launchSettings.json rename to dotnet/samples/02-agents/AGUI/Step05_StateManagement/Server/Properties/launchSettings.json diff --git a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/RecipeModels.cs b/dotnet/samples/02-agents/AGUI/Step05_StateManagement/Server/RecipeModels.cs similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/RecipeModels.cs rename to dotnet/samples/02-agents/AGUI/Step05_StateManagement/Server/RecipeModels.cs diff --git a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/Server.csproj b/dotnet/samples/02-agents/AGUI/Step05_StateManagement/Server/Server.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/Server.csproj rename to dotnet/samples/02-agents/AGUI/Step05_StateManagement/Server/Server.csproj diff --git a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/SharedStateAgent.cs b/dotnet/samples/02-agents/AGUI/Step05_StateManagement/Server/SharedStateAgent.cs similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/SharedStateAgent.cs rename to dotnet/samples/02-agents/AGUI/Step05_StateManagement/Server/SharedStateAgent.cs diff --git a/dotnet/samples/GettingStarted/AGUI/Step01_GettingStarted/Server/appsettings.Development.json b/dotnet/samples/02-agents/AGUI/Step05_StateManagement/Server/appsettings.Development.json similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step01_GettingStarted/Server/appsettings.Development.json rename to dotnet/samples/02-agents/AGUI/Step05_StateManagement/Server/appsettings.Development.json diff --git a/dotnet/samples/GettingStarted/AGUI/Step03_FrontendTools/Server/appsettings.json b/dotnet/samples/02-agents/AGUI/Step05_StateManagement/Server/appsettings.json similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step03_FrontendTools/Server/appsettings.json rename to dotnet/samples/02-agents/AGUI/Step05_StateManagement/Server/appsettings.json diff --git a/dotnet/samples/GettingStarted/AgentOpenTelemetry/AgentOpenTelemetry.csproj b/dotnet/samples/02-agents/AgentOpenTelemetry/AgentOpenTelemetry.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentOpenTelemetry/AgentOpenTelemetry.csproj rename to dotnet/samples/02-agents/AgentOpenTelemetry/AgentOpenTelemetry.csproj diff --git a/dotnet/samples/GettingStarted/AgentOpenTelemetry/Program.cs b/dotnet/samples/02-agents/AgentOpenTelemetry/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentOpenTelemetry/Program.cs rename to dotnet/samples/02-agents/AgentOpenTelemetry/Program.cs diff --git a/dotnet/samples/GettingStarted/AgentOpenTelemetry/README.md b/dotnet/samples/02-agents/AgentOpenTelemetry/README.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentOpenTelemetry/README.md rename to dotnet/samples/02-agents/AgentOpenTelemetry/README.md diff --git a/dotnet/samples/GettingStarted/AgentOpenTelemetry/start-demo.ps1 b/dotnet/samples/02-agents/AgentOpenTelemetry/start-demo.ps1 similarity index 100% rename from dotnet/samples/GettingStarted/AgentOpenTelemetry/start-demo.ps1 rename to dotnet/samples/02-agents/AgentOpenTelemetry/start-demo.ps1 diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_A2A/Agent_With_A2A.csproj b/dotnet/samples/02-agents/AgentProviders/Agent_With_A2A/Agent_With_A2A.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_A2A/Agent_With_A2A.csproj rename to dotnet/samples/02-agents/AgentProviders/Agent_With_A2A/Agent_With_A2A.csproj diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_A2A/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_A2A/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_A2A/Program.cs rename to dotnet/samples/02-agents/AgentProviders/Agent_With_A2A/Program.cs diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_A2A/README.md b/dotnet/samples/02-agents/AgentProviders/Agent_With_A2A/README.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_A2A/README.md rename to dotnet/samples/02-agents/AgentProviders/Agent_With_A2A/README.md diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Anthropic/Agent_With_Anthropic.csproj b/dotnet/samples/02-agents/AgentProviders/Agent_With_Anthropic/Agent_With_Anthropic.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_Anthropic/Agent_With_Anthropic.csproj rename to dotnet/samples/02-agents/AgentProviders/Agent_With_Anthropic/Agent_With_Anthropic.csproj diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Anthropic/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_Anthropic/Program.cs similarity index 97% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_Anthropic/Program.cs rename to dotnet/samples/02-agents/AgentProviders/Agent_With_Anthropic/Program.cs index a099d5aad3..da26d65cfe 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Anthropic/Program.cs +++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_Anthropic/Program.cs @@ -7,7 +7,7 @@ using Azure.Identity; using Microsoft.Agents.AI; -string deploymentName = Environment.GetEnvironmentVariable("ANTHROPIC_DEPLOYMENT_NAME") ?? "claude-haiku-4-5"; +string deploymentName = Environment.GetEnvironmentVariable("ANTHROPIC_CHAT_MODEL_NAME") ?? "claude-haiku-4-5"; // The resource is the subdomain name / first name coming before '.services.ai.azure.com' in the endpoint Uri // ie: https://(resource name).services.ai.azure.com/anthropic/v1/chat/completions diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Anthropic/README.md b/dotnet/samples/02-agents/AgentProviders/Agent_With_Anthropic/README.md similarity index 91% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_Anthropic/README.md rename to dotnet/samples/02-agents/AgentProviders/Agent_With_Anthropic/README.md index afcf391572..c1a569874b 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Anthropic/README.md +++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_Anthropic/README.md @@ -22,7 +22,7 @@ Set the following environment variables: ```powershell $env:ANTHROPIC_API_KEY="your-anthropic-api-key" # Replace with your Anthropic API key -$env:ANTHROPIC_DEPLOYMENT_NAME="claude-haiku-4-5" # Optional, defaults to claude-haiku-4-5 +$env:ANTHROPIC_CHAT_MODEL_NAME="claude-haiku-4-5" # Optional, defaults to claude-haiku-4-5 ``` ### For Azure Foundry with API Key @@ -35,7 +35,7 @@ Set the following environment variables: ```powershell $env:ANTHROPIC_RESOURCE="your-foundry-resource-name" # Replace with your Azure Foundry resource name (subdomain before .services.ai.azure.com) $env:ANTHROPIC_API_KEY="your-anthropic-api-key" # Replace with your Anthropic API key -$env:ANTHROPIC_DEPLOYMENT_NAME="claude-haiku-4-5" # Optional, defaults to claude-haiku-4-5 +$env:ANTHROPIC_CHAT_MODEL_NAME="claude-haiku-4-5" # Optional, defaults to claude-haiku-4-5 ``` ### For Azure Foundry with Azure CLI @@ -47,7 +47,7 @@ Set the following environment variables: ```powershell $env:ANTHROPIC_RESOURCE="your-foundry-resource-name" # Replace with your Azure Foundry resource name (subdomain before .services.ai.azure.com) -$env:ANTHROPIC_DEPLOYMENT_NAME="claude-haiku-4-5" # Optional, defaults to claude-haiku-4-5 +$env:ANTHROPIC_CHAT_MODEL_NAME="claude-haiku-4-5" # Optional, defaults to claude-haiku-4-5 ``` **Note**: When using Azure Foundry with Azure CLI, make sure you're logged in with `az login` and have access to the Azure Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively). diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgentsPersistent/Agent_With_AzureAIAgentsPersistent.csproj b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIAgentsPersistent/Agent_With_AzureAIAgentsPersistent.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgentsPersistent/Agent_With_AzureAIAgentsPersistent.csproj rename to dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIAgentsPersistent/Agent_With_AzureAIAgentsPersistent.csproj diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgentsPersistent/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIAgentsPersistent/Program.cs similarity index 86% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgentsPersistent/Program.cs rename to dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIAgentsPersistent/Program.cs index 20da6b3720..691fb20328 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgentsPersistent/Program.cs +++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIAgentsPersistent/Program.cs @@ -6,8 +6,8 @@ using Azure.Identity; using Microsoft.Agents.AI; -var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -var deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +var deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; const string JokerName = "Joker"; const string JokerInstructions = "You are good at telling jokes."; diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgentsPersistent/README.md b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIAgentsPersistent/README.md similarity index 77% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgentsPersistent/README.md rename to dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIAgentsPersistent/README.md index d6b5497601..969795d87f 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgentsPersistent/README.md +++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIAgentsPersistent/README.md @@ -21,6 +21,6 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIProject/Agent_With_AzureAIProject.csproj b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Agent_With_AzureAIProject.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIProject/Agent_With_AzureAIProject.csproj rename to dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Agent_With_AzureAIProject.csproj diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIProject/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Program.cs similarity index 90% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIProject/Program.cs rename to dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Program.cs index ced1665951..acdd0829ab 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIProject/Program.cs +++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Program.cs @@ -7,8 +7,8 @@ using Azure.Identity; using Microsoft.Agents.AI; -var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -var deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +var deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; const string JokerName = "JokerAgent"; diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIProject/README.md b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/README.md similarity index 77% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIProject/README.md rename to dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/README.md index 7e4a28f6a1..66fcbf8297 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIProject/README.md +++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/README.md @@ -21,6 +21,6 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureFoundryModel/Agent_With_AzureFoundryModel.csproj b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureFoundryModel/Agent_With_AzureFoundryModel.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureFoundryModel/Agent_With_AzureFoundryModel.csproj rename to dotnet/samples/02-agents/AgentProviders/Agent_With_AzureFoundryModel/Agent_With_AzureFoundryModel.csproj diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureFoundryModel/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureFoundryModel/Program.cs similarity index 83% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureFoundryModel/Program.cs rename to dotnet/samples/02-agents/AgentProviders/Agent_With_AzureFoundryModel/Program.cs index 752b6d0ec1..fe682d388a 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureFoundryModel/Program.cs +++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureFoundryModel/Program.cs @@ -11,9 +11,9 @@ using OpenAI; using OpenAI.Chat; -var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_OPENAI_ENDPOINT is not set."); -var apiKey = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_OPENAI_API_KEY"); -var model = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_MODEL_DEPLOYMENT") ?? "Phi-4-mini-instruct"; +var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); +var apiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY"); +var model = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "Phi-4-mini-instruct"; // Since we are using the OpenAI Client SDK, we need to override the default endpoint to point to Azure Foundry. var clientOptions = new OpenAIClientOptions() { Endpoint = new Uri(endpoint) }; diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureFoundryModel/README.md b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureFoundryModel/README.md similarity index 84% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureFoundryModel/README.md rename to dotnet/samples/02-agents/AgentProviders/Agent_With_AzureFoundryModel/README.md index b5e65ea209..6d5b6badd7 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureFoundryModel/README.md +++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureFoundryModel/README.md @@ -13,7 +13,7 @@ Before you begin, ensure you have the following prerequisites: - .NET 10 SDK or later - Azure AI Foundry resource - A model deployment in your Azure AI Foundry resource. This example defaults to using the `Phi-4-mini-instruct` model, -so if you want to use a different model, ensure that you set your `AZURE_FOUNDRY_MODEL_DEPLOYMENT` environment +so if you want to use a different model, ensure that you set your `AZURE_AI_MODEL_DEPLOYMENT_NAME` environment variable to the name of your deployed model. - An API key or role based authentication to access the Azure AI Foundry resource @@ -24,11 +24,11 @@ Set the following environment variables: ```powershell # Replace with your Azure AI Foundry resource endpoint # Ensure that you have the "/openai/v1/" path in the URL, since this is required when using the OpenAI SDK to access Azure Foundry models. -$env:AZURE_FOUNDRY_OPENAI_ENDPOINT="https://ai-foundry-.services.ai.azure.com/openai/v1/" +$env:AZURE_OPENAI_ENDPOINT="https://ai-foundry-.services.ai.azure.com/openai/v1/" # Optional, defaults to using Azure CLI for authentication if not provided -$env:AZURE_FOUNDRY_OPENAI_API_KEY="************" +$env:AZURE_OPENAI_API_KEY="************" # Optional, defaults to Phi-4-mini-instruct -$env:AZURE_FOUNDRY_MODEL_DEPLOYMENT="Phi-4-mini-instruct" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="Phi-4-mini-instruct" ``` diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureOpenAIChatCompletion/Agent_With_AzureOpenAIChatCompletion.csproj b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureOpenAIChatCompletion/Agent_With_AzureOpenAIChatCompletion.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureOpenAIChatCompletion/Agent_With_AzureOpenAIChatCompletion.csproj rename to dotnet/samples/02-agents/AgentProviders/Agent_With_AzureOpenAIChatCompletion/Agent_With_AzureOpenAIChatCompletion.csproj diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureOpenAIChatCompletion/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureOpenAIChatCompletion/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureOpenAIChatCompletion/Program.cs rename to dotnet/samples/02-agents/AgentProviders/Agent_With_AzureOpenAIChatCompletion/Program.cs diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureOpenAIChatCompletion/README.md b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureOpenAIChatCompletion/README.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureOpenAIChatCompletion/README.md rename to dotnet/samples/02-agents/AgentProviders/Agent_With_AzureOpenAIChatCompletion/README.md diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureOpenAIResponses/Agent_With_AzureOpenAIResponses.csproj b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureOpenAIResponses/Agent_With_AzureOpenAIResponses.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureOpenAIResponses/Agent_With_AzureOpenAIResponses.csproj rename to dotnet/samples/02-agents/AgentProviders/Agent_With_AzureOpenAIResponses/Agent_With_AzureOpenAIResponses.csproj diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureOpenAIResponses/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureOpenAIResponses/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureOpenAIResponses/Program.cs rename to dotnet/samples/02-agents/AgentProviders/Agent_With_AzureOpenAIResponses/Program.cs diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureOpenAIResponses/README.md b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureOpenAIResponses/README.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureOpenAIResponses/README.md rename to dotnet/samples/02-agents/AgentProviders/Agent_With_AzureOpenAIResponses/README.md diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Agent_With_CustomImplementation.csproj b/dotnet/samples/02-agents/AgentProviders/Agent_With_CustomImplementation/Agent_With_CustomImplementation.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Agent_With_CustomImplementation.csproj rename to dotnet/samples/02-agents/AgentProviders/Agent_With_CustomImplementation/Agent_With_CustomImplementation.csproj diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_CustomImplementation/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Program.cs rename to dotnet/samples/02-agents/AgentProviders/Agent_With_CustomImplementation/Program.cs diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/README.md b/dotnet/samples/02-agents/AgentProviders/Agent_With_CustomImplementation/README.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/README.md rename to dotnet/samples/02-agents/AgentProviders/Agent_With_CustomImplementation/README.md diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_GitHubCopilot/Agent_With_GitHubCopilot.csproj b/dotnet/samples/02-agents/AgentProviders/Agent_With_GitHubCopilot/Agent_With_GitHubCopilot.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_GitHubCopilot/Agent_With_GitHubCopilot.csproj rename to dotnet/samples/02-agents/AgentProviders/Agent_With_GitHubCopilot/Agent_With_GitHubCopilot.csproj diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_GitHubCopilot/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_GitHubCopilot/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_GitHubCopilot/Program.cs rename to dotnet/samples/02-agents/AgentProviders/Agent_With_GitHubCopilot/Program.cs diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_GitHubCopilot/README.md b/dotnet/samples/02-agents/AgentProviders/Agent_With_GitHubCopilot/README.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_GitHubCopilot/README.md rename to dotnet/samples/02-agents/AgentProviders/Agent_With_GitHubCopilot/README.md diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_GoogleGemini/Agent_With_GoogleGemini.csproj b/dotnet/samples/02-agents/AgentProviders/Agent_With_GoogleGemini/Agent_With_GoogleGemini.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_GoogleGemini/Agent_With_GoogleGemini.csproj rename to dotnet/samples/02-agents/AgentProviders/Agent_With_GoogleGemini/Agent_With_GoogleGemini.csproj diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_GoogleGemini/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_GoogleGemini/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_GoogleGemini/Program.cs rename to dotnet/samples/02-agents/AgentProviders/Agent_With_GoogleGemini/Program.cs diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_GoogleGemini/README.md b/dotnet/samples/02-agents/AgentProviders/Agent_With_GoogleGemini/README.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_GoogleGemini/README.md rename to dotnet/samples/02-agents/AgentProviders/Agent_With_GoogleGemini/README.md diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_ONNX/Agent_With_ONNX.csproj b/dotnet/samples/02-agents/AgentProviders/Agent_With_ONNX/Agent_With_ONNX.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_ONNX/Agent_With_ONNX.csproj rename to dotnet/samples/02-agents/AgentProviders/Agent_With_ONNX/Agent_With_ONNX.csproj diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_ONNX/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_ONNX/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_ONNX/Program.cs rename to dotnet/samples/02-agents/AgentProviders/Agent_With_ONNX/Program.cs diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_ONNX/README.md b/dotnet/samples/02-agents/AgentProviders/Agent_With_ONNX/README.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_ONNX/README.md rename to dotnet/samples/02-agents/AgentProviders/Agent_With_ONNX/README.md diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Ollama/Agent_With_Ollama.csproj b/dotnet/samples/02-agents/AgentProviders/Agent_With_Ollama/Agent_With_Ollama.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_Ollama/Agent_With_Ollama.csproj rename to dotnet/samples/02-agents/AgentProviders/Agent_With_Ollama/Agent_With_Ollama.csproj diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Ollama/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_Ollama/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_Ollama/Program.cs rename to dotnet/samples/02-agents/AgentProviders/Agent_With_Ollama/Program.cs diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Ollama/README.md b/dotnet/samples/02-agents/AgentProviders/Agent_With_Ollama/README.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_Ollama/README.md rename to dotnet/samples/02-agents/AgentProviders/Agent_With_Ollama/README.md diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIAssistants/Agent_With_OpenAIAssistants.csproj b/dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIAssistants/Agent_With_OpenAIAssistants.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIAssistants/Agent_With_OpenAIAssistants.csproj rename to dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIAssistants/Agent_With_OpenAIAssistants.csproj diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIAssistants/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIAssistants/Program.cs similarity index 95% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIAssistants/Program.cs rename to dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIAssistants/Program.cs index fa7edfb52c..02d19ab52c 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIAssistants/Program.cs +++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIAssistants/Program.cs @@ -12,7 +12,7 @@ using OpenAI.Assistants; var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set."); -var model = Environment.GetEnvironmentVariable("OPENAI_MODEL") ?? "gpt-4o-mini"; +var model = Environment.GetEnvironmentVariable("OPENAI_CHAT_MODEL_NAME") ?? "gpt-4o-mini"; const string JokerName = "Joker"; const string JokerInstructions = "You are good at telling jokes."; diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIAssistants/README.md b/dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIAssistants/README.md similarity index 84% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIAssistants/README.md rename to dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIAssistants/README.md index 05d2380b78..b0a7638ab5 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIAssistants/README.md +++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIAssistants/README.md @@ -12,5 +12,5 @@ Set the following environment variables: ```powershell $env:OPENAI_API_KEY="*****" # Replace with your OpenAI API key -$env:OPENAI_MODEL="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:OPENAI_CHAT_MODEL_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIChatCompletion/Agent_With_OpenAIChatCompletion.csproj b/dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIChatCompletion/Agent_With_OpenAIChatCompletion.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIChatCompletion/Agent_With_OpenAIChatCompletion.csproj rename to dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIChatCompletion/Agent_With_OpenAIChatCompletion.csproj diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIChatCompletion/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIChatCompletion/Program.cs similarity index 87% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIChatCompletion/Program.cs rename to dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIChatCompletion/Program.cs index 3b22c21b3b..f5af4d2369 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIChatCompletion/Program.cs +++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIChatCompletion/Program.cs @@ -8,7 +8,7 @@ using OpenAI.Chat; var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set."); -var model = Environment.GetEnvironmentVariable("OPENAI_MODEL") ?? "gpt-4o-mini"; +var model = Environment.GetEnvironmentVariable("OPENAI_CHAT_MODEL_NAME") ?? "gpt-4o-mini"; AIAgent agent = new OpenAIClient( apiKey) diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIResponses/README.md b/dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIChatCompletion/README.md similarity index 75% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIResponses/README.md rename to dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIChatCompletion/README.md index 70fc472214..ef7ce3ae02 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIResponses/README.md +++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIChatCompletion/README.md @@ -9,5 +9,5 @@ Set the following environment variables: ```powershell $env:OPENAI_API_KEY="*****" # Replace with your OpenAI api key -$env:OPENAI_MODEL="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:OPENAI_CHAT_MODEL_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIResponses/Agent_With_OpenAIResponses.csproj b/dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIResponses/Agent_With_OpenAIResponses.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIResponses/Agent_With_OpenAIResponses.csproj rename to dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIResponses/Agent_With_OpenAIResponses.csproj diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIResponses/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIResponses/Program.cs similarity index 87% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIResponses/Program.cs rename to dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIResponses/Program.cs index 1e5883c67d..611f3f9a9a 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIResponses/Program.cs +++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIResponses/Program.cs @@ -7,7 +7,7 @@ using OpenAI.Responses; var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set."); -var model = Environment.GetEnvironmentVariable("OPENAI_MODEL") ?? "gpt-4o-mini"; +var model = Environment.GetEnvironmentVariable("OPENAI_CHAT_MODEL_NAME") ?? "gpt-4o-mini"; AIAgent agent = new OpenAIClient( apiKey) diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIChatCompletion/README.md b/dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIResponses/README.md similarity index 75% rename from dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIChatCompletion/README.md rename to dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIResponses/README.md index 70fc472214..ef7ce3ae02 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_OpenAIChatCompletion/README.md +++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIResponses/README.md @@ -9,5 +9,5 @@ Set the following environment variables: ```powershell $env:OPENAI_API_KEY="*****" # Replace with your OpenAI api key -$env:OPENAI_MODEL="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:OPENAI_CHAT_MODEL_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` diff --git a/dotnet/samples/GettingStarted/AgentProviders/README.md b/dotnet/samples/02-agents/AgentProviders/README.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentProviders/README.md rename to dotnet/samples/02-agents/AgentProviders/README.md diff --git a/dotnet/samples/GettingStarted/AgentSkills/Agent_Step01_BasicSkills/Agent_Step01_BasicSkills.csproj b/dotnet/samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/Agent_Step01_BasicSkills.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentSkills/Agent_Step01_BasicSkills/Agent_Step01_BasicSkills.csproj rename to dotnet/samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/Agent_Step01_BasicSkills.csproj diff --git a/dotnet/samples/GettingStarted/AgentSkills/Agent_Step01_BasicSkills/Program.cs b/dotnet/samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentSkills/Agent_Step01_BasicSkills/Program.cs rename to dotnet/samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/Program.cs diff --git a/dotnet/samples/GettingStarted/AgentSkills/Agent_Step01_BasicSkills/README.md b/dotnet/samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/README.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentSkills/Agent_Step01_BasicSkills/README.md rename to dotnet/samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/README.md diff --git a/dotnet/samples/GettingStarted/AgentSkills/Agent_Step01_BasicSkills/skills/expense-report/SKILL.md b/dotnet/samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/skills/expense-report/SKILL.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentSkills/Agent_Step01_BasicSkills/skills/expense-report/SKILL.md rename to dotnet/samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/skills/expense-report/SKILL.md diff --git a/dotnet/samples/GettingStarted/AgentSkills/Agent_Step01_BasicSkills/skills/expense-report/assets/expense-report-template.md b/dotnet/samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/skills/expense-report/assets/expense-report-template.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentSkills/Agent_Step01_BasicSkills/skills/expense-report/assets/expense-report-template.md rename to dotnet/samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/skills/expense-report/assets/expense-report-template.md diff --git a/dotnet/samples/GettingStarted/AgentSkills/Agent_Step01_BasicSkills/skills/expense-report/references/POLICY_FAQ.md b/dotnet/samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/skills/expense-report/references/POLICY_FAQ.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentSkills/Agent_Step01_BasicSkills/skills/expense-report/references/POLICY_FAQ.md rename to dotnet/samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/skills/expense-report/references/POLICY_FAQ.md diff --git a/dotnet/samples/GettingStarted/AgentSkills/README.md b/dotnet/samples/02-agents/AgentSkills/README.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentSkills/README.md rename to dotnet/samples/02-agents/AgentSkills/README.md diff --git a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step01_Running/Agent_Anthropic_Step01_Running.csproj b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step01_Running/Agent_Anthropic_Step01_Running.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step01_Running/Agent_Anthropic_Step01_Running.csproj rename to dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step01_Running/Agent_Anthropic_Step01_Running.csproj diff --git a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step01_Running/Program.cs b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step01_Running/Program.cs similarity index 89% rename from dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step01_Running/Program.cs rename to dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step01_Running/Program.cs index 7aee814c0c..3d9c715588 100644 --- a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step01_Running/Program.cs +++ b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step01_Running/Program.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.AI; var apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY") ?? throw new InvalidOperationException("ANTHROPIC_API_KEY is not set."); -var model = Environment.GetEnvironmentVariable("ANTHROPIC_MODEL") ?? "claude-haiku-4-5"; +var model = Environment.GetEnvironmentVariable("ANTHROPIC_CHAT_MODEL_NAME") ?? "claude-haiku-4-5"; AIAgent agent = new AnthropicClient(new ClientOptions { ApiKey = apiKey }) .AsAIAgent(model: model, instructions: "You are good at telling jokes.", name: "Joker"); diff --git a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step01_Running/README.md b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step01_Running/README.md similarity index 87% rename from dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step01_Running/README.md rename to dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step01_Running/README.md index 4800650bd9..5f58a43765 100644 --- a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step01_Running/README.md +++ b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step01_Running/README.md @@ -21,7 +21,7 @@ Set the following environment variables: ```powershell $env:ANTHROPIC_API_KEY="your-anthropic-api-key" # Replace with your Anthropic API key -$env:ANTHROPIC_MODEL="your-anthropic-model" # Replace with your Anthropic model +$env:ANTHROPIC_CHAT_MODEL_NAME="your-anthropic-model" # Replace with your Anthropic model ``` ## Run the sample @@ -29,7 +29,7 @@ $env:ANTHROPIC_MODEL="your-anthropic-model" # Replace with your Anthropic model Navigate to the AgentWithAnthropic sample directory and run: ```powershell -cd dotnet\samples\GettingStarted\AgentWithAnthropic +cd dotnet\samples\02-agents\AgentWithAnthropic dotnet run --project .\Agent_Anthropic_Step01_Running ``` diff --git a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/Agent_Anthropic_Step02_Reasoning.csproj b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/Agent_Anthropic_Step02_Reasoning.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/Agent_Anthropic_Step02_Reasoning.csproj rename to dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/Agent_Anthropic_Step02_Reasoning.csproj diff --git a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/Program.cs b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/Program.cs similarity index 95% rename from dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/Program.cs rename to dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/Program.cs index 78633cb7a8..e88bbd9ac7 100644 --- a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/Program.cs +++ b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/Program.cs @@ -9,7 +9,7 @@ using Microsoft.Extensions.AI; var apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY") ?? throw new InvalidOperationException("ANTHROPIC_API_KEY is not set."); -var model = Environment.GetEnvironmentVariable("ANTHROPIC_MODEL") ?? "claude-haiku-4-5"; +var model = Environment.GetEnvironmentVariable("ANTHROPIC_CHAT_MODEL_NAME") ?? "claude-haiku-4-5"; var maxTokens = 4096; var thinkingTokens = 2048; diff --git a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/README.md b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/README.md similarity index 90% rename from dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/README.md rename to dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/README.md index ae088b2386..4242111122 100644 --- a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/README.md +++ b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step02_Reasoning/README.md @@ -23,7 +23,7 @@ Set the following environment variables: ```powershell $env:ANTHROPIC_API_KEY="your-anthropic-api-key" # Replace with your Anthropic API key -$env:ANTHROPIC_MODEL="your-anthropic-model" # Replace with your Anthropic model +$env:ANTHROPIC_CHAT_MODEL_NAME="your-anthropic-model" # Replace with your Anthropic model ``` ## Run the sample @@ -31,7 +31,7 @@ $env:ANTHROPIC_MODEL="your-anthropic-model" # Replace with your Anthropic model Navigate to the AgentWithAnthropic sample directory and run: ```powershell -cd dotnet\samples\GettingStarted\AgentWithAnthropic +cd dotnet\samples\02-agents\AgentWithAnthropic dotnet run --project .\Agent_Anthropic_Step02_Reasoning ``` diff --git a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/Agent_Anthropic_Step03_UsingFunctionTools.csproj b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/Agent_Anthropic_Step03_UsingFunctionTools.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/Agent_Anthropic_Step03_UsingFunctionTools.csproj rename to dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/Agent_Anthropic_Step03_UsingFunctionTools.csproj diff --git a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/Program.cs b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/Program.cs similarity index 94% rename from dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/Program.cs rename to dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/Program.cs index c58c7ebcc8..1727c43825 100644 --- a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/Program.cs +++ b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/Program.cs @@ -9,7 +9,7 @@ using Microsoft.Extensions.AI; var apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY") ?? throw new InvalidOperationException("ANTHROPIC_API_KEY is not set."); -var model = Environment.GetEnvironmentVariable("ANTHROPIC_MODEL") ?? "claude-haiku-4-5"; +var model = Environment.GetEnvironmentVariable("ANTHROPIC_CHAT_MODEL_NAME") ?? "claude-haiku-4-5"; [Description("Get the weather for a given location.")] static string GetWeather([Description("The location to get the weather for.")] string location) diff --git a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/README.md b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/README.md similarity index 91% rename from dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/README.md rename to dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/README.md index 6c905864ef..9160a97d02 100644 --- a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/README.md +++ b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step03_UsingFunctionTools/README.md @@ -23,7 +23,7 @@ Set the following environment variables: ```powershell $env:ANTHROPIC_API_KEY="your-anthropic-api-key" # Replace with your Anthropic API key -$env:ANTHROPIC_MODEL="your-anthropic-model" # Replace with your Anthropic model +$env:ANTHROPIC_CHAT_MODEL_NAME="your-anthropic-model" # Replace with your Anthropic model ``` ## Run the sample @@ -31,7 +31,7 @@ $env:ANTHROPIC_MODEL="your-anthropic-model" # Replace with your Anthropic model Navigate to the AgentWithAnthropic sample directory and run: ```powershell -cd dotnet\samples\GettingStarted\AgentWithAnthropic +cd dotnet\samples\02-agents\AgentWithAnthropic dotnet run --project .\Agent_Anthropic_Step03_UsingFunctionTools ``` diff --git a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/Agent_Anthropic_Step04_UsingSkills.csproj b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/Agent_Anthropic_Step04_UsingSkills.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/Agent_Anthropic_Step04_UsingSkills.csproj rename to dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/Agent_Anthropic_Step04_UsingSkills.csproj diff --git a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/Program.cs b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/Program.cs similarity index 97% rename from dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/Program.cs rename to dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/Program.cs index f67c25214f..9d356a8598 100644 --- a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/Program.cs +++ b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/Program.cs @@ -19,7 +19,7 @@ string apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY") ?? throw new InvalidOperationException("ANTHROPIC_API_KEY is not set."); // Skills require Claude 4.5 models (Sonnet 4.5, Haiku 4.5, or Opus 4.5) -string model = Environment.GetEnvironmentVariable("ANTHROPIC_MODEL") ?? "claude-sonnet-4-5-20250929"; +string model = Environment.GetEnvironmentVariable("ANTHROPIC_CHAT_MODEL_NAME") ?? "claude-sonnet-4-5-20250929"; // Create the Anthropic client AnthropicClient anthropicClient = new() { ApiKey = apiKey }; diff --git a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/README.md b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/README.md similarity index 95% rename from dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/README.md rename to dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/README.md index f94b16cac9..322d475008 100644 --- a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/README.md +++ b/dotnet/samples/02-agents/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/README.md @@ -24,7 +24,7 @@ Set the following environment variables: ```powershell $env:ANTHROPIC_API_KEY="your-anthropic-api-key" # Replace with your Anthropic API key -$env:ANTHROPIC_MODEL="your-anthropic-model" # Replace with your Anthropic model (e.g., claude-sonnet-4-5-20250929) +$env:ANTHROPIC_CHAT_MODEL_NAME="your-anthropic-model" # Replace with your Anthropic model (e.g., claude-sonnet-4-5-20250929) ``` ## Run the sample @@ -32,7 +32,7 @@ $env:ANTHROPIC_MODEL="your-anthropic-model" # Replace with your Anthropic model Navigate to the AgentWithAnthropic sample directory and run: ```powershell -cd dotnet\samples\GettingStarted\AgentWithAnthropic +cd dotnet\samples\02-agents\AgentWithAnthropic dotnet run --project .\Agent_Anthropic_Step04_UsingSkills ``` diff --git a/dotnet/samples/GettingStarted/AgentWithAnthropic/README.md b/dotnet/samples/02-agents/AgentWithAnthropic/README.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithAnthropic/README.md rename to dotnet/samples/02-agents/AgentWithAnthropic/README.md diff --git a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/AgentWithMemory_Step01_ChatHistoryMemory.csproj b/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/AgentWithMemory_Step01_ChatHistoryMemory.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/AgentWithMemory_Step01_ChatHistoryMemory.csproj rename to dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/AgentWithMemory_Step01_ChatHistoryMemory.csproj diff --git a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/Program.cs b/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/Program.cs rename to dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/Program.cs diff --git a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/AgentWithMemory_Step02_MemoryUsingMem0.csproj b/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/AgentWithMemory_Step02_MemoryUsingMem0.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/AgentWithMemory_Step02_MemoryUsingMem0.csproj rename to dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/AgentWithMemory_Step02_MemoryUsingMem0.csproj diff --git a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/Program.cs b/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/Program.cs similarity index 96% rename from dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/Program.cs rename to dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/Program.cs index d1d6a60f9d..f1842eb634 100644 --- a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/Program.cs +++ b/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/Program.cs @@ -17,7 +17,7 @@ var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; var mem0ServiceUri = Environment.GetEnvironmentVariable("MEM0_ENDPOINT") ?? throw new InvalidOperationException("MEM0_ENDPOINT is not set."); -var mem0ApiKey = Environment.GetEnvironmentVariable("MEM0_APIKEY") ?? throw new InvalidOperationException("MEM0_APIKEY is not set."); +var mem0ApiKey = Environment.GetEnvironmentVariable("MEM0_API_KEY") ?? throw new InvalidOperationException("MEM0_API_KEY is not set."); // Create an HttpClient for Mem0 with the required base address and authentication. using HttpClient mem0HttpClient = new(); diff --git a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/AgentWithMemory_Step04_MemoryUsingFoundry.csproj b/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/AgentWithMemory_Step04_MemoryUsingFoundry.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/AgentWithMemory_Step04_MemoryUsingFoundry.csproj rename to dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/AgentWithMemory_Step04_MemoryUsingFoundry.csproj diff --git a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/Program.cs b/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/Program.cs similarity index 89% rename from dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/Program.cs rename to dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/Program.cs index b3533e6d1d..914eda330a 100644 --- a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/Program.cs +++ b/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/Program.cs @@ -13,10 +13,10 @@ using Microsoft.Agents.AI; using Microsoft.Agents.AI.FoundryMemory; -string foundryEndpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set."); -string memoryStoreName = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_MEMORY_STORE_NAME") ?? "memory-store-sample"; -string deploymentName = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_MODEL") ?? "gpt-4.1-mini"; -string embeddingModelName = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_EMBEDDING_MODEL") ?? "text-embedding-ada-002"; +string foundryEndpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string memoryStoreName = Environment.GetEnvironmentVariable("AZURE_AI_MEMORY_STORE_ID") ?? "memory-store-sample"; +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string embeddingModelName = Environment.GetEnvironmentVariable("AZURE_AI_EMBEDDING_DEPLOYMENT_NAME") ?? "text-embedding-ada-002"; // Create an AIProjectClient for Foundry with Azure Identity authentication. DefaultAzureCredential credential = new(); diff --git a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/README.md b/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/README.md similarity index 88% rename from dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/README.md rename to dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/README.md index dfea386d82..bcc70b0103 100644 --- a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/README.md +++ b/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/README.md @@ -22,12 +22,12 @@ This sample demonstrates how to create and run an agent that uses Azure AI Found ```bash # Azure AI Foundry project endpoint and memory store name -export FOUNDRY_PROJECT_ENDPOINT="https://your-account.services.ai.azure.com/api/projects/your-project" -export FOUNDRY_PROJECT_MEMORY_STORE_NAME="my_memory_store" +export AZURE_AI_PROJECT_ENDPOINT="https://your-account.services.ai.azure.com/api/projects/your-project" +export AZURE_AI_MEMORY_STORE_ID="my_memory_store" # Model deployment names (models deployed in your Foundry project) -export FOUNDRY_PROJECT_MODEL="gpt-4o-mini" -export FOUNDRY_PROJECT_EMBEDDING_MODEL="text-embedding-ada-002" +export AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" +export AZURE_AI_EMBEDDING_DEPLOYMENT_NAME="text-embedding-ada-002" ``` ## Run the Sample diff --git a/dotnet/samples/GettingStarted/AgentWithMemory/README.md b/dotnet/samples/02-agents/AgentWithMemory/README.md similarity index 80% rename from dotnet/samples/GettingStarted/AgentWithMemory/README.md rename to dotnet/samples/02-agents/AgentWithMemory/README.md index 4f35adcd09..893ba03772 100644 --- a/dotnet/samples/GettingStarted/AgentWithMemory/README.md +++ b/dotnet/samples/02-agents/AgentWithMemory/README.md @@ -6,7 +6,7 @@ These samples show how to create an agent with the Agent Framework that uses Mem |---|---| |[Chat History memory](./AgentWithMemory_Step01_ChatHistoryMemory/)|This sample demonstrates how to enable an agent to remember messages from previous conversations.| |[Memory with MemoryStore](./AgentWithMemory_Step02_MemoryUsingMem0/)|This sample demonstrates how to create and run an agent that uses the Mem0 service to extract and retrieve individual memories.| -|[Custom Memory Implementation](./AgentWithMemory_Step03_CustomMemory/)|This sample demonstrates how to create a custom memory component and attach it to an agent.| +|[Custom Memory Implementation](../../01-get-started/04_memory/)|This sample demonstrates how to create a custom memory component and attach it to an agent.| |[Memory with Azure AI Foundry](./AgentWithMemory_Step04_MemoryUsingFoundry/)|This sample demonstrates how to create and run an agent that uses Azure AI Foundry's managed memory service to extract and retrieve individual memories.| -> **See also**: [Memory Search with Foundry Agents](../FoundryAgents/FoundryAgents_Step26_MemorySearch/) - demonstrates using the built-in Memory Search tool with Azure Foundry Agents. +> **See also**: [Memory Search with Foundry Agents](../FoundryAgents/FoundryAgents_Step22_MemorySearch/) - demonstrates using the built-in Memory Search tool with Azure Foundry Agents. diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Agent_OpenAI_Step01_Running.csproj b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Agent_OpenAI_Step01_Running.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Agent_OpenAI_Step01_Running.csproj rename to dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Agent_OpenAI_Step01_Running.csproj diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Program.cs b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Program.cs similarity index 92% rename from dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Program.cs rename to dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Program.cs index 78ea76e03f..e2bd31055a 100644 --- a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Program.cs +++ b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Program.cs @@ -8,7 +8,7 @@ using OpenAI.Chat; var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set."); -var model = Environment.GetEnvironmentVariable("OPENAI_MODEL") ?? "gpt-4o-mini"; +var model = Environment.GetEnvironmentVariable("OPENAI_CHAT_MODEL_NAME") ?? "gpt-4o-mini"; AIAgent agent = new OpenAIClient(apiKey) .GetChatClient(model) diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Agent_OpenAI_Step02_Reasoning.csproj b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Agent_OpenAI_Step02_Reasoning.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Agent_OpenAI_Step02_Reasoning.csproj rename to dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Agent_OpenAI_Step02_Reasoning.csproj diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Program.cs b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Program.cs similarity index 95% rename from dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Program.cs rename to dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Program.cs index 426b40f1f5..d13d0d5346 100644 --- a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Program.cs +++ b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Program.cs @@ -7,7 +7,7 @@ using OpenAI; var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set."); -var model = Environment.GetEnvironmentVariable("OPENAI_MODEL") ?? "gpt-5"; +var model = Environment.GetEnvironmentVariable("OPENAI_CHAT_MODEL_NAME") ?? "gpt-5"; var client = new OpenAIClient(apiKey) .GetResponsesClient(model) diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/Agent_OpenAI_Step03_CreateFromChatClient.csproj b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/Agent_OpenAI_Step03_CreateFromChatClient.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/Agent_OpenAI_Step03_CreateFromChatClient.csproj rename to dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/Agent_OpenAI_Step03_CreateFromChatClient.csproj diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/OpenAIChatClientAgent.cs b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/OpenAIChatClientAgent.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/OpenAIChatClientAgent.cs rename to dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/OpenAIChatClientAgent.cs diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/Program.cs b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/Program.cs similarity index 93% rename from dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/Program.cs rename to dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/Program.cs index b046afbf0d..5efc6c0ad6 100644 --- a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/Program.cs +++ b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/Program.cs @@ -7,7 +7,7 @@ using OpenAIChatClientSample; string apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set."); -string model = Environment.GetEnvironmentVariable("OPENAI_MODEL") ?? "gpt-4o-mini"; +string model = Environment.GetEnvironmentVariable("OPENAI_CHAT_MODEL_NAME") ?? "gpt-4o-mini"; // Create a ChatClient directly from OpenAIClient ChatClient chatClient = new OpenAIClient(apiKey).GetChatClient(model); diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/README.md b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/README.md similarity index 94% rename from dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/README.md rename to dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/README.md index a4d9dd76a5..9c91e964eb 100644 --- a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/README.md +++ b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/README.md @@ -13,7 +13,7 @@ This sample demonstrates how to create an AI agent directly from an `OpenAI.Chat 1. Set the required environment variables: ```bash set OPENAI_API_KEY=your_api_key_here - set OPENAI_MODEL=gpt-4o-mini + set OPENAI_CHAT_MODEL_NAME=gpt-4o-mini ``` 2. Run the sample: diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient.csproj b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient.csproj rename to dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient.csproj diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/OpenAIResponseClientAgent.cs b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/OpenAIResponseClientAgent.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/OpenAIResponseClientAgent.cs rename to dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/OpenAIResponseClientAgent.cs diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Program.cs b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Program.cs similarity index 93% rename from dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Program.cs rename to dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Program.cs index 5c229cc57d..8004770c21 100644 --- a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Program.cs +++ b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Program.cs @@ -7,7 +7,7 @@ using OpenAIResponseClientSample; var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set."); -var model = Environment.GetEnvironmentVariable("OPENAI_MODEL") ?? "gpt-4o-mini"; +var model = Environment.GetEnvironmentVariable("OPENAI_CHAT_MODEL_NAME") ?? "gpt-4o-mini"; // Create a ResponsesClient directly from OpenAIClient ResponsesClient responseClient = new OpenAIClient(apiKey).GetResponsesClient(model); diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/README.md b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/README.md similarity index 95% rename from dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/README.md rename to dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/README.md index 32e19caa62..1acbe3137d 100644 --- a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/README.md +++ b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/README.md @@ -13,7 +13,7 @@ This sample demonstrates how to create an AI agent directly from an `OpenAI.Resp 1. Set the required environment variables: ```bash set OPENAI_API_KEY=your_api_key_here - set OPENAI_MODEL=gpt-4o-mini + set OPENAI_CHAT_MODEL_NAME=gpt-4o-mini ``` 2. Run the sample: diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Agent_OpenAI_Step05_Conversation.csproj b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Agent_OpenAI_Step05_Conversation.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Agent_OpenAI_Step05_Conversation.csproj rename to dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Agent_OpenAI_Step05_Conversation.csproj diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Program.cs b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Program.cs similarity index 98% rename from dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Program.cs rename to dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Program.cs index 9659d3dc40..921acbad0d 100644 --- a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Program.cs +++ b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Program.cs @@ -15,7 +15,7 @@ using OpenAI.Conversations; string apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set."); -string model = Environment.GetEnvironmentVariable("OPENAI_MODEL") ?? "gpt-4o-mini"; +string model = Environment.GetEnvironmentVariable("OPENAI_CHAT_MODEL_NAME") ?? "gpt-4o-mini"; // Create a ConversationClient directly from OpenAIClient OpenAIClient openAIClient = new(apiKey); diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/README.md b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/README.md similarity index 98% rename from dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/README.md rename to dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/README.md index 9706ca05f1..1b4d393418 100644 --- a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/README.md +++ b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/README.md @@ -69,7 +69,7 @@ foreach (ClientResult result in getConversationItemsResults.GetRawPages()) 1. Set the required environment variables: ```powershell $env:OPENAI_API_KEY = "your_api_key_here" - $env:OPENAI_MODEL = "gpt-4o-mini" + $env:OPENAI_CHAT_MODEL_NAME = "gpt-4o-mini" ``` 2. Run the sample: diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/README.md b/dotnet/samples/02-agents/AgentWithOpenAI/README.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithOpenAI/README.md rename to dotnet/samples/02-agents/AgentWithOpenAI/README.md diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/AgentWithRAG_Step01_BasicTextRAG.csproj b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/AgentWithRAG_Step01_BasicTextRAG.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/AgentWithRAG_Step01_BasicTextRAG.csproj rename to dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/AgentWithRAG_Step01_BasicTextRAG.csproj diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/Program.cs b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/Program.cs rename to dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/Program.cs diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchDocument.cs b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchDocument.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchDocument.cs rename to dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchDocument.cs diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchStore.cs b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchStore.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchStore.cs rename to dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchStore.cs diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchStoreOptions.cs b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchStoreOptions.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchStoreOptions.cs rename to dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchStoreOptions.cs diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchStoreUpsertOptions.cs b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchStoreUpsertOptions.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchStoreUpsertOptions.cs rename to dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/TextSearchStore/TextSearchStoreUpsertOptions.cs diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/AgentWithRAG_Step02_CustomVectorStoreRAG.csproj b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/AgentWithRAG_Step02_CustomVectorStoreRAG.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/AgentWithRAG_Step02_CustomVectorStoreRAG.csproj rename to dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/AgentWithRAG_Step02_CustomVectorStoreRAG.csproj diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs rename to dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/README.md b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/README.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/README.md rename to dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/README.md diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/AgentWithRAG_Step03_CustomRAGDataSource.csproj b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/AgentWithRAG_Step03_CustomRAGDataSource.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/AgentWithRAG_Step03_CustomRAGDataSource.csproj rename to dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/AgentWithRAG_Step03_CustomRAGDataSource.csproj diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/Program.cs b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/Program.cs rename to dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/Program.cs diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/AgentWithRAG_Step04_FoundryServiceRAG.csproj b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/AgentWithRAG_Step04_FoundryServiceRAG.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/AgentWithRAG_Step04_FoundryServiceRAG.csproj rename to dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/AgentWithRAG_Step04_FoundryServiceRAG.csproj diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/Program.cs b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/Program.cs similarity index 90% rename from dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/Program.cs rename to dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/Program.cs index 4234be6c5a..c356bccbd9 100644 --- a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/Program.cs +++ b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/Program.cs @@ -11,8 +11,8 @@ using OpenAI.Files; using OpenAI.VectorStores; -var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -var deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +var deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; // Create an AI Project client and get an OpenAI client that works with the foundry service. // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/contoso-outdoors-knowledge-base.md b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/contoso-outdoors-knowledge-base.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/contoso-outdoors-knowledge-base.md rename to dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/contoso-outdoors-knowledge-base.md diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/README.md b/dotnet/samples/02-agents/AgentWithRAG/README.md similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithRAG/README.md rename to dotnet/samples/02-agents/AgentWithRAG/README.md diff --git a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step03_CustomMemory/AgentWithMemory_Step03_CustomMemory.csproj b/dotnet/samples/02-agents/Agents/Agent_Step01_UsingFunctionToolsWithApprovals/Agent_Step01_UsingFunctionToolsWithApprovals.csproj similarity index 100% rename from dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step03_CustomMemory/AgentWithMemory_Step03_CustomMemory.csproj rename to dotnet/samples/02-agents/Agents/Agent_Step01_UsingFunctionToolsWithApprovals/Agent_Step01_UsingFunctionToolsWithApprovals.csproj diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step04_UsingFunctionToolsWithApprovals/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step01_UsingFunctionToolsWithApprovals/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step04_UsingFunctionToolsWithApprovals/Program.cs rename to dotnet/samples/02-agents/Agents/Agent_Step01_UsingFunctionToolsWithApprovals/Program.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/AIAgentBuilderExtensions.cs b/dotnet/samples/02-agents/Agents/Agent_Step02_StructuredOutput/AIAgentBuilderExtensions.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/AIAgentBuilderExtensions.cs rename to dotnet/samples/02-agents/Agents/Agent_Step02_StructuredOutput/AIAgentBuilderExtensions.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step01_Running/Agent_Step01_Running.csproj b/dotnet/samples/02-agents/Agents/Agent_Step02_StructuredOutput/Agent_Step02_StructuredOutput.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step01_Running/Agent_Step01_Running.csproj rename to dotnet/samples/02-agents/Agents/Agent_Step02_StructuredOutput/Agent_Step02_StructuredOutput.csproj diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step02_StructuredOutput/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/Program.cs rename to dotnet/samples/02-agents/Agents/Agent_Step02_StructuredOutput/Program.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/README.md b/dotnet/samples/02-agents/Agents/Agent_Step02_StructuredOutput/README.md similarity index 97% rename from dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/README.md rename to dotnet/samples/02-agents/Agents/Agent_Step02_StructuredOutput/README.md index 2471e92194..5652fe9b0a 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/README.md +++ b/dotnet/samples/02-agents/Agents/Agent_Step02_StructuredOutput/README.md @@ -36,7 +36,7 @@ $env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o- Navigate to the sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput +cd dotnet/samples/02-agents/Agents/Agent_Step02_StructuredOutput dotnet run ``` diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/StructuredOutputAgent.cs b/dotnet/samples/02-agents/Agents/Agent_Step02_StructuredOutput/StructuredOutputAgent.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/StructuredOutputAgent.cs rename to dotnet/samples/02-agents/Agents/Agent_Step02_StructuredOutput/StructuredOutputAgent.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/StructuredOutputAgentOptions.cs b/dotnet/samples/02-agents/Agents/Agent_Step02_StructuredOutput/StructuredOutputAgentOptions.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/StructuredOutputAgentOptions.cs rename to dotnet/samples/02-agents/Agents/Agent_Step02_StructuredOutput/StructuredOutputAgentOptions.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/StructuredOutputAgentResponse.cs b/dotnet/samples/02-agents/Agents/Agent_Step02_StructuredOutput/StructuredOutputAgentResponse.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/StructuredOutputAgentResponse.cs rename to dotnet/samples/02-agents/Agents/Agent_Step02_StructuredOutput/StructuredOutputAgentResponse.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step02_MultiturnConversation/Agent_Step02_MultiturnConversation.csproj b/dotnet/samples/02-agents/Agents/Agent_Step03_PersistedConversations/Agent_Step03_PersistedConversations.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step02_MultiturnConversation/Agent_Step02_MultiturnConversation.csproj rename to dotnet/samples/02-agents/Agents/Agent_Step03_PersistedConversations/Agent_Step03_PersistedConversations.csproj diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step06_PersistedConversations/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step03_PersistedConversations/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step06_PersistedConversations/Program.cs rename to dotnet/samples/02-agents/Agents/Agent_Step03_PersistedConversations/Program.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyChatHistoryStorage/Agent_Step07_3rdPartyChatHistoryStorage.csproj b/dotnet/samples/02-agents/Agents/Agent_Step04_3rdPartyChatHistoryStorage/Agent_Step04_3rdPartyChatHistoryStorage.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyChatHistoryStorage/Agent_Step07_3rdPartyChatHistoryStorage.csproj rename to dotnet/samples/02-agents/Agents/Agent_Step04_3rdPartyChatHistoryStorage/Agent_Step04_3rdPartyChatHistoryStorage.csproj diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyChatHistoryStorage/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step04_3rdPartyChatHistoryStorage/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyChatHistoryStorage/Program.cs rename to dotnet/samples/02-agents/Agents/Agent_Step04_3rdPartyChatHistoryStorage/Program.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step08_Observability/Agent_Step08_Observability.csproj b/dotnet/samples/02-agents/Agents/Agent_Step05_Observability/Agent_Step05_Observability.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step08_Observability/Agent_Step08_Observability.csproj rename to dotnet/samples/02-agents/Agents/Agent_Step05_Observability/Agent_Step05_Observability.csproj diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step08_Observability/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step05_Observability/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step08_Observability/Program.cs rename to dotnet/samples/02-agents/Agents/Agent_Step05_Observability/Program.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step09_DependencyInjection/Agent_Step09_DependencyInjection.csproj b/dotnet/samples/02-agents/Agents/Agent_Step06_DependencyInjection/Agent_Step06_DependencyInjection.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step09_DependencyInjection/Agent_Step09_DependencyInjection.csproj rename to dotnet/samples/02-agents/Agents/Agent_Step06_DependencyInjection/Agent_Step06_DependencyInjection.csproj diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step09_DependencyInjection/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step06_DependencyInjection/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step09_DependencyInjection/Program.cs rename to dotnet/samples/02-agents/Agents/Agent_Step06_DependencyInjection/Program.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/Agent_Step10_AsMcpTool.csproj b/dotnet/samples/02-agents/Agents/Agent_Step07_AsMcpTool/Agent_Step07_AsMcpTool.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/Agent_Step10_AsMcpTool.csproj rename to dotnet/samples/02-agents/Agents/Agent_Step07_AsMcpTool/Agent_Step07_AsMcpTool.csproj diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step07_AsMcpTool/Program.cs similarity index 85% rename from dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/Program.cs rename to dotnet/samples/02-agents/Agents/Agent_Step07_AsMcpTool/Program.cs index 3ecad341b0..d621227ea0 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/Program.cs +++ b/dotnet/samples/02-agents/Agents/Agent_Step07_AsMcpTool/Program.cs @@ -9,8 +9,8 @@ using Microsoft.Extensions.Hosting; using ModelContextProtocol.Server; -var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -var deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +var deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/README.md b/dotnet/samples/02-agents/Agents/Agent_Step07_AsMcpTool/README.md similarity index 85% rename from dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/README.md rename to dotnet/samples/02-agents/Agents/Agent_Step07_AsMcpTool/README.md index 56701066d1..e35cf01e90 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/README.md +++ b/dotnet/samples/02-agents/Agents/Agent_Step07_AsMcpTool/README.md @@ -10,7 +10,7 @@ Alternatively, use the QuickstartClient sample from this repository: https://git To use the [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector), follow these steps: -1. Open a terminal in the Agent_Step10_AsMcpTool project directory. +1. Open a terminal in the Agent_Step07_AsMcpTool project directory. 1. Run the `npx @modelcontextprotocol/inspector dotnet run --framework net10.0` command to start the MCP Inspector. Make sure you have [node.js](https://nodejs.org/en/download/) and npm installed. ```bash npx @modelcontextprotocol/inspector dotnet run --framework net10.0 @@ -21,8 +21,8 @@ To use the [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector) ``` 1. Open a web browser and navigate to the URL displayed in the terminal. If not opened automatically, this will open the MCP Inspector interface. 1. In the MCP Inspector interface, add the following environment variables to allow your MCP server to access Azure AI Foundry Project to create and run the agent: - - AZURE_FOUNDRY_PROJECT_ENDPOINT = https://your-resource.openai.azure.com/ # Replace with your Azure AI Foundry Project endpoint - - AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME = gpt-4o-mini # Replace with your model deployment name + - AZURE_AI_PROJECT_ENDPOINT = https://your-resource.openai.azure.com/ # Replace with your Azure AI Foundry Project endpoint + - AZURE_AI_MODEL_DEPLOYMENT_NAME = gpt-4o-mini # Replace with your model deployment name 1. Find and click the `Connect` button in the MCP Inspector interface to connect to the MCP server. 1. As soon as the connection is established, open the `Tools` tab in the MCP Inspector interface and select the `Joker` tool from the list. 1. Specify your prompt as a value for the `query` argument, for example: `Tell me a joke about a pirate` and click the `Run Tool` button to run the tool. diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/Agent_Step11_UsingImages.csproj b/dotnet/samples/02-agents/Agents/Agent_Step08_UsingImages/Agent_Step08_UsingImages.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/Agent_Step11_UsingImages.csproj rename to dotnet/samples/02-agents/Agents/Agent_Step08_UsingImages/Agent_Step08_UsingImages.csproj diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step08_UsingImages/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/Program.cs rename to dotnet/samples/02-agents/Agents/Agent_Step08_UsingImages/Program.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/README.md b/dotnet/samples/02-agents/Agents/Agent_Step08_UsingImages/README.md similarity index 98% rename from dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/README.md rename to dotnet/samples/02-agents/Agents/Agent_Step08_UsingImages/README.md index 49d1bff4a2..e70c09f513 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/README.md +++ b/dotnet/samples/02-agents/Agents/Agent_Step08_UsingImages/README.md @@ -37,7 +37,7 @@ $env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o" # Replace with your model deployment Navigate to the sample directory and run: ```powershell -cd Agent_Step11_UsingImages +cd Agent_Step08_UsingImages dotnet run ``` diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step12_AsFunctionTool/Agent_Step12_AsFunctionTool.csproj b/dotnet/samples/02-agents/Agents/Agent_Step09_AsFunctionTool/Agent_Step09_AsFunctionTool.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step12_AsFunctionTool/Agent_Step12_AsFunctionTool.csproj rename to dotnet/samples/02-agents/Agents/Agent_Step09_AsFunctionTool/Agent_Step09_AsFunctionTool.csproj diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step12_AsFunctionTool/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step09_AsFunctionTool/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step12_AsFunctionTool/Program.cs rename to dotnet/samples/02-agents/Agents/Agent_Step09_AsFunctionTool/Program.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step13_BackgroundResponsesWithToolsAndPersistence/Agent_Step13_BackgroundResponsesWithToolsAndPersistence.csproj b/dotnet/samples/02-agents/Agents/Agent_Step10_BackgroundResponsesWithToolsAndPersistence/Agent_Step10_BackgroundResponsesWithToolsAndPersistence.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step13_BackgroundResponsesWithToolsAndPersistence/Agent_Step13_BackgroundResponsesWithToolsAndPersistence.csproj rename to dotnet/samples/02-agents/Agents/Agent_Step10_BackgroundResponsesWithToolsAndPersistence/Agent_Step10_BackgroundResponsesWithToolsAndPersistence.csproj diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step13_BackgroundResponsesWithToolsAndPersistence/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step10_BackgroundResponsesWithToolsAndPersistence/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step13_BackgroundResponsesWithToolsAndPersistence/Program.cs rename to dotnet/samples/02-agents/Agents/Agent_Step10_BackgroundResponsesWithToolsAndPersistence/Program.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step13_BackgroundResponsesWithToolsAndPersistence/README.md b/dotnet/samples/02-agents/Agents/Agent_Step10_BackgroundResponsesWithToolsAndPersistence/README.md similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step13_BackgroundResponsesWithToolsAndPersistence/README.md rename to dotnet/samples/02-agents/Agents/Agent_Step10_BackgroundResponsesWithToolsAndPersistence/README.md diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step14_Middleware/Agent_Step14_Middleware.csproj b/dotnet/samples/02-agents/Agents/Agent_Step11_Middleware/Agent_Step11_Middleware.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step14_Middleware/Agent_Step14_Middleware.csproj rename to dotnet/samples/02-agents/Agents/Agent_Step11_Middleware/Agent_Step11_Middleware.csproj diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step14_Middleware/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step11_Middleware/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step14_Middleware/Program.cs rename to dotnet/samples/02-agents/Agents/Agent_Step11_Middleware/Program.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step14_Middleware/README.md b/dotnet/samples/02-agents/Agents/Agent_Step11_Middleware/README.md similarity index 95% rename from dotnet/samples/GettingStarted/Agents/Agent_Step14_Middleware/README.md rename to dotnet/samples/02-agents/Agents/Agent_Step11_Middleware/README.md index 53a59c20a7..74895e0cdf 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step14_Middleware/README.md +++ b/dotnet/samples/02-agents/Agents/Agent_Step11_Middleware/README.md @@ -37,7 +37,7 @@ Attempting to use function middleware on agents that do not wrap a ChatClientAge Use PowerShell: ```powershell -cd dotnet/samples/GettingStarted/Agents/Agent_Step14_Middleware +cd dotnet/samples/02-agents/Agents/Agent_Step11_Middleware dotnet run ``` diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step15_Plugins/Agent_Step15_Plugins.csproj b/dotnet/samples/02-agents/Agents/Agent_Step12_Plugins/Agent_Step12_Plugins.csproj similarity index 92% rename from dotnet/samples/GettingStarted/Agents/Agent_Step15_Plugins/Agent_Step15_Plugins.csproj rename to dotnet/samples/02-agents/Agents/Agent_Step12_Plugins/Agent_Step12_Plugins.csproj index ae2f9ac194..122c2e77a4 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step15_Plugins/Agent_Step15_Plugins.csproj +++ b/dotnet/samples/02-agents/Agents/Agent_Step12_Plugins/Agent_Step12_Plugins.csproj @@ -7,7 +7,7 @@ enable enable $(NoWarn);CA1812 - Agent_Step15_Plugins + Agent_Step12_Plugins diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step15_Plugins/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step12_Plugins/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step15_Plugins/Program.cs rename to dotnet/samples/02-agents/Agents/Agent_Step12_Plugins/Program.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step03_UsingFunctionTools/Agent_Step03_UsingFunctionTools.csproj b/dotnet/samples/02-agents/Agents/Agent_Step13_ChatReduction/Agent_Step13_ChatReduction.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step03_UsingFunctionTools/Agent_Step03_UsingFunctionTools.csproj rename to dotnet/samples/02-agents/Agents/Agent_Step13_ChatReduction/Agent_Step13_ChatReduction.csproj diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step16_ChatReduction/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step13_ChatReduction/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step16_ChatReduction/Program.cs rename to dotnet/samples/02-agents/Agents/Agent_Step13_ChatReduction/Program.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step17_BackgroundResponses/Agent_Step17_BackgroundResponses.csproj b/dotnet/samples/02-agents/Agents/Agent_Step14_BackgroundResponses/Agent_Step14_BackgroundResponses.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step17_BackgroundResponses/Agent_Step17_BackgroundResponses.csproj rename to dotnet/samples/02-agents/Agents/Agent_Step14_BackgroundResponses/Agent_Step14_BackgroundResponses.csproj diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step17_BackgroundResponses/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step14_BackgroundResponses/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step17_BackgroundResponses/Program.cs rename to dotnet/samples/02-agents/Agents/Agent_Step14_BackgroundResponses/Program.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step17_BackgroundResponses/README.md b/dotnet/samples/02-agents/Agents/Agent_Step14_BackgroundResponses/README.md similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step17_BackgroundResponses/README.md rename to dotnet/samples/02-agents/Agents/Agent_Step14_BackgroundResponses/README.md diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step18_DeepResearch/Agent_Step18_DeepResearch.csproj b/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/Agent_Step15_DeepResearch.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step18_DeepResearch/Agent_Step18_DeepResearch.csproj rename to dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/Agent_Step15_DeepResearch.csproj diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step18_DeepResearch/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/Program.cs similarity index 82% rename from dotnet/samples/GettingStarted/Agents/Agent_Step18_DeepResearch/Program.cs rename to dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/Program.cs index c14b9e5b55..cbbc327948 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step18_DeepResearch/Program.cs +++ b/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/Program.cs @@ -6,10 +6,10 @@ using Azure.Identity; using Microsoft.Agents.AI; -var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -var deepResearchDeploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEEP_RESEARCH_DEPLOYMENT_NAME") ?? "o3-deep-research"; -var modelDeploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o"; -var bingConnectionId = Environment.GetEnvironmentVariable("BING_CONNECTION_ID") ?? throw new InvalidOperationException("BING_CONNECTION_ID is not set."); +var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +var deepResearchDeploymentName = Environment.GetEnvironmentVariable("AZURE_AI_REASONING_DEPLOYMENT_NAME") ?? "o3-deep-research"; +var modelDeploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o"; +var bingConnectionId = Environment.GetEnvironmentVariable("AZURE_AI_BING_CONNECTION_ID") ?? throw new InvalidOperationException("AZURE_AI_BING_CONNECTION_ID is not set."); // Configure extended network timeout for long-running Deep Research tasks. PersistentAgentsAdministrationClientOptions persistentAgentsClientOptions = new(); diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step18_DeepResearch/README.md b/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/README.md similarity index 85% rename from dotnet/samples/GettingStarted/Agents/Agent_Step18_DeepResearch/README.md rename to dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/README.md index 0404054306..dc24ba4554 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step18_DeepResearch/README.md +++ b/dotnet/samples/02-agents/Agents/Agent_Step15_DeepResearch/README.md @@ -35,13 +35,13 @@ Set the following environment variables: ```powershell # Replace with your Azure AI Foundry project endpoint -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com/" +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com/" # Replace with your Bing connection ID from the project -$env:BING_CONNECTION_ID="/subscriptions/.../connections/your-bing-connection" +$env:AZURE_AI_BING_CONNECTION_ID="/subscriptions/.../connections/your-bing-connection" # Optional, defaults to o3-deep-research -$env:AZURE_FOUNDRY_PROJECT_DEEP_RESEARCH_DEPLOYMENT_NAME="o3-deep-research" +$env:AZURE_AI_REASONING_DEPLOYMENT_NAME="o3-deep-research" # Optional, defaults to gpt-4o -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o" +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o" diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step19_Declarative/Agent_Step19_Declarative.csproj b/dotnet/samples/02-agents/Agents/Agent_Step16_Declarative/Agent_Step16_Declarative.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step19_Declarative/Agent_Step19_Declarative.csproj rename to dotnet/samples/02-agents/Agents/Agent_Step16_Declarative/Agent_Step16_Declarative.csproj diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step19_Declarative/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step16_Declarative/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step19_Declarative/Program.cs rename to dotnet/samples/02-agents/Agents/Agent_Step16_Declarative/Program.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step20_AdditionalAIContext/Agent_Step20_AdditionalAIContext.csproj b/dotnet/samples/02-agents/Agents/Agent_Step17_AdditionalAIContext/Agent_Step17_AdditionalAIContext.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step20_AdditionalAIContext/Agent_Step20_AdditionalAIContext.csproj rename to dotnet/samples/02-agents/Agents/Agent_Step17_AdditionalAIContext/Agent_Step17_AdditionalAIContext.csproj diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step20_AdditionalAIContext/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step17_AdditionalAIContext/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Agents/Agent_Step20_AdditionalAIContext/Program.cs rename to dotnet/samples/02-agents/Agents/Agent_Step17_AdditionalAIContext/Program.cs diff --git a/dotnet/samples/GettingStarted/Agents/README.md b/dotnet/samples/02-agents/Agents/README.md similarity index 75% rename from dotnet/samples/GettingStarted/Agents/README.md rename to dotnet/samples/02-agents/Agents/README.md index 64af82a23e..116cbfc06b 100644 --- a/dotnet/samples/GettingStarted/Agents/README.md +++ b/dotnet/samples/02-agents/Agents/README.md @@ -26,34 +26,31 @@ Before you begin, ensure you have the following prerequisites: |Sample|Description| |---|---| -|[Running a simple agent](./Agent_Step01_Running/)|This sample demonstrates how to create and run a basic agent with instructions| -|[Multi-turn conversation with a simple agent](./Agent_Step02_MultiturnConversation/)|This sample demonstrates how to implement a multi-turn conversation with a simple agent| -|[Using function tools with a simple agent](./Agent_Step03_UsingFunctionTools/)|This sample demonstrates how to use function tools with a simple agent| |[Using OpenAPI function tools with a simple agent](https://github.com/microsoft/semantic-kernel/tree/main/dotnet/samples/AgentFrameworkMigration/AzureOpenAI/Step04_ToolCall_WithOpenAPI)|This sample demonstrates how to create function tools from an OpenAPI spec and use them with a simple agent (note that this sample is in the Semantic Kernel repository)| -|[Using function tools with approvals](./Agent_Step04_UsingFunctionToolsWithApprovals/)|This sample demonstrates how to use function tools where approvals require human in the loop approvals before execution| -|[Structured output with a simple agent](./Agent_Step05_StructuredOutput/)|This sample demonstrates how to use structured output with a simple agent| -|[Persisted conversations with a simple agent](./Agent_Step06_PersistedConversations/)|This sample demonstrates how to persist conversations and reload them later. This is useful for cases where an agent is hosted in a stateless service| -|[3rd party chat history storage with a simple agent](./Agent_Step07_3rdPartyChatHistoryStorage/)|This sample demonstrates how to store chat history in a 3rd party storage solution| -|[Observability with a simple agent](./Agent_Step08_Observability/)|This sample demonstrates how to add telemetry to a simple agent| -|[Dependency injection with a simple agent](./Agent_Step09_DependencyInjection/)|This sample demonstrates how to add and resolve an agent with a dependency injection container| -|[Exposing a simple agent as MCP tool](./Agent_Step10_AsMcpTool/)|This sample demonstrates how to expose an agent as an MCP tool| -|[Using images with a simple agent](./Agent_Step11_UsingImages/)|This sample demonstrates how to use image multi-modality with an AI agent| -|[Exposing a simple agent as a function tool](./Agent_Step12_AsFunctionTool/)|This sample demonstrates how to expose an agent as a function tool| -|[Background responses with tools and persistence](./Agent_Step13_BackgroundResponsesWithToolsAndPersistence/)|This sample demonstrates advanced background response scenarios including function calling during background operations and state persistence| -|[Using middleware with an agent](./Agent_Step14_Middleware/)|This sample demonstrates how to use middleware with an agent| -|[Using plugins with an agent](./Agent_Step15_Plugins/)|This sample demonstrates how to use plugins with an agent| -|[Reducing chat history size](./Agent_Step16_ChatReduction/)|This sample demonstrates how to reduce the chat history to constrain its size, where chat history is maintained locally| -|[Background responses](./Agent_Step17_BackgroundResponses/)|This sample demonstrates how to use background responses for long-running operations with polling and resumption support| -|[Deep research with an agent](./Agent_Step18_DeepResearch/)|This sample demonstrates how to use the Deep Research Tool to perform comprehensive research on complex topics| -|[Declarative agent](./Agent_Step19_Declarative/)|This sample demonstrates how to declaratively define an agent.| -|[Providing additional AI Context to an agent using multiple AIContextProviders](./Agent_Step20_AdditionalAIContext/)|This sample demonstrates how to inject additional AI context into a ChatClientAgent using multiple custom AIContextProvider components that are attached to the agent.| +|[Using function tools with approvals](./Agent_Step01_UsingFunctionToolsWithApprovals/)|This sample demonstrates how to use function tools where approvals require human in the loop approvals before execution| +|[Structured output with a simple agent](./Agent_Step02_StructuredOutput/)|This sample demonstrates how to use structured output with a simple agent| +|[Persisted conversations with a simple agent](./Agent_Step03_PersistedConversations/)|This sample demonstrates how to persist conversations and reload them later. This is useful for cases where an agent is hosted in a stateless service| +|[3rd party chat history storage with a simple agent](./Agent_Step04_3rdPartyChatHistoryStorage/)|This sample demonstrates how to store chat history in a 3rd party storage solution| +|[Observability with a simple agent](./Agent_Step05_Observability/)|This sample demonstrates how to add telemetry to a simple agent| +|[Dependency injection with a simple agent](./Agent_Step06_DependencyInjection/)|This sample demonstrates how to add and resolve an agent with a dependency injection container| +|[Exposing a simple agent as MCP tool](./Agent_Step07_AsMcpTool/)|This sample demonstrates how to expose an agent as an MCP tool| +|[Using images with a simple agent](./Agent_Step08_UsingImages/)|This sample demonstrates how to use image multi-modality with an AI agent| +|[Exposing a simple agent as a function tool](./Agent_Step09_AsFunctionTool/)|This sample demonstrates how to expose an agent as a function tool| +|[Background responses with tools and persistence](./Agent_Step10_BackgroundResponsesWithToolsAndPersistence/)|This sample demonstrates advanced background response scenarios including function calling during background operations and state persistence| +|[Using middleware with an agent](./Agent_Step11_Middleware/)|This sample demonstrates how to use middleware with an agent| +|[Using plugins with an agent](./Agent_Step12_Plugins/)|This sample demonstrates how to use plugins with an agent| +|[Reducing chat history size](./Agent_Step13_ChatReduction/)|This sample demonstrates how to reduce the chat history to constrain its size, where chat history is maintained locally| +|[Background responses](./Agent_Step14_BackgroundResponses/)|This sample demonstrates how to use background responses for long-running operations with polling and resumption support| +|[Deep research with an agent](./Agent_Step15_DeepResearch/)|This sample demonstrates how to use the Deep Research Tool to perform comprehensive research on complex topics| +|[Declarative agent](./Agent_Step16_Declarative/)|This sample demonstrates how to declaratively define an agent.| +|[Providing additional AI Context to an agent using multiple AIContextProviders](./Agent_Step17_AdditionalAIContext/)|This sample demonstrates how to inject additional AI context into a ChatClientAgent using multiple custom AIContextProvider components that are attached to the agent.| ## Running the samples from the console To run the samples, navigate to the desired sample directory, e.g. ```powershell -cd Agents_Step01_Running +cd Agent_Step01_UsingFunctionToolsWithApprovals ``` Set the following environment variables: diff --git a/dotnet/samples/GettingStarted/DeclarativeAgents/ChatClient/DeclarativeChatClientAgents.csproj b/dotnet/samples/02-agents/DeclarativeAgents/ChatClient/DeclarativeChatClientAgents.csproj similarity index 100% rename from dotnet/samples/GettingStarted/DeclarativeAgents/ChatClient/DeclarativeChatClientAgents.csproj rename to dotnet/samples/02-agents/DeclarativeAgents/ChatClient/DeclarativeChatClientAgents.csproj diff --git a/dotnet/samples/GettingStarted/DeclarativeAgents/ChatClient/Program.cs b/dotnet/samples/02-agents/DeclarativeAgents/ChatClient/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/DeclarativeAgents/ChatClient/Program.cs rename to dotnet/samples/02-agents/DeclarativeAgents/ChatClient/Program.cs diff --git a/dotnet/samples/GettingStarted/DeclarativeAgents/ChatClient/Properties/launchSettings.json b/dotnet/samples/02-agents/DeclarativeAgents/ChatClient/Properties/launchSettings.json similarity index 100% rename from dotnet/samples/GettingStarted/DeclarativeAgents/ChatClient/Properties/launchSettings.json rename to dotnet/samples/02-agents/DeclarativeAgents/ChatClient/Properties/launchSettings.json diff --git a/dotnet/samples/GettingStarted/DevUI/DevUI_Step01_BasicUsage/DevUI_Step01_BasicUsage.csproj b/dotnet/samples/02-agents/DevUI/DevUI_Step01_BasicUsage/DevUI_Step01_BasicUsage.csproj similarity index 100% rename from dotnet/samples/GettingStarted/DevUI/DevUI_Step01_BasicUsage/DevUI_Step01_BasicUsage.csproj rename to dotnet/samples/02-agents/DevUI/DevUI_Step01_BasicUsage/DevUI_Step01_BasicUsage.csproj diff --git a/dotnet/samples/GettingStarted/DevUI/DevUI_Step01_BasicUsage/Program.cs b/dotnet/samples/02-agents/DevUI/DevUI_Step01_BasicUsage/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/DevUI/DevUI_Step01_BasicUsage/Program.cs rename to dotnet/samples/02-agents/DevUI/DevUI_Step01_BasicUsage/Program.cs diff --git a/dotnet/samples/GettingStarted/DevUI/DevUI_Step01_BasicUsage/Properties/launchSettings.json b/dotnet/samples/02-agents/DevUI/DevUI_Step01_BasicUsage/Properties/launchSettings.json similarity index 100% rename from dotnet/samples/GettingStarted/DevUI/DevUI_Step01_BasicUsage/Properties/launchSettings.json rename to dotnet/samples/02-agents/DevUI/DevUI_Step01_BasicUsage/Properties/launchSettings.json diff --git a/dotnet/samples/GettingStarted/DevUI/DevUI_Step01_BasicUsage/README.md b/dotnet/samples/02-agents/DevUI/DevUI_Step01_BasicUsage/README.md similarity index 100% rename from dotnet/samples/GettingStarted/DevUI/DevUI_Step01_BasicUsage/README.md rename to dotnet/samples/02-agents/DevUI/DevUI_Step01_BasicUsage/README.md diff --git a/dotnet/samples/GettingStarted/DevUI/README.md b/dotnet/samples/02-agents/DevUI/README.md similarity index 100% rename from dotnet/samples/GettingStarted/DevUI/README.md rename to dotnet/samples/02-agents/DevUI/README.md diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/FoundryAgents_Evaluations_Step01_RedTeaming.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/FoundryAgents_Evaluations_Step01_RedTeaming.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/FoundryAgents_Evaluations_Step01_RedTeaming.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/FoundryAgents_Evaluations_Step01_RedTeaming.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/Program.cs similarity index 94% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/Program.cs index 93a34428c8..60a859c28f 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/Program.cs @@ -13,8 +13,8 @@ using Azure.AI.Projects; using Azure.Identity; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; Console.WriteLine("=" + new string('=', 79)); Console.WriteLine("RED TEAMING EVALUATION SAMPLE"); diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/README.md similarity index 92% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/README.md index f46c7af8ef..24e4a62b35 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming/README.md @@ -32,8 +32,8 @@ Red teaming is only available in regions that support risk and safety evaluators Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com/api/projects/your-project" # Replace with your Azure Foundry project endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com/api/projects/your-project" # Replace with your Azure Foundry project endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` ## Run the sample @@ -41,7 +41,7 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults t Navigate to the sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming +cd dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step01_RedTeaming dotnet run ``` diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/FoundryAgents_Evaluations_Step02_SelfReflection.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/FoundryAgents_Evaluations_Step02_SelfReflection.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/FoundryAgents_Evaluations_Step02_SelfReflection.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/FoundryAgents_Evaluations_Step02_SelfReflection.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/Program.cs similarity index 98% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/Program.cs index 3faf740c0a..8f8c9fa4ee 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/Program.cs @@ -24,8 +24,8 @@ using ChatMessage = Microsoft.Extensions.AI.ChatMessage; using ChatRole = Microsoft.Extensions.AI.ChatRole; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; string openAiEndpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); string evaluatorDeploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? deploymentName; diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/README.md similarity index 94% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/README.md index 8dcb22bd3c..d71eeca6af 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection/README.md @@ -37,9 +37,9 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-project.api.azureml.ms" # Azure Foundry project endpoint +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-project.api.azureml.ms" # Azure Foundry project endpoint $env:AZURE_OPENAI_ENDPOINT="https://your-openai.openai.azure.com/" # Azure OpenAI endpoint (for quality evaluators) -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Model deployment name +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Model deployment name ``` **Note**: For best evaluation results, use GPT-4o or GPT-4o-mini as the evaluator model. The groundedness evaluator has been tested and tuned for these models. @@ -49,7 +49,7 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Mod Navigate to the sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection +cd dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Evaluations_Step02_SelfReflection dotnet run ``` diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step01.1_Basics/FoundryAgents_Step01.1_Basics.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/FoundryAgents_Step01.1_Basics.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step01.1_Basics/FoundryAgents_Step01.1_Basics.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/FoundryAgents_Step01.1_Basics.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step01.1_Basics/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/Program.cs similarity index 91% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step01.1_Basics/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/Program.cs index 72450b1ae4..72676bed45 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step01.1_Basics/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/Program.cs @@ -7,8 +7,8 @@ using Azure.Identity; using Microsoft.Agents.AI; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; const string JokerName = "JokerAgent"; diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step01.1_Basics/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/README.md similarity index 82% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step01.1_Basics/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/README.md index ce56e05755..ce5eca8277 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step01.1_Basics/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/README.md @@ -19,8 +19,8 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` ## Run the sample @@ -28,7 +28,7 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults t Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents +cd dotnet/samples/02-agents/FoundryAgents dotnet run --project .\FoundryAgents_Step01.1_Basics ``` diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step01.2_Running/FoundryAgents_Step01.2_Running.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/FoundryAgents_Step01.2_Running.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step01.2_Running/FoundryAgents_Step01.2_Running.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/FoundryAgents_Step01.2_Running.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step01.2_Running/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/Program.cs similarity index 89% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step01.2_Running/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/Program.cs index 79ded7c557..dd5db03b15 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step01.2_Running/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/Program.cs @@ -7,8 +7,8 @@ using Azure.Identity; using Microsoft.Agents.AI; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; const string JokerInstructions = "You are good at telling jokes."; const string JokerName = "JokerAgent"; diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step01.2_Running/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/README.md similarity index 81% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step01.2_Running/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/README.md index 53254e1975..40cb5e107d 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step01.2_Running/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/README.md @@ -22,8 +22,8 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` ## Run the sample @@ -31,7 +31,7 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults t Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents +cd dotnet/samples/02-agents/FoundryAgents dotnet run --project .\FoundryAgents_Step01.2_Running ``` diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/FoundryAgents_Step02_MultiturnConversation.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/FoundryAgents_Step02_MultiturnConversation.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/FoundryAgents_Step02_MultiturnConversation.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/FoundryAgents_Step02_MultiturnConversation.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/Program.cs similarity index 92% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/Program.cs index d74180db14..1ac51c30ad 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/Program.cs @@ -7,8 +7,8 @@ using Azure.Identity; using Microsoft.Agents.AI; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; const string JokerInstructions = "You are good at telling jokes."; const string JokerName = "JokerAgent"; diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/README.md similarity index 88% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/README.md index 6611287bd9..86721bf960 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/README.md @@ -24,8 +24,8 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` ## Run the sample @@ -33,7 +33,7 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults t Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents +cd dotnet/samples/02-agents/FoundryAgents dotnet run --project .\FoundryAgents_Step02_MultiturnConversation ``` diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/FoundryAgents_Step03_UsingFunctionTools.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/FoundryAgents_Step03_UsingFunctionTools.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/FoundryAgents_Step03_UsingFunctionTools.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/FoundryAgents_Step03_UsingFunctionTools.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/Program.cs similarity index 92% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/Program.cs index 43ff40aa04..cfd74000a6 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/Program.cs @@ -9,8 +9,8 @@ using Microsoft.Agents.AI; using Microsoft.Extensions.AI; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; [Description("Get the weather for a given location.")] static string GetWeather([Description("The location to get the weather for.")] string location) diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/README.md similarity index 83% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/README.md index 35bef8a999..fa9b5baf21 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step03_UsingFunctionTools/README.md @@ -23,8 +23,8 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` ## Run the sample @@ -32,7 +32,7 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults t Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents +cd dotnet/samples/02-agents/FoundryAgents dotnet run --project .\FoundryAgents_Step03.1_UsingFunctionTools ``` diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/FoundryAgents_Step04_UsingFunctionToolsWithApprovals.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/FoundryAgents_Step04_UsingFunctionToolsWithApprovals.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/FoundryAgents_Step04_UsingFunctionToolsWithApprovals.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/FoundryAgents_Step04_UsingFunctionToolsWithApprovals.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/Program.cs similarity index 93% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/Program.cs index f7a847aeb4..f33fae35f4 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/Program.cs @@ -11,8 +11,8 @@ using Microsoft.Agents.AI; using Microsoft.Extensions.AI; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; // Create a sample function tool that the agent can use. [Description("Get the weather for a given location.")] diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/README.md similarity index 85% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/README.md index 5a797acd0f..42cbd6ba32 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/README.md @@ -23,8 +23,8 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` ## Run the sample @@ -32,7 +32,7 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults t Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents +cd dotnet/samples/02-agents/FoundryAgents dotnet run --project .\FoundryAgents_Step04_UsingFunctionToolsWithApprovals ``` diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step05_StructuredOutput/FoundryAgents_Step05_StructuredOutput.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step05_StructuredOutput/FoundryAgents_Step05_StructuredOutput.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step05_StructuredOutput/FoundryAgents_Step05_StructuredOutput.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step05_StructuredOutput/FoundryAgents_Step05_StructuredOutput.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step05_StructuredOutput/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step05_StructuredOutput/Program.cs similarity index 94% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step05_StructuredOutput/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step05_StructuredOutput/Program.cs index 9d031bc64b..3c02a4cec2 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step05_StructuredOutput/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step05_StructuredOutput/Program.cs @@ -12,8 +12,8 @@ #pragma warning disable CA5399 -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; const string AssistantInstructions = "You are a helpful assistant that extracts structured information about people."; const string AssistantName = "StructuredOutputAssistant"; diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step05_StructuredOutput/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step05_StructuredOutput/README.md similarity index 83% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step05_StructuredOutput/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step05_StructuredOutput/README.md index 956a2542e9..4c44230e18 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step05_StructuredOutput/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step05_StructuredOutput/README.md @@ -23,8 +23,8 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` ## Run the sample @@ -32,7 +32,7 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults t Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents +cd dotnet/samples/02-agents/FoundryAgents dotnet run --project .\FoundryAgents_Step05_StructuredOutput ``` diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step06_PersistedConversations/FoundryAgents_Step06_PersistedConversations.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step06_PersistedConversations/FoundryAgents_Step06_PersistedConversations.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step06_PersistedConversations/FoundryAgents_Step06_PersistedConversations.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step06_PersistedConversations/FoundryAgents_Step06_PersistedConversations.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step06_PersistedConversations/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step06_PersistedConversations/Program.cs similarity index 91% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step06_PersistedConversations/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step06_PersistedConversations/Program.cs index 2d163b0fb1..d8a5a7cd35 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step06_PersistedConversations/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step06_PersistedConversations/Program.cs @@ -7,8 +7,8 @@ using Azure.Identity; using Microsoft.Agents.AI; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; const string JokerInstructions = "You are good at telling jokes."; const string JokerName = "JokerAgent"; diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step06_PersistedConversations/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step06_PersistedConversations/README.md similarity index 83% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step06_PersistedConversations/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step06_PersistedConversations/README.md index 29c2233748..57a032e9ec 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step06_PersistedConversations/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step06_PersistedConversations/README.md @@ -23,8 +23,8 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` ## Run the sample @@ -32,7 +32,7 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults t Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents +cd dotnet/samples/02-agents/FoundryAgents dotnet run --project .\FoundryAgents_Step06_PersistedConversations ``` diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step07_Observability/FoundryAgents_Step07_Observability.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step07_Observability/FoundryAgents_Step07_Observability.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step07_Observability/FoundryAgents_Step07_Observability.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step07_Observability/FoundryAgents_Step07_Observability.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step07_Observability/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step07_Observability/Program.cs similarity index 91% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step07_Observability/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step07_Observability/Program.cs index 35bac61639..257e24859f 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step07_Observability/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step07_Observability/Program.cs @@ -9,8 +9,8 @@ using OpenTelemetry; using OpenTelemetry.Trace; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; string? applicationInsightsConnectionString = Environment.GetEnvironmentVariable("APPLICATIONINSIGHTS_CONNECTION_STRING"); const string JokerInstructions = "You are good at telling jokes."; diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step07_Observability/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step07_Observability/README.md similarity index 85% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step07_Observability/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step07_Observability/README.md index 30f7014dff..459434bce2 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step07_Observability/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step07_Observability/README.md @@ -25,8 +25,8 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini $env:APPLICATIONINSIGHTS_CONNECTION_STRING="your-connection-string" # Optional, for Azure Monitor integration ``` @@ -35,7 +35,7 @@ $env:APPLICATIONINSIGHTS_CONNECTION_STRING="your-connection-string" # Optional, Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents +cd dotnet/samples/02-agents/FoundryAgents dotnet run --project .\FoundryAgents_Step07_Observability ``` diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step08_DependencyInjection/FoundryAgents_Step08_DependencyInjection.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step08_DependencyInjection/FoundryAgents_Step08_DependencyInjection.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step08_DependencyInjection/FoundryAgents_Step08_DependencyInjection.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step08_DependencyInjection/FoundryAgents_Step08_DependencyInjection.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step08_DependencyInjection/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step08_DependencyInjection/Program.cs similarity index 94% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step08_DependencyInjection/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step08_DependencyInjection/Program.cs index 16708c4e99..b7a9874e7b 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step08_DependencyInjection/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step08_DependencyInjection/Program.cs @@ -9,8 +9,8 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; const string JokerInstructions = "You are good at telling jokes."; const string JokerName = "JokerAgent"; diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step08_DependencyInjection/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step08_DependencyInjection/README.md similarity index 84% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step08_DependencyInjection/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step08_DependencyInjection/README.md index 580821bb0a..12760e736f 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step08_DependencyInjection/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step08_DependencyInjection/README.md @@ -24,8 +24,8 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` ## Run the sample @@ -33,7 +33,7 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults t Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents +cd dotnet/samples/02-agents/FoundryAgents dotnet run --project .\FoundryAgents_Step08_DependencyInjection ``` diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/FoundryAgents_Step09_UsingMcpClientAsTools.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/FoundryAgents_Step09_UsingMcpClientAsTools.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/FoundryAgents_Step09_UsingMcpClientAsTools.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/FoundryAgents_Step09_UsingMcpClientAsTools.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/Program.cs similarity index 90% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/Program.cs index e33f754912..e1968122a4 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/Program.cs @@ -8,8 +8,8 @@ using Microsoft.Extensions.AI; using ModelContextProtocol.Client; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; Console.WriteLine("Starting MCP Stdio for @modelcontextprotocol/server-github ... "); diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/README.md similarity index 85% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/README.md index b2d923fc2f..e4e3fe537a 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step09_UsingMcpClientAsTools/README.md @@ -24,8 +24,8 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` ## Run the sample @@ -33,7 +33,7 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults t Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents +cd dotnet/samples/02-agents/FoundryAgents dotnet run --project .\FoundryAgents_Step09_UsingMcpClientAsTools ``` diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step10_UsingImages/Assets/walkway.jpg b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/Assets/walkway.jpg similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step10_UsingImages/Assets/walkway.jpg rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/Assets/walkway.jpg diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step10_UsingImages/FoundryAgents_Step10_UsingImages.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/FoundryAgents_Step10_UsingImages.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step10_UsingImages/FoundryAgents_Step10_UsingImages.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/FoundryAgents_Step10_UsingImages.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step10_UsingImages/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/Program.cs similarity index 87% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step10_UsingImages/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/Program.cs index 732ca25dde..d44d62df51 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step10_UsingImages/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/Program.cs @@ -7,8 +7,8 @@ using Microsoft.Agents.AI; using Microsoft.Extensions.AI; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o"; const string VisionInstructions = "You are a helpful agent that can analyze images"; const string VisionName = "VisionAgent"; diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step10_UsingImages/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/README.md similarity index 84% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step10_UsingImages/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/README.md index d90f5cf208..220104a291 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step10_UsingImages/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step10_UsingImages/README.md @@ -29,8 +29,8 @@ Before running this sample, ensure you have: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-resource.openai.azure.com/" # Replace with your Azure Foundry Project endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o" # Replace with your model deployment name (optional, defaults to gpt-4o) +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-resource.openai.azure.com/" # Replace with your Azure Foundry Project endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o" # Replace with your model deployment name (optional, defaults to gpt-4o) ``` ## Run the sample @@ -38,7 +38,7 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o" # Replace with your model de Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents +cd dotnet/samples/02-agents/FoundryAgents dotnet run --project .\FoundryAgents_Step10_UsingImages ``` diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/FoundryAgents_Step11_AsFunctionTool.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/FoundryAgents_Step11_AsFunctionTool.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/FoundryAgents_Step11_AsFunctionTool.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/FoundryAgents_Step11_AsFunctionTool.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/Program.cs similarity index 90% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/Program.cs index 7bbe478b4c..585725322e 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/Program.cs @@ -8,8 +8,8 @@ using Microsoft.Agents.AI; using Microsoft.Extensions.AI; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; const string WeatherInstructions = "You answer questions about the weather."; const string WeatherName = "WeatherAgent"; diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/README.md similarity index 83% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/README.md index 4b64b7e712..5da59b6edb 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step11_AsFunctionTool/README.md @@ -23,8 +23,8 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` ## Run the sample @@ -32,7 +32,7 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults t Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents +cd dotnet/samples/02-agents/FoundryAgents dotnet run --project .\FoundryAgents_Step11_AsFunctionTool ``` diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step12_Middleware/FoundryAgents_Step12_Middleware.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/FoundryAgents_Step12_Middleware.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step12_Middleware/FoundryAgents_Step12_Middleware.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/FoundryAgents_Step12_Middleware.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step12_Middleware/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/Program.cs similarity index 97% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step12_Middleware/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/Program.cs index c91cc6a6f1..7ea6bc88a3 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step12_Middleware/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/Program.cs @@ -13,8 +13,8 @@ using Microsoft.Extensions.AI; // Get Azure AI Foundry configuration from environment variables -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = System.Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = System.Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o"; const string AssistantInstructions = "You are an AI assistant that helps people find information."; const string AssistantName = "InformationAssistant"; diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step12_Middleware/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/README.md similarity index 89% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step12_Middleware/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/README.md index eaaafe7ab1..96d12d9828 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step12_Middleware/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/README.md @@ -30,8 +30,8 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` ## Running the Sample @@ -39,7 +39,7 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults t Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents +cd dotnet/samples/02-agents/FoundryAgents dotnet run --project .\FoundryAgents_Step12_Middleware ``` diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step13_Plugins/FoundryAgents_Step13_Plugins.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step13_Plugins/FoundryAgents_Step13_Plugins.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step13_Plugins/FoundryAgents_Step13_Plugins.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step13_Plugins/FoundryAgents_Step13_Plugins.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step13_Plugins/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step13_Plugins/Program.cs similarity index 96% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step13_Plugins/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step13_Plugins/Program.cs index 1f18dc21db..244d83d632 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step13_Plugins/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step13_Plugins/Program.cs @@ -15,8 +15,8 @@ using Microsoft.Extensions.AI; using Microsoft.Extensions.DependencyInjection; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; const string AssistantInstructions = "You are a helpful assistant that helps people find information."; const string AssistantName = "PluginAssistant"; diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step13_Plugins/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step13_Plugins/README.md similarity index 83% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step13_Plugins/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step13_Plugins/README.md index 0aeccf5789..5c52ffcd1c 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step13_Plugins/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step13_Plugins/README.md @@ -23,8 +23,8 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` ## Run the sample @@ -32,7 +32,7 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults t Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents +cd dotnet/samples/02-agents/FoundryAgents dotnet run --project .\FoundryAgents_Step13_Plugins ``` diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/FoundryAgents_Step14_CodeInterpreter.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/FoundryAgents_Step14_CodeInterpreter.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/FoundryAgents_Step14_CodeInterpreter.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/FoundryAgents_Step14_CodeInterpreter.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/Program.cs similarity index 94% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/Program.cs index 77bfa6f8d7..854d317495 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/Program.cs @@ -11,8 +11,8 @@ using OpenAI.Assistants; using OpenAI.Responses; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; const string AgentInstructions = "You are a personal math tutor. When asked a math question, write and run code using the python tool to answer the question."; const string AgentNameMEAI = "CoderAgent-MEAI"; diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/README.md similarity index 86% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/README.md index a3dd4d50b9..34fa18c94c 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/README.md @@ -24,8 +24,8 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` ## Run the sample @@ -33,7 +33,7 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults t Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents +cd dotnet/samples/02-agents/FoundryAgents dotnet run --project .\FoundryAgents_Step14_CodeInterpreter ``` diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_browser_search.png b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_browser_search.png similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_browser_search.png rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_browser_search.png diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_search_results.png b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_search_results.png similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_search_results.png rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_search_results.png diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_search_typed.png b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_search_typed.png similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_search_typed.png rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Assets/cua_search_typed.png diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step15_ComputerUse/ComputerUseUtil.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/ComputerUseUtil.cs similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step15_ComputerUse/ComputerUseUtil.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/ComputerUseUtil.cs diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step15_ComputerUse/FoundryAgents_Step15_ComputerUse.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/FoundryAgents_Step15_ComputerUse.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step15_ComputerUse/FoundryAgents_Step15_ComputerUse.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/FoundryAgents_Step15_ComputerUse.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step15_ComputerUse/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Program.cs similarity index 97% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step15_ComputerUse/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Program.cs index 6cfab6edbe..1c5510218a 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step15_ComputerUse/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Program.cs @@ -15,8 +15,8 @@ internal sealed class Program { private static async Task Main(string[] args) { - string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); - string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "computer-use-preview"; + string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); + string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "computer-use-preview"; // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step15_ComputerUse/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/README.md similarity index 89% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step15_ComputerUse/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/README.md index a44227f6c7..092f2bd1cf 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step15_ComputerUse/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/README.md @@ -35,8 +35,8 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="computer-use-preview" # Optional, defaults to computer-use-preview +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="computer-use-preview" # Optional, defaults to computer-use-preview ``` ## Run the sample @@ -44,7 +44,7 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="computer-use-preview" # Optional, d Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents +cd dotnet/samples/02-agents/FoundryAgents dotnet run --project .\FoundryAgents_Step15_ComputerUse ``` diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch/FoundryAgents_Step18_FileSearch.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/FoundryAgents_Step16_FileSearch.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch/FoundryAgents_Step18_FileSearch.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/FoundryAgents_Step16_FileSearch.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/Program.cs similarity index 95% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/Program.cs index 7b133e0651..36f28c2387 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/Program.cs @@ -11,8 +11,8 @@ using OpenAI.Files; using OpenAI.Responses; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; const string AgentInstructions = "You are a helpful assistant that can search through uploaded files to answer questions."; diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/README.md similarity index 83% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/README.md index e8f431ba44..db74868d3d 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/README.md @@ -24,8 +24,8 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` ## Run the sample @@ -33,8 +33,8 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults t Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents -dotnet run --project .\FoundryAgents_Step18_FileSearch +cd dotnet/samples/02-agents/FoundryAgents +dotnet run --project .\FoundryAgents_Step16_FileSearch ``` ## Expected behavior diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step19_OpenAPITools/FoundryAgents_Step19_OpenAPITools.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/FoundryAgents_Step17_OpenAPITools.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step19_OpenAPITools/FoundryAgents_Step19_OpenAPITools.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/FoundryAgents_Step17_OpenAPITools.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step19_OpenAPITools/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/Program.cs similarity index 94% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step19_OpenAPITools/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/Program.cs index 8d17bb0f91..2ee5a94458 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step19_OpenAPITools/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/Program.cs @@ -9,8 +9,8 @@ using OpenAI.Responses; // Warning: DefaultAzureCredential is intended for simplicity in development. For production scenarios, consider using a more specific credential. -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; const string AgentInstructions = "You are a helpful assistant that can use the countries API to retrieve information about countries by their currency code."; diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step19_OpenAPITools/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/README.md similarity index 79% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step19_OpenAPITools/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/README.md index e7a2f2cb52..a859f6b963 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step19_OpenAPITools/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/README.md @@ -1,4 +1,4 @@ -# Using OpenAPI Tools with AI Agents +# Using OpenAPI Tools with AI Agents This sample demonstrates how to use OpenAPI tools with AI agents. OpenAPI tools allow agents to call external REST APIs defined by OpenAPI specifications. @@ -23,8 +23,8 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` ## Run the sample @@ -32,8 +32,8 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults t Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents -dotnet run --project .\FoundryAgents_Step19_OpenAPITools +cd dotnet/samples/02-agents/FoundryAgents +dotnet run --project .\FoundryAgents_Step17_OpenAPITools ``` ## Expected behavior diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step21_BingCustomSearch/FoundryAgents_Step21_BingCustomSearch.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/FoundryAgents_Step18_BingCustomSearch.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step21_BingCustomSearch/FoundryAgents_Step21_BingCustomSearch.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/FoundryAgents_Step18_BingCustomSearch.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step21_BingCustomSearch/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/Program.cs similarity index 82% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step21_BingCustomSearch/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/Program.cs index 33ee4ec511..365bf6ed08 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step21_BingCustomSearch/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/Program.cs @@ -8,10 +8,10 @@ using Microsoft.Agents.AI; using OpenAI.Responses; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; -string connectionId = Environment.GetEnvironmentVariable("BING_CUSTOM_SEARCH_PROJECT_CONNECTION_ID") ?? throw new InvalidOperationException("BING_CUSTOM_SEARCH_PROJECT_CONNECTION_ID is not set."); -string instanceName = Environment.GetEnvironmentVariable("BING_CUSTOM_SEARCH_INSTANCE_NAME") ?? throw new InvalidOperationException("BING_CUSTOM_SEARCH_INSTANCE_NAME is not set."); +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string connectionId = Environment.GetEnvironmentVariable("AZURE_AI_CUSTOM_SEARCH_CONNECTION_ID") ?? throw new InvalidOperationException("AZURE_AI_CUSTOM_SEARCH_CONNECTION_ID is not set."); +string instanceName = Environment.GetEnvironmentVariable("AZURE_AI_CUSTOM_SEARCH_INSTANCE_NAME") ?? throw new InvalidOperationException("AZURE_AI_CUSTOM_SEARCH_INSTANCE_NAME is not set."); const string AgentInstructions = """ You are a helpful agent that can use Bing Custom Search tools to assist users. diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step21_BingCustomSearch/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/README.md similarity index 95% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step21_BingCustomSearch/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/README.md index 86f12c71bd..ccc1873a04 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step21_BingCustomSearch/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/README.md @@ -1,4 +1,4 @@ -# Using Bing Custom Search with AI Agents +# Using Bing Custom Search with AI Agents This sample demonstrates how to use the Bing Custom Search tool with AI agents to perform customized web searches. @@ -49,8 +49,8 @@ $env:BING_CUSTOM_SEARCH_INSTANCE_NAME="your-configuration-name" Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents -dotnet run --project .\FoundryAgents_Step21_BingCustomSearch +cd dotnet/samples/02-agents/FoundryAgents +dotnet run --project .\FoundryAgents_Step18_BingCustomSearch ``` ## Expected behavior diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step22_SharePoint/FoundryAgents_Step22_SharePoint.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/FoundryAgents_Step19_SharePoint.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step22_SharePoint/FoundryAgents_Step22_SharePoint.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/FoundryAgents_Step19_SharePoint.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step22_SharePoint/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step22_SharePoint/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/Program.cs diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step22_SharePoint/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/README.md similarity index 96% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step22_SharePoint/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/README.md index 9f89aaf652..ccbd699011 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step22_SharePoint/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/README.md @@ -33,8 +33,8 @@ $env:SHAREPOINT_PROJECT_CONNECTION_ID="your-sharepoint-connection-id" # Require Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents -dotnet run --project .\FoundryAgents_Step22_SharePoint +cd dotnet/samples/02-agents/FoundryAgents +dotnet run --project .\FoundryAgents_Step19_SharePoint ``` ## Expected behavior diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step25_WebSearch/FoundryAgents_Step25_WebSearch.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/FoundryAgents_Step20_MicrosoftFabric.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step25_WebSearch/FoundryAgents_Step25_WebSearch.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/FoundryAgents_Step20_MicrosoftFabric.csproj diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/Program.cs new file mode 100644 index 0000000000..2f13c2c30c --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/Program.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to use Microsoft Fabric Tool with AI Agents. + +using Azure.AI.Projects; +using Azure.AI.Projects.OpenAI; +using Azure.Identity; +using Microsoft.Agents.AI; +using OpenAI.Responses; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string fabricConnectionId = Environment.GetEnvironmentVariable("FABRIC_PROJECT_CONNECTION_ID") ?? throw new InvalidOperationException("FABRIC_PROJECT_CONNECTION_ID is not set."); + +const string AgentInstructions = "You are a helpful assistant with access to Microsoft Fabric data. Answer questions based on data available through your Fabric connection."; + +// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents. +// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. +// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid +// latency issues, unintended credential probing, and potential security risks from fallback mechanisms. +AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential()); + +// Configure Microsoft Fabric tool options with project connection +var fabricToolOptions = new FabricDataAgentToolOptions(); +fabricToolOptions.ProjectConnections.Add(new ToolProjectConnection(fabricConnectionId)); + +AIAgent agent = await CreateAgentWithMEAIAsync(); +// AIAgent agent = await CreateAgentWithNativeSDKAsync(); + +Console.WriteLine($"Created agent: {agent.Name}"); + +// Run the agent with a sample query +AgentResponse response = await agent.RunAsync("What data is available in the connected Fabric workspace?"); + +Console.WriteLine("\n=== Agent Response ==="); +foreach (var message in response.Messages) +{ + Console.WriteLine(message.Text); +} + +// Cleanup by deleting the agent +await aiProjectClient.Agents.DeleteAgentAsync(agent.Name); +Console.WriteLine($"\nDeleted agent: {agent.Name}"); + +// --- Agent Creation Options --- + +// Option 1 - Using AsAITool wrapping for the ResponseTool returned by AgentTool.CreateMicrosoftFabricTool (MEAI + AgentFramework) +async Task CreateAgentWithMEAIAsync() +{ + return await aiProjectClient.CreateAIAgentAsync( + model: deploymentName, + name: "FabricAgent-MEAI", + instructions: AgentInstructions, + tools: [((ResponseTool)AgentTool.CreateMicrosoftFabricTool(fabricToolOptions)).AsAITool()]); +} + +// Option 2 - Using PromptAgentDefinition with AgentTool.CreateMicrosoftFabricTool (Native SDK) +async Task CreateAgentWithNativeSDKAsync() +{ + return await aiProjectClient.CreateAIAgentAsync( + name: "FabricAgent-NATIVE", + creationOptions: new AgentVersionCreationOptions( + new PromptAgentDefinition(model: deploymentName) + { + Instructions = AgentInstructions, + Tools = + { + AgentTool.CreateMicrosoftFabricTool(fabricToolOptions), + } + }) + ); +} diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/README.md new file mode 100644 index 0000000000..a5faf79d9d --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/README.md @@ -0,0 +1,57 @@ +# Using Microsoft Fabric Tool with AI Agents + +This sample demonstrates how to use the Microsoft Fabric tool with AI Agents, allowing agents to query and interact with data in Microsoft Fabric workspaces. + +## What this sample demonstrates + +- Creating agents with Microsoft Fabric data access capabilities +- Using FabricDataAgentToolOptions to configure Fabric connections +- Two agent creation approaches: MEAI abstraction (Option 1) and Native SDK (Option 2) +- Managing agent lifecycle (creation and deletion) + +## Agent creation options + +This sample provides two approaches for creating agents with Microsoft Fabric: + +- **Option 1 - MEAI + AgentFramework**: Uses the Agent Framework `ResponseTool` wrapped with `AsAITool()` to call the `CreateAIAgentAsync` overload that accepts `tools:[]`, while still relying on the same underlying Azure AI Projects SDK types as Option 2. +- **Option 2 - Native SDK**: Uses `PromptAgentDefinition` with `AgentVersionCreationOptions` to create the agent directly with the Azure AI Projects SDK types. + +Both options produce the same result. Toggle between them by commenting/uncommenting the corresponding `CreateAgentWith*Async` call in `Program.cs`. + +## Prerequisites + +Before you begin, ensure you have the following prerequisites: + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (for Azure credential authentication) +- A Microsoft Fabric workspace with a configured project connection in Azure Foundry + +**Note**: This demo uses Azure Default credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure Foundry resource. + +Set the following environment variables: + +```powershell +$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" +$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:FABRIC_PROJECT_CONNECTION_ID="your-fabric-connection-id" # The Fabric project connection ID from Azure Foundry +``` + +## Run the sample + +Navigate to the FoundryAgents sample directory and run: + +```powershell +cd dotnet/samples/02-agents/FoundryAgents +dotnet run --project .\FoundryAgents_Step20_MicrosoftFabric +``` + +## Expected behavior + +The sample will: + +1. Create an agent with Microsoft Fabric tool capabilities +2. Configure the agent with a Fabric project connection +3. Run the agent with a query about available Fabric data +4. Display the agent's response +5. Clean up resources by deleting the agent diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/FoundryAgents_Step21_WebSearch.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/FoundryAgents_Step21_WebSearch.csproj new file mode 100644 index 0000000000..4d17fe06bb --- /dev/null +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/FoundryAgents_Step21_WebSearch.csproj @@ -0,0 +1,22 @@ + + + + Exe + net10.0 + + enable + enable + $(NoWarn);CA1812;CS8321 + + + + + + + + + + + + + diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step25_WebSearch/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/Program.cs similarity index 91% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step25_WebSearch/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/Program.cs index 77e5d5aeb7..1ac312ddae 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step25_WebSearch/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/Program.cs @@ -9,8 +9,8 @@ using Microsoft.Extensions.AI; using OpenAI.Responses; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; const string AgentInstructions = "You are a helpful assistant that can search the web to find current information and answer questions accurately."; const string AgentName = "WebSearchAgent"; diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step25_WebSearch/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/README.md similarity index 95% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step25_WebSearch/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/README.md index 95de275e8d..8da390878c 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step25_WebSearch/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/README.md @@ -34,8 +34,8 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults t Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents -dotnet run --project .\FoundryAgents_Step25_WebSearch +cd dotnet/samples/02-agents/FoundryAgents +dotnet run --project .\FoundryAgents_Step21_WebSearch ``` ## Expected behavior diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step26_MemorySearch/FoundryAgents_Step26_MemorySearch.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/FoundryAgents_Step22_MemorySearch.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step26_MemorySearch/FoundryAgents_Step26_MemorySearch.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/FoundryAgents_Step22_MemorySearch.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step26_MemorySearch/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/Program.cs similarity index 93% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step26_MemorySearch/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/Program.cs index 10bb2efe8d..97eed4e838 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step26_MemorySearch/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/Program.cs @@ -10,13 +10,13 @@ using Microsoft.Agents.AI; using OpenAI.Responses; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; // Memory store configuration // NOTE: Memory stores must be created beforehand via Azure Portal or Python SDK. // The .NET SDK currently only supports using existing memory stores with agents. -string memoryStoreName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_MEMORY_STORE_NAME") ?? throw new InvalidOperationException("AZURE_FOUNDRY_MEMORY_STORE_NAME is not set."); +string memoryStoreName = Environment.GetEnvironmentVariable("AZURE_AI_MEMORY_STORE_ID") ?? throw new InvalidOperationException("AZURE_AI_MEMORY_STORE_ID is not set."); const string AgentInstructions = """ You are a helpful assistant that remembers past conversations. diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step26_MemorySearch/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/README.md similarity index 97% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step26_MemorySearch/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/README.md index 316509d32c..9e6d79d579 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step26_MemorySearch/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/README.md @@ -70,8 +70,8 @@ $env:AZURE_AI_MEMORY_STORE_NAME="your-memory-store-name" # Required - name of p Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents -dotnet run --project .\FoundryAgents_Step26_MemorySearch +cd dotnet/samples/02-agents/FoundryAgents +dotnet run --project .\FoundryAgents_Step22_MemorySearch ``` ## Expected behavior diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step27_LocalMCP/FoundryAgents_Step27_LocalMCP.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP/FoundryAgents_Step23_LocalMCP.csproj similarity index 100% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step27_LocalMCP/FoundryAgents_Step27_LocalMCP.csproj rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP/FoundryAgents_Step23_LocalMCP.csproj diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step27_LocalMCP/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP/Program.cs similarity index 94% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step27_LocalMCP/Program.cs rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP/Program.cs index a9c4d4b7ed..d41771ef37 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step27_LocalMCP/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP/Program.cs @@ -11,8 +11,8 @@ using Microsoft.Extensions.AI; using ModelContextProtocol.Client; -string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; +string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; const string AgentInstructions = "You are a helpful assistant that can help with Microsoft documentation questions. Use the Microsoft Learn MCP tool to search for documentation."; const string AgentName = "DocsAgent"; diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step27_LocalMCP/README.md b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP/README.md similarity index 83% rename from dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step27_LocalMCP/README.md rename to dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP/README.md index e8883b7e95..8651108987 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step27_LocalMCP/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step23_LocalMCP/README.md @@ -23,8 +23,8 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` ## Run the sample @@ -32,8 +32,8 @@ $env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults t Navigate to the FoundryAgents sample directory and run: ```powershell -cd dotnet/samples/GettingStarted/FoundryAgents -dotnet run --project .\FoundryAgents_Step27_LocalMCP +cd dotnet/samples/02-agents/FoundryAgents +dotnet run --project .\FoundryAgents_Step23_LocalMCP ``` ## Expected behavior diff --git a/dotnet/samples/GettingStarted/FoundryAgents/README.md b/dotnet/samples/02-agents/FoundryAgents/README.md similarity index 89% rename from dotnet/samples/GettingStarted/FoundryAgents/README.md rename to dotnet/samples/02-agents/FoundryAgents/README.md index 8f83afbd1e..426a8cdad5 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/README.md +++ b/dotnet/samples/02-agents/FoundryAgents/README.md @@ -58,12 +58,14 @@ Before you begin, ensure you have the following prerequisites: |[Using plugins](./FoundryAgents_Step13_Plugins/)|This sample demonstrates how to use plugins with a Foundry agent| |[Code interpreter](./FoundryAgents_Step14_CodeInterpreter/)|This sample demonstrates how to use the code interpreter tool with a Foundry agent| |[Computer use](./FoundryAgents_Step15_ComputerUse/)|This sample demonstrates how to use computer use capabilities with a Foundry agent| -|[Bing Custom Search](./FoundryAgents_Step21_BingCustomSearch/)|This sample demonstrates how to use Bing Custom Search tool with a Foundry agent| -|[SharePoint grounding](./FoundryAgents_Step22_SharePoint/)|This sample demonstrates how to use the SharePoint grounding tool with a Foundry agent| -|[Web search](./FoundryAgents_Step25_WebSearch/)|This sample demonstrates how to use the Responses API web search tool with a Foundry agent| -|[Memory search](./FoundryAgents_Step26_MemorySearch/)|This sample demonstrates how to use memory search tool with a Foundry agent| -|[File search](./FoundryAgents_Step18_FileSearch/)|This sample demonstrates how to use the file search tool with a Foundry agent| -|[Local MCP](./FoundryAgents_Step27_LocalMCP/)|This sample demonstrates how to use a local MCP client with a Foundry agent| +|[File search](./FoundryAgents_Step16_FileSearch/)|This sample demonstrates how to use the file search tool with a Foundry agent| +|[OpenAPI tools](./FoundryAgents_Step17_OpenAPITools/)|This sample demonstrates how to use OpenAPI tools with a Foundry agent| +|[Bing Custom Search](./FoundryAgents_Step18_BingCustomSearch/)|This sample demonstrates how to use Bing Custom Search tool with a Foundry agent| +|[SharePoint grounding](./FoundryAgents_Step19_SharePoint/)|This sample demonstrates how to use the SharePoint grounding tool with a Foundry agent| +|[Microsoft Fabric](./FoundryAgents_Step20_MicrosoftFabric/)|This sample demonstrates how to use Microsoft Fabric tool with a Foundry agent| +|[Web search](./FoundryAgents_Step21_WebSearch/)|This sample demonstrates how to use the Responses API web search tool with a Foundry agent| +|[Memory search](./FoundryAgents_Step22_MemorySearch/)|This sample demonstrates how to use memory search tool with a Foundry agent| +|[Local MCP](./FoundryAgents_Step23_LocalMCP/)|This sample demonstrates how to use a local MCP client with a Foundry agent| ## Evaluation Samples @@ -87,8 +89,8 @@ cd FoundryAgents_Step01.2_Running Set the following environment variables: ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini ``` If the variables are not set, you will be prompted for the values when running the samples. diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server/Agent_MCP_Server.csproj b/dotnet/samples/02-agents/ModelContextProtocol/Agent_MCP_Server/Agent_MCP_Server.csproj similarity index 100% rename from dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server/Agent_MCP_Server.csproj rename to dotnet/samples/02-agents/ModelContextProtocol/Agent_MCP_Server/Agent_MCP_Server.csproj diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server/Program.cs b/dotnet/samples/02-agents/ModelContextProtocol/Agent_MCP_Server/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server/Program.cs rename to dotnet/samples/02-agents/ModelContextProtocol/Agent_MCP_Server/Program.cs diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server/README.md b/dotnet/samples/02-agents/ModelContextProtocol/Agent_MCP_Server/README.md similarity index 100% rename from dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server/README.md rename to dotnet/samples/02-agents/ModelContextProtocol/Agent_MCP_Server/README.md diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/Agent_MCP_Server_Auth.csproj b/dotnet/samples/02-agents/ModelContextProtocol/Agent_MCP_Server_Auth/Agent_MCP_Server_Auth.csproj similarity index 100% rename from dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/Agent_MCP_Server_Auth.csproj rename to dotnet/samples/02-agents/ModelContextProtocol/Agent_MCP_Server_Auth/Agent_MCP_Server_Auth.csproj diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/Program.cs b/dotnet/samples/02-agents/ModelContextProtocol/Agent_MCP_Server_Auth/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/Program.cs rename to dotnet/samples/02-agents/ModelContextProtocol/Agent_MCP_Server_Auth/Program.cs diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/README.md b/dotnet/samples/02-agents/ModelContextProtocol/Agent_MCP_Server_Auth/README.md similarity index 100% rename from dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/README.md rename to dotnet/samples/02-agents/ModelContextProtocol/Agent_MCP_Server_Auth/README.md diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/FoundryAgent_Hosted_MCP.csproj b/dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/FoundryAgent_Hosted_MCP.csproj similarity index 100% rename from dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/FoundryAgent_Hosted_MCP.csproj rename to dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/FoundryAgent_Hosted_MCP.csproj diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs b/dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs similarity index 94% rename from dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs rename to dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs index f6397ce182..99d26c103d 100644 --- a/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs +++ b/dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs @@ -9,8 +9,8 @@ using Microsoft.Agents.AI; using Microsoft.Extensions.AI; -var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); -var model = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4.1-mini"; +var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +var model = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4.1-mini"; // Get a client to create/retrieve server side agents with. // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/README.md b/dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/README.md similarity index 69% rename from dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/README.md rename to dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/README.md index f3be7da576..a172ec63cf 100644 --- a/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/README.md +++ b/dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/README.md @@ -11,6 +11,6 @@ Before you begin, ensure you have the following prerequisites: Set the following environment variables: ```powershell -$env:FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint -$env:FOUNDRY_MODEL_DEPLOYMENT_NAME="gpt-4.1-mini" # Optional, defaults to gpt-4.1-mini +$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4.1-mini" # Optional, defaults to gpt-4.1-mini ``` diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/README.md b/dotnet/samples/02-agents/ModelContextProtocol/README.md similarity index 100% rename from dotnet/samples/GettingStarted/ModelContextProtocol/README.md rename to dotnet/samples/02-agents/ModelContextProtocol/README.md diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs b/dotnet/samples/02-agents/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs rename to dotnet/samples/02-agents/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/README.md b/dotnet/samples/02-agents/ModelContextProtocol/ResponseAgent_Hosted_MCP/README.md similarity index 100% rename from dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/README.md rename to dotnet/samples/02-agents/ModelContextProtocol/ResponseAgent_Hosted_MCP/README.md diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/ResponseAgent_Hosted_MCP.csproj b/dotnet/samples/02-agents/ModelContextProtocol/ResponseAgent_Hosted_MCP/ResponseAgent_Hosted_MCP.csproj similarity index 100% rename from dotnet/samples/GettingStarted/ModelContextProtocol/ResponseAgent_Hosted_MCP/ResponseAgent_Hosted_MCP.csproj rename to dotnet/samples/02-agents/ModelContextProtocol/ResponseAgent_Hosted_MCP/ResponseAgent_Hosted_MCP.csproj diff --git a/dotnet/samples/GettingStarted/README.md b/dotnet/samples/02-agents/README.md similarity index 74% rename from dotnet/samples/GettingStarted/README.md rename to dotnet/samples/02-agents/README.md index 6fe68fc94f..b901645f88 100644 --- a/dotnet/samples/GettingStarted/README.md +++ b/dotnet/samples/02-agents/README.md @@ -12,10 +12,11 @@ of the agent framework. |[Agent Providers](./AgentProviders/README.md)|Getting started with creating agents using various providers| |[Agents With Retrieval Augmented Generation (RAG)](./AgentWithRAG/README.md)|Adding Retrieval Augmented Generation (RAG) capabilities to your agents.| |[Agents With Memory](./AgentWithMemory/README.md)|Adding Memory capabilities to your agents.| -|[A2A](./A2A/README.md)|Getting started with A2A (Agent-to-Agent) specific features| |[Agent Open Telemetry](./AgentOpenTelemetry/README.md)|Getting started with OpenTelemetry for agents| |[Agent With OpenAI exchange types](./AgentWithOpenAI/README.md)|Using OpenAI exchange types with agents| |[Agent With Anthropic](./AgentWithAnthropic/README.md)|Getting started with agents using Anthropic Claude| -|[Workflow](./Workflows/README.md)|Getting started with Workflow| |[Model Context Protocol](./ModelContextProtocol/README.md)|Getting started with Model Context Protocol| |[Agent Skills](./AgentSkills/README.md)|Getting started with Agent Skills| +|[Declarative Agents](./DeclarativeAgents)|Loading and executing AI agents from YAML configuration files| │ +|[AG-UI](./AGUI/README.md)|Getting started with AG-UI (Agent UI Protocol) servers and clients| │ +|[Dev UI](./DevUI/README.md)|Interactive web interface for testing and debugging AI agents during development| \ No newline at end of file diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/CustomAgentExecutors.csproj b/dotnet/samples/03-workflows/Agents/CustomAgentExecutors/CustomAgentExecutors.csproj similarity index 61% rename from dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/CustomAgentExecutors.csproj rename to dotnet/samples/03-workflows/Agents/CustomAgentExecutors/CustomAgentExecutors.csproj index 2ab222887c..6776b73b15 100644 --- a/dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/CustomAgentExecutors.csproj +++ b/dotnet/samples/03-workflows/Agents/CustomAgentExecutors/CustomAgentExecutors.csproj @@ -15,11 +15,11 @@ - - + - + diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/Program.cs b/dotnet/samples/03-workflows/Agents/CustomAgentExecutors/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/Program.cs rename to dotnet/samples/03-workflows/Agents/CustomAgentExecutors/Program.cs diff --git a/dotnet/samples/03-workflows/Agents/FoundryAgent/FoundryAgent.csproj b/dotnet/samples/03-workflows/Agents/FoundryAgent/FoundryAgent.csproj new file mode 100644 index 0000000000..30227d3f20 --- /dev/null +++ b/dotnet/samples/03-workflows/Agents/FoundryAgent/FoundryAgent.csproj @@ -0,0 +1,22 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + + + + diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/FoundryAgent/Program.cs b/dotnet/samples/03-workflows/Agents/FoundryAgent/Program.cs similarity index 95% rename from dotnet/samples/GettingStarted/Workflows/Agents/FoundryAgent/Program.cs rename to dotnet/samples/03-workflows/Agents/FoundryAgent/Program.cs index 016138aeea..f322bb882d 100644 --- a/dotnet/samples/GettingStarted/Workflows/Agents/FoundryAgent/Program.cs +++ b/dotnet/samples/03-workflows/Agents/FoundryAgent/Program.cs @@ -21,9 +21,9 @@ public static class Program private static async Task Main() { // Set up the Azure OpenAI client - var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") - ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); - var deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") + ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); + var deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; var persistentAgentsClient = new PersistentAgentsClient(endpoint, new AzureCliCredential()); // Create agents diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/GroupChatToolApproval/DeploymentGroupChatManager.cs b/dotnet/samples/03-workflows/Agents/GroupChatToolApproval/DeploymentGroupChatManager.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Agents/GroupChatToolApproval/DeploymentGroupChatManager.cs rename to dotnet/samples/03-workflows/Agents/GroupChatToolApproval/DeploymentGroupChatManager.cs diff --git a/dotnet/samples/03-workflows/Agents/GroupChatToolApproval/GroupChatToolApproval.csproj b/dotnet/samples/03-workflows/Agents/GroupChatToolApproval/GroupChatToolApproval.csproj new file mode 100644 index 0000000000..e926a8375a --- /dev/null +++ b/dotnet/samples/03-workflows/Agents/GroupChatToolApproval/GroupChatToolApproval.csproj @@ -0,0 +1,22 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + + + + diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/GroupChatToolApproval/Program.cs b/dotnet/samples/03-workflows/Agents/GroupChatToolApproval/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Agents/GroupChatToolApproval/Program.cs rename to dotnet/samples/03-workflows/Agents/GroupChatToolApproval/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/GroupChatToolApproval/README.md b/dotnet/samples/03-workflows/Agents/GroupChatToolApproval/README.md similarity index 91% rename from dotnet/samples/GettingStarted/Workflows/Agents/GroupChatToolApproval/README.md rename to dotnet/samples/03-workflows/Agents/GroupChatToolApproval/README.md index 84c6baa83c..f569b836e9 100644 --- a/dotnet/samples/GettingStarted/Workflows/Agents/GroupChatToolApproval/README.md +++ b/dotnet/samples/03-workflows/Agents/GroupChatToolApproval/README.md @@ -65,6 +65,6 @@ The sample will show: ## Related Samples -- [Agent Function Tools with Approvals](../../../Agents/Agent_Step04_UsingFunctionToolsWithApprovals) - Basic function approval pattern -- [Agent Workflow Patterns](../../_Foundational/04_AgentWorkflowPatterns) - Group chat without approvals +- [Agent Function Tools with Approvals](../../../02-agents/Agents/Agent_Step01_UsingFunctionToolsWithApprovals) - Basic function approval pattern +- [Agent Workflow Patterns](../../_StartHere/03_AgentWorkflowPatterns) - Group chat without approvals - [Human-in-the-Loop Basic](../../HumanInTheLoop/HumanInTheLoopBasic) - Workflow-level human interaction diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/Program.cs b/dotnet/samples/03-workflows/Agents/WorkflowAsAnAgent/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/Program.cs rename to dotnet/samples/03-workflows/Agents/WorkflowAsAnAgent/Program.cs diff --git a/dotnet/samples/03-workflows/Agents/WorkflowAsAnAgent/WorkflowAsAnAgent.csproj b/dotnet/samples/03-workflows/Agents/WorkflowAsAnAgent/WorkflowAsAnAgent.csproj new file mode 100644 index 0000000000..e926a8375a --- /dev/null +++ b/dotnet/samples/03-workflows/Agents/WorkflowAsAnAgent/WorkflowAsAnAgent.csproj @@ -0,0 +1,22 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + + + + diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/WorkflowFactory.cs b/dotnet/samples/03-workflows/Agents/WorkflowAsAnAgent/WorkflowFactory.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/WorkflowFactory.cs rename to dotnet/samples/03-workflows/Agents/WorkflowAsAnAgent/WorkflowFactory.cs diff --git a/dotnet/samples/GettingStarted/Workflows/Loop/Loop.csproj b/dotnet/samples/03-workflows/Checkpoint/CheckpointAndRehydrate/CheckpointAndRehydrate.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Loop/Loop.csproj rename to dotnet/samples/03-workflows/Checkpoint/CheckpointAndRehydrate/CheckpointAndRehydrate.csproj diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/Program.cs b/dotnet/samples/03-workflows/Checkpoint/CheckpointAndRehydrate/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/Program.cs rename to dotnet/samples/03-workflows/Checkpoint/CheckpointAndRehydrate/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/WorkflowFactory.cs b/dotnet/samples/03-workflows/Checkpoint/CheckpointAndRehydrate/WorkflowFactory.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/WorkflowFactory.cs rename to dotnet/samples/03-workflows/Checkpoint/CheckpointAndRehydrate/WorkflowFactory.cs diff --git a/dotnet/samples/GettingStarted/Workflows/Visualization/Visualization.csproj b/dotnet/samples/03-workflows/Checkpoint/CheckpointAndResume/CheckpointAndResume.csproj similarity index 75% rename from dotnet/samples/GettingStarted/Workflows/Visualization/Visualization.csproj rename to dotnet/samples/03-workflows/Checkpoint/CheckpointAndResume/CheckpointAndResume.csproj index 57b1fef0e1..0de620de0c 100644 --- a/dotnet/samples/GettingStarted/Workflows/Visualization/Visualization.csproj +++ b/dotnet/samples/03-workflows/Checkpoint/CheckpointAndResume/CheckpointAndResume.csproj @@ -1,4 +1,4 @@ - + Exe @@ -10,7 +10,6 @@ - diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/Program.cs b/dotnet/samples/03-workflows/Checkpoint/CheckpointAndResume/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/Program.cs rename to dotnet/samples/03-workflows/Checkpoint/CheckpointAndResume/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/WorkflowFactory.cs b/dotnet/samples/03-workflows/Checkpoint/CheckpointAndResume/WorkflowFactory.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/WorkflowFactory.cs rename to dotnet/samples/03-workflows/Checkpoint/CheckpointAndResume/WorkflowFactory.cs diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/CheckpointWithHumanInTheLoop.csproj b/dotnet/samples/03-workflows/Checkpoint/CheckpointWithHumanInTheLoop/CheckpointWithHumanInTheLoop.csproj similarity index 69% rename from dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/CheckpointWithHumanInTheLoop.csproj rename to dotnet/samples/03-workflows/Checkpoint/CheckpointWithHumanInTheLoop/CheckpointWithHumanInTheLoop.csproj index 2f41070759..0de620de0c 100644 --- a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/CheckpointWithHumanInTheLoop.csproj +++ b/dotnet/samples/03-workflows/Checkpoint/CheckpointWithHumanInTheLoop/CheckpointWithHumanInTheLoop.csproj @@ -9,7 +9,7 @@ - + diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/Program.cs b/dotnet/samples/03-workflows/Checkpoint/CheckpointWithHumanInTheLoop/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/Program.cs rename to dotnet/samples/03-workflows/Checkpoint/CheckpointWithHumanInTheLoop/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/WorkflowFactory.cs b/dotnet/samples/03-workflows/Checkpoint/CheckpointWithHumanInTheLoop/WorkflowFactory.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/WorkflowFactory.cs rename to dotnet/samples/03-workflows/Checkpoint/CheckpointWithHumanInTheLoop/WorkflowFactory.cs diff --git a/dotnet/samples/03-workflows/Concurrent/Concurrent/Concurrent.csproj b/dotnet/samples/03-workflows/Concurrent/Concurrent/Concurrent.csproj new file mode 100644 index 0000000000..35897932e0 --- /dev/null +++ b/dotnet/samples/03-workflows/Concurrent/Concurrent/Concurrent.csproj @@ -0,0 +1,27 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + + + + + + diff --git a/dotnet/samples/GettingStarted/Workflows/Concurrent/Concurrent/Program.cs b/dotnet/samples/03-workflows/Concurrent/Concurrent/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Concurrent/Concurrent/Program.cs rename to dotnet/samples/03-workflows/Concurrent/Concurrent/Program.cs diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step16_ChatReduction/Agent_Step16_ChatReduction.csproj b/dotnet/samples/03-workflows/Concurrent/MapReduce/MapReduce.csproj similarity index 82% rename from dotnet/samples/GettingStarted/Agents/Agent_Step16_ChatReduction/Agent_Step16_ChatReduction.csproj rename to dotnet/samples/03-workflows/Concurrent/MapReduce/MapReduce.csproj index 0f9de7c359..21a7f8c176 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step16_ChatReduction/Agent_Step16_ChatReduction.csproj +++ b/dotnet/samples/03-workflows/Concurrent/MapReduce/MapReduce.csproj @@ -3,19 +3,18 @@ Exe net10.0 - + enable - enable - - - + - + + + - + \ No newline at end of file diff --git a/dotnet/samples/GettingStarted/Workflows/Concurrent/MapReduce/Program.cs b/dotnet/samples/03-workflows/Concurrent/MapReduce/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Concurrent/MapReduce/Program.cs rename to dotnet/samples/03-workflows/Concurrent/MapReduce/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/01_EdgeCondition.csproj b/dotnet/samples/03-workflows/ConditionalEdges/01_EdgeCondition/01_EdgeCondition.csproj similarity index 74% rename from dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/01_EdgeCondition.csproj rename to dotnet/samples/03-workflows/ConditionalEdges/01_EdgeCondition/01_EdgeCondition.csproj index 422c1ca55f..f071e69f06 100644 --- a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/01_EdgeCondition.csproj +++ b/dotnet/samples/03-workflows/ConditionalEdges/01_EdgeCondition/01_EdgeCondition.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/Program.cs b/dotnet/samples/03-workflows/ConditionalEdges/01_EdgeCondition/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/Program.cs rename to dotnet/samples/03-workflows/ConditionalEdges/01_EdgeCondition/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/Resources.cs b/dotnet/samples/03-workflows/ConditionalEdges/01_EdgeCondition/Resources.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/Resources.cs rename to dotnet/samples/03-workflows/ConditionalEdges/01_EdgeCondition/Resources.cs diff --git a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/02_SwitchCase.csproj b/dotnet/samples/03-workflows/ConditionalEdges/02_SwitchCase/02_SwitchCase.csproj similarity index 74% rename from dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/02_SwitchCase.csproj rename to dotnet/samples/03-workflows/ConditionalEdges/02_SwitchCase/02_SwitchCase.csproj index 422c1ca55f..f071e69f06 100644 --- a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/02_SwitchCase.csproj +++ b/dotnet/samples/03-workflows/ConditionalEdges/02_SwitchCase/02_SwitchCase.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/Program.cs b/dotnet/samples/03-workflows/ConditionalEdges/02_SwitchCase/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/Program.cs rename to dotnet/samples/03-workflows/ConditionalEdges/02_SwitchCase/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/Resources.cs b/dotnet/samples/03-workflows/ConditionalEdges/02_SwitchCase/Resources.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/Resources.cs rename to dotnet/samples/03-workflows/ConditionalEdges/02_SwitchCase/Resources.cs diff --git a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/03_MultiSelection.csproj b/dotnet/samples/03-workflows/ConditionalEdges/03_MultiSelection/03_MultiSelection.csproj similarity index 74% rename from dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/03_MultiSelection.csproj rename to dotnet/samples/03-workflows/ConditionalEdges/03_MultiSelection/03_MultiSelection.csproj index 422c1ca55f..f071e69f06 100644 --- a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/03_MultiSelection.csproj +++ b/dotnet/samples/03-workflows/ConditionalEdges/03_MultiSelection/03_MultiSelection.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/Program.cs b/dotnet/samples/03-workflows/ConditionalEdges/03_MultiSelection/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/Program.cs rename to dotnet/samples/03-workflows/ConditionalEdges/03_MultiSelection/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/Resources.cs b/dotnet/samples/03-workflows/ConditionalEdges/03_MultiSelection/Resources.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/Resources.cs rename to dotnet/samples/03-workflows/ConditionalEdges/03_MultiSelection/Resources.cs diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/ConfirmInput/ConfirmInput.csproj b/dotnet/samples/03-workflows/Declarative/ConfirmInput/ConfirmInput.csproj similarity index 78% rename from dotnet/samples/GettingStarted/Workflows/Declarative/ConfirmInput/ConfirmInput.csproj rename to dotnet/samples/03-workflows/Declarative/ConfirmInput/ConfirmInput.csproj index da32d18b99..dac2f49921 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/ConfirmInput/ConfirmInput.csproj +++ b/dotnet/samples/03-workflows/Declarative/ConfirmInput/ConfirmInput.csproj @@ -1,4 +1,4 @@ - + Exe @@ -25,8 +25,8 @@ - - + + diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/ConfirmInput/ConfirmInput.yaml b/dotnet/samples/03-workflows/Declarative/ConfirmInput/ConfirmInput.yaml similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Declarative/ConfirmInput/ConfirmInput.yaml rename to dotnet/samples/03-workflows/Declarative/ConfirmInput/ConfirmInput.yaml diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/ConfirmInput/Program.cs b/dotnet/samples/03-workflows/Declarative/ConfirmInput/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Declarative/ConfirmInput/Program.cs rename to dotnet/samples/03-workflows/Declarative/ConfirmInput/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/CustomerSupport/CustomerSupport.csproj b/dotnet/samples/03-workflows/Declarative/CustomerSupport/CustomerSupport.csproj similarity index 73% rename from dotnet/samples/GettingStarted/Workflows/Declarative/CustomerSupport/CustomerSupport.csproj rename to dotnet/samples/03-workflows/Declarative/CustomerSupport/CustomerSupport.csproj index 583dbc6e8f..0bc83997d0 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/CustomerSupport/CustomerSupport.csproj +++ b/dotnet/samples/03-workflows/Declarative/CustomerSupport/CustomerSupport.csproj @@ -1,4 +1,4 @@ - + Exe @@ -25,12 +25,12 @@ - - + + - + Always diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/CustomerSupport/Program.cs b/dotnet/samples/03-workflows/Declarative/CustomerSupport/Program.cs similarity index 99% rename from dotnet/samples/GettingStarted/Workflows/Declarative/CustomerSupport/Program.cs rename to dotnet/samples/03-workflows/Declarative/CustomerSupport/Program.cs index 0547c37b72..b5df45a399 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/CustomerSupport/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/CustomerSupport/Program.cs @@ -98,7 +98,7 @@ await aiProjectClient.CreateAgentAsync( } private static PromptAgentDefinition DefineSelfServiceAgent(IConfiguration configuration) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ @@ -145,7 +145,7 @@ Use your knowledge to work with the user to provide the best possible troublesho }; private static PromptAgentDefinition DefineTicketingAgent(IConfiguration configuration, TicketingPlugin plugin) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ @@ -209,7 +209,7 @@ Include the following information in the TicketSummary. }; private static PromptAgentDefinition DefineTicketRoutingAgent(IConfiguration configuration, TicketingPlugin plugin) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ @@ -254,7 +254,7 @@ Determine how to route the given issue to the appropriate support team. }; private static PromptAgentDefinition DefineWindowsSupportAgent(IConfiguration configuration, TicketingPlugin plugin) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ @@ -324,7 +324,7 @@ Use your knowledge to work with the user to provide the best possible troublesho }; private static PromptAgentDefinition DefineResolutionAgent(IConfiguration configuration, TicketingPlugin plugin) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ @@ -358,7 +358,7 @@ Always include the resolution details. }; private static PromptAgentDefinition TicketEscalationAgent(IConfiguration configuration, TicketingPlugin plugin) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ diff --git a/dotnet/samples/03-workflows/Declarative/CustomerSupport/Properties/launchSettings.json b/dotnet/samples/03-workflows/Declarative/CustomerSupport/Properties/launchSettings.json new file mode 100644 index 0000000000..0d3ba15fc1 --- /dev/null +++ b/dotnet/samples/03-workflows/Declarative/CustomerSupport/Properties/launchSettings.json @@ -0,0 +1,19 @@ +{ + "profiles": { + "Default": { + "commandName": "Project" + }, + "Reboot": { + "commandName": "Project", + "commandLineArgs": "\"My PC keeps rebooting and I can't use it.\"" + }, + "License": { + "commandName": "Project", + "commandLineArgs": "\"My M365 Office license key isn't activating.\"" + }, + "Windows": { + "commandName": "Project", + "commandLineArgs": "\"How do I change my mouse speed settings?\"" + } + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/CustomerSupport/TicketingPlugin.cs b/dotnet/samples/03-workflows/Declarative/CustomerSupport/TicketingPlugin.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Declarative/CustomerSupport/TicketingPlugin.cs rename to dotnet/samples/03-workflows/Declarative/CustomerSupport/TicketingPlugin.cs diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/DeepResearch/DeepResearch.csproj b/dotnet/samples/03-workflows/Declarative/DeepResearch/DeepResearch.csproj similarity index 74% rename from dotnet/samples/GettingStarted/Workflows/Declarative/DeepResearch/DeepResearch.csproj rename to dotnet/samples/03-workflows/Declarative/DeepResearch/DeepResearch.csproj index 413fa56210..cd533a0707 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/DeepResearch/DeepResearch.csproj +++ b/dotnet/samples/03-workflows/Declarative/DeepResearch/DeepResearch.csproj @@ -1,4 +1,4 @@ - + Exe @@ -25,12 +25,12 @@ - - + + - + Always diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/DeepResearch/Program.cs b/dotnet/samples/03-workflows/Declarative/DeepResearch/Program.cs similarity index 99% rename from dotnet/samples/GettingStarted/Workflows/Declarative/DeepResearch/Program.cs rename to dotnet/samples/03-workflows/Declarative/DeepResearch/Program.cs index ae410a0371..98d75d250b 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/DeepResearch/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/DeepResearch/Program.cs @@ -89,7 +89,7 @@ await aiProjectClient.CreateAgentAsync( } private static PromptAgentDefinition DefineResearchAgent(IConfiguration configuration) => - new(configuration.GetValue(Application.Settings.FoundryModelFull)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ @@ -121,7 +121,7 @@ 4. EDUCATED GUESSES }; private static PromptAgentDefinition DefinePlannerAgent(IConfiguration configuration) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = // TODO: Use Structured Inputs / Prompt Template """ @@ -140,7 +140,7 @@ Your only job is to devise an efficient plan that identifies (by name) how a tea }; private static PromptAgentDefinition DefineManagerAgent(IConfiguration configuration) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = // TODO: Use Structured Inputs / Prompt Template """ @@ -226,7 +226,7 @@ private static PromptAgentDefinition DefineManagerAgent(IConfiguration configura }; private static PromptAgentDefinition DefineSummaryAgent(IConfiguration configuration) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ @@ -241,7 +241,7 @@ so please ensure it is complete and self-contained. }; private static PromptAgentDefinition DefineKnowledgeAgent(IConfiguration configuration) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Tools = { @@ -252,7 +252,7 @@ private static PromptAgentDefinition DefineKnowledgeAgent(IConfiguration configu }; private static PromptAgentDefinition DefineCoderAgent(IConfiguration configuration) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ @@ -266,7 +266,7 @@ You solve problem by writing and executing code. }; private static PromptAgentDefinition DefineWeatherAgent(IConfiguration configuration) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ diff --git a/dotnet/samples/03-workflows/Declarative/DeepResearch/Properties/launchSettings.json b/dotnet/samples/03-workflows/Declarative/DeepResearch/Properties/launchSettings.json new file mode 100644 index 0000000000..0c6de510b2 --- /dev/null +++ b/dotnet/samples/03-workflows/Declarative/DeepResearch/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "Default": { + "commandName": "Project" + }, + "Bus Stop": { + "commandName": "Project", + "commandLineArgs": "\"What is the closest bus-stop that is next to ISHONI YAKINIKU in Seattle?\"" + } + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/DeepResearch/wttr.json b/dotnet/samples/03-workflows/Declarative/DeepResearch/wttr.json similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Declarative/DeepResearch/wttr.json rename to dotnet/samples/03-workflows/Declarative/DeepResearch/wttr.json diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/ExecuteCode/ExecuteCode.csproj b/dotnet/samples/03-workflows/Declarative/ExecuteCode/ExecuteCode.csproj similarity index 77% rename from dotnet/samples/GettingStarted/Workflows/Declarative/ExecuteCode/ExecuteCode.csproj rename to dotnet/samples/03-workflows/Declarative/ExecuteCode/ExecuteCode.csproj index 9725826c7a..6a9c4957c2 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/ExecuteCode/ExecuteCode.csproj +++ b/dotnet/samples/03-workflows/Declarative/ExecuteCode/ExecuteCode.csproj @@ -1,4 +1,4 @@ - + Exe @@ -26,8 +26,8 @@ - - + + diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/ExecuteCode/Generated.cs b/dotnet/samples/03-workflows/Declarative/ExecuteCode/Generated.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Declarative/ExecuteCode/Generated.cs rename to dotnet/samples/03-workflows/Declarative/ExecuteCode/Generated.cs diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/ExecuteCode/Program.cs b/dotnet/samples/03-workflows/Declarative/ExecuteCode/Program.cs similarity index 98% rename from dotnet/samples/GettingStarted/Workflows/Declarative/ExecuteCode/Program.cs rename to dotnet/samples/03-workflows/Declarative/ExecuteCode/Program.cs index 0566a5ff55..67d467266b 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/ExecuteCode/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/ExecuteCode/Program.cs @@ -17,7 +17,7 @@ namespace Demo.DeclarativeCode; /// /// /// Configuration -/// Define FOUNDRY_PROJECT_ENDPOINT as a user-secret or environment variable that +/// Define AZURE_AI_PROJECT_ENDPOINT as a user-secret or environment variable that /// points to your Foundry project endpoint. /// internal sealed class Program diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/ExecuteWorkflow/ExecuteWorkflow.csproj b/dotnet/samples/03-workflows/Declarative/ExecuteWorkflow/ExecuteWorkflow.csproj similarity index 78% rename from dotnet/samples/GettingStarted/Workflows/Declarative/ExecuteWorkflow/ExecuteWorkflow.csproj rename to dotnet/samples/03-workflows/Declarative/ExecuteWorkflow/ExecuteWorkflow.csproj index 074a31121d..fce40b64d4 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/ExecuteWorkflow/ExecuteWorkflow.csproj +++ b/dotnet/samples/03-workflows/Declarative/ExecuteWorkflow/ExecuteWorkflow.csproj @@ -25,8 +25,8 @@ - - + + diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/ExecuteWorkflow/Program.cs b/dotnet/samples/03-workflows/Declarative/ExecuteWorkflow/Program.cs similarity index 98% rename from dotnet/samples/GettingStarted/Workflows/Declarative/ExecuteWorkflow/Program.cs rename to dotnet/samples/03-workflows/Declarative/ExecuteWorkflow/Program.cs index fdbf9d1026..0d80cb686d 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/ExecuteWorkflow/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/ExecuteWorkflow/Program.cs @@ -19,7 +19,7 @@ namespace Demo.DeclarativeWorkflow; /// /// /// Configuration -/// Define FOUNDRY_PROJECT_ENDPOINT as a user-secret or environment variable that +/// Define AZURE_AI_PROJECT_ENDPOINT as a user-secret or environment variable that /// points to your Foundry project endpoint. /// Usage /// Provide the path to the workflow definition file as the first argument. diff --git a/dotnet/samples/03-workflows/Declarative/ExecuteWorkflow/Properties/launchSettings.json b/dotnet/samples/03-workflows/Declarative/ExecuteWorkflow/Properties/launchSettings.json new file mode 100644 index 0000000000..b36e876ef8 --- /dev/null +++ b/dotnet/samples/03-workflows/Declarative/ExecuteWorkflow/Properties/launchSettings.json @@ -0,0 +1,32 @@ +{ + "profiles": { + "Marketing": { + "commandName": "Project", + "commandLineArgs": "\"Marketing.yaml\" \"An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours\"" + }, + "MathChat": { + "commandName": "Project", + "commandLineArgs": "\"MathChat.yaml\" \"How would you compute the value of PI?\"" + }, + "Question": { + "commandName": "Project", + "commandLineArgs": "\"Question.yaml\" \"Iko\"" + }, + "Research": { + "commandName": "Project", + "commandLineArgs": "\"DeepResearch.yaml\" \"What is the closest bus-stop that is next to ISHONI YAKINIKU in Seattle?\"" + }, + "ResponseObject": { + "commandName": "Project", + "commandLineArgs": "\"ResponseObject.yaml\" \"Can you help me plan a trip somewhere soon?\"" + }, + "UserInput": { + "commandName": "Project", + "commandLineArgs": "\"UserInput.yaml\" \"Iko\"" + }, + "ParseValue": { + "commandName": "Project", + "commandLineArgs": "\"Pradeep-ParseValue-Number.yaml\" \"Test this case:\"" + } + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/FunctionTools/FunctionTools.csproj b/dotnet/samples/03-workflows/Declarative/FunctionTools/FunctionTools.csproj similarity index 78% rename from dotnet/samples/GettingStarted/Workflows/Declarative/FunctionTools/FunctionTools.csproj rename to dotnet/samples/03-workflows/Declarative/FunctionTools/FunctionTools.csproj index f8a51cb0f2..f890fb30a8 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/FunctionTools/FunctionTools.csproj +++ b/dotnet/samples/03-workflows/Declarative/FunctionTools/FunctionTools.csproj @@ -1,4 +1,4 @@ - + Exe @@ -25,8 +25,8 @@ - - + + diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/FunctionTools/FunctionTools.yaml b/dotnet/samples/03-workflows/Declarative/FunctionTools/FunctionTools.yaml similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Declarative/FunctionTools/FunctionTools.yaml rename to dotnet/samples/03-workflows/Declarative/FunctionTools/FunctionTools.yaml diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/FunctionTools/MenuPlugin.cs b/dotnet/samples/03-workflows/Declarative/FunctionTools/MenuPlugin.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Declarative/FunctionTools/MenuPlugin.cs rename to dotnet/samples/03-workflows/Declarative/FunctionTools/MenuPlugin.cs diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/FunctionTools/Program.cs b/dotnet/samples/03-workflows/Declarative/FunctionTools/Program.cs similarity index 99% rename from dotnet/samples/GettingStarted/Workflows/Declarative/FunctionTools/Program.cs rename to dotnet/samples/03-workflows/Declarative/FunctionTools/Program.cs index a5000635e4..8218e7c057 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/FunctionTools/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/FunctionTools/Program.cs @@ -70,7 +70,7 @@ await aiProjectClient.CreateAgentAsync( private static PromptAgentDefinition DefineMenuAgent(IConfiguration configuration, AIFunction[] functions) { PromptAgentDefinition agentDefinition = - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ diff --git a/dotnet/samples/03-workflows/Declarative/FunctionTools/Properties/launchSettings.json b/dotnet/samples/03-workflows/Declarative/FunctionTools/Properties/launchSettings.json new file mode 100644 index 0000000000..defb6318d3 --- /dev/null +++ b/dotnet/samples/03-workflows/Declarative/FunctionTools/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "Default": { + "commandName": "Project" + }, + "Soup": { + "commandName": "Project", + "commandLineArgs": "\"What is the soup of the day?\"" + } + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/GenerateCode/GenerateCode.csproj b/dotnet/samples/03-workflows/Declarative/GenerateCode/GenerateCode.csproj similarity index 84% rename from dotnet/samples/GettingStarted/Workflows/Declarative/GenerateCode/GenerateCode.csproj rename to dotnet/samples/03-workflows/Declarative/GenerateCode/GenerateCode.csproj index 117e27abd8..a85173d289 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/GenerateCode/GenerateCode.csproj +++ b/dotnet/samples/03-workflows/Declarative/GenerateCode/GenerateCode.csproj @@ -1,4 +1,4 @@ - + Exe @@ -24,7 +24,7 @@ - + diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/GenerateCode/Program.cs b/dotnet/samples/03-workflows/Declarative/GenerateCode/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Declarative/GenerateCode/Program.cs rename to dotnet/samples/03-workflows/Declarative/GenerateCode/Program.cs diff --git a/dotnet/samples/03-workflows/Declarative/GenerateCode/Properties/launchSettings.json b/dotnet/samples/03-workflows/Declarative/GenerateCode/Properties/launchSettings.json new file mode 100644 index 0000000000..692664eb00 --- /dev/null +++ b/dotnet/samples/03-workflows/Declarative/GenerateCode/Properties/launchSettings.json @@ -0,0 +1,28 @@ +{ + "profiles": { + "Marketing": { + "commandName": "Project", + "commandLineArgs": "\"Marketing.yaml\"" + }, + "MathChat": { + "commandName": "Project", + "commandLineArgs": "\"MathChat.yaml\"" + }, + "Question": { + "commandName": "Project", + "commandLineArgs": "\"Question.yaml\"" + }, + "Research": { + "commandName": "Project", + "commandLineArgs": "\"DeepResearch.yaml\"" + }, + "ResponseObject": { + "commandName": "Project", + "commandLineArgs": "\"ResponseObject.yaml\"" + }, + "UserInput": { + "commandName": "Project", + "commandLineArgs": "\"UserInput.yaml\"" + } + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/HostedWorkflow/HostedWorkflow.csproj b/dotnet/samples/03-workflows/Declarative/HostedWorkflow/HostedWorkflow.csproj similarity index 74% rename from dotnet/samples/GettingStarted/Workflows/Declarative/HostedWorkflow/HostedWorkflow.csproj rename to dotnet/samples/03-workflows/Declarative/HostedWorkflow/HostedWorkflow.csproj index 3cbd0ada95..f9379f38a3 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/HostedWorkflow/HostedWorkflow.csproj +++ b/dotnet/samples/03-workflows/Declarative/HostedWorkflow/HostedWorkflow.csproj @@ -1,4 +1,4 @@ - + Exe @@ -26,12 +26,12 @@ - - + + - + Always diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/HostedWorkflow/Program.cs b/dotnet/samples/03-workflows/Declarative/HostedWorkflow/Program.cs similarity index 98% rename from dotnet/samples/GettingStarted/Workflows/Declarative/HostedWorkflow/Program.cs rename to dotnet/samples/03-workflows/Declarative/HostedWorkflow/Program.cs index 2cf24a137b..272e83f983 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/HostedWorkflow/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/HostedWorkflow/Program.cs @@ -19,7 +19,7 @@ namespace Demo.DeclarativeWorkflow; /// /// /// Configuration -/// Define FOUNDRY_PROJECT_ENDPOINT as a user-secret or environment variable that +/// Define AZURE_AI_PROJECT_ENDPOINT as a user-secret or environment variable that /// points to your Foundry project endpoint. /// Usage /// Provide the path to the workflow definition file as the first argument. @@ -111,7 +111,7 @@ await agentClient.CreateAgentAsync( } private static PromptAgentDefinition DefineStudentAgent(IConfiguration configuration) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ @@ -124,7 +124,7 @@ You have the math-skills of a 6th grader. }; private static PromptAgentDefinition DefineTeacherAgent(IConfiguration configuration) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/InputArguments.csproj b/dotnet/samples/03-workflows/Declarative/InputArguments/InputArguments.csproj similarity index 78% rename from dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/InputArguments.csproj rename to dotnet/samples/03-workflows/Declarative/InputArguments/InputArguments.csproj index 5ef0b7e99e..45bc44eaf3 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/InputArguments.csproj +++ b/dotnet/samples/03-workflows/Declarative/InputArguments/InputArguments.csproj @@ -1,4 +1,4 @@ - + Exe @@ -25,8 +25,8 @@ - - + + diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/InputArguments.yaml b/dotnet/samples/03-workflows/Declarative/InputArguments/InputArguments.yaml similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/InputArguments.yaml rename to dotnet/samples/03-workflows/Declarative/InputArguments/InputArguments.yaml diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/Program.cs b/dotnet/samples/03-workflows/Declarative/InputArguments/Program.cs similarity index 99% rename from dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/Program.cs rename to dotnet/samples/03-workflows/Declarative/InputArguments/Program.cs index 523865cc62..65a365b143 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/InputArguments/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/InputArguments/Program.cs @@ -69,7 +69,7 @@ await aiProjectClient.CreateAgentAsync( } private static PromptAgentDefinition DefineLocationTriageAgent(IConfiguration configuration) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ @@ -80,7 +80,7 @@ Your only job is to solicit a location from the user. }; private static PromptAgentDefinition DefineLocationCaptureAgent(IConfiguration configuration) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ @@ -129,7 +129,7 @@ City level precision is sufficient. }; private static PromptAgentDefinition DefineLocationAwareAgent(IConfiguration configuration) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { // Parameterized instructions reference the "location" input argument. Instructions = diff --git a/dotnet/samples/03-workflows/Declarative/InputArguments/Properties/launchSettings.json b/dotnet/samples/03-workflows/Declarative/InputArguments/Properties/launchSettings.json new file mode 100644 index 0000000000..f95e48286c --- /dev/null +++ b/dotnet/samples/03-workflows/Declarative/InputArguments/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "Default": { + "commandName": "Project" + }, + "Vacation": { + "commandName": "Project", + "commandLineArgs": "\"I'd like to go on vacation.\"" + } + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/InvokeFunctionTool/InvokeFunctionTool.csproj b/dotnet/samples/03-workflows/Declarative/InvokeFunctionTool/InvokeFunctionTool.csproj similarity index 80% rename from dotnet/samples/GettingStarted/Workflows/Declarative/InvokeFunctionTool/InvokeFunctionTool.csproj rename to dotnet/samples/03-workflows/Declarative/InvokeFunctionTool/InvokeFunctionTool.csproj index 23e1c91e0a..67229da4b8 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/InvokeFunctionTool/InvokeFunctionTool.csproj +++ b/dotnet/samples/03-workflows/Declarative/InvokeFunctionTool/InvokeFunctionTool.csproj @@ -25,8 +25,8 @@ - - + + diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/InvokeFunctionTool/InvokeFunctionTool.yaml b/dotnet/samples/03-workflows/Declarative/InvokeFunctionTool/InvokeFunctionTool.yaml similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Declarative/InvokeFunctionTool/InvokeFunctionTool.yaml rename to dotnet/samples/03-workflows/Declarative/InvokeFunctionTool/InvokeFunctionTool.yaml diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/InvokeFunctionTool/MenuPlugin.cs b/dotnet/samples/03-workflows/Declarative/InvokeFunctionTool/MenuPlugin.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Declarative/InvokeFunctionTool/MenuPlugin.cs rename to dotnet/samples/03-workflows/Declarative/InvokeFunctionTool/MenuPlugin.cs diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/InvokeFunctionTool/Program.cs b/dotnet/samples/03-workflows/Declarative/InvokeFunctionTool/Program.cs similarity index 99% rename from dotnet/samples/GettingStarted/Workflows/Declarative/InvokeFunctionTool/Program.cs rename to dotnet/samples/03-workflows/Declarative/InvokeFunctionTool/Program.cs index 456bba0f88..fb20764977 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/InvokeFunctionTool/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/InvokeFunctionTool/Program.cs @@ -66,7 +66,7 @@ await aiProjectClient.CreateAgentAsync( private static PromptAgentDefinition DefineMenuAgent(IConfiguration configuration, AIFunction[] functions) { PromptAgentDefinition agentDefinition = - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ diff --git a/dotnet/samples/03-workflows/Declarative/InvokeMcpTool/InvokeMcpTool.csproj b/dotnet/samples/03-workflows/Declarative/InvokeMcpTool/InvokeMcpTool.csproj new file mode 100644 index 0000000000..317d93c4e9 --- /dev/null +++ b/dotnet/samples/03-workflows/Declarative/InvokeMcpTool/InvokeMcpTool.csproj @@ -0,0 +1,39 @@ + + + + Exe + net10.0 + enable + enable + + + + true + true + true + true + + + + + + + + + + + + + + + + + + + + + Always + + + + diff --git a/dotnet/samples/03-workflows/Declarative/InvokeMcpTool/InvokeMcpTool.yaml b/dotnet/samples/03-workflows/Declarative/InvokeMcpTool/InvokeMcpTool.yaml new file mode 100644 index 0000000000..7b942cb2bd --- /dev/null +++ b/dotnet/samples/03-workflows/Declarative/InvokeMcpTool/InvokeMcpTool.yaml @@ -0,0 +1,63 @@ +# +# This workflow demonstrates invoking MCP tools directly from a declarative workflow. +# Uses the Foundry MCP server to search AI model details. +# +# The workflow: +# 1. Accepts a model search term as input +# 2. Invokes the Foundry MCP tool +# 3. Invokes the Microsoft Learn MCP tool +# 4. Uses an agent to summarize the results +# +# Example input: +# gpt-4.1 +# +kind: Workflow +trigger: + + kind: OnConversationStart + id: workflow_invoke_mcp_tool + actions: + + # Set the search query from user input or use default + - kind: SetVariable + id: set_search_query + variable: Local.SearchQuery + value: =System.LastMessage.Text + + # Invoke MCP search tool on Foundry MCP server + - kind: InvokeMcpTool + id: invoke_foundry_search + serverUrl: https://mcp.ai.azure.com + serverLabel: azure_mcp_server + toolName: model_details_get + conversationId: =System.ConversationId + arguments: + modelName: =Local.SearchQuery + output: + autoSend: true + result: Local.FoundrySearchResult + + # Invoke MCP search tool on Microsoft Learn server + - kind: InvokeMcpTool + id: invoke_docs_search + serverUrl: https://learn.microsoft.com/api/mcp + serverLabel: microsoft_docs + toolName: microsoft_docs_search + conversationId: =System.ConversationId + arguments: + query: =Local.SearchQuery + output: + autoSend: true + result: Local.DocsSearchResult + + # Use the search agent to provide a helpful response based on results + - kind: InvokeAzureAgent + id: summarize_results + agent: + name: McpSearchAgent + conversationId: =System.ConversationId + input: + messages: =UserMessage("Based on the search results for '" & Local.SearchQuery & "', please provide a helpful summary.") + output: + autoSend: true + result: Local.Summary diff --git a/dotnet/samples/03-workflows/Declarative/InvokeMcpTool/Program.cs b/dotnet/samples/03-workflows/Declarative/InvokeMcpTool/Program.cs new file mode 100644 index 0000000000..926afcfc3c --- /dev/null +++ b/dotnet/samples/03-workflows/Declarative/InvokeMcpTool/Program.cs @@ -0,0 +1,141 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample demonstrates using the InvokeMcpTool action to call MCP (Model Context Protocol) +// server tools directly from a declarative workflow. MCP servers expose tools that can be +// invoked to perform specific tasks, like searching documentation or executing operations. + +using Azure.AI.Projects; +using Azure.AI.Projects.OpenAI; +using Azure.Core; +using Azure.Identity; +using Microsoft.Agents.AI.Workflows.Declarative.Mcp; +using Microsoft.Extensions.Configuration; +using Shared.Foundry; +using Shared.Workflows; + +namespace Demo.Workflows.Declarative.InvokeMcpTool; + +/// +/// Demonstrates a workflow that uses InvokeMcpTool to call MCP server tools +/// directly from the workflow. +/// +/// +/// +/// The InvokeMcpTool action allows workflows to invoke tools on MCP (Model Context Protocol) +/// servers. This enables: +/// +/// +/// Searching external data sources like documentation +/// Executing operations on remote servers +/// Integrating with MCP-compatible services +/// +/// +/// This sample uses the Microsoft Learn MCP server to search Azure documentation and the Azure foundry MCP server to get AI model details. +/// When you run the sample, provide an AI model (e.g. gpt-4.1-mini) as input, +/// The workflow will use the MCP tools to find relevant information about the model from Microsoft Learn and foundry, then an agent will summarize the results. +/// +/// +/// See the README.md file in the parent folder (../README.md) for detailed +/// information about the configuration required to run this sample. +/// +/// +internal sealed class Program +{ + public static async Task Main(string[] args) + { + // Initialize configuration + IConfiguration configuration = Application.InitializeConfig(); + Uri foundryEndpoint = new(configuration.GetValue(Application.Settings.FoundryEndpoint)); + + // Ensure sample agent exists in Foundry + await CreateAgentAsync(foundryEndpoint, configuration); + + // Get input from command line or console + string workflowInput = Application.GetInput(args); + + // Create the MCP tool handler for invoking MCP server tools. + // The HttpClient callback allows configuring authentication per MCP server. + // Different MCP servers may require different authentication configurations. + // For Production scenarios, consider implementing a more robust HttpClient management strategy to reuse HttpClient instances and manage their lifetimes appropriately. + List createdHttpClients = []; + // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. + DefaultAzureCredential credential = new(); + DefaultMcpToolHandler mcpToolHandler = new( + httpClientProvider: async (serverUrl, cancellationToken) => + { + if (serverUrl.StartsWith("https://mcp.ai.azure.com", StringComparison.OrdinalIgnoreCase)) + { + // Acquire token for the Azure MCP server + AccessToken token = await credential.GetTokenAsync( + new TokenRequestContext(["https://mcp.ai.azure.com/.default"]), + cancellationToken); + + // Create HttpClient with Authorization header + HttpClient httpClient = new(); + httpClient.DefaultRequestHeaders.Authorization = + new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token.Token); + createdHttpClients.Add(httpClient); + return httpClient; + } + + if (serverUrl.StartsWith("https://learn.microsoft.com", StringComparison.OrdinalIgnoreCase)) + { + // Microsoft Learn MCP server does not require authentication + HttpClient httpClient = new(); + createdHttpClients.Add(httpClient); + return httpClient; + } + + // Return null for unknown servers to use the default HttpClient without auth. + return null; + }); + + try + { + // Create the workflow factory with MCP tool provider + WorkflowFactory workflowFactory = new("InvokeMcpTool.yaml", foundryEndpoint) + { + McpToolHandler = mcpToolHandler + }; + + // Execute the workflow + WorkflowRunner runner = new() { UseJsonCheckpoints = true }; + await runner.ExecuteAsync(workflowFactory.CreateWorkflow, workflowInput); + } + finally + { + // Clean up connections and dispose created HttpClients + await mcpToolHandler.DisposeAsync(); + + foreach (HttpClient httpClient in createdHttpClients) + { + httpClient.Dispose(); + } + } + } + + private static async Task CreateAgentAsync(Uri foundryEndpoint, IConfiguration configuration) + { + // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. + AIProjectClient aiProjectClient = new(foundryEndpoint, new DefaultAzureCredential()); + + await aiProjectClient.CreateAgentAsync( + agentName: "McpSearchAgent", + agentDefinition: DefineSearchAgent(configuration), + agentDescription: "Provides information based on search results"); + } + + private static PromptAgentDefinition DefineSearchAgent(IConfiguration configuration) + { + return new PromptAgentDefinition(configuration.GetValue(Application.Settings.FoundryModel)) + { + Instructions = + """ + You are a helpful assistant that answers questions based on search results. + Use the information provided in the conversation history to answer questions. + If the information is already available in the conversation, use it directly. + Be concise and helpful in your responses. + """ + }; + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/Marketing/Marketing.csproj b/dotnet/samples/03-workflows/Declarative/Marketing/Marketing.csproj similarity index 73% rename from dotnet/samples/GettingStarted/Workflows/Declarative/Marketing/Marketing.csproj rename to dotnet/samples/03-workflows/Declarative/Marketing/Marketing.csproj index ceba7b740b..20e5843554 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/Marketing/Marketing.csproj +++ b/dotnet/samples/03-workflows/Declarative/Marketing/Marketing.csproj @@ -1,4 +1,4 @@ - + Exe @@ -25,12 +25,12 @@ - - + + - + Always diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/Marketing/Program.cs b/dotnet/samples/03-workflows/Declarative/Marketing/Program.cs similarity index 99% rename from dotnet/samples/GettingStarted/Workflows/Declarative/Marketing/Program.cs rename to dotnet/samples/03-workflows/Declarative/Marketing/Program.cs index 43ff497930..308303c162 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/Marketing/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/Marketing/Program.cs @@ -68,7 +68,7 @@ await aiProjectClient.CreateAgentAsync( } private static PromptAgentDefinition DefineAnalystAgent(IConfiguration configuration) => - new(configuration.GetValue(Application.Settings.FoundryModelFull)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ @@ -86,7 +86,7 @@ private static PromptAgentDefinition DefineAnalystAgent(IConfiguration configura }; private static PromptAgentDefinition DefineWriterAgent(IConfiguration configuration) => - new(configuration.GetValue(Application.Settings.FoundryModelFull)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ @@ -97,7 +97,7 @@ Output should be short (around 150 words), output just the copy as a single text }; private static PromptAgentDefinition DefineEditorAgent(IConfiguration configuration) => - new(configuration.GetValue(Application.Settings.FoundryModelFull)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ diff --git a/dotnet/samples/03-workflows/Declarative/Marketing/Properties/launchSettings.json b/dotnet/samples/03-workflows/Declarative/Marketing/Properties/launchSettings.json new file mode 100644 index 0000000000..be4e107472 --- /dev/null +++ b/dotnet/samples/03-workflows/Declarative/Marketing/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "Default": { + "commandName": "Project" + }, + "Water Bottle": { + "commandName": "Project", + "commandLineArgs": "\"An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours.\"" + } + } +} diff --git a/dotnet/samples/03-workflows/Declarative/OpenAIChatAgent/Properties/launchSettings.json b/dotnet/samples/03-workflows/Declarative/OpenAIChatAgent/Properties/launchSettings.json new file mode 100644 index 0000000000..b36e876ef8 --- /dev/null +++ b/dotnet/samples/03-workflows/Declarative/OpenAIChatAgent/Properties/launchSettings.json @@ -0,0 +1,32 @@ +{ + "profiles": { + "Marketing": { + "commandName": "Project", + "commandLineArgs": "\"Marketing.yaml\" \"An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours\"" + }, + "MathChat": { + "commandName": "Project", + "commandLineArgs": "\"MathChat.yaml\" \"How would you compute the value of PI?\"" + }, + "Question": { + "commandName": "Project", + "commandLineArgs": "\"Question.yaml\" \"Iko\"" + }, + "Research": { + "commandName": "Project", + "commandLineArgs": "\"DeepResearch.yaml\" \"What is the closest bus-stop that is next to ISHONI YAKINIKU in Seattle?\"" + }, + "ResponseObject": { + "commandName": "Project", + "commandLineArgs": "\"ResponseObject.yaml\" \"Can you help me plan a trip somewhere soon?\"" + }, + "UserInput": { + "commandName": "Project", + "commandLineArgs": "\"UserInput.yaml\" \"Iko\"" + }, + "ParseValue": { + "commandName": "Project", + "commandLineArgs": "\"Pradeep-ParseValue-Number.yaml\" \"Test this case:\"" + } + } +} diff --git a/dotnet/samples/03-workflows/Declarative/OpenAIResponseAgent/Properties/launchSettings.json b/dotnet/samples/03-workflows/Declarative/OpenAIResponseAgent/Properties/launchSettings.json new file mode 100644 index 0000000000..b36e876ef8 --- /dev/null +++ b/dotnet/samples/03-workflows/Declarative/OpenAIResponseAgent/Properties/launchSettings.json @@ -0,0 +1,32 @@ +{ + "profiles": { + "Marketing": { + "commandName": "Project", + "commandLineArgs": "\"Marketing.yaml\" \"An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours\"" + }, + "MathChat": { + "commandName": "Project", + "commandLineArgs": "\"MathChat.yaml\" \"How would you compute the value of PI?\"" + }, + "Question": { + "commandName": "Project", + "commandLineArgs": "\"Question.yaml\" \"Iko\"" + }, + "Research": { + "commandName": "Project", + "commandLineArgs": "\"DeepResearch.yaml\" \"What is the closest bus-stop that is next to ISHONI YAKINIKU in Seattle?\"" + }, + "ResponseObject": { + "commandName": "Project", + "commandLineArgs": "\"ResponseObject.yaml\" \"Can you help me plan a trip somewhere soon?\"" + }, + "UserInput": { + "commandName": "Project", + "commandLineArgs": "\"UserInput.yaml\" \"Iko\"" + }, + "ParseValue": { + "commandName": "Project", + "commandLineArgs": "\"Pradeep-ParseValue-Number.yaml\" \"Test this case:\"" + } + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/README.md b/dotnet/samples/03-workflows/Declarative/README.md similarity index 73% rename from dotnet/samples/GettingStarted/Workflows/Declarative/README.md rename to dotnet/samples/03-workflows/Declarative/README.md index 665c37101e..2ad3e59c0d 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/README.md +++ b/dotnet/samples/03-workflows/Declarative/README.md @@ -18,16 +18,16 @@ The configuraton required by the samples is: |Setting Name| Description| |:--|:--| -|FOUNDRY_PROJECT_ENDPOINT| The endpoint URL of your Azure Foundry Project.| -|FOUNDRY_MODEL_DEPLOYMENT_NAME| The name of the model deployment to use -|FOUNDRY_CONNECTION_GROUNDING_TOOL| The name of the Bing Grounding connection configured in your Azure Foundry Project.| +|AZURE_AI_PROJECT_ENDPOINT| The endpoint URL of your Azure Foundry Project.| +|AZURE_AI_MODEL_DEPLOYMENT_NAME| The name of the model deployment to use +|AZURE_AI_BING_CONNECTION_ID| The name of the Bing Grounding connection configured in your Azure Foundry Project.| To set your secrets with .NET Secret Manager: 1. From the root of the repository, navigate the console to the project folder: ``` - cd dotnet/samples/GettingStarted/Workflows/Declarative/ExecuteWorkflow + cd dotnet/samples/03-workflows/Declarative/ExecuteWorkflow ``` 2. Examine existing secret definitions: @@ -45,27 +45,27 @@ To set your secrets with .NET Secret Manager: 4. Define setting that identifies your Azure Foundry Project (endpoint): ``` - dotnet user-secrets set "FOUNDRY_PROJECT_ENDPOINT" "https://..." + dotnet user-secrets set "AZURE_AI_PROJECT_ENDPOINT" "https://..." ``` 5. Define setting that identifies your Azure Foundry Model Deployment (endpoint): ``` - dotnet user-secrets set "FOUNDRY_MODEL_DEPLOYMENT_NAME" "gpt-5" + dotnet user-secrets set "AZURE_AI_MODEL_DEPLOYMENT_NAME" "gpt-5" ``` 6. Define setting that identifies your Bing Grounding connection: ``` - dotnet user-secrets set "FOUNDRY_CONNECTION_GROUNDING_TOOL" "mybinggrounding" + dotnet user-secrets set "AZURE_AI_BING_CONNECTION_ID" "mybinggrounding" ``` You may alternatively set your secrets as an environment variable (PowerShell): ```pwsh -$env:FOUNDRY_PROJECT_ENDPOINT="https://..." -$env:FOUNDRY_MODEL_DEPLOYMENT_NAME="gpt-5" -$env:FOUNDRY_CONNECTION_GROUNDING_TOOL="mybinggrounding" +$env:AZURE_AI_PROJECT_ENDPOINT="https://..." +$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-5" +$env:AZURE_AI_BING_CONNECTION_ID="mybinggrounding" ``` ### Authorization @@ -86,7 +86,7 @@ To run the sampes from the command line: 1. From the root of the repository, navigate the console to the project folder: ```sh - cd dotnet/samples/GettingStarted/Workflows/Declarative/Marketing + cd dotnet/samples/03-workflows/Declarative/Marketing dotnet run Marketing ``` diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/StudentTeacher/Program.cs b/dotnet/samples/03-workflows/Declarative/StudentTeacher/Program.cs similarity index 99% rename from dotnet/samples/GettingStarted/Workflows/Declarative/StudentTeacher/Program.cs rename to dotnet/samples/03-workflows/Declarative/StudentTeacher/Program.cs index 80afd61911..28523c031e 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/StudentTeacher/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/StudentTeacher/Program.cs @@ -63,7 +63,7 @@ await aiProjectClient.CreateAgentAsync( } private static PromptAgentDefinition DefineStudentAgent(IConfiguration configuration) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ @@ -76,7 +76,7 @@ You have the math-skills of a 6th grader. }; private static PromptAgentDefinition DefineTeacherAgent(IConfiguration configuration) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ diff --git a/dotnet/samples/03-workflows/Declarative/StudentTeacher/Properties/launchSettings.json b/dotnet/samples/03-workflows/Declarative/StudentTeacher/Properties/launchSettings.json new file mode 100644 index 0000000000..e428c6e5f3 --- /dev/null +++ b/dotnet/samples/03-workflows/Declarative/StudentTeacher/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "Default": { + "commandName": "Project" + }, + "Compute PI": { + "commandName": "Project", + "commandLineArgs": "\"How would you compute the value of PI based on its fundamental definition?\"" + } + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/StudentTeacher/StudentTeacher.csproj b/dotnet/samples/03-workflows/Declarative/StudentTeacher/StudentTeacher.csproj similarity index 73% rename from dotnet/samples/GettingStarted/Workflows/Declarative/StudentTeacher/StudentTeacher.csproj rename to dotnet/samples/03-workflows/Declarative/StudentTeacher/StudentTeacher.csproj index 862e39bd99..8136706b8d 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/StudentTeacher/StudentTeacher.csproj +++ b/dotnet/samples/03-workflows/Declarative/StudentTeacher/StudentTeacher.csproj @@ -1,4 +1,4 @@ - + Exe @@ -25,12 +25,12 @@ - - + + - + Always diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/ToolApproval/Program.cs b/dotnet/samples/03-workflows/Declarative/ToolApproval/Program.cs similarity index 99% rename from dotnet/samples/GettingStarted/Workflows/Declarative/ToolApproval/Program.cs rename to dotnet/samples/03-workflows/Declarative/ToolApproval/Program.cs index 691c3d2243..544974e096 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/ToolApproval/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/ToolApproval/Program.cs @@ -59,7 +59,7 @@ await aiProjectClient.CreateAgentAsync( } private static PromptAgentDefinition DefineSearchAgent(IConfiguration configuration) => - new(configuration.GetValue(Application.Settings.FoundryModelMini)) + new(configuration.GetValue(Application.Settings.FoundryModel)) { Instructions = """ diff --git a/dotnet/samples/03-workflows/Declarative/ToolApproval/Properties/launchSettings.json b/dotnet/samples/03-workflows/Declarative/ToolApproval/Properties/launchSettings.json new file mode 100644 index 0000000000..74291845be --- /dev/null +++ b/dotnet/samples/03-workflows/Declarative/ToolApproval/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "Default": { + "commandName": "Project" + }, + "Graph API": { + "commandName": "Project", + "commandLineArgs": "\"What is Microsoft Graph API used for?\"" + } + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/ToolApproval/ToolApproval.csproj b/dotnet/samples/03-workflows/Declarative/ToolApproval/ToolApproval.csproj similarity index 78% rename from dotnet/samples/GettingStarted/Workflows/Declarative/ToolApproval/ToolApproval.csproj rename to dotnet/samples/03-workflows/Declarative/ToolApproval/ToolApproval.csproj index 1ebaa26645..a44e140f1f 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/ToolApproval/ToolApproval.csproj +++ b/dotnet/samples/03-workflows/Declarative/ToolApproval/ToolApproval.csproj @@ -1,4 +1,4 @@ - + Exe @@ -25,8 +25,8 @@ - - + + diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/ToolApproval/ToolApproval.yaml b/dotnet/samples/03-workflows/Declarative/ToolApproval/ToolApproval.yaml similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Declarative/ToolApproval/ToolApproval.yaml rename to dotnet/samples/03-workflows/Declarative/ToolApproval/ToolApproval.yaml diff --git a/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/HumanInTheLoopBasic.csproj b/dotnet/samples/03-workflows/HumanInTheLoop/HumanInTheLoopBasic/HumanInTheLoopBasic.csproj similarity index 69% rename from dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/HumanInTheLoopBasic.csproj rename to dotnet/samples/03-workflows/HumanInTheLoop/HumanInTheLoopBasic/HumanInTheLoopBasic.csproj index 2f41070759..0de620de0c 100644 --- a/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/HumanInTheLoopBasic.csproj +++ b/dotnet/samples/03-workflows/HumanInTheLoop/HumanInTheLoopBasic/HumanInTheLoopBasic.csproj @@ -9,7 +9,7 @@ - + diff --git a/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/Program.cs b/dotnet/samples/03-workflows/HumanInTheLoop/HumanInTheLoopBasic/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/Program.cs rename to dotnet/samples/03-workflows/HumanInTheLoop/HumanInTheLoopBasic/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/WorkflowFactory.cs b/dotnet/samples/03-workflows/HumanInTheLoop/HumanInTheLoopBasic/WorkflowFactory.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/WorkflowFactory.cs rename to dotnet/samples/03-workflows/HumanInTheLoop/HumanInTheLoopBasic/WorkflowFactory.cs diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/CheckpointAndRehydrate.csproj b/dotnet/samples/03-workflows/Loop/Loop.csproj similarity index 69% rename from dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/CheckpointAndRehydrate.csproj rename to dotnet/samples/03-workflows/Loop/Loop.csproj index 2f41070759..8430479a72 100644 --- a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/CheckpointAndRehydrate.csproj +++ b/dotnet/samples/03-workflows/Loop/Loop.csproj @@ -9,7 +9,7 @@ - + diff --git a/dotnet/samples/GettingStarted/Workflows/Loop/Program.cs b/dotnet/samples/03-workflows/Loop/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Loop/Program.cs rename to dotnet/samples/03-workflows/Loop/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflows/Observability/ApplicationInsights/ApplicationInsights.csproj b/dotnet/samples/03-workflows/Observability/ApplicationInsights/ApplicationInsights.csproj similarity index 83% rename from dotnet/samples/GettingStarted/Workflows/Observability/ApplicationInsights/ApplicationInsights.csproj rename to dotnet/samples/03-workflows/Observability/ApplicationInsights/ApplicationInsights.csproj index 4c91a01fad..0e868d5705 100644 --- a/dotnet/samples/GettingStarted/Workflows/Observability/ApplicationInsights/ApplicationInsights.csproj +++ b/dotnet/samples/03-workflows/Observability/ApplicationInsights/ApplicationInsights.csproj @@ -18,7 +18,7 @@ - + \ No newline at end of file diff --git a/dotnet/samples/GettingStarted/Workflows/Observability/ApplicationInsights/Program.cs b/dotnet/samples/03-workflows/Observability/ApplicationInsights/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Observability/ApplicationInsights/Program.cs rename to dotnet/samples/03-workflows/Observability/ApplicationInsights/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflows/Observability/AspireDashboard/AspireDashboard.csproj b/dotnet/samples/03-workflows/Observability/AspireDashboard/AspireDashboard.csproj similarity index 84% rename from dotnet/samples/GettingStarted/Workflows/Observability/AspireDashboard/AspireDashboard.csproj rename to dotnet/samples/03-workflows/Observability/AspireDashboard/AspireDashboard.csproj index 57b34f3d69..73e0cf67fb 100644 --- a/dotnet/samples/GettingStarted/Workflows/Observability/AspireDashboard/AspireDashboard.csproj +++ b/dotnet/samples/03-workflows/Observability/AspireDashboard/AspireDashboard.csproj @@ -19,7 +19,7 @@ - + \ No newline at end of file diff --git a/dotnet/samples/GettingStarted/Workflows/Observability/AspireDashboard/Program.cs b/dotnet/samples/03-workflows/Observability/AspireDashboard/Program.cs similarity index 98% rename from dotnet/samples/GettingStarted/Workflows/Observability/AspireDashboard/Program.cs rename to dotnet/samples/03-workflows/Observability/AspireDashboard/Program.cs index 06eebfba9d..23fcfe5f4e 100644 --- a/dotnet/samples/GettingStarted/Workflows/Observability/AspireDashboard/Program.cs +++ b/dotnet/samples/03-workflows/Observability/AspireDashboard/Program.cs @@ -29,7 +29,7 @@ public static class Program private static async Task Main() { // Configure OpenTelemetry for Aspire dashboard - var otlpEndpoint = Environment.GetEnvironmentVariable("OTLP_ENDPOINT") ?? "http://localhost:4317"; + var otlpEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4317"; var resourceBuilder = ResourceBuilder .CreateDefault() diff --git a/dotnet/samples/GettingStarted/Workflows/Observability/WorkflowAsAnAgent/Program.cs b/dotnet/samples/03-workflows/Observability/WorkflowAsAnAgent/Program.cs similarity index 99% rename from dotnet/samples/GettingStarted/Workflows/Observability/WorkflowAsAnAgent/Program.cs rename to dotnet/samples/03-workflows/Observability/WorkflowAsAnAgent/Program.cs index 19a3339754..f1911dc43f 100644 --- a/dotnet/samples/GettingStarted/Workflows/Observability/WorkflowAsAnAgent/Program.cs +++ b/dotnet/samples/03-workflows/Observability/WorkflowAsAnAgent/Program.cs @@ -50,7 +50,7 @@ private static async Task Main() { // Set up observability var applicationInsightsConnectionString = Environment.GetEnvironmentVariable("APPLICATIONINSIGHTS_CONNECTION_STRING"); - var otlpEndpoint = Environment.GetEnvironmentVariable("OTLP_ENDPOINT") ?? "http://localhost:4317"; + var otlpEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4317"; var resourceBuilder = ResourceBuilder .CreateDefault() diff --git a/dotnet/samples/GettingStarted/Workflows/Observability/WorkflowAsAnAgent/WorkflowAsAnAgentObservability.csproj b/dotnet/samples/03-workflows/Observability/WorkflowAsAnAgent/WorkflowAsAnAgentObservability.csproj similarity index 72% rename from dotnet/samples/GettingStarted/Workflows/Observability/WorkflowAsAnAgent/WorkflowAsAnAgentObservability.csproj rename to dotnet/samples/03-workflows/Observability/WorkflowAsAnAgent/WorkflowAsAnAgentObservability.csproj index 6a2d02be9b..280631fdc9 100644 --- a/dotnet/samples/GettingStarted/Workflows/Observability/WorkflowAsAnAgent/WorkflowAsAnAgentObservability.csproj +++ b/dotnet/samples/03-workflows/Observability/WorkflowAsAnAgent/WorkflowAsAnAgentObservability.csproj @@ -22,11 +22,11 @@ - - + - + diff --git a/dotnet/samples/GettingStarted/Workflows/Observability/WorkflowAsAnAgent/WorkflowHelper.cs b/dotnet/samples/03-workflows/Observability/WorkflowAsAnAgent/WorkflowHelper.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Observability/WorkflowAsAnAgent/WorkflowHelper.cs rename to dotnet/samples/03-workflows/Observability/WorkflowAsAnAgent/WorkflowHelper.cs diff --git a/dotnet/samples/GettingStarted/Workflows/README.md b/dotnet/samples/03-workflows/README.md similarity index 68% rename from dotnet/samples/GettingStarted/Workflows/README.md rename to dotnet/samples/03-workflows/README.md index f7704d0e42..2b8d375654 100644 --- a/dotnet/samples/GettingStarted/Workflows/README.md +++ b/dotnet/samples/03-workflows/README.md @@ -6,20 +6,19 @@ The getting started with workflow samples demonstrate the fundamental concepts a ### Foundational Concepts - Start Here -Please begin with the [Foundational](./_Foundational) samples in order. These three samples introduce the core concepts of executors, edges, agents in workflows, streaming, and workflow construction. +Please begin with the [Start Here](./_StartHere) samples in order. These three samples introduce the core concepts of executors, edges, agents in workflows, streaming, and workflow construction. -> The folder name starts with an underscore (`_Foundational`) to ensure it appears first in the explorer view. +> The folder name starts with an underscore (`_StartHere`) to ensure it appears first in the explorer view. | Sample | Concepts | |--------|----------| -| [Executors and Edges](./_Foundational/01_ExecutorsAndEdges) | Minimal workflow with basic executors and edges | -| [Streaming](./_Foundational/02_Streaming) | Extends workflows with event streaming | -| [Agents](./_Foundational/03_AgentsInWorkflows) | Use agents in workflows | -| [Agentic Workflow Patterns](./_Foundational/04_AgentWorkflowPatterns) | Demonstrates common agentic workflow patterns | -| [Multi-Service Workflows](./_Foundational/05_MultiModelService) | Shows using multiple AI services in the same workflow | -| [Sub-Workflows](./_Foundational/06_SubWorkflows) | Demonstrates composing workflows hierarchically by embedding workflows as executors | -| [Mixed Workflow with Agents and Executors](./_Foundational/07_MixedWorkflowAgentsAndExecutors) | Shows how to mix agents and executors with adapter pattern for type conversion and protocol handling | -| [Writer-Critic Workflow](./_Foundational/08_WriterCriticWorkflow) | Demonstrates iterative refinement with quality gates, max iteration safety, multiple message handlers, and conditional routing for feedback loops | +| [Streaming](./_StartHere/01_Streaming) | Extends workflows with event streaming | +| [Agents](./_StartHere/02_AgentsInWorkflows) | Use agents in workflows | +| [Agentic Workflow Patterns](./_StartHere/03_AgentWorkflowPatterns) | Demonstrates common agentic workflow patterns | +| [Multi-Service Workflows](./_StartHere/04_MultiModelService) | Shows using multiple AI services in the same workflow | +| [Sub-Workflows](./_StartHere/05_SubWorkflows) | Demonstrates composing workflows hierarchically by embedding workflows as executors | +| [Mixed Workflow with Agents and Executors](./_StartHere/06_MixedWorkflowAgentsAndExecutors) | Shows how to mix agents and executors with adapter pattern for type conversion and protocol handling | +| [Writer-Critic Workflow](./_StartHere/07_WriterCriticWorkflow) | Demonstrates iterative refinement with quality gates, max iteration safety, multiple message handlers, and conditional routing for feedback loops | Once completed, please proceed to other samples listed below. diff --git a/dotnet/samples/GettingStarted/Workflows/Resources/Lorem_Ipsum.txt b/dotnet/samples/03-workflows/Resources/Lorem_Ipsum.txt similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Resources/Lorem_Ipsum.txt rename to dotnet/samples/03-workflows/Resources/Lorem_Ipsum.txt diff --git a/dotnet/samples/GettingStarted/Workflows/Resources/ambiguous_email.txt b/dotnet/samples/03-workflows/Resources/ambiguous_email.txt similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Resources/ambiguous_email.txt rename to dotnet/samples/03-workflows/Resources/ambiguous_email.txt diff --git a/dotnet/samples/GettingStarted/Workflows/Resources/email.txt b/dotnet/samples/03-workflows/Resources/email.txt similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Resources/email.txt rename to dotnet/samples/03-workflows/Resources/email.txt diff --git a/dotnet/samples/GettingStarted/Workflows/Resources/spam.txt b/dotnet/samples/03-workflows/Resources/spam.txt similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Resources/spam.txt rename to dotnet/samples/03-workflows/Resources/spam.txt diff --git a/dotnet/samples/GettingStarted/Workflows/SharedStates/Program.cs b/dotnet/samples/03-workflows/SharedStates/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/SharedStates/Program.cs rename to dotnet/samples/03-workflows/SharedStates/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflows/SharedStates/Resources.cs b/dotnet/samples/03-workflows/SharedStates/Resources.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/SharedStates/Resources.cs rename to dotnet/samples/03-workflows/SharedStates/Resources.cs diff --git a/dotnet/samples/GettingStarted/Workflows/SharedStates/SharedStates.csproj b/dotnet/samples/03-workflows/SharedStates/SharedStates.csproj similarity index 79% rename from dotnet/samples/GettingStarted/Workflows/SharedStates/SharedStates.csproj rename to dotnet/samples/03-workflows/SharedStates/SharedStates.csproj index 35f87e7ebe..f8d9efcc03 100644 --- a/dotnet/samples/GettingStarted/Workflows/SharedStates/SharedStates.csproj +++ b/dotnet/samples/03-workflows/SharedStates/SharedStates.csproj @@ -9,7 +9,7 @@ - + diff --git a/dotnet/samples/GettingStarted/Workflows/Visualization/Program.cs b/dotnet/samples/03-workflows/Visualization/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Visualization/Program.cs rename to dotnet/samples/03-workflows/Visualization/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflows/Visualization/README.md b/dotnet/samples/03-workflows/Visualization/README.md similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Visualization/README.md rename to dotnet/samples/03-workflows/Visualization/README.md diff --git a/dotnet/samples/GettingStarted/Workflows/Visualization/Resources/graphviz_render.png b/dotnet/samples/03-workflows/Visualization/Resources/graphviz_render.png similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Visualization/Resources/graphviz_render.png rename to dotnet/samples/03-workflows/Visualization/Resources/graphviz_render.png diff --git a/dotnet/samples/GettingStarted/Workflows/Visualization/Resources/mermaid_render.png b/dotnet/samples/03-workflows/Visualization/Resources/mermaid_render.png similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Visualization/Resources/mermaid_render.png rename to dotnet/samples/03-workflows/Visualization/Resources/mermaid_render.png diff --git a/dotnet/samples/03-workflows/Visualization/Visualization.csproj b/dotnet/samples/03-workflows/Visualization/Visualization.csproj new file mode 100644 index 0000000000..7f9f03dff4 --- /dev/null +++ b/dotnet/samples/03-workflows/Visualization/Visualization.csproj @@ -0,0 +1,16 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/CheckpointAndResume.csproj b/dotnet/samples/03-workflows/_StartHere/01_Streaming/01_Streaming.csproj similarity index 69% rename from dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/CheckpointAndResume.csproj rename to dotnet/samples/03-workflows/_StartHere/01_Streaming/01_Streaming.csproj index 2f41070759..0de620de0c 100644 --- a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/CheckpointAndResume.csproj +++ b/dotnet/samples/03-workflows/_StartHere/01_Streaming/01_Streaming.csproj @@ -9,7 +9,7 @@ - + diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/02_Streaming/Program.cs b/dotnet/samples/03-workflows/_StartHere/01_Streaming/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/_Foundational/02_Streaming/Program.cs rename to dotnet/samples/03-workflows/_StartHere/01_Streaming/Program.cs diff --git a/dotnet/samples/03-workflows/_StartHere/02_AgentsInWorkflows/02_AgentsInWorkflows.csproj b/dotnet/samples/03-workflows/_StartHere/02_AgentsInWorkflows/02_AgentsInWorkflows.csproj new file mode 100644 index 0000000000..e926a8375a --- /dev/null +++ b/dotnet/samples/03-workflows/_StartHere/02_AgentsInWorkflows/02_AgentsInWorkflows.csproj @@ -0,0 +1,22 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + + + + diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/03_AgentsInWorkflows/Program.cs b/dotnet/samples/03-workflows/_StartHere/02_AgentsInWorkflows/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/_Foundational/03_AgentsInWorkflows/Program.cs rename to dotnet/samples/03-workflows/_StartHere/02_AgentsInWorkflows/Program.cs diff --git a/dotnet/samples/03-workflows/_StartHere/03_AgentWorkflowPatterns/03_AgentWorkflowPatterns.csproj b/dotnet/samples/03-workflows/_StartHere/03_AgentWorkflowPatterns/03_AgentWorkflowPatterns.csproj new file mode 100644 index 0000000000..e926a8375a --- /dev/null +++ b/dotnet/samples/03-workflows/_StartHere/03_AgentWorkflowPatterns/03_AgentWorkflowPatterns.csproj @@ -0,0 +1,22 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + + + + diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/04_AgentWorkflowPatterns/Program.cs b/dotnet/samples/03-workflows/_StartHere/03_AgentWorkflowPatterns/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/_Foundational/04_AgentWorkflowPatterns/Program.cs rename to dotnet/samples/03-workflows/_StartHere/03_AgentWorkflowPatterns/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/05_MultiModelService/05_MultiModelService.csproj b/dotnet/samples/03-workflows/_StartHere/04_MultiModelService/04_MultiModelService.csproj similarity index 68% rename from dotnet/samples/GettingStarted/Workflows/_Foundational/05_MultiModelService/05_MultiModelService.csproj rename to dotnet/samples/03-workflows/_StartHere/04_MultiModelService/04_MultiModelService.csproj index bc5cc0d67d..ee2bd37bf2 100644 --- a/dotnet/samples/GettingStarted/Workflows/_Foundational/05_MultiModelService/05_MultiModelService.csproj +++ b/dotnet/samples/03-workflows/_StartHere/04_MultiModelService/04_MultiModelService.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/05_MultiModelService/Program.cs b/dotnet/samples/03-workflows/_StartHere/04_MultiModelService/Program.cs similarity index 95% rename from dotnet/samples/GettingStarted/Workflows/_Foundational/05_MultiModelService/Program.cs rename to dotnet/samples/03-workflows/_StartHere/04_MultiModelService/Program.cs index 92414ff3b2..5edc956ccb 100644 --- a/dotnet/samples/GettingStarted/Workflows/_Foundational/05_MultiModelService/Program.cs +++ b/dotnet/samples/03-workflows/_StartHere/04_MultiModelService/Program.cs @@ -10,13 +10,13 @@ // Create the IChatClients to talk to different services. IChatClient aws = new AmazonBedrockRuntimeClient( - Environment.GetEnvironmentVariable("BEDROCK_ACCESSKEY"!), - Environment.GetEnvironmentVariable("BEDROCK_SECRETACCESSKEY")!, + Environment.GetEnvironmentVariable("BEDROCK_ACCESS_KEY"!), + Environment.GetEnvironmentVariable("BEDROCK_SECRET_KEY")!, Amazon.RegionEndpoint.USEast1) .AsIChatClient("amazon.nova-pro-v1:0"); IChatClient anthropic = new Anthropic.AnthropicClient( - new() { ApiKey = Environment.GetEnvironmentVariable("ANTHROPIC_APIKEY") }) + new() { ApiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY") }) .AsIChatClient("claude-sonnet-4-20250514"); IChatClient openai = new OpenAI.OpenAIClient( diff --git a/dotnet/samples/03-workflows/_StartHere/05_SubWorkflows/05_SubWorkflows.csproj b/dotnet/samples/03-workflows/_StartHere/05_SubWorkflows/05_SubWorkflows.csproj new file mode 100644 index 0000000000..072cc2cec3 --- /dev/null +++ b/dotnet/samples/03-workflows/_StartHere/05_SubWorkflows/05_SubWorkflows.csproj @@ -0,0 +1,16 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + \ No newline at end of file diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/06_SubWorkflows/Program.cs b/dotnet/samples/03-workflows/_StartHere/05_SubWorkflows/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/_Foundational/06_SubWorkflows/Program.cs rename to dotnet/samples/03-workflows/_StartHere/05_SubWorkflows/Program.cs diff --git a/dotnet/samples/03-workflows/_StartHere/06_MixedWorkflowAgentsAndExecutors/06_MixedWorkflowAgentsAndExecutors.csproj b/dotnet/samples/03-workflows/_StartHere/06_MixedWorkflowAgentsAndExecutors/06_MixedWorkflowAgentsAndExecutors.csproj new file mode 100644 index 0000000000..e926a8375a --- /dev/null +++ b/dotnet/samples/03-workflows/_StartHere/06_MixedWorkflowAgentsAndExecutors/06_MixedWorkflowAgentsAndExecutors.csproj @@ -0,0 +1,22 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + + + + diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/07_MixedWorkflowAgentsAndExecutors/Program.cs b/dotnet/samples/03-workflows/_StartHere/06_MixedWorkflowAgentsAndExecutors/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/_Foundational/07_MixedWorkflowAgentsAndExecutors/Program.cs rename to dotnet/samples/03-workflows/_StartHere/06_MixedWorkflowAgentsAndExecutors/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/07_MixedWorkflowAgentsAndExecutors/README.md b/dotnet/samples/03-workflows/_StartHere/06_MixedWorkflowAgentsAndExecutors/README.md similarity index 99% rename from dotnet/samples/GettingStarted/Workflows/_Foundational/07_MixedWorkflowAgentsAndExecutors/README.md rename to dotnet/samples/03-workflows/_StartHere/06_MixedWorkflowAgentsAndExecutors/README.md index 4ec203892b..5b93a83b6f 100644 --- a/dotnet/samples/GettingStarted/Workflows/_Foundational/07_MixedWorkflowAgentsAndExecutors/README.md +++ b/dotnet/samples/03-workflows/_StartHere/06_MixedWorkflowAgentsAndExecutors/README.md @@ -146,8 +146,8 @@ I cannot process this request as it appears to contain unsafe content. ## Related Samples +- **05_first_workflow** - Basic executor and edge concepts - **03_AgentsInWorkflows** - Introduction to using agents in workflows -- **01_ExecutorsAndEdges** - Basic executor and edge concepts - **02_Streaming** - Understanding streaming events - **Concurrent** - Parallel processing with fan-out/fan-in patterns diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/08_WriterCriticWorkflow/08_WriterCriticWorkflow.csproj b/dotnet/samples/03-workflows/_StartHere/07_WriterCriticWorkflow/07_WriterCriticWorkflow.csproj similarity index 61% rename from dotnet/samples/GettingStarted/Workflows/_Foundational/08_WriterCriticWorkflow/08_WriterCriticWorkflow.csproj rename to dotnet/samples/03-workflows/_StartHere/07_WriterCriticWorkflow/07_WriterCriticWorkflow.csproj index b9139c05ba..8486cebfb1 100644 --- a/dotnet/samples/GettingStarted/Workflows/_Foundational/08_WriterCriticWorkflow/08_WriterCriticWorkflow.csproj +++ b/dotnet/samples/03-workflows/_StartHere/07_WriterCriticWorkflow/07_WriterCriticWorkflow.csproj @@ -1,4 +1,4 @@ - + Exe @@ -10,12 +10,12 @@ - - + - + diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/08_WriterCriticWorkflow/Program.cs b/dotnet/samples/03-workflows/_StartHere/07_WriterCriticWorkflow/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/_Foundational/08_WriterCriticWorkflow/Program.cs rename to dotnet/samples/03-workflows/_StartHere/07_WriterCriticWorkflow/Program.cs diff --git a/dotnet/samples/GettingStarted/A2A/A2AAgent_AsFunctionTools/A2AAgent_AsFunctionTools.csproj b/dotnet/samples/04-hosting/A2A/A2AAgent_AsFunctionTools/A2AAgent_AsFunctionTools.csproj similarity index 100% rename from dotnet/samples/GettingStarted/A2A/A2AAgent_AsFunctionTools/A2AAgent_AsFunctionTools.csproj rename to dotnet/samples/04-hosting/A2A/A2AAgent_AsFunctionTools/A2AAgent_AsFunctionTools.csproj diff --git a/dotnet/samples/GettingStarted/A2A/A2AAgent_AsFunctionTools/Program.cs b/dotnet/samples/04-hosting/A2A/A2AAgent_AsFunctionTools/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/A2A/A2AAgent_AsFunctionTools/Program.cs rename to dotnet/samples/04-hosting/A2A/A2AAgent_AsFunctionTools/Program.cs diff --git a/dotnet/samples/GettingStarted/A2A/A2AAgent_AsFunctionTools/README.md b/dotnet/samples/04-hosting/A2A/A2AAgent_AsFunctionTools/README.md similarity index 100% rename from dotnet/samples/GettingStarted/A2A/A2AAgent_AsFunctionTools/README.md rename to dotnet/samples/04-hosting/A2A/A2AAgent_AsFunctionTools/README.md diff --git a/dotnet/samples/GettingStarted/A2A/A2AAgent_PollingForTaskCompletion/A2AAgent_PollingForTaskCompletion.csproj b/dotnet/samples/04-hosting/A2A/A2AAgent_PollingForTaskCompletion/A2AAgent_PollingForTaskCompletion.csproj similarity index 100% rename from dotnet/samples/GettingStarted/A2A/A2AAgent_PollingForTaskCompletion/A2AAgent_PollingForTaskCompletion.csproj rename to dotnet/samples/04-hosting/A2A/A2AAgent_PollingForTaskCompletion/A2AAgent_PollingForTaskCompletion.csproj diff --git a/dotnet/samples/GettingStarted/A2A/A2AAgent_PollingForTaskCompletion/Program.cs b/dotnet/samples/04-hosting/A2A/A2AAgent_PollingForTaskCompletion/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/A2A/A2AAgent_PollingForTaskCompletion/Program.cs rename to dotnet/samples/04-hosting/A2A/A2AAgent_PollingForTaskCompletion/Program.cs diff --git a/dotnet/samples/GettingStarted/A2A/A2AAgent_PollingForTaskCompletion/README.md b/dotnet/samples/04-hosting/A2A/A2AAgent_PollingForTaskCompletion/README.md similarity index 100% rename from dotnet/samples/GettingStarted/A2A/A2AAgent_PollingForTaskCompletion/README.md rename to dotnet/samples/04-hosting/A2A/A2AAgent_PollingForTaskCompletion/README.md diff --git a/dotnet/samples/GettingStarted/A2A/README.md b/dotnet/samples/04-hosting/A2A/README.md similarity index 95% rename from dotnet/samples/GettingStarted/A2A/README.md rename to dotnet/samples/04-hosting/A2A/README.md index b513ffa929..55539a8322 100644 --- a/dotnet/samples/GettingStarted/A2A/README.md +++ b/dotnet/samples/04-hosting/A2A/README.md @@ -3,7 +3,7 @@ These samples demonstrate how to work with Agent-to-Agent (A2A) specific features in the Agent Framework. For other samples that demonstrate how to use AIAgent instances, -see the [Getting Started With Agents](../Agents/README.md) samples. +see the [Getting Started With Agents](../../02-agents/Agents/README.md) samples. ## Prerequisites diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/.editorconfig b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/.editorconfig similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/.editorconfig rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/.editorconfig diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/01_SingleAgent/01_SingleAgent.csproj b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/01_SingleAgent/01_SingleAgent.csproj similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/01_SingleAgent/01_SingleAgent.csproj rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/01_SingleAgent/01_SingleAgent.csproj diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/01_SingleAgent/Program.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/01_SingleAgent/Program.cs similarity index 97% rename from dotnet/samples/Durable/Agents/AzureFunctions/01_SingleAgent/Program.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/01_SingleAgent/Program.cs index cc000bd815..9ce6c1ee28 100644 --- a/dotnet/samples/Durable/Agents/AzureFunctions/01_SingleAgent/Program.cs +++ b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/01_SingleAgent/Program.cs @@ -14,8 +14,8 @@ // Get the Azure OpenAI endpoint and deployment name from environment variables. string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") - ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") + ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT_NAME is not set."); // Use Azure Key Credential if provided, otherwise use Azure CLI Credential. string? azureOpenAiKey = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY"); diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/01_SingleAgent/README.md b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/01_SingleAgent/README.md similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/01_SingleAgent/README.md rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/01_SingleAgent/README.md diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/01_SingleAgent/demo.http b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/01_SingleAgent/demo.http similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/01_SingleAgent/demo.http rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/01_SingleAgent/demo.http diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/01_SingleAgent/host.json b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/01_SingleAgent/host.json similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/01_SingleAgent/host.json rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/01_SingleAgent/host.json diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/04_AgentOrchestration_Conditionals/local.settings.json b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/01_SingleAgent/local.settings.json similarity index 82% rename from dotnet/samples/Durable/Agents/AzureFunctions/04_AgentOrchestration_Conditionals/local.settings.json rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/01_SingleAgent/local.settings.json index 54dfbb5664..5f6d7d3340 100644 --- a/dotnet/samples/Durable/Agents/AzureFunctions/04_AgentOrchestration_Conditionals/local.settings.json +++ b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/01_SingleAgent/local.settings.json @@ -5,6 +5,6 @@ "AzureWebJobsStorage": "UseDevelopmentStorage=true", "DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None", "AZURE_OPENAI_ENDPOINT": "", - "AZURE_OPENAI_DEPLOYMENT": "" + "AZURE_OPENAI_DEPLOYMENT_NAME": "" } } diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/02_AgentOrchestration_Chaining/02_AgentOrchestration_Chaining.csproj b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/02_AgentOrchestration_Chaining/02_AgentOrchestration_Chaining.csproj similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/02_AgentOrchestration_Chaining/02_AgentOrchestration_Chaining.csproj rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/02_AgentOrchestration_Chaining/02_AgentOrchestration_Chaining.csproj diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/02_AgentOrchestration_Chaining/FunctionTriggers.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/02_AgentOrchestration_Chaining/FunctionTriggers.cs similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/02_AgentOrchestration_Chaining/FunctionTriggers.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/02_AgentOrchestration_Chaining/FunctionTriggers.cs diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/02_AgentOrchestration_Chaining/Program.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/02_AgentOrchestration_Chaining/Program.cs similarity index 96% rename from dotnet/samples/Durable/Agents/AzureFunctions/02_AgentOrchestration_Chaining/Program.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/02_AgentOrchestration_Chaining/Program.cs index 13d0852915..b682e638a7 100644 --- a/dotnet/samples/Durable/Agents/AzureFunctions/02_AgentOrchestration_Chaining/Program.cs +++ b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/02_AgentOrchestration_Chaining/Program.cs @@ -14,11 +14,11 @@ // Get the Azure OpenAI endpoint and deployment name from environment variables. string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") - ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") + ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT_NAME is not set."); // Use Azure Key Credential if provided, otherwise use Azure CLI Credential. -string? azureOpenAiKey = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY"); +string? azureOpenAiKey = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY"); // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid // latency issues, unintended credential probing, and potential security risks from fallback mechanisms. diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/02_AgentOrchestration_Chaining/README.md b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/02_AgentOrchestration_Chaining/README.md similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/02_AgentOrchestration_Chaining/README.md rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/02_AgentOrchestration_Chaining/README.md diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/02_AgentOrchestration_Chaining/demo.http b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/02_AgentOrchestration_Chaining/demo.http similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/02_AgentOrchestration_Chaining/demo.http rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/02_AgentOrchestration_Chaining/demo.http diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/02_AgentOrchestration_Chaining/host.json b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/02_AgentOrchestration_Chaining/host.json similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/02_AgentOrchestration_Chaining/host.json rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/02_AgentOrchestration_Chaining/host.json diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/01_SingleAgent/local.settings.json b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/02_AgentOrchestration_Chaining/local.settings.json similarity index 82% rename from dotnet/samples/Durable/Agents/AzureFunctions/01_SingleAgent/local.settings.json rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/02_AgentOrchestration_Chaining/local.settings.json index 54dfbb5664..5f6d7d3340 100644 --- a/dotnet/samples/Durable/Agents/AzureFunctions/01_SingleAgent/local.settings.json +++ b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/02_AgentOrchestration_Chaining/local.settings.json @@ -5,6 +5,6 @@ "AzureWebJobsStorage": "UseDevelopmentStorage=true", "DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None", "AZURE_OPENAI_ENDPOINT": "", - "AZURE_OPENAI_DEPLOYMENT": "" + "AZURE_OPENAI_DEPLOYMENT_NAME": "" } } diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/03_AgentOrchestration_Concurrency/03_AgentOrchestration_Concurrency.csproj b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/03_AgentOrchestration_Concurrency/03_AgentOrchestration_Concurrency.csproj similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/03_AgentOrchestration_Concurrency/03_AgentOrchestration_Concurrency.csproj rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/03_AgentOrchestration_Concurrency/03_AgentOrchestration_Concurrency.csproj diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/03_AgentOrchestration_Concurrency/FunctionTriggers.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/03_AgentOrchestration_Concurrency/FunctionTriggers.cs similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/03_AgentOrchestration_Concurrency/FunctionTriggers.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/03_AgentOrchestration_Concurrency/FunctionTriggers.cs diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/03_AgentOrchestration_Concurrency/Program.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/03_AgentOrchestration_Concurrency/Program.cs similarity index 96% rename from dotnet/samples/Durable/Agents/AzureFunctions/03_AgentOrchestration_Concurrency/Program.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/03_AgentOrchestration_Concurrency/Program.cs index 5cf8aec044..11e2ec3f49 100644 --- a/dotnet/samples/Durable/Agents/AzureFunctions/03_AgentOrchestration_Concurrency/Program.cs +++ b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/03_AgentOrchestration_Concurrency/Program.cs @@ -14,11 +14,11 @@ // Get the Azure OpenAI endpoint and deployment name from environment variables. string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") - ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") + ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT_NAME is not set."); // Use Azure Key Credential if provided, otherwise use Azure CLI Credential. -string? azureOpenAiKey = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY"); +string? azureOpenAiKey = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY"); // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid // latency issues, unintended credential probing, and potential security risks from fallback mechanisms. diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/03_AgentOrchestration_Concurrency/README.md b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/03_AgentOrchestration_Concurrency/README.md similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/03_AgentOrchestration_Concurrency/README.md rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/03_AgentOrchestration_Concurrency/README.md diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/03_AgentOrchestration_Concurrency/demo.http b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/03_AgentOrchestration_Concurrency/demo.http similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/03_AgentOrchestration_Concurrency/demo.http rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/03_AgentOrchestration_Concurrency/demo.http diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/03_AgentOrchestration_Concurrency/host.json b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/03_AgentOrchestration_Concurrency/host.json similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/03_AgentOrchestration_Concurrency/host.json rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/03_AgentOrchestration_Concurrency/host.json diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/02_AgentOrchestration_Chaining/local.settings.json b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/03_AgentOrchestration_Concurrency/local.settings.json similarity index 82% rename from dotnet/samples/Durable/Agents/AzureFunctions/02_AgentOrchestration_Chaining/local.settings.json rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/03_AgentOrchestration_Concurrency/local.settings.json index 54dfbb5664..5f6d7d3340 100644 --- a/dotnet/samples/Durable/Agents/AzureFunctions/02_AgentOrchestration_Chaining/local.settings.json +++ b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/03_AgentOrchestration_Concurrency/local.settings.json @@ -5,6 +5,6 @@ "AzureWebJobsStorage": "UseDevelopmentStorage=true", "DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None", "AZURE_OPENAI_ENDPOINT": "", - "AZURE_OPENAI_DEPLOYMENT": "" + "AZURE_OPENAI_DEPLOYMENT_NAME": "" } } diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/04_AgentOrchestration_Conditionals/04_AgentOrchestration_Conditionals.csproj b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/04_AgentOrchestration_Conditionals/04_AgentOrchestration_Conditionals.csproj similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/04_AgentOrchestration_Conditionals/04_AgentOrchestration_Conditionals.csproj rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/04_AgentOrchestration_Conditionals/04_AgentOrchestration_Conditionals.csproj diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/04_AgentOrchestration_Conditionals/FunctionTriggers.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/04_AgentOrchestration_Conditionals/FunctionTriggers.cs similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/04_AgentOrchestration_Conditionals/FunctionTriggers.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/04_AgentOrchestration_Conditionals/FunctionTriggers.cs diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/04_AgentOrchestration_Conditionals/Models.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/04_AgentOrchestration_Conditionals/Models.cs similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/04_AgentOrchestration_Conditionals/Models.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/04_AgentOrchestration_Conditionals/Models.cs diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/04_AgentOrchestration_Conditionals/Program.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/04_AgentOrchestration_Conditionals/Program.cs similarity index 96% rename from dotnet/samples/Durable/Agents/AzureFunctions/04_AgentOrchestration_Conditionals/Program.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/04_AgentOrchestration_Conditionals/Program.cs index 803b15f487..114ec7073f 100644 --- a/dotnet/samples/Durable/Agents/AzureFunctions/04_AgentOrchestration_Conditionals/Program.cs +++ b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/04_AgentOrchestration_Conditionals/Program.cs @@ -14,11 +14,11 @@ // Get the Azure OpenAI endpoint and deployment name from environment variables. string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") - ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") + ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT_NAME is not set."); // Use Azure Key Credential if provided, otherwise use Azure CLI Credential. -string? azureOpenAiKey = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY"); +string? azureOpenAiKey = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY"); // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid // latency issues, unintended credential probing, and potential security risks from fallback mechanisms. diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/04_AgentOrchestration_Conditionals/README.md b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/04_AgentOrchestration_Conditionals/README.md similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/04_AgentOrchestration_Conditionals/README.md rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/04_AgentOrchestration_Conditionals/README.md diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/04_AgentOrchestration_Conditionals/demo.http b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/04_AgentOrchestration_Conditionals/demo.http similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/04_AgentOrchestration_Conditionals/demo.http rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/04_AgentOrchestration_Conditionals/demo.http diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/04_AgentOrchestration_Conditionals/host.json b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/04_AgentOrchestration_Conditionals/host.json similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/04_AgentOrchestration_Conditionals/host.json rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/04_AgentOrchestration_Conditionals/host.json diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/03_AgentOrchestration_Concurrency/local.settings.json b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/04_AgentOrchestration_Conditionals/local.settings.json similarity index 82% rename from dotnet/samples/Durable/Agents/AzureFunctions/03_AgentOrchestration_Concurrency/local.settings.json rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/04_AgentOrchestration_Conditionals/local.settings.json index 54dfbb5664..5f6d7d3340 100644 --- a/dotnet/samples/Durable/Agents/AzureFunctions/03_AgentOrchestration_Concurrency/local.settings.json +++ b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/04_AgentOrchestration_Conditionals/local.settings.json @@ -5,6 +5,6 @@ "AzureWebJobsStorage": "UseDevelopmentStorage=true", "DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None", "AZURE_OPENAI_ENDPOINT": "", - "AZURE_OPENAI_DEPLOYMENT": "" + "AZURE_OPENAI_DEPLOYMENT_NAME": "" } } diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/05_AgentOrchestration_HITL/05_AgentOrchestration_HITL.csproj b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/05_AgentOrchestration_HITL/05_AgentOrchestration_HITL.csproj similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/05_AgentOrchestration_HITL/05_AgentOrchestration_HITL.csproj rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/05_AgentOrchestration_HITL/05_AgentOrchestration_HITL.csproj diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/05_AgentOrchestration_HITL/FunctionTriggers.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/05_AgentOrchestration_HITL/FunctionTriggers.cs similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/05_AgentOrchestration_HITL/FunctionTriggers.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/05_AgentOrchestration_HITL/FunctionTriggers.cs diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/05_AgentOrchestration_HITL/Models.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/05_AgentOrchestration_HITL/Models.cs similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/05_AgentOrchestration_HITL/Models.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/05_AgentOrchestration_HITL/Models.cs diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/05_AgentOrchestration_HITL/Program.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/05_AgentOrchestration_HITL/Program.cs similarity index 96% rename from dotnet/samples/Durable/Agents/AzureFunctions/05_AgentOrchestration_HITL/Program.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/05_AgentOrchestration_HITL/Program.cs index eecd6de570..331f435f89 100644 --- a/dotnet/samples/Durable/Agents/AzureFunctions/05_AgentOrchestration_HITL/Program.cs +++ b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/05_AgentOrchestration_HITL/Program.cs @@ -14,11 +14,11 @@ // Get the Azure OpenAI endpoint and deployment name from environment variables. string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") - ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") + ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT_NAME is not set."); // Use Azure Key Credential if provided, otherwise use Azure CLI Credential. -string? azureOpenAiKey = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY"); +string? azureOpenAiKey = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY"); // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid // latency issues, unintended credential probing, and potential security risks from fallback mechanisms. diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/05_AgentOrchestration_HITL/README.md b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/05_AgentOrchestration_HITL/README.md similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/05_AgentOrchestration_HITL/README.md rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/05_AgentOrchestration_HITL/README.md diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/05_AgentOrchestration_HITL/demo.http b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/05_AgentOrchestration_HITL/demo.http similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/05_AgentOrchestration_HITL/demo.http rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/05_AgentOrchestration_HITL/demo.http diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/05_AgentOrchestration_HITL/host.json b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/05_AgentOrchestration_HITL/host.json similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/05_AgentOrchestration_HITL/host.json rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/05_AgentOrchestration_HITL/host.json diff --git a/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/05_AgentOrchestration_HITL/local.settings.json b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/05_AgentOrchestration_HITL/local.settings.json new file mode 100644 index 0000000000..5f6d7d3340 --- /dev/null +++ b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/05_AgentOrchestration_HITL/local.settings.json @@ -0,0 +1,10 @@ +{ + "IsEncrypted": false, + "Values": { + "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated", + "AzureWebJobsStorage": "UseDevelopmentStorage=true", + "DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None", + "AZURE_OPENAI_ENDPOINT": "", + "AZURE_OPENAI_DEPLOYMENT_NAME": "" + } +} diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/06_LongRunningTools.csproj b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/06_LongRunningTools.csproj similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/06_LongRunningTools.csproj rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/06_LongRunningTools.csproj diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/FunctionTriggers.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/FunctionTriggers.cs similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/FunctionTriggers.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/FunctionTriggers.cs diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/Models.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/Models.cs similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/Models.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/Models.cs diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/Program.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/Program.cs similarity index 97% rename from dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/Program.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/Program.cs index a06d2d8e63..f1ee396565 100644 --- a/dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/Program.cs +++ b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/Program.cs @@ -18,11 +18,11 @@ // Get the Azure OpenAI endpoint and deployment name from environment variables. string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") - ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") + ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT_NAME is not set."); // Use Azure Key Credential if provided, otherwise use Azure CLI Credential. -string? azureOpenAiKey = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY"); +string? azureOpenAiKey = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY"); // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid // latency issues, unintended credential probing, and potential security risks from fallback mechanisms. diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/README.md b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/README.md similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/README.md rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/README.md diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/Tools.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/Tools.cs similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/Tools.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/Tools.cs diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/demo.http b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/demo.http similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/demo.http rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/demo.http diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/host.json b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/host.json similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/host.json rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/host.json diff --git a/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/local.settings.json b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/local.settings.json new file mode 100644 index 0000000000..5f6d7d3340 --- /dev/null +++ b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/06_LongRunningTools/local.settings.json @@ -0,0 +1,10 @@ +{ + "IsEncrypted": false, + "Values": { + "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated", + "AzureWebJobsStorage": "UseDevelopmentStorage=true", + "DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None", + "AZURE_OPENAI_ENDPOINT": "", + "AZURE_OPENAI_DEPLOYMENT_NAME": "" + } +} diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/07_AgentAsMcpTool/07_AgentAsMcpTool.csproj b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/07_AgentAsMcpTool/07_AgentAsMcpTool.csproj similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/07_AgentAsMcpTool/07_AgentAsMcpTool.csproj rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/07_AgentAsMcpTool/07_AgentAsMcpTool.csproj diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/07_AgentAsMcpTool/Program.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/07_AgentAsMcpTool/Program.cs similarity index 97% rename from dotnet/samples/Durable/Agents/AzureFunctions/07_AgentAsMcpTool/Program.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/07_AgentAsMcpTool/Program.cs index a0767f8860..b371fb3cdc 100644 --- a/dotnet/samples/Durable/Agents/AzureFunctions/07_AgentAsMcpTool/Program.cs +++ b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/07_AgentAsMcpTool/Program.cs @@ -20,11 +20,11 @@ // Get the Azure OpenAI endpoint and deployment name from environment variables. string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") - ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") + ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT_NAME is not set."); // Use Azure Key Credential if provided, otherwise use Azure CLI Credential. -string? azureOpenAiKey = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY"); +string? azureOpenAiKey = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY"); // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid // latency issues, unintended credential probing, and potential security risks from fallback mechanisms. diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/07_AgentAsMcpTool/README.md b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/07_AgentAsMcpTool/README.md similarity index 94% rename from dotnet/samples/Durable/Agents/AzureFunctions/07_AgentAsMcpTool/README.md rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/07_AgentAsMcpTool/README.md index a8efad04de..632e84d3e4 100644 --- a/dotnet/samples/Durable/Agents/AzureFunctions/07_AgentAsMcpTool/README.md +++ b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/07_AgentAsMcpTool/README.md @@ -38,8 +38,8 @@ Update your `local.settings.json` with your Azure OpenAI credentials: { "Values": { "AZURE_OPENAI_ENDPOINT": "https://your-resource.openai.azure.com/", - "AZURE_OPENAI_DEPLOYMENT": "your-deployment-name", - "AZURE_OPENAI_KEY": "your-api-key-if-not-using-rbac" + "AZURE_OPENAI_DEPLOYMENT_NAME": "your-deployment-name", + "AZURE_OPENAI_API_KEY": "your-api-key-if-not-using-rbac" } } ``` @@ -49,7 +49,7 @@ Update your `local.settings.json` with your Azure OpenAI credentials: 1. **Start the Function App**: ```bash - cd dotnet/samples/AzureFunctions/07_AgentAsMcpTool + cd dotnet/samples/04-hosting/DurableAgents/AzureFunctions/07_AgentAsMcpTool func start ``` diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/07_AgentAsMcpTool/host.json b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/07_AgentAsMcpTool/host.json similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/07_AgentAsMcpTool/host.json rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/07_AgentAsMcpTool/host.json diff --git a/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/07_AgentAsMcpTool/local.settings.json b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/07_AgentAsMcpTool/local.settings.json new file mode 100644 index 0000000000..5f6d7d3340 --- /dev/null +++ b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/07_AgentAsMcpTool/local.settings.json @@ -0,0 +1,10 @@ +{ + "IsEncrypted": false, + "Values": { + "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated", + "AzureWebJobsStorage": "UseDevelopmentStorage=true", + "DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None", + "AZURE_OPENAI_ENDPOINT": "", + "AZURE_OPENAI_DEPLOYMENT_NAME": "" + } +} diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/08_ReliableStreaming/08_ReliableStreaming.csproj b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/08_ReliableStreaming/08_ReliableStreaming.csproj similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/08_ReliableStreaming/08_ReliableStreaming.csproj rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/08_ReliableStreaming/08_ReliableStreaming.csproj diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/08_ReliableStreaming/FunctionTriggers.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/08_ReliableStreaming/FunctionTriggers.cs similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/08_ReliableStreaming/FunctionTriggers.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/08_ReliableStreaming/FunctionTriggers.cs diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/08_ReliableStreaming/Program.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/08_ReliableStreaming/Program.cs similarity index 98% rename from dotnet/samples/Durable/Agents/AzureFunctions/08_ReliableStreaming/Program.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/08_ReliableStreaming/Program.cs index 3850f967dc..feedf0eb05 100644 --- a/dotnet/samples/Durable/Agents/AzureFunctions/08_ReliableStreaming/Program.cs +++ b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/08_ReliableStreaming/Program.cs @@ -26,8 +26,8 @@ // Get the Azure OpenAI endpoint and deployment name from environment variables. string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") - ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") + ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT_NAME is not set."); // Get Redis connection string from environment variable. string redisConnectionString = Environment.GetEnvironmentVariable("REDIS_CONNECTION_STRING") @@ -39,7 +39,7 @@ out int ttlMinutes) ? ttlMinutes : 10; // Use Azure Key Credential if provided, otherwise use Azure CLI Credential. -string? azureOpenAiKey = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY"); +string? azureOpenAiKey = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY"); // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid // latency issues, unintended credential probing, and potential security risks from fallback mechanisms. diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/08_ReliableStreaming/README.md b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/08_ReliableStreaming/README.md similarity index 98% rename from dotnet/samples/Durable/Agents/AzureFunctions/08_ReliableStreaming/README.md rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/08_ReliableStreaming/README.md index fd13f23ecd..a8607ce11e 100644 --- a/dotnet/samples/Durable/Agents/AzureFunctions/08_ReliableStreaming/README.md +++ b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/08_ReliableStreaming/README.md @@ -239,8 +239,8 @@ These patterns may be overkill for: | `REDIS_CONNECTION_STRING` | Redis connection string | `localhost:6379` | | `REDIS_STREAM_TTL_MINUTES` | How long streams are retained after last write | `10` | | `AZURE_OPENAI_ENDPOINT` | Azure OpenAI endpoint URL | (required) | -| `AZURE_OPENAI_DEPLOYMENT` | Azure OpenAI deployment name | (required) | -| `AZURE_OPENAI_KEY` | API key (optional, uses Azure CLI auth if not set) | (optional) | +| `AZURE_OPENAI_DEPLOYMENT_NAME` | Azure OpenAI deployment name | (required) | +| `AZURE_OPENAI_API_KEY` | API key (optional, uses Azure CLI auth if not set) | (optional) | ## Cleanup diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/08_ReliableStreaming/RedisStreamResponseHandler.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/08_ReliableStreaming/RedisStreamResponseHandler.cs similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/08_ReliableStreaming/RedisStreamResponseHandler.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/08_ReliableStreaming/RedisStreamResponseHandler.cs diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/08_ReliableStreaming/Tools.cs b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/08_ReliableStreaming/Tools.cs similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/08_ReliableStreaming/Tools.cs rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/08_ReliableStreaming/Tools.cs diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/08_ReliableStreaming/host.json b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/08_ReliableStreaming/host.json similarity index 100% rename from dotnet/samples/Durable/Agents/AzureFunctions/08_ReliableStreaming/host.json rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/08_ReliableStreaming/host.json diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/08_ReliableStreaming/local.settings.json b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/08_ReliableStreaming/local.settings.json similarity index 85% rename from dotnet/samples/Durable/Agents/AzureFunctions/08_ReliableStreaming/local.settings.json rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/08_ReliableStreaming/local.settings.json index 5dfdb17999..71e7ff8dac 100644 --- a/dotnet/samples/Durable/Agents/AzureFunctions/08_ReliableStreaming/local.settings.json +++ b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/08_ReliableStreaming/local.settings.json @@ -5,7 +5,7 @@ "AzureWebJobsStorage": "UseDevelopmentStorage=true", "DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None", "AZURE_OPENAI_ENDPOINT": "", - "AZURE_OPENAI_DEPLOYMENT": "", + "AZURE_OPENAI_DEPLOYMENT_NAME": "", "REDIS_CONNECTION_STRING": "localhost:6379", "REDIS_STREAM_TTL_MINUTES": "10" } diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/README.md b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/README.md similarity index 94% rename from dotnet/samples/Durable/Agents/AzureFunctions/README.md rename to dotnet/samples/04-hosting/DurableAgents/AzureFunctions/README.md index 2545712ea4..ed34b820d0 100644 --- a/dotnet/samples/Durable/Agents/AzureFunctions/README.md +++ b/dotnet/samples/04-hosting/DurableAgents/AzureFunctions/README.md @@ -54,18 +54,18 @@ More information on how to configure RBAC permissions for Azure OpenAI can be fo ### Setting an API key for the Azure OpenAI service -As an alternative to configuring Azure RBAC permissions, you can set an API key for the Azure OpenAI service by setting the `AZURE_OPENAI_KEY` environment variable. +As an alternative to configuring Azure RBAC permissions, you can set an API key for the Azure OpenAI service by setting the `AZURE_OPENAI_API_KEY` environment variable. Bash (Linux/macOS/WSL): ```bash -export AZURE_OPENAI_KEY="your-api-key" +export AZURE_OPENAI_API_KEY="your-api-key" ``` PowerShell: ```powershell -$env:AZURE_OPENAI_KEY="your-api-key" +$env:AZURE_OPENAI_API_KEY="your-api-key" ``` ### Start Durable Task Scheduler @@ -107,7 +107,7 @@ Each sample has its own `local.settings.json` file that contains the environment { "Values": { "AZURE_OPENAI_ENDPOINT": "https://your-resource.openai.azure.com/", - "AZURE_OPENAI_DEPLOYMENT": "your-deployment-name" + "AZURE_OPENAI_DEPLOYMENT_NAME": "your-deployment-name" } } ``` @@ -118,14 +118,14 @@ Alternatively, you can set the environment variables in the command line. ```bash export AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/" -export AZURE_OPENAI_DEPLOYMENT="your-deployment-name" +export AZURE_OPENAI_DEPLOYMENT_NAME="your-deployment-name" ``` ### PowerShell ```powershell $env:AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/" -$env:AZURE_OPENAI_DEPLOYMENT="your-deployment-name" +$env:AZURE_OPENAI_DEPLOYMENT_NAME="your-deployment-name" ``` These environment variables, when set, will override the values in the `local.settings.json` file, making it convenient to test the sample without having to update the `local.settings.json` file. @@ -135,7 +135,7 @@ These environment variables, when set, will override the values in the `local.se Navigate to the sample directory and start the Azure Functions app: ```bash -cd dotnet/samples/AzureFunctions/01_SingleAgent +cd dotnet/samples/04-hosting/DurableAgents/AzureFunctions/01_SingleAgent func start ``` diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/01_SingleAgent/01_SingleAgent.csproj b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/01_SingleAgent/01_SingleAgent.csproj similarity index 100% rename from dotnet/samples/Durable/Agents/ConsoleApps/01_SingleAgent/01_SingleAgent.csproj rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/01_SingleAgent/01_SingleAgent.csproj diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/01_SingleAgent/Program.cs b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/01_SingleAgent/Program.cs similarity index 98% rename from dotnet/samples/Durable/Agents/ConsoleApps/01_SingleAgent/Program.cs rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/01_SingleAgent/Program.cs index 7b54cf7c0a..c331d28ccc 100644 --- a/dotnet/samples/Durable/Agents/ConsoleApps/01_SingleAgent/Program.cs +++ b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/01_SingleAgent/Program.cs @@ -16,15 +16,15 @@ // Get the Azure OpenAI endpoint and deployment name from environment variables. string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") - ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") + ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT_NAME is not set."); // Get DTS connection string from environment variable string dtsConnectionString = Environment.GetEnvironmentVariable("DURABLE_TASK_SCHEDULER_CONNECTION_STRING") ?? "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None"; // Use Azure Key Credential if provided, otherwise use Azure CLI Credential. -string? azureOpenAiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY"); +string? azureOpenAiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY"); // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid // latency issues, unintended credential probing, and potential security risks from fallback mechanisms. diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/01_SingleAgent/README.md b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/01_SingleAgent/README.md similarity index 96% rename from dotnet/samples/Durable/Agents/ConsoleApps/01_SingleAgent/README.md rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/01_SingleAgent/README.md index 7c921b0d87..927cd80e0a 100644 --- a/dotnet/samples/Durable/Agents/ConsoleApps/01_SingleAgent/README.md +++ b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/01_SingleAgent/README.md @@ -17,7 +17,7 @@ See the [README.md](../README.md) file in the parent directory for more informat With the environment setup, you can run the sample: ```bash -cd dotnet/samples/DurableAgents/ConsoleApps/01_SingleAgent +cd dotnet/samples/04-hosting/DurableAgents/ConsoleApps/01_SingleAgent dotnet run --framework net10.0 ``` diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/02_AgentOrchestration_Chaining/02_AgentOrchestration_Chaining.csproj b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining/02_AgentOrchestration_Chaining.csproj similarity index 100% rename from dotnet/samples/Durable/Agents/ConsoleApps/02_AgentOrchestration_Chaining/02_AgentOrchestration_Chaining.csproj rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining/02_AgentOrchestration_Chaining.csproj diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/02_AgentOrchestration_Chaining/Models.cs b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining/Models.cs similarity index 100% rename from dotnet/samples/Durable/Agents/ConsoleApps/02_AgentOrchestration_Chaining/Models.cs rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining/Models.cs diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/02_AgentOrchestration_Chaining/Program.cs b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining/Program.cs similarity index 98% rename from dotnet/samples/Durable/Agents/ConsoleApps/02_AgentOrchestration_Chaining/Program.cs rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining/Program.cs index 77af66252c..6af759a62b 100644 --- a/dotnet/samples/Durable/Agents/ConsoleApps/02_AgentOrchestration_Chaining/Program.cs +++ b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining/Program.cs @@ -20,15 +20,15 @@ // Get the Azure OpenAI endpoint and deployment name from environment variables. string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") - ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") + ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT_NAME is not set."); // Get DTS connection string from environment variable string dtsConnectionString = Environment.GetEnvironmentVariable("DURABLE_TASK_SCHEDULER_CONNECTION_STRING") ?? "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None"; // Use Azure Key Credential if provided, otherwise use Azure CLI Credential. -string? azureOpenAiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY"); +string? azureOpenAiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY"); // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid // latency issues, unintended credential probing, and potential security risks from fallback mechanisms. diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/02_AgentOrchestration_Chaining/README.md b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining/README.md similarity index 96% rename from dotnet/samples/Durable/Agents/ConsoleApps/02_AgentOrchestration_Chaining/README.md rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining/README.md index 504f036dae..83a69b37a4 100644 --- a/dotnet/samples/Durable/Agents/ConsoleApps/02_AgentOrchestration_Chaining/README.md +++ b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining/README.md @@ -18,7 +18,7 @@ See the [README.md](../README.md) file in the parent directory for more informat With the environment setup, you can run the sample: ```bash -cd dotnet/samples/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining +cd dotnet/samples/04-hosting/DurableAgents/ConsoleApps/02_AgentOrchestration_Chaining dotnet run --framework net10.0 ``` diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/03_AgentOrchestration_Concurrency/03_AgentOrchestration_Concurrency.csproj b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/03_AgentOrchestration_Concurrency/03_AgentOrchestration_Concurrency.csproj similarity index 100% rename from dotnet/samples/Durable/Agents/ConsoleApps/03_AgentOrchestration_Concurrency/03_AgentOrchestration_Concurrency.csproj rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/03_AgentOrchestration_Concurrency/03_AgentOrchestration_Concurrency.csproj diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/03_AgentOrchestration_Concurrency/Models.cs b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/03_AgentOrchestration_Concurrency/Models.cs similarity index 100% rename from dotnet/samples/Durable/Agents/ConsoleApps/03_AgentOrchestration_Concurrency/Models.cs rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/03_AgentOrchestration_Concurrency/Models.cs diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/03_AgentOrchestration_Concurrency/Program.cs b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/03_AgentOrchestration_Concurrency/Program.cs similarity index 99% rename from dotnet/samples/Durable/Agents/ConsoleApps/03_AgentOrchestration_Concurrency/Program.cs rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/03_AgentOrchestration_Concurrency/Program.cs index fd7e601f94..714558abc0 100644 --- a/dotnet/samples/Durable/Agents/ConsoleApps/03_AgentOrchestration_Concurrency/Program.cs +++ b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/03_AgentOrchestration_Concurrency/Program.cs @@ -20,15 +20,15 @@ // Get the Azure OpenAI endpoint and deployment name from environment variables. string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") - ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") + ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT_NAME is not set."); // Get DTS connection string from environment variable string dtsConnectionString = Environment.GetEnvironmentVariable("DURABLE_TASK_SCHEDULER_CONNECTION_STRING") ?? "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None"; // Use Azure Key Credential if provided, otherwise use Azure CLI Credential. -string? azureOpenAiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY"); +string? azureOpenAiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY"); // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid // latency issues, unintended credential probing, and potential security risks from fallback mechanisms. diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/03_AgentOrchestration_Concurrency/README.md b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/03_AgentOrchestration_Concurrency/README.md similarity index 96% rename from dotnet/samples/Durable/Agents/ConsoleApps/03_AgentOrchestration_Concurrency/README.md rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/03_AgentOrchestration_Concurrency/README.md index 2ac1a504c8..da75416bea 100644 --- a/dotnet/samples/Durable/Agents/ConsoleApps/03_AgentOrchestration_Concurrency/README.md +++ b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/03_AgentOrchestration_Concurrency/README.md @@ -18,7 +18,7 @@ See the [README.md](../README.md) file in the parent directory for more informat With the environment setup, you can run the sample: ```bash -cd dotnet/samples/DurableAgents/ConsoleApps/03_AgentOrchestration_Concurrency +cd dotnet/samples/04-hosting/DurableAgents/ConsoleApps/03_AgentOrchestration_Concurrency dotnet run --framework net10.0 ``` diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/04_AgentOrchestration_Conditionals/04_AgentOrchestration_Conditionals.csproj b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/04_AgentOrchestration_Conditionals/04_AgentOrchestration_Conditionals.csproj similarity index 100% rename from dotnet/samples/Durable/Agents/ConsoleApps/04_AgentOrchestration_Conditionals/04_AgentOrchestration_Conditionals.csproj rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/04_AgentOrchestration_Conditionals/04_AgentOrchestration_Conditionals.csproj diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/04_AgentOrchestration_Conditionals/Models.cs b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/04_AgentOrchestration_Conditionals/Models.cs similarity index 100% rename from dotnet/samples/Durable/Agents/ConsoleApps/04_AgentOrchestration_Conditionals/Models.cs rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/04_AgentOrchestration_Conditionals/Models.cs diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/04_AgentOrchestration_Conditionals/Program.cs b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/04_AgentOrchestration_Conditionals/Program.cs similarity index 99% rename from dotnet/samples/Durable/Agents/ConsoleApps/04_AgentOrchestration_Conditionals/Program.cs rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/04_AgentOrchestration_Conditionals/Program.cs index dfef999613..7b1751f298 100644 --- a/dotnet/samples/Durable/Agents/ConsoleApps/04_AgentOrchestration_Conditionals/Program.cs +++ b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/04_AgentOrchestration_Conditionals/Program.cs @@ -19,15 +19,15 @@ // Get the Azure OpenAI endpoint and deployment name from environment variables. string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") - ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") + ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT_NAME is not set."); // Get DTS connection string from environment variable string dtsConnectionString = Environment.GetEnvironmentVariable("DURABLE_TASK_SCHEDULER_CONNECTION_STRING") ?? "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None"; // Use Azure Key Credential if provided, otherwise use Azure CLI Credential. -string? azureOpenAiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY"); +string? azureOpenAiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY"); // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid // latency issues, unintended credential probing, and potential security risks from fallback mechanisms. diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/04_AgentOrchestration_Conditionals/README.md b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/04_AgentOrchestration_Conditionals/README.md similarity index 97% rename from dotnet/samples/Durable/Agents/ConsoleApps/04_AgentOrchestration_Conditionals/README.md rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/04_AgentOrchestration_Conditionals/README.md index 646e5eda4e..c10b33145a 100644 --- a/dotnet/samples/Durable/Agents/ConsoleApps/04_AgentOrchestration_Conditionals/README.md +++ b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/04_AgentOrchestration_Conditionals/README.md @@ -18,7 +18,7 @@ See the [README.md](../README.md) file in the parent directory for more informat With the environment setup, you can run the sample: ```bash -cd dotnet/samples/DurableAgents/ConsoleApps/04_AgentOrchestration_Conditionals +cd dotnet/samples/04-hosting/DurableAgents/ConsoleApps/04_AgentOrchestration_Conditionals dotnet run --framework net10.0 ``` diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/05_AgentOrchestration_HITL/05_AgentOrchestration_HITL.csproj b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/05_AgentOrchestration_HITL/05_AgentOrchestration_HITL.csproj similarity index 100% rename from dotnet/samples/Durable/Agents/ConsoleApps/05_AgentOrchestration_HITL/05_AgentOrchestration_HITL.csproj rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/05_AgentOrchestration_HITL/05_AgentOrchestration_HITL.csproj diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/05_AgentOrchestration_HITL/Models.cs b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/05_AgentOrchestration_HITL/Models.cs similarity index 100% rename from dotnet/samples/Durable/Agents/ConsoleApps/05_AgentOrchestration_HITL/Models.cs rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/05_AgentOrchestration_HITL/Models.cs diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/05_AgentOrchestration_HITL/Program.cs b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/05_AgentOrchestration_HITL/Program.cs similarity index 99% rename from dotnet/samples/Durable/Agents/ConsoleApps/05_AgentOrchestration_HITL/Program.cs rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/05_AgentOrchestration_HITL/Program.cs index ec98d55b5a..80c09fee01 100644 --- a/dotnet/samples/Durable/Agents/ConsoleApps/05_AgentOrchestration_HITL/Program.cs +++ b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/05_AgentOrchestration_HITL/Program.cs @@ -20,15 +20,15 @@ // Get the Azure OpenAI endpoint and deployment name from environment variables. string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") - ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") + ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT_NAME is not set."); // Get DTS connection string from environment variable string dtsConnectionString = Environment.GetEnvironmentVariable("DURABLE_TASK_SCHEDULER_CONNECTION_STRING") ?? "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None"; // Use Azure Key Credential if provided, otherwise use Azure CLI Credential. -string? azureOpenAiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY"); +string? azureOpenAiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY"); // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid // latency issues, unintended credential probing, and potential security risks from fallback mechanisms. diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/05_AgentOrchestration_HITL/README.md b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/05_AgentOrchestration_HITL/README.md similarity index 96% rename from dotnet/samples/Durable/Agents/ConsoleApps/05_AgentOrchestration_HITL/README.md rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/05_AgentOrchestration_HITL/README.md index 1386dfbcb1..ec0fe9911e 100644 --- a/dotnet/samples/Durable/Agents/ConsoleApps/05_AgentOrchestration_HITL/README.md +++ b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/05_AgentOrchestration_HITL/README.md @@ -18,7 +18,7 @@ See the [README.md](../README.md) file in the parent directory for more informat With the environment setup, you can run the sample: ```bash -cd dotnet/samples/DurableAgents/ConsoleApps/05_AgentOrchestration_HITL +cd dotnet/samples/04-hosting/DurableAgents/ConsoleApps/05_AgentOrchestration_HITL dotnet run --framework net10.0 ``` diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/06_LongRunningTools/06_LongRunningTools.csproj b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/06_LongRunningTools/06_LongRunningTools.csproj similarity index 100% rename from dotnet/samples/Durable/Agents/ConsoleApps/06_LongRunningTools/06_LongRunningTools.csproj rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/06_LongRunningTools/06_LongRunningTools.csproj diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/06_LongRunningTools/Models.cs b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/06_LongRunningTools/Models.cs similarity index 100% rename from dotnet/samples/Durable/Agents/ConsoleApps/06_LongRunningTools/Models.cs rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/06_LongRunningTools/Models.cs diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/06_LongRunningTools/Program.cs b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/06_LongRunningTools/Program.cs similarity index 99% rename from dotnet/samples/Durable/Agents/ConsoleApps/06_LongRunningTools/Program.cs rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/06_LongRunningTools/Program.cs index 6a8fe08b8d..203edca308 100644 --- a/dotnet/samples/Durable/Agents/ConsoleApps/06_LongRunningTools/Program.cs +++ b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/06_LongRunningTools/Program.cs @@ -21,15 +21,15 @@ // Get the Azure OpenAI endpoint and deployment name from environment variables. string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") - ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") + ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT_NAME is not set."); // Get DTS connection string from environment variable string dtsConnectionString = Environment.GetEnvironmentVariable("DURABLE_TASK_SCHEDULER_CONNECTION_STRING") ?? "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None"; // Use Azure Key Credential if provided, otherwise use Azure CLI Credential. -string? azureOpenAiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY"); +string? azureOpenAiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY"); // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid // latency issues, unintended credential probing, and potential security risks from fallback mechanisms. diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/06_LongRunningTools/README.md b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/06_LongRunningTools/README.md similarity index 98% rename from dotnet/samples/Durable/Agents/ConsoleApps/06_LongRunningTools/README.md rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/06_LongRunningTools/README.md index b0dd69b129..1c87ab50ed 100644 --- a/dotnet/samples/Durable/Agents/ConsoleApps/06_LongRunningTools/README.md +++ b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/06_LongRunningTools/README.md @@ -19,7 +19,7 @@ See the [README.md](../README.md) file in the parent directory for more informat With the environment setup, you can run the sample: ```bash -cd dotnet/samples/DurableAgents/ConsoleApps/06_LongRunningTools +cd dotnet/samples/04-hosting/DurableAgents/ConsoleApps/06_LongRunningTools dotnet run --framework net10.0 ``` diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/07_ReliableStreaming/07_ReliableStreaming.csproj b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/07_ReliableStreaming/07_ReliableStreaming.csproj similarity index 100% rename from dotnet/samples/Durable/Agents/ConsoleApps/07_ReliableStreaming/07_ReliableStreaming.csproj rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/07_ReliableStreaming/07_ReliableStreaming.csproj diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/07_ReliableStreaming/Program.cs b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/07_ReliableStreaming/Program.cs similarity index 99% rename from dotnet/samples/Durable/Agents/ConsoleApps/07_ReliableStreaming/Program.cs rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/07_ReliableStreaming/Program.cs index 9efe28a937..3abc5c8701 100644 --- a/dotnet/samples/Durable/Agents/ConsoleApps/07_ReliableStreaming/Program.cs +++ b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/07_ReliableStreaming/Program.cs @@ -22,8 +22,8 @@ // Get the Azure OpenAI endpoint and deployment name from environment variables. string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); -string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") - ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") + ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT_NAME is not set."); // Get Redis connection string from environment variable. string redisConnectionString = Environment.GetEnvironmentVariable("REDIS_CONNECTION_STRING") @@ -37,7 +37,7 @@ ?? "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None"; // Use Azure Key Credential if provided, otherwise use Azure CLI Credential. -string? azureOpenAiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY"); +string? azureOpenAiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY"); // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid // latency issues, unintended credential probing, and potential security risks from fallback mechanisms. diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/07_ReliableStreaming/README.md b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/07_ReliableStreaming/README.md similarity index 97% rename from dotnet/samples/Durable/Agents/ConsoleApps/07_ReliableStreaming/README.md rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/07_ReliableStreaming/README.md index c1956157e8..b93a66191c 100644 --- a/dotnet/samples/Durable/Agents/ConsoleApps/07_ReliableStreaming/README.md +++ b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/07_ReliableStreaming/README.md @@ -32,7 +32,7 @@ docker ps | grep redis With the environment setup, you can run the sample: ```bash -cd dotnet/samples/DurableAgents/ConsoleApps/07_ReliableStreaming +cd dotnet/samples/04-hosting/DurableAgents/ConsoleApps/07_ReliableStreaming dotnet run --framework net10.0 ``` @@ -168,8 +168,8 @@ This sample provides **at-least-once delivery** with the following characteristi | `REDIS_CONNECTION_STRING` | Redis connection string | `localhost:6379` | | `REDIS_STREAM_TTL_MINUTES` | How long streams are retained after last write | `10` | | `AZURE_OPENAI_ENDPOINT` | Azure OpenAI endpoint URL | (required) | -| `AZURE_OPENAI_DEPLOYMENT` | Azure OpenAI deployment name | (required) | -| `AZURE_OPENAI_KEY` | API key (optional, uses Azure CLI auth if not set) | (optional) | +| `AZURE_OPENAI_DEPLOYMENT_NAME` | Azure OpenAI deployment name | (required) | +| `AZURE_OPENAI_API_KEY` | API key (optional, uses Azure CLI auth if not set) | (optional) | ## Cleanup diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/07_ReliableStreaming/RedisStreamResponseHandler.cs b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/07_ReliableStreaming/RedisStreamResponseHandler.cs similarity index 100% rename from dotnet/samples/Durable/Agents/ConsoleApps/07_ReliableStreaming/RedisStreamResponseHandler.cs rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/07_ReliableStreaming/RedisStreamResponseHandler.cs diff --git a/dotnet/samples/Durable/Agents/ConsoleApps/README.md b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/README.md similarity index 95% rename from dotnet/samples/Durable/Agents/ConsoleApps/README.md rename to dotnet/samples/04-hosting/DurableAgents/ConsoleApps/README.md index 1bd2b0d224..9f52715256 100644 --- a/dotnet/samples/Durable/Agents/ConsoleApps/README.md +++ b/dotnet/samples/04-hosting/DurableAgents/ConsoleApps/README.md @@ -53,18 +53,18 @@ More information on how to configure RBAC permissions for Azure OpenAI can be fo ### Setting an API key for the Azure OpenAI service -As an alternative to configuring Azure RBAC permissions, you can set an API key for the Azure OpenAI service by setting the `AZURE_OPENAI_KEY` environment variable. +As an alternative to configuring Azure RBAC permissions, you can set an API key for the Azure OpenAI service by setting the `AZURE_OPENAI_API_KEY` environment variable. Bash (Linux/macOS/WSL): ```bash -export AZURE_OPENAI_KEY="your-api-key" +export AZURE_OPENAI_API_KEY="your-api-key" ``` PowerShell: ```powershell -$env:AZURE_OPENAI_KEY="your-api-key" +$env:AZURE_OPENAI_API_KEY="your-api-key" ``` ### Start Durable Task Scheduler @@ -85,7 +85,7 @@ Each sample reads configuration from environment variables. You'll need to set t ```bash export AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/" -export AZURE_OPENAI_DEPLOYMENT="your-deployment-name" +export AZURE_OPENAI_DEPLOYMENT_NAME="your-deployment-name" ``` ### Running the Console Apps @@ -93,7 +93,7 @@ export AZURE_OPENAI_DEPLOYMENT="your-deployment-name" Navigate to the sample directory and run the console app: ```bash -cd dotnet/samples/DurableAgents/ConsoleApps/01_SingleAgent +cd dotnet/samples/04-hosting/DurableAgents/ConsoleApps/01_SingleAgent dotnet run --framework net10.0 ``` diff --git a/dotnet/samples/Durable/Directory.Build.props b/dotnet/samples/04-hosting/DurableAgents/Directory.Build.props similarity index 80% rename from dotnet/samples/Durable/Directory.Build.props rename to dotnet/samples/04-hosting/DurableAgents/Directory.Build.props index 7c4cb7dea2..63c25dd5f0 100644 --- a/dotnet/samples/Durable/Directory.Build.props +++ b/dotnet/samples/04-hosting/DurableAgents/Directory.Build.props @@ -1,6 +1,6 @@ - + diff --git a/dotnet/samples/A2AClientServer/A2AClient/A2AClient.csproj b/dotnet/samples/05-end-to-end/A2AClientServer/A2AClient/A2AClient.csproj similarity index 60% rename from dotnet/samples/A2AClientServer/A2AClient/A2AClient.csproj rename to dotnet/samples/05-end-to-end/A2AClientServer/A2AClient/A2AClient.csproj index 6b88c5c697..01a5a069bc 100644 --- a/dotnet/samples/A2AClientServer/A2AClient/A2AClient.csproj +++ b/dotnet/samples/05-end-to-end/A2AClientServer/A2AClient/A2AClient.csproj @@ -15,9 +15,9 @@ - - - + + + diff --git a/dotnet/samples/A2AClientServer/A2AClient/HostClientAgent.cs b/dotnet/samples/05-end-to-end/A2AClientServer/A2AClient/HostClientAgent.cs similarity index 100% rename from dotnet/samples/A2AClientServer/A2AClient/HostClientAgent.cs rename to dotnet/samples/05-end-to-end/A2AClientServer/A2AClient/HostClientAgent.cs diff --git a/dotnet/samples/A2AClientServer/A2AClient/Program.cs b/dotnet/samples/05-end-to-end/A2AClientServer/A2AClient/Program.cs similarity index 100% rename from dotnet/samples/A2AClientServer/A2AClient/Program.cs rename to dotnet/samples/05-end-to-end/A2AClientServer/A2AClient/Program.cs diff --git a/dotnet/samples/A2AClientServer/A2AClient/README.md b/dotnet/samples/05-end-to-end/A2AClientServer/A2AClient/README.md similarity index 85% rename from dotnet/samples/A2AClientServer/A2AClient/README.md rename to dotnet/samples/05-end-to-end/A2AClientServer/A2AClient/README.md index c542430b22..eb233fb8d1 100644 --- a/dotnet/samples/A2AClientServer/A2AClient/README.md +++ b/dotnet/samples/05-end-to-end/A2AClientServer/A2AClient/README.md @@ -18,9 +18,9 @@ To run the sample, follow these steps: The agent urls are provided as a ` ` delimited list of strings ```powershell -cd dotnet/samples/A2AClientServer/A2AClient +cd dotnet/samples/05-end-to-end/A2AClientServer/A2AClient -$env:OPENAI_MODEL="gpt-4o-mini" +$env:OPENAI_CHAT_MODEL_NAME="gpt-4o-mini" $env:OPENAI_API_KEY="" $env:AGENT_URLS="http://localhost:5000/policy;http://localhost:5000/invoice;http://localhost:5000/logistics" ``` diff --git a/dotnet/samples/A2AClientServer/A2AServer/A2AServer.csproj b/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/A2AServer.csproj similarity index 54% rename from dotnet/samples/A2AClientServer/A2AServer/A2AServer.csproj rename to dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/A2AServer.csproj index 0a3b170a0b..be5ff472c1 100644 --- a/dotnet/samples/A2AClientServer/A2AServer/A2AServer.csproj +++ b/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/A2AServer.csproj @@ -19,12 +19,12 @@ - - + + - - - + + + diff --git a/dotnet/samples/A2AClientServer/A2AServer/A2AServer.http b/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/A2AServer.http similarity index 100% rename from dotnet/samples/A2AClientServer/A2AServer/A2AServer.http rename to dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/A2AServer.http diff --git a/dotnet/samples/A2AClientServer/A2AServer/HostAgentFactory.cs b/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/HostAgentFactory.cs similarity index 100% rename from dotnet/samples/A2AClientServer/A2AServer/HostAgentFactory.cs rename to dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/HostAgentFactory.cs diff --git a/dotnet/samples/A2AClientServer/A2AServer/Models/InvoiceQuery.cs b/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/Models/InvoiceQuery.cs similarity index 100% rename from dotnet/samples/A2AClientServer/A2AServer/Models/InvoiceQuery.cs rename to dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/Models/InvoiceQuery.cs diff --git a/dotnet/samples/A2AClientServer/A2AServer/Program.cs b/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/Program.cs similarity index 96% rename from dotnet/samples/A2AClientServer/A2AServer/Program.cs rename to dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/Program.cs index bd344c46c8..b8a10ac647 100644 --- a/dotnet/samples/A2AClientServer/A2AServer/Program.cs +++ b/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/Program.cs @@ -36,8 +36,8 @@ .Build(); string? apiKey = configuration["OPENAI_API_KEY"]; -string model = configuration["OPENAI_MODEL"] ?? "gpt-4o-mini"; -string? endpoint = configuration["AZURE_FOUNDRY_PROJECT_ENDPOINT"]; +string model = configuration["OPENAI_CHAT_MODEL_NAME"] ?? "gpt-4o-mini"; +string? endpoint = configuration["AZURE_AI_PROJECT_ENDPOINT"]; var invoiceQueryPlugin = new InvoiceQuery(); IList tools = diff --git a/dotnet/samples/A2AClientServer/README.md b/dotnet/samples/05-end-to-end/A2AClientServer/README.md similarity index 98% rename from dotnet/samples/A2AClientServer/README.md rename to dotnet/samples/05-end-to-end/A2AClientServer/README.md index 04b9968e76..eea3763791 100644 --- a/dotnet/samples/A2AClientServer/README.md +++ b/dotnet/samples/05-end-to-end/A2AClientServer/README.md @@ -84,7 +84,7 @@ You must create the agents in an Azure AI Foundry project and then provide the p ``` ```powershell -$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://ai-foundry-your-project.services.ai.azure.com/api/projects/ai-proj-ga-your-project" # Replace with your Foundry Project endpoint +$env:AZURE_AI_PROJECT_ENDPOINT="https://ai-foundry-your-project.services.ai.azure.com/api/projects/ai-proj-ga-your-project" # Replace with your Foundry Project endpoint ``` Use the following commands to run each A2A server diff --git a/dotnet/samples/A2AClientServer/a2a-inspector-agent-card.png b/dotnet/samples/05-end-to-end/A2AClientServer/a2a-inspector-agent-card.png similarity index 100% rename from dotnet/samples/A2AClientServer/a2a-inspector-agent-card.png rename to dotnet/samples/05-end-to-end/A2AClientServer/a2a-inspector-agent-card.png diff --git a/dotnet/samples/A2AClientServer/a2a-inspector-raw-json-response.png b/dotnet/samples/05-end-to-end/A2AClientServer/a2a-inspector-raw-json-response.png similarity index 100% rename from dotnet/samples/A2AClientServer/a2a-inspector-raw-json-response.png rename to dotnet/samples/05-end-to-end/A2AClientServer/a2a-inspector-raw-json-response.png diff --git a/dotnet/samples/A2AClientServer/a2a-inspector-send-message.png b/dotnet/samples/05-end-to-end/A2AClientServer/a2a-inspector-send-message.png similarity index 100% rename from dotnet/samples/A2AClientServer/a2a-inspector-send-message.png rename to dotnet/samples/05-end-to-end/A2AClientServer/a2a-inspector-send-message.png diff --git a/dotnet/samples/A2AClientServer/demo-architecture.png b/dotnet/samples/05-end-to-end/A2AClientServer/demo-architecture.png similarity index 100% rename from dotnet/samples/A2AClientServer/demo-architecture.png rename to dotnet/samples/05-end-to-end/A2AClientServer/demo-architecture.png diff --git a/dotnet/samples/A2AClientServer/rest-client-agent-card.png b/dotnet/samples/05-end-to-end/A2AClientServer/rest-client-agent-card.png similarity index 100% rename from dotnet/samples/A2AClientServer/rest-client-agent-card.png rename to dotnet/samples/05-end-to-end/A2AClientServer/rest-client-agent-card.png diff --git a/dotnet/samples/A2AClientServer/rest-client-send-message.png b/dotnet/samples/05-end-to-end/A2AClientServer/rest-client-send-message.png similarity index 100% rename from dotnet/samples/A2AClientServer/rest-client-send-message.png rename to dotnet/samples/05-end-to-end/A2AClientServer/rest-client-send-message.png diff --git a/dotnet/samples/AGUIClientServer/AGUIClient/AGUIClient.csproj b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIClient/AGUIClient.csproj similarity index 70% rename from dotnet/samples/AGUIClientServer/AGUIClient/AGUIClient.csproj rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIClient/AGUIClient.csproj index 7d80fa7cda..8a45c09ce0 100644 --- a/dotnet/samples/AGUIClientServer/AGUIClient/AGUIClient.csproj +++ b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIClient/AGUIClient.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/dotnet/samples/AGUIClientServer/AGUIClient/AGUIClientSerializerContext.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIClient/AGUIClientSerializerContext.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIClient/AGUIClientSerializerContext.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIClient/AGUIClientSerializerContext.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIClient/Program.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIClient/Program.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIClient/Program.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIClient/Program.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIClient/README.md b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIClient/README.md similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIClient/README.md rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIClient/README.md diff --git a/dotnet/samples/AGUIClientServer/AGUIClient/SensorRequest.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIClient/SensorRequest.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIClient/SensorRequest.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIClient/SensorRequest.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIClient/SensorResponse.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIClient/SensorResponse.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIClient/SensorResponse.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIClient/SensorResponse.cs diff --git a/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/AGUIDojoServer.csproj b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/AGUIDojoServer.csproj new file mode 100644 index 0000000000..eb2dc3f77e --- /dev/null +++ b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/AGUIDojoServer.csproj @@ -0,0 +1,22 @@ + + + + Exe + net10.0 + enable + enable + b9c3f1e1-2fb4-5g29-0e52-53e2b7g9gf21 + + + + + + + + + + + + + + diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/AGUIDojoServerSerializerContext.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/AGUIDojoServerSerializerContext.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIDojoServer/AGUIDojoServerSerializerContext.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/AGUIDojoServerSerializerContext.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/AgenticPlanningTools.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/AgenticUI/AgenticPlanningTools.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/AgenticPlanningTools.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/AgenticUI/AgenticPlanningTools.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/AgenticUIAgent.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/AgenticUI/AgenticUIAgent.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/AgenticUIAgent.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/AgenticUI/AgenticUIAgent.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/JsonPatchOperation.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/AgenticUI/JsonPatchOperation.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/JsonPatchOperation.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/AgenticUI/JsonPatchOperation.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/Plan.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/AgenticUI/Plan.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/Plan.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/AgenticUI/Plan.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/Step.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/AgenticUI/Step.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/Step.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/AgenticUI/Step.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/StepStatus.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/AgenticUI/StepStatus.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/StepStatus.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/AgenticUI/StepStatus.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/BackendToolRendering/WeatherInfo.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/BackendToolRendering/WeatherInfo.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIDojoServer/BackendToolRendering/WeatherInfo.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/BackendToolRendering/WeatherInfo.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/ChatClientAgentFactory.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/ChatClientAgentFactory.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIDojoServer/ChatClientAgentFactory.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/ChatClientAgentFactory.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/PredictiveStateUpdates/DocumentState.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/PredictiveStateUpdates/DocumentState.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIDojoServer/PredictiveStateUpdates/DocumentState.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/PredictiveStateUpdates/DocumentState.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/PredictiveStateUpdates/PredictiveStateUpdatesAgent.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/PredictiveStateUpdates/PredictiveStateUpdatesAgent.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIDojoServer/PredictiveStateUpdates/PredictiveStateUpdatesAgent.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/PredictiveStateUpdates/PredictiveStateUpdatesAgent.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/Program.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/Program.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIDojoServer/Program.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/Program.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/Properties/launchSettings.json b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/Properties/launchSettings.json similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIDojoServer/Properties/launchSettings.json rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/Properties/launchSettings.json diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/SharedState/Ingredient.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/SharedState/Ingredient.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIDojoServer/SharedState/Ingredient.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/SharedState/Ingredient.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/SharedState/Recipe.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/SharedState/Recipe.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIDojoServer/SharedState/Recipe.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/SharedState/Recipe.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/SharedState/RecipeResponse.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/SharedState/RecipeResponse.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIDojoServer/SharedState/RecipeResponse.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/SharedState/RecipeResponse.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/SharedState/SharedStateAgent.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/SharedState/SharedStateAgent.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIDojoServer/SharedState/SharedStateAgent.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/SharedState/SharedStateAgent.cs diff --git a/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/appsettings.Development.json b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/appsettings.Development.json similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/appsettings.Development.json rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/appsettings.Development.json diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/appsettings.json b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/appsettings.json similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIDojoServer/appsettings.json rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIDojoServer/appsettings.json diff --git a/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIServer/AGUIServer.csproj b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIServer/AGUIServer.csproj new file mode 100644 index 0000000000..c26e3eebad --- /dev/null +++ b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIServer/AGUIServer.csproj @@ -0,0 +1,22 @@ + + + + Exe + net10.0 + enable + enable + a8b2e9f0-1ea3-4f18-9d41-42d1a6f8fe10 + + + + + + + + + + + + + + diff --git a/dotnet/samples/AGUIClientServer/AGUIServer/AGUIServer.http b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIServer/AGUIServer.http similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIServer/AGUIServer.http rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIServer/AGUIServer.http diff --git a/dotnet/samples/AGUIClientServer/AGUIServer/AGUIServerSerializerContext.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIServer/AGUIServerSerializerContext.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIServer/AGUIServerSerializerContext.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIServer/AGUIServerSerializerContext.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIServer/Program.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIServer/Program.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIServer/Program.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIServer/Program.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIServer/Properties/launchSettings.json b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIServer/Properties/launchSettings.json similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIServer/Properties/launchSettings.json rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIServer/Properties/launchSettings.json diff --git a/dotnet/samples/AGUIClientServer/AGUIServer/ServerWeatherForecastRequest.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIServer/ServerWeatherForecastRequest.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIServer/ServerWeatherForecastRequest.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIServer/ServerWeatherForecastRequest.cs diff --git a/dotnet/samples/AGUIClientServer/AGUIServer/ServerWeatherForecastResponse.cs b/dotnet/samples/05-end-to-end/AGUIClientServer/AGUIServer/ServerWeatherForecastResponse.cs similarity index 100% rename from dotnet/samples/AGUIClientServer/AGUIServer/ServerWeatherForecastResponse.cs rename to dotnet/samples/05-end-to-end/AGUIClientServer/AGUIServer/ServerWeatherForecastResponse.cs diff --git a/dotnet/samples/AGUIClientServer/README.md b/dotnet/samples/05-end-to-end/AGUIClientServer/README.md similarity index 100% rename from dotnet/samples/AGUIClientServer/README.md rename to dotnet/samples/05-end-to-end/AGUIClientServer/README.md diff --git a/dotnet/samples/AGUIWebChat/Client/AGUIWebChatClient.csproj b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/AGUIWebChatClient.csproj similarity index 75% rename from dotnet/samples/AGUIWebChat/Client/AGUIWebChatClient.csproj rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/AGUIWebChatClient.csproj index b28e53df6e..fef0deb3ec 100644 --- a/dotnet/samples/AGUIWebChat/Client/AGUIWebChatClient.csproj +++ b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/AGUIWebChatClient.csproj @@ -8,7 +8,7 @@ - + diff --git a/dotnet/samples/AGUIWebChat/Client/Components/App.razor b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/App.razor similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/App.razor rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/App.razor diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Layout/LoadingSpinner.razor b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Layout/LoadingSpinner.razor similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Layout/LoadingSpinner.razor rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Layout/LoadingSpinner.razor diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Layout/LoadingSpinner.razor.css b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Layout/LoadingSpinner.razor.css similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Layout/LoadingSpinner.razor.css rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Layout/LoadingSpinner.razor.css diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Layout/MainLayout.razor b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Layout/MainLayout.razor similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Layout/MainLayout.razor rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Layout/MainLayout.razor diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Layout/MainLayout.razor.css b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Layout/MainLayout.razor.css similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Layout/MainLayout.razor.css rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Layout/MainLayout.razor.css diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/Chat.razor b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/Chat.razor similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/Chat.razor rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/Chat.razor diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/Chat.razor.css b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/Chat.razor.css similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/Chat.razor.css rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/Chat.razor.css diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatCitation.razor b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatCitation.razor similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatCitation.razor rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatCitation.razor diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatCitation.razor.css b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatCitation.razor.css similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatCitation.razor.css rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatCitation.razor.css diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatHeader.razor b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatHeader.razor similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatHeader.razor rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatHeader.razor diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatHeader.razor.css b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatHeader.razor.css similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatHeader.razor.css rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatHeader.razor.css diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatInput.razor b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatInput.razor similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatInput.razor rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatInput.razor diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatInput.razor.css b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatInput.razor.css similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatInput.razor.css rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatInput.razor.css diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatInput.razor.js b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatInput.razor.js similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatInput.razor.js rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatInput.razor.js diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageItem.razor b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageItem.razor similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageItem.razor rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageItem.razor diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageItem.razor.css b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageItem.razor.css similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageItem.razor.css rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageItem.razor.css diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageList.razor b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageList.razor similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageList.razor rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageList.razor diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageList.razor.css b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageList.razor.css similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageList.razor.css rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageList.razor.css diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageList.razor.js b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageList.razor.js similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageList.razor.js rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatMessageList.razor.js diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatSuggestions.razor b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatSuggestions.razor similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatSuggestions.razor rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatSuggestions.razor diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatSuggestions.razor.css b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatSuggestions.razor.css similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Pages/Chat/ChatSuggestions.razor.css rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Pages/Chat/ChatSuggestions.razor.css diff --git a/dotnet/samples/AGUIWebChat/Client/Components/Routes.razor b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Routes.razor similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/Routes.razor rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/Routes.razor diff --git a/dotnet/samples/AGUIWebChat/Client/Components/_Imports.razor b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/_Imports.razor similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/Components/_Imports.razor rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Components/_Imports.razor diff --git a/dotnet/samples/AGUIWebChat/Client/Program.cs b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Program.cs similarity index 91% rename from dotnet/samples/AGUIWebChat/Client/Program.cs rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Program.cs index c145227062..ff5d1cacd7 100644 --- a/dotnet/samples/AGUIWebChat/Client/Program.cs +++ b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Program.cs @@ -9,7 +9,7 @@ builder.Services.AddRazorComponents() .AddInteractiveServerComponents(); -string serverUrl = builder.Configuration["SERVER_URL"] ?? "http://localhost:5100"; +string serverUrl = builder.Configuration["AGUI_SERVER_URL"] ?? "http://localhost:5100"; builder.Services.AddHttpClient("aguiserver", httpClient => httpClient.BaseAddress = new Uri(serverUrl)); diff --git a/dotnet/samples/AGUIWebChat/Client/Properties/launchSettings.json b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Properties/launchSettings.json similarity index 86% rename from dotnet/samples/AGUIWebChat/Client/Properties/launchSettings.json rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/Properties/launchSettings.json index 348e16bc3b..068f935461 100644 --- a/dotnet/samples/AGUIWebChat/Client/Properties/launchSettings.json +++ b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/Properties/launchSettings.json @@ -8,7 +8,7 @@ "applicationUrl": "http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", - "SERVER_URL": "http://localhost:5100" + "AGUI_SERVER_URL": "http://localhost:5100" } } } diff --git a/dotnet/samples/AGUIWebChat/Client/wwwroot/app.css b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/wwwroot/app.css similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/wwwroot/app.css rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/wwwroot/app.css diff --git a/dotnet/samples/AGUIWebChat/Client/wwwroot/favicon.png b/dotnet/samples/05-end-to-end/AGUIWebChat/Client/wwwroot/favicon.png similarity index 100% rename from dotnet/samples/AGUIWebChat/Client/wwwroot/favicon.png rename to dotnet/samples/05-end-to-end/AGUIWebChat/Client/wwwroot/favicon.png diff --git a/dotnet/samples/AGUIWebChat/README.md b/dotnet/samples/05-end-to-end/AGUIWebChat/README.md similarity index 94% rename from dotnet/samples/AGUIWebChat/README.md rename to dotnet/samples/05-end-to-end/AGUIWebChat/README.md index bdb8ae25d2..0e42757fa1 100644 --- a/dotnet/samples/AGUIWebChat/README.md +++ b/dotnet/samples/05-end-to-end/AGUIWebChat/README.md @@ -89,7 +89,7 @@ The server exposes the agent via the AG-UI protocol at `http://localhost:5100/ag The client (`Client/Program.cs`) configures an `AGUIChatClient` to connect to the server: ```csharp -string serverUrl = builder.Configuration["SERVER_URL"] ?? "http://localhost:5100"; +string serverUrl = builder.Configuration["AGUI_SERVER_URL"] ?? "http://localhost:5100"; builder.Services.AddHttpClient("aguiserver", httpClient => httpClient.BaseAddress = new Uri(serverUrl)); @@ -141,17 +141,17 @@ The client connects to the server URL specified in `Client/Properties/launchSett "http": { "applicationUrl": "http://localhost:5000", "environmentVariables": { - "SERVER_URL": "http://localhost:5100" + "AGUI_SERVER_URL": "http://localhost:5100" } } } } ``` -To change the server URL, modify the `SERVER_URL` environment variable in the client's launch settings or provide it at runtime: +To change the server URL, modify the `AGUI_SERVER_URL` environment variable in the client's launch settings or provide it at runtime: ```powershell -$env:SERVER_URL="http://your-server:5100" +$env:AGUI_SERVER_URL="http://your-server:5100" dotnet run ``` diff --git a/dotnet/samples/05-end-to-end/AGUIWebChat/Server/AGUIWebChatServer.csproj b/dotnet/samples/05-end-to-end/AGUIWebChat/Server/AGUIWebChatServer.csproj new file mode 100644 index 0000000000..e798d23506 --- /dev/null +++ b/dotnet/samples/05-end-to-end/AGUIWebChat/Server/AGUIWebChatServer.csproj @@ -0,0 +1,21 @@ + + + + Exe + net10.0 + enable + enable + + + + + + + + + + + + + + diff --git a/dotnet/samples/AGUIWebChat/Server/Program.cs b/dotnet/samples/05-end-to-end/AGUIWebChat/Server/Program.cs similarity index 100% rename from dotnet/samples/AGUIWebChat/Server/Program.cs rename to dotnet/samples/05-end-to-end/AGUIWebChat/Server/Program.cs diff --git a/dotnet/samples/AGUIWebChat/Server/Properties/launchSettings.json b/dotnet/samples/05-end-to-end/AGUIWebChat/Server/Properties/launchSettings.json similarity index 100% rename from dotnet/samples/AGUIWebChat/Server/Properties/launchSettings.json rename to dotnet/samples/05-end-to-end/AGUIWebChat/Server/Properties/launchSettings.json diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/ActorFrameworkWebApplicationExtensions.cs b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/ActorFrameworkWebApplicationExtensions.cs similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/ActorFrameworkWebApplicationExtensions.cs rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/ActorFrameworkWebApplicationExtensions.cs diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/AgentWebChat.AgentHost.csproj b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/AgentWebChat.AgentHost.csproj similarity index 55% rename from dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/AgentWebChat.AgentHost.csproj rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/AgentWebChat.AgentHost.csproj index f71becf5d3..4ed6d27cbd 100644 --- a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/AgentWebChat.AgentHost.csproj +++ b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/AgentWebChat.AgentHost.csproj @@ -8,13 +8,13 @@ - - - - - - - + + + + + + + diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Custom/CustomAITools.cs b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/Custom/CustomAITools.cs similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Custom/CustomAITools.cs rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/Custom/CustomAITools.cs diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Program.cs b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/Program.cs similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Program.cs rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/Program.cs diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Properties/launchSettings.json b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/Properties/launchSettings.json similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Properties/launchSettings.json rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/Properties/launchSettings.json diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientConnectionInfo.cs b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientConnectionInfo.cs similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientConnectionInfo.cs rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientConnectionInfo.cs diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientExtensions.cs b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientExtensions.cs similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientExtensions.cs rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientExtensions.cs diff --git a/dotnet/samples/GettingStarted/AGUI/Step02_BackendTools/Server/appsettings.Development.json b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/appsettings.Development.json similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step02_BackendTools/Server/appsettings.Development.json rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/appsettings.Development.json diff --git a/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/appsettings.json b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/appsettings.json similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/appsettings.json rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AgentHost/appsettings.json diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AppHost/AgentWebChat.AppHost.csproj b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AppHost/AgentWebChat.AppHost.csproj similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.AppHost/AgentWebChat.AppHost.csproj rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AppHost/AgentWebChat.AppHost.csproj diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AppHost/ModelExtensions.cs b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AppHost/ModelExtensions.cs similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.AppHost/ModelExtensions.cs rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AppHost/ModelExtensions.cs diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AppHost/Program.cs b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AppHost/Program.cs similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.AppHost/Program.cs rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AppHost/Program.cs diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AppHost/Properties/launchSettings.json b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AppHost/Properties/launchSettings.json similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.AppHost/Properties/launchSettings.json rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AppHost/Properties/launchSettings.json diff --git a/dotnet/samples/GettingStarted/AGUI/Step03_FrontendTools/Server/appsettings.Development.json b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AppHost/appsettings.Development.json similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step03_FrontendTools/Server/appsettings.Development.json rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AppHost/appsettings.Development.json diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.AppHost/appsettings.json b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AppHost/appsettings.json similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.AppHost/appsettings.json rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.AppHost/appsettings.json diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.ServiceDefaults/AgentWebChat.ServiceDefaults.csproj b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.ServiceDefaults/AgentWebChat.ServiceDefaults.csproj similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.ServiceDefaults/AgentWebChat.ServiceDefaults.csproj rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.ServiceDefaults/AgentWebChat.ServiceDefaults.csproj diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.ServiceDefaults/ServiceDefaultsExtensions.cs b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.ServiceDefaults/ServiceDefaultsExtensions.cs similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.ServiceDefaults/ServiceDefaultsExtensions.cs rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.ServiceDefaults/ServiceDefaultsExtensions.cs diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/A2AAgentClient.cs b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/A2AAgentClient.cs similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.Web/A2AAgentClient.cs rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/A2AAgentClient.cs diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/AgentDiscoveryClient.cs b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/AgentDiscoveryClient.cs similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.Web/AgentDiscoveryClient.cs rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/AgentDiscoveryClient.cs diff --git a/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/AgentWebChat.Web.csproj b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/AgentWebChat.Web.csproj new file mode 100644 index 0000000000..5335499168 --- /dev/null +++ b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/AgentWebChat.Web.csproj @@ -0,0 +1,18 @@ + + + + net10.0 + enable + enable + $(NoWarn);CA1812 + + + + + + + + + + + diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/App.razor b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/Components/App.razor similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/App.razor rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/Components/App.razor diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/Layout/MainLayout.razor b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/Components/Layout/MainLayout.razor similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/Layout/MainLayout.razor rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/Components/Layout/MainLayout.razor diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/Layout/MainLayout.razor.css b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/Components/Layout/MainLayout.razor.css similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/Layout/MainLayout.razor.css rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/Components/Layout/MainLayout.razor.css diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/Pages/Error.razor b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/Components/Pages/Error.razor similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/Pages/Error.razor rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/Components/Pages/Error.razor diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/Pages/Home.razor b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/Components/Pages/Home.razor similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/Pages/Home.razor rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/Components/Pages/Home.razor diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/Routes.razor b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/Components/Routes.razor similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/Routes.razor rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/Components/Routes.razor diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/_Imports.razor b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/Components/_Imports.razor similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.Web/Components/_Imports.razor rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/Components/_Imports.razor diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/IAgentClient.cs b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/IAgentClient.cs similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.Web/IAgentClient.cs rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/IAgentClient.cs diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/OpenAIChatCompletionsAgentClient.cs b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/OpenAIChatCompletionsAgentClient.cs similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.Web/OpenAIChatCompletionsAgentClient.cs rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/OpenAIChatCompletionsAgentClient.cs diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/OpenAIResponsesAgentClient.cs b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/OpenAIResponsesAgentClient.cs similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.Web/OpenAIResponsesAgentClient.cs rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/OpenAIResponsesAgentClient.cs diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/Program.cs b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/Program.cs similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.Web/Program.cs rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/Program.cs diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/Properties/launchSettings.json b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/Properties/launchSettings.json similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.Web/Properties/launchSettings.json rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/Properties/launchSettings.json diff --git a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/appsettings.Development.json b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/appsettings.Development.json similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/appsettings.Development.json rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/appsettings.Development.json diff --git a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/appsettings.json b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/appsettings.json similarity index 100% rename from dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/appsettings.json rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/appsettings.json diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/wwwroot/app.css b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/wwwroot/app.css similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.Web/wwwroot/app.css rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/wwwroot/app.css diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/wwwroot/favicon.png b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/wwwroot/favicon.png similarity index 100% rename from dotnet/samples/AgentWebChat/AgentWebChat.Web/wwwroot/favicon.png rename to dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/wwwroot/favicon.png diff --git a/dotnet/samples/Purview/AgentWithPurview/AgentWithPurview.csproj b/dotnet/samples/05-end-to-end/AgentWithPurview/AgentWithPurview.csproj similarity index 100% rename from dotnet/samples/Purview/AgentWithPurview/AgentWithPurview.csproj rename to dotnet/samples/05-end-to-end/AgentWithPurview/AgentWithPurview.csproj diff --git a/dotnet/samples/Purview/AgentWithPurview/Program.cs b/dotnet/samples/05-end-to-end/AgentWithPurview/Program.cs similarity index 100% rename from dotnet/samples/Purview/AgentWithPurview/Program.cs rename to dotnet/samples/05-end-to-end/AgentWithPurview/Program.cs diff --git a/dotnet/samples/HostedAgents/AgentWithHostedMCP/AgentWithHostedMCP.csproj b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/AgentWithHostedMCP.csproj similarity index 100% rename from dotnet/samples/HostedAgents/AgentWithHostedMCP/AgentWithHostedMCP.csproj rename to dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/AgentWithHostedMCP.csproj diff --git a/dotnet/samples/HostedAgents/AgentWithHostedMCP/Dockerfile b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/Dockerfile similarity index 100% rename from dotnet/samples/HostedAgents/AgentWithHostedMCP/Dockerfile rename to dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/Dockerfile diff --git a/dotnet/samples/HostedAgents/AgentWithHostedMCP/Program.cs b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/Program.cs similarity index 100% rename from dotnet/samples/HostedAgents/AgentWithHostedMCP/Program.cs rename to dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/Program.cs diff --git a/dotnet/samples/HostedAgents/AgentWithHostedMCP/README.md b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/README.md similarity index 100% rename from dotnet/samples/HostedAgents/AgentWithHostedMCP/README.md rename to dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/README.md diff --git a/dotnet/samples/HostedAgents/AgentWithHostedMCP/agent.yaml b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/agent.yaml similarity index 100% rename from dotnet/samples/HostedAgents/AgentWithHostedMCP/agent.yaml rename to dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/agent.yaml diff --git a/dotnet/samples/HostedAgents/AgentWithHostedMCP/run-requests.http b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/run-requests.http similarity index 100% rename from dotnet/samples/HostedAgents/AgentWithHostedMCP/run-requests.http rename to dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/run-requests.http diff --git a/dotnet/samples/HostedAgents/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj similarity index 100% rename from dotnet/samples/HostedAgents/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj rename to dotnet/samples/05-end-to-end/HostedAgents/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj diff --git a/dotnet/samples/HostedAgents/AgentWithTextSearchRag/Dockerfile b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithTextSearchRag/Dockerfile similarity index 100% rename from dotnet/samples/HostedAgents/AgentWithTextSearchRag/Dockerfile rename to dotnet/samples/05-end-to-end/HostedAgents/AgentWithTextSearchRag/Dockerfile diff --git a/dotnet/samples/HostedAgents/AgentWithTextSearchRag/Program.cs b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithTextSearchRag/Program.cs similarity index 100% rename from dotnet/samples/HostedAgents/AgentWithTextSearchRag/Program.cs rename to dotnet/samples/05-end-to-end/HostedAgents/AgentWithTextSearchRag/Program.cs diff --git a/dotnet/samples/HostedAgents/AgentWithTextSearchRag/README.md b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithTextSearchRag/README.md similarity index 100% rename from dotnet/samples/HostedAgents/AgentWithTextSearchRag/README.md rename to dotnet/samples/05-end-to-end/HostedAgents/AgentWithTextSearchRag/README.md diff --git a/dotnet/samples/HostedAgents/AgentWithTextSearchRag/agent.yaml b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithTextSearchRag/agent.yaml similarity index 100% rename from dotnet/samples/HostedAgents/AgentWithTextSearchRag/agent.yaml rename to dotnet/samples/05-end-to-end/HostedAgents/AgentWithTextSearchRag/agent.yaml diff --git a/dotnet/samples/HostedAgents/AgentWithTextSearchRag/run-requests.http b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithTextSearchRag/run-requests.http similarity index 100% rename from dotnet/samples/HostedAgents/AgentWithTextSearchRag/run-requests.http rename to dotnet/samples/05-end-to-end/HostedAgents/AgentWithTextSearchRag/run-requests.http diff --git a/dotnet/samples/HostedAgents/AgentsInWorkflows/AgentsInWorkflows.csproj b/dotnet/samples/05-end-to-end/HostedAgents/AgentsInWorkflows/AgentsInWorkflows.csproj similarity index 100% rename from dotnet/samples/HostedAgents/AgentsInWorkflows/AgentsInWorkflows.csproj rename to dotnet/samples/05-end-to-end/HostedAgents/AgentsInWorkflows/AgentsInWorkflows.csproj diff --git a/dotnet/samples/HostedAgents/AgentsInWorkflows/Dockerfile b/dotnet/samples/05-end-to-end/HostedAgents/AgentsInWorkflows/Dockerfile similarity index 100% rename from dotnet/samples/HostedAgents/AgentsInWorkflows/Dockerfile rename to dotnet/samples/05-end-to-end/HostedAgents/AgentsInWorkflows/Dockerfile diff --git a/dotnet/samples/HostedAgents/AgentsInWorkflows/Program.cs b/dotnet/samples/05-end-to-end/HostedAgents/AgentsInWorkflows/Program.cs similarity index 100% rename from dotnet/samples/HostedAgents/AgentsInWorkflows/Program.cs rename to dotnet/samples/05-end-to-end/HostedAgents/AgentsInWorkflows/Program.cs diff --git a/dotnet/samples/HostedAgents/AgentsInWorkflows/README.md b/dotnet/samples/05-end-to-end/HostedAgents/AgentsInWorkflows/README.md similarity index 100% rename from dotnet/samples/HostedAgents/AgentsInWorkflows/README.md rename to dotnet/samples/05-end-to-end/HostedAgents/AgentsInWorkflows/README.md diff --git a/dotnet/samples/HostedAgents/AgentsInWorkflows/agent.yaml b/dotnet/samples/05-end-to-end/HostedAgents/AgentsInWorkflows/agent.yaml similarity index 100% rename from dotnet/samples/HostedAgents/AgentsInWorkflows/agent.yaml rename to dotnet/samples/05-end-to-end/HostedAgents/AgentsInWorkflows/agent.yaml diff --git a/dotnet/samples/HostedAgents/AgentsInWorkflows/run-requests.http b/dotnet/samples/05-end-to-end/HostedAgents/AgentsInWorkflows/run-requests.http similarity index 100% rename from dotnet/samples/HostedAgents/AgentsInWorkflows/run-requests.http rename to dotnet/samples/05-end-to-end/HostedAgents/AgentsInWorkflows/run-requests.http diff --git a/dotnet/samples/M365Agent/AFAgentApplication.cs b/dotnet/samples/05-end-to-end/M365Agent/AFAgentApplication.cs similarity index 100% rename from dotnet/samples/M365Agent/AFAgentApplication.cs rename to dotnet/samples/05-end-to-end/M365Agent/AFAgentApplication.cs diff --git a/dotnet/samples/M365Agent/Agents/AdaptiveCardAIContent.cs b/dotnet/samples/05-end-to-end/M365Agent/Agents/AdaptiveCardAIContent.cs similarity index 100% rename from dotnet/samples/M365Agent/Agents/AdaptiveCardAIContent.cs rename to dotnet/samples/05-end-to-end/M365Agent/Agents/AdaptiveCardAIContent.cs diff --git a/dotnet/samples/M365Agent/Agents/WeatherForecastAgent.cs b/dotnet/samples/05-end-to-end/M365Agent/Agents/WeatherForecastAgent.cs similarity index 100% rename from dotnet/samples/M365Agent/Agents/WeatherForecastAgent.cs rename to dotnet/samples/05-end-to-end/M365Agent/Agents/WeatherForecastAgent.cs diff --git a/dotnet/samples/M365Agent/Agents/WeatherForecastAgentResponse.cs b/dotnet/samples/05-end-to-end/M365Agent/Agents/WeatherForecastAgentResponse.cs similarity index 100% rename from dotnet/samples/M365Agent/Agents/WeatherForecastAgentResponse.cs rename to dotnet/samples/05-end-to-end/M365Agent/Agents/WeatherForecastAgentResponse.cs diff --git a/dotnet/samples/M365Agent/Agents/WeatherForecastAgentResponseContentType.cs b/dotnet/samples/05-end-to-end/M365Agent/Agents/WeatherForecastAgentResponseContentType.cs similarity index 100% rename from dotnet/samples/M365Agent/Agents/WeatherForecastAgentResponseContentType.cs rename to dotnet/samples/05-end-to-end/M365Agent/Agents/WeatherForecastAgentResponseContentType.cs diff --git a/dotnet/samples/M365Agent/Auth/AspNetExtensions.cs b/dotnet/samples/05-end-to-end/M365Agent/Auth/AspNetExtensions.cs similarity index 100% rename from dotnet/samples/M365Agent/Auth/AspNetExtensions.cs rename to dotnet/samples/05-end-to-end/M365Agent/Auth/AspNetExtensions.cs diff --git a/dotnet/samples/M365Agent/Auth/TokenValidationOptions.cs b/dotnet/samples/05-end-to-end/M365Agent/Auth/TokenValidationOptions.cs similarity index 100% rename from dotnet/samples/M365Agent/Auth/TokenValidationOptions.cs rename to dotnet/samples/05-end-to-end/M365Agent/Auth/TokenValidationOptions.cs diff --git a/dotnet/samples/M365Agent/JsonUtilities.cs b/dotnet/samples/05-end-to-end/M365Agent/JsonUtilities.cs similarity index 100% rename from dotnet/samples/M365Agent/JsonUtilities.cs rename to dotnet/samples/05-end-to-end/M365Agent/JsonUtilities.cs diff --git a/dotnet/samples/M365Agent/M365Agent.csproj b/dotnet/samples/05-end-to-end/M365Agent/M365Agent.csproj similarity index 88% rename from dotnet/samples/M365Agent/M365Agent.csproj rename to dotnet/samples/05-end-to-end/M365Agent/M365Agent.csproj index f40d404204..72352b7f01 100644 --- a/dotnet/samples/M365Agent/M365Agent.csproj +++ b/dotnet/samples/05-end-to-end/M365Agent/M365Agent.csproj @@ -23,7 +23,7 @@ - + \ No newline at end of file diff --git a/dotnet/samples/M365Agent/Program.cs b/dotnet/samples/05-end-to-end/M365Agent/Program.cs similarity index 100% rename from dotnet/samples/M365Agent/Program.cs rename to dotnet/samples/05-end-to-end/M365Agent/Program.cs diff --git a/dotnet/samples/M365Agent/Properties/launchSettings.json b/dotnet/samples/05-end-to-end/M365Agent/Properties/launchSettings.json similarity index 100% rename from dotnet/samples/M365Agent/Properties/launchSettings.json rename to dotnet/samples/05-end-to-end/M365Agent/Properties/launchSettings.json diff --git a/dotnet/samples/M365Agent/README.md b/dotnet/samples/05-end-to-end/M365Agent/README.md similarity index 100% rename from dotnet/samples/M365Agent/README.md rename to dotnet/samples/05-end-to-end/M365Agent/README.md diff --git a/dotnet/samples/M365Agent/appManifest/color.png b/dotnet/samples/05-end-to-end/M365Agent/appManifest/color.png similarity index 100% rename from dotnet/samples/M365Agent/appManifest/color.png rename to dotnet/samples/05-end-to-end/M365Agent/appManifest/color.png diff --git a/dotnet/samples/M365Agent/appManifest/manifest.json b/dotnet/samples/05-end-to-end/M365Agent/appManifest/manifest.json similarity index 100% rename from dotnet/samples/M365Agent/appManifest/manifest.json rename to dotnet/samples/05-end-to-end/M365Agent/appManifest/manifest.json diff --git a/dotnet/samples/M365Agent/appManifest/outline.png b/dotnet/samples/05-end-to-end/M365Agent/appManifest/outline.png similarity index 100% rename from dotnet/samples/M365Agent/appManifest/outline.png rename to dotnet/samples/05-end-to-end/M365Agent/appManifest/outline.png diff --git a/dotnet/samples/M365Agent/appsettings.json.template b/dotnet/samples/05-end-to-end/M365Agent/appsettings.json.template similarity index 100% rename from dotnet/samples/M365Agent/appsettings.json.template rename to dotnet/samples/05-end-to-end/M365Agent/appsettings.json.template diff --git a/dotnet/samples/AGENTS.md b/dotnet/samples/AGENTS.md new file mode 100644 index 0000000000..1578b39a26 --- /dev/null +++ b/dotnet/samples/AGENTS.md @@ -0,0 +1,130 @@ +# Samples Structure & Design Choices — .NET + +> This file documents the structure and conventions of the .NET samples so that +> agents (AI or human) can maintain them without rediscovering decisions. + +## Directory layout + +``` +dotnet/samples/ +├── 01-get-started/ # Progressive tutorial (steps 01–06) +│ ├── 01_hello_agent/ # Create and run your first agent +│ ├── 02_add_tools/ # Add function tools +│ ├── 03_multi_turn/ # Multi-turn conversations with AgentSession +│ ├── 04_memory/ # Agent memory with AIContextProvider +│ ├── 05_first_workflow/ # Build a workflow with executors and edges +│ └── 06_host_your_agent/ # Host your agent via Azure Functions +├── 02-agents/ # Deep-dive concept samples +│ ├── Agents/ # Core agent patterns (tools, structured output, +│ │ # conversations, middleware, plugins, MCP, etc.) +│ ├── AgentProviders/ # One project per provider (Azure OpenAI, OpenAI, +│ │ # Anthropic, Gemini, Ollama, ONNX, Foundry, etc.) +│ ├── AgentOpenTelemetry/ # OpenTelemetry integration +│ ├── AgentSkills/ # Agent skills patterns +│ ├── AgentWithAnthropic/ # Anthropic-specific samples +│ ├── AgentWithMemory/ # Memory providers (chat history, Mem0, Foundry) +│ ├── AgentWithOpenAI/ # OpenAI-specific samples +│ ├── AgentWithRAG/ # RAG patterns (text, vector store, Foundry) +│ ├── AGUI/ # AG-UI protocol samples +│ ├── DeclarativeAgents/ # Declarative agent definitions +│ ├── DevUI/ # DevUI samples +│ ├── FoundryAgents/ # Azure AI Foundry agent samples +│ └── ModelContextProtocol/ # MCP server/client patterns +├── 03-workflows/ # Workflow patterns +│ ├── _StartHere/ # Introductory workflow samples +│ ├── Agents/ # Agents in workflows +│ ├── Checkpoint/ # Checkpointing & resume +│ ├── Concurrent/ # Concurrent execution +│ ├── ConditionalEdges/ # Conditional routing +│ ├── Declarative/ # YAML-based workflows +│ ├── HumanInTheLoop/ # HITL patterns +│ ├── Loop/ # Loop patterns +│ ├── Observability/ # Workflow telemetry +│ ├── SharedStates/ # State isolation +│ └── Visualization/ # Workflow visualization +├── 04-hosting/ # Deployment & hosting +│ ├── A2A/ # Agent-to-Agent protocol +│ └── DurableAgents/ # Durable task framework +│ ├── AzureFunctions/ # Azure Functions hosting +│ └── ConsoleApps/ # Console app hosting +├── 05-end-to-end/ # Complete applications +│ ├── A2AClientServer/ # A2A client/server demo +│ ├── AgentWebChat/ # Aspire-based web chat +│ ├── AgentWithPurview/ # Purview integration +│ ├── AGUIClientServer/ # AG-UI client/server demo +│ ├── AGUIWebChat/ # AG-UI web chat +│ ├── HostedAgents/ # Hosted agent scenarios +│ └── M365Agent/ # Microsoft 365 agent +``` + +## Design principles + +1. **Progressive complexity**: Sections 01→05 build from "hello world" to + production. Within 01-get-started, projects are numbered 01–06 and each step + adds exactly one concept. + +2. **One concept per project** in 01-get-started. Each step is a standalone + C# project with a single `Program.cs` file. + +3. **Workflows preserved**: 03-workflows/ keeps the upstream folder names + intact. Do not rename or restructure workflow samples. + +4. **Per-project structure**: Each sample is a separate .csproj. Shared build + configuration is inherited from `Directory.Build.props`. + +## Default provider + +All canonical samples (01-get-started) use **Azure OpenAI** via `AzureOpenAIClient` +with `DefaultAzureCredential`: + +```csharp +using Azure.AI.OpenAI; +using Azure.Identity; +using Microsoft.Agents.AI; +using OpenAI.Chat; + +var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") + ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); +var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. +// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid +// latency issues, unintended credential probing, and potential security risks from fallback mechanisms. +AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential()) + .GetChatClient(deploymentName) + .AsAIAgent(instructions: "...", name: "..."); +``` + +Environment variables: +- `AZURE_OPENAI_ENDPOINT` — Your Azure OpenAI endpoint +- `AZURE_OPENAI_DEPLOYMENT_NAME` — Model deployment name (defaults to `gpt-4o-mini`) + +For authentication, run `az login` before running samples. + +## Snippet tags for docs integration + +Samples embed named snippet regions for future `:::code` integration: + +```csharp +// +code here +// +``` + +## Building and running + +All samples use project references to the framework source. To build and run: + +```bash +cd dotnet/samples/01-get-started/01_hello_agent +dotnet run +``` + +## Current API notes + +- `AIAgent` is the primary agent abstraction (created via `ChatClient.AsAIAgent(...)`) +- `AgentSession` manages multi-turn conversation state +- `AIContextProvider` injects memory and context +- Prefer `client.GetChatClient(deployment).AsAIAgent(...)` extension method pattern +- Azure Functions hosting uses `ConfigureDurableAgents(options => options.AddAIAgent(agent))` +- Workflows use `WorkflowBuilder` with `Executor` and edge connections diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/AGUIDojoServer.csproj b/dotnet/samples/AGUIClientServer/AGUIDojoServer/AGUIDojoServer.csproj deleted file mode 100644 index cea8efff76..0000000000 --- a/dotnet/samples/AGUIClientServer/AGUIDojoServer/AGUIDojoServer.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - Exe - net10.0 - enable - enable - b9c3f1e1-2fb4-5g29-0e52-53e2b7g9gf21 - - - - - - - - - - - - - - diff --git a/dotnet/samples/AGUIClientServer/AGUIServer/AGUIServer.csproj b/dotnet/samples/AGUIClientServer/AGUIServer/AGUIServer.csproj deleted file mode 100644 index ccfe22923a..0000000000 --- a/dotnet/samples/AGUIClientServer/AGUIServer/AGUIServer.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - Exe - net10.0 - enable - enable - a8b2e9f0-1ea3-4f18-9d41-42d1a6f8fe10 - - - - - - - - - - - - - - diff --git a/dotnet/samples/AGUIWebChat/Server/AGUIWebChatServer.csproj b/dotnet/samples/AGUIWebChat/Server/AGUIWebChatServer.csproj deleted file mode 100644 index c45adfd4a8..0000000000 --- a/dotnet/samples/AGUIWebChat/Server/AGUIWebChatServer.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - Exe - net10.0 - enable - enable - - - - - - - - - - - - - - diff --git a/dotnet/samples/AgentWebChat/AgentWebChat.Web/AgentWebChat.Web.csproj b/dotnet/samples/AgentWebChat/AgentWebChat.Web/AgentWebChat.Web.csproj deleted file mode 100644 index fd26f56191..0000000000 --- a/dotnet/samples/AgentWebChat/AgentWebChat.Web/AgentWebChat.Web.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - net10.0 - enable - enable - $(NoWarn);CA1812 - - - - - - - - - - - diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/05_AgentOrchestration_HITL/local.settings.json b/dotnet/samples/Durable/Agents/AzureFunctions/05_AgentOrchestration_HITL/local.settings.json deleted file mode 100644 index 54dfbb5664..0000000000 --- a/dotnet/samples/Durable/Agents/AzureFunctions/05_AgentOrchestration_HITL/local.settings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "IsEncrypted": false, - "Values": { - "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated", - "AzureWebJobsStorage": "UseDevelopmentStorage=true", - "DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None", - "AZURE_OPENAI_ENDPOINT": "", - "AZURE_OPENAI_DEPLOYMENT": "" - } -} diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/local.settings.json b/dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/local.settings.json deleted file mode 100644 index 54dfbb5664..0000000000 --- a/dotnet/samples/Durable/Agents/AzureFunctions/06_LongRunningTools/local.settings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "IsEncrypted": false, - "Values": { - "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated", - "AzureWebJobsStorage": "UseDevelopmentStorage=true", - "DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None", - "AZURE_OPENAI_ENDPOINT": "", - "AZURE_OPENAI_DEPLOYMENT": "" - } -} diff --git a/dotnet/samples/Durable/Agents/AzureFunctions/07_AgentAsMcpTool/local.settings.json b/dotnet/samples/Durable/Agents/AzureFunctions/07_AgentAsMcpTool/local.settings.json deleted file mode 100644 index 54dfbb5664..0000000000 --- a/dotnet/samples/Durable/Agents/AzureFunctions/07_AgentAsMcpTool/local.settings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "IsEncrypted": false, - "Values": { - "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated", - "AzureWebJobsStorage": "UseDevelopmentStorage=true", - "DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None", - "AZURE_OPENAI_ENDPOINT": "", - "AZURE_OPENAI_DEPLOYMENT": "" - } -} diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/FoundryAgent/FoundryAgent.csproj b/dotnet/samples/GettingStarted/Workflows/Agents/FoundryAgent/FoundryAgent.csproj deleted file mode 100644 index f75c7fd28b..0000000000 --- a/dotnet/samples/GettingStarted/Workflows/Agents/FoundryAgent/FoundryAgent.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - Exe - net10.0 - - enable - enable - - - - - - - - - - - - - - diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/GroupChatToolApproval/GroupChatToolApproval.csproj b/dotnet/samples/GettingStarted/Workflows/Agents/GroupChatToolApproval/GroupChatToolApproval.csproj deleted file mode 100644 index d0c0656ade..0000000000 --- a/dotnet/samples/GettingStarted/Workflows/Agents/GroupChatToolApproval/GroupChatToolApproval.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - Exe - net10.0 - - enable - enable - - - - - - - - - - - - - - diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/WorkflowAsAnAgent.csproj b/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/WorkflowAsAnAgent.csproj deleted file mode 100644 index d0c0656ade..0000000000 --- a/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/WorkflowAsAnAgent.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - Exe - net10.0 - - enable - enable - - - - - - - - - - - - - - diff --git a/dotnet/samples/GettingStarted/Workflows/Concurrent/Concurrent/Concurrent.csproj b/dotnet/samples/GettingStarted/Workflows/Concurrent/Concurrent/Concurrent.csproj deleted file mode 100644 index e756a0bf1d..0000000000 --- a/dotnet/samples/GettingStarted/Workflows/Concurrent/Concurrent/Concurrent.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - Exe - net10.0 - - enable - enable - - - - - - - - - - - - - - diff --git a/dotnet/samples/GettingStarted/Workflows/Concurrent/MapReduce/MapReduce.csproj b/dotnet/samples/GettingStarted/Workflows/Concurrent/MapReduce/MapReduce.csproj deleted file mode 100644 index fd311b7be3..0000000000 --- a/dotnet/samples/GettingStarted/Workflows/Concurrent/MapReduce/MapReduce.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - Exe - net10.0 - - enable - - - - - - - - - - - - - \ No newline at end of file diff --git a/dotnet/samples/GettingStarted/Workflows/Directory.Build.props b/dotnet/samples/GettingStarted/Workflows/Directory.Build.props deleted file mode 100644 index 8ad5839332..0000000000 --- a/dotnet/samples/GettingStarted/Workflows/Directory.Build.props +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/01_ExecutorsAndEdges.csproj b/dotnet/samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/01_ExecutorsAndEdges.csproj deleted file mode 100644 index 2f41070759..0000000000 --- a/dotnet/samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/01_ExecutorsAndEdges.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - Exe - net10.0 - - enable - enable - - - - - - - diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/02_Streaming/02_Streaming.csproj b/dotnet/samples/GettingStarted/Workflows/_Foundational/02_Streaming/02_Streaming.csproj deleted file mode 100644 index 2f41070759..0000000000 --- a/dotnet/samples/GettingStarted/Workflows/_Foundational/02_Streaming/02_Streaming.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - Exe - net10.0 - - enable - enable - - - - - - - diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/03_AgentsInWorkflows/03_AgentsInWorkflows.csproj b/dotnet/samples/GettingStarted/Workflows/_Foundational/03_AgentsInWorkflows/03_AgentsInWorkflows.csproj deleted file mode 100644 index d0c0656ade..0000000000 --- a/dotnet/samples/GettingStarted/Workflows/_Foundational/03_AgentsInWorkflows/03_AgentsInWorkflows.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - Exe - net10.0 - - enable - enable - - - - - - - - - - - - - - diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/04_AgentWorkflowPatterns/04_AgentWorkflowPatterns.csproj b/dotnet/samples/GettingStarted/Workflows/_Foundational/04_AgentWorkflowPatterns/04_AgentWorkflowPatterns.csproj deleted file mode 100644 index d0c0656ade..0000000000 --- a/dotnet/samples/GettingStarted/Workflows/_Foundational/04_AgentWorkflowPatterns/04_AgentWorkflowPatterns.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - Exe - net10.0 - - enable - enable - - - - - - - - - - - - - - diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/06_SubWorkflows/06_SubWorkflows.csproj b/dotnet/samples/GettingStarted/Workflows/_Foundational/06_SubWorkflows/06_SubWorkflows.csproj deleted file mode 100644 index 6c33744eee..0000000000 --- a/dotnet/samples/GettingStarted/Workflows/_Foundational/06_SubWorkflows/06_SubWorkflows.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - Exe - net10.0 - - enable - enable - - - - - - - - \ No newline at end of file diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/07_MixedWorkflowAgentsAndExecutors/07_MixedWorkflowAgentsAndExecutors.csproj b/dotnet/samples/GettingStarted/Workflows/_Foundational/07_MixedWorkflowAgentsAndExecutors/07_MixedWorkflowAgentsAndExecutors.csproj deleted file mode 100644 index d0c0656ade..0000000000 --- a/dotnet/samples/GettingStarted/Workflows/_Foundational/07_MixedWorkflowAgentsAndExecutors/07_MixedWorkflowAgentsAndExecutors.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - Exe - net10.0 - - enable - enable - - - - - - - - - - - - - - diff --git a/dotnet/samples/README.md b/dotnet/samples/README.md index 9dfe07e2b3..e5d3b90ae2 100644 --- a/dotnet/samples/README.md +++ b/dotnet/samples/README.md @@ -9,18 +9,45 @@ All these are supported using the single `ChatClientAgent` class. The Agent Framework also supports creating proxy agents, that allow accessing remote agents as if they were local agents. These are supported using various `AIAgent` subclasses. -## Sample Categories +## Sample Structure -The samples are subdivided into the following categories: +| Folder | Description | +|--------|-------------| +| [`01-get-started/`](./01-get-started/) | Progressive tutorial: hello agent → hosting | +| [`02-agents/`](./02-agents/) | Deep-dive by concept: tools, middleware, providers, orchestrations | +| [`03-workflows/`](./03-workflows/) | Workflow patterns: sequential, concurrent, state, declarative | +| [`04-hosting/`](./04-hosting/) | Deployment: Azure Functions, Durable Tasks, A2A | +| [`05-end-to-end/`](./05-end-to-end/) | Full applications, evaluation, demos | -- [Getting Started - Agents](./GettingStarted/Agents/README.md): Basic steps to get started with the agent framework. +## Getting Started + +Start with `01-get-started/` and work through the numbered files: + +1. **[01_hello_agent](./01-get-started/01_hello_agent/Program.cs)** — Create and run your first agent +2. **[02_add_tools](./01-get-started/02_add_tools/Program.cs)** — Add function tools +3. **[03_multi_turn](./01-get-started/03_multi_turn/Program.cs)** — Multi-turn conversations with `AgentSession` +4. **[04_memory](./01-get-started/04_memory/Program.cs)** — Agent memory with `AIContextProvider` +5. **[05_first_workflow](./01-get-started/05_first_workflow/Program.cs)** — Build a workflow with executors and edges +6. **[06_host_your_agent](./01-get-started/06_host_your_agent/Program.cs)** — Host your agent via Azure Functions + +## Additional Samples + +Some additional samples of note include: + +- [Agents](./02-agents/Agents/README.md): Basic steps to get started with the agent framework. These samples demonstrate the fundamental concepts and functionalities of the agent framework when using the `AIAgent` and can be used with any underlying service that provides an `AIAgent` implementation. -- [Getting Started - Agent Providers](./GettingStarted/AgentProviders/README.md): Shows how to create an AIAgent instance for a selection of providers. -- [Getting Started - Agent Telemetry](./GettingStarted/AgentOpenTelemetry/README.md): Demo which showcases the integration of OpenTelemetry with the Microsoft Agent Framework using Azure OpenAI and .NET Aspire Dashboard for telemetry visualization. -- [Semantic Kernel to Agent Framework Migration](https://github.com/microsoft/semantic-kernel/tree/main/dotnet/samples/AgentFrameworkMigration): For instructions and samples describing how to migrate from Semantic Kernel to Microsoft Agent Framework -- [Durable Agents - Azure Functions](./Durable/Agents/AzureFunctions/README.md): Samples for using the Microsoft Agent Framework with Azure Functions via the durable task extension. -- [Durable Agents - Console Apps](./Durable/Agents/ConsoleApps/README.md): Samples demonstrating durable agents in console applications. +- [Agent Providers](./02-agents/AgentProviders/README.md): Shows how to create an AIAgent instance for a selection of providers. +- [Agent Telemetry](./02-agents/AgentOpenTelemetry/README.md): Demo which showcases the integration of OpenTelemetry with the Microsoft Agent Framework using Azure OpenAI and .NET Aspire Dashboard for telemetry visualization. +- [Durable Agents - Azure Functions](./04-hosting/DurableAgents/AzureFunctions/README.md): Samples for using the Microsoft Agent Framework with Azure Functions via the durable task extension. +- [Durable Agents - Console Apps](./04-hosting/DurableAgents/ConsoleApps/README.md): Samples demonstrating durable agents in console applications. + +## Migration from Semantic Kernel + +If you are migrating from Semantic Kernel to the Microsoft Agent Framework, the following resources provide guidance and side-by-side examples to help you transition your existing agents, tools, and orchestration patterns. +The migration samples map Semantic Kernel primitives (such as `ChatCompletionAgent` and Team orchestrations) to their Agent Framework equivalents (such as `ChatClientAgent` and workflow builders). + +For an in-depth migration guide, see the [official migration documentation](https://learn.microsoft.com/en-us/agent-framework/migration-guide/from-semantic-kernel). ## Prerequisites diff --git a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs index 9b6cdc2ffd..27882375d7 100644 --- a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs +++ b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs @@ -98,7 +98,7 @@ public async Task ProcessChatContentAsync(IEnumerable try { - (bool shouldBlockResponse, _) = await this._scopedProcessor.ProcessMessagesAsync(response.Messages, options?.ConversationId, Activity.UploadText, this._purviewSettings, resolvedUserId, cancellationToken).ConfigureAwait(false); + (bool shouldBlockResponse, _) = await this._scopedProcessor.ProcessMessagesAsync(response.Messages, options?.ConversationId, Activity.DownloadText, this._purviewSettings, resolvedUserId, cancellationToken).ConfigureAwait(false); if (shouldBlockResponse) { if (this._logger.IsEnabled(LogLevel.Information)) @@ -186,7 +186,7 @@ public async Task ProcessAgentContentAsync(IEnumerable +/// Default implementation of using the MCP C# SDK. +/// +/// +/// This provider supports per-server authentication via the httpClientProvider callback. +/// The callback allows different MCP servers to use different authentication configurations by returning +/// a pre-configured for each server. +/// +public sealed class DefaultMcpToolHandler : IMcpToolHandler, IAsyncDisposable +{ + private readonly Func>? _httpClientProvider; + private readonly Dictionary _clients = []; + private readonly Dictionary _ownedHttpClients = []; + private readonly SemaphoreSlim _clientLock = new(1, 1); + + /// + /// Initializes a new instance of the class. + /// + /// + /// An optional callback that provides an for each MCP server. + /// The callback receives (serverUrl, cancellationToken) and should return an HttpClient + /// configured with any required authentication. Return to use a default HttpClient with no auth. + /// + public DefaultMcpToolHandler(Func>? httpClientProvider = null) + { + this._httpClientProvider = httpClientProvider; + } + + /// + public async Task InvokeToolAsync( + string serverUrl, + string? serverLabel, + string toolName, + IDictionary? arguments, + IDictionary? headers, + string? connectionName, + CancellationToken cancellationToken = default) + { + // TODO: Handle connectionName and server label appropriately when Hosted scenario supports them. For now, ignore + McpServerToolResultContent resultContent = new(Guid.NewGuid().ToString()); + McpClient client = await this.GetOrCreateClientAsync(serverUrl, serverLabel, headers, cancellationToken).ConfigureAwait(false); + + // Convert IDictionary to IReadOnlyDictionary for CallToolAsync + IReadOnlyDictionary? readOnlyArguments = arguments is null + ? null + : arguments as IReadOnlyDictionary ?? new Dictionary(arguments); + + CallToolResult result = await client.CallToolAsync( + toolName, + readOnlyArguments, + cancellationToken: cancellationToken).ConfigureAwait(false); + + // Map MCP content blocks to MEAI AIContent types + PopulateResultContent(resultContent, result); + + return resultContent; + } + + /// + public async ValueTask DisposeAsync() + { + await this._clientLock.WaitAsync().ConfigureAwait(false); + try + { + foreach (McpClient client in this._clients.Values) + { + await client.DisposeAsync().ConfigureAwait(false); + } + + this._clients.Clear(); + + // Dispose only HttpClients that the handler created (not user-provided ones) + foreach (HttpClient httpClient in this._ownedHttpClients.Values) + { + httpClient.Dispose(); + } + + this._ownedHttpClients.Clear(); + } + finally + { + this._clientLock.Release(); + } + + this._clientLock.Dispose(); + } + + private async Task GetOrCreateClientAsync( + string serverUrl, + string? serverLabel, + IDictionary? headers, + CancellationToken cancellationToken) + { + string normalizedUrl = serverUrl.Trim().ToUpperInvariant(); + string clientCacheKey = $"{normalizedUrl}|{ComputeHeadersHash(headers)}"; + + await this._clientLock.WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + if (this._clients.TryGetValue(clientCacheKey, out McpClient? existingClient)) + { + return existingClient; + } + + McpClient newClient = await this.CreateClientAsync(serverUrl, serverLabel, headers, normalizedUrl, cancellationToken).ConfigureAwait(false); + this._clients[clientCacheKey] = newClient; + return newClient; + } + finally + { + this._clientLock.Release(); + } + } + + private async Task CreateClientAsync( + string serverUrl, + string? serverLabel, + IDictionary? headers, + string httpClientCacheKey, + CancellationToken cancellationToken) + { + // Get or create HttpClient (Can be shared across McpClients for the same server) + HttpClient? httpClient = null; + + if (this._httpClientProvider is not null) + { + httpClient = await this._httpClientProvider(serverUrl, cancellationToken).ConfigureAwait(false); + } + + if (httpClient is null && !this._ownedHttpClients.TryGetValue(httpClientCacheKey, out httpClient)) + { + httpClient = new HttpClient(); + this._ownedHttpClients[httpClientCacheKey] = httpClient; + } + + HttpClientTransportOptions transportOptions = new() + { + Endpoint = new Uri(serverUrl), + Name = serverLabel ?? "McpClient", + AdditionalHeaders = headers, + TransportMode = HttpTransportMode.AutoDetect + }; + + HttpClientTransport transport = new(transportOptions, httpClient); + + return await McpClient.CreateAsync(transport, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + private static string ComputeHeadersHash(IDictionary? headers) + { + if (headers is null || headers.Count == 0) + { + return string.Empty; + } + + // Build a deterministic, sorted representation of the headers + // Within a single process lifetime, the hashcodes are consistent. + // This will ensure that the same set of headers always produces the same hash, regardless of order. + SortedDictionary sorted = new(headers.ToDictionary(h => h.Key.ToUpperInvariant(), h => h.Value.ToUpperInvariant())); + int hashCode = 17; + foreach (KeyValuePair kvp in sorted) + { + hashCode = (hashCode * 31) + StringComparer.OrdinalIgnoreCase.GetHashCode(kvp.Key); + hashCode = (hashCode * 31) + StringComparer.OrdinalIgnoreCase.GetHashCode(kvp.Value); + } + + return hashCode.ToString(CultureInfo.InvariantCulture); + } + + private static void PopulateResultContent(McpServerToolResultContent resultContent, CallToolResult result) + { + // Ensure Output list is initialized + resultContent.Output ??= []; + + if (result.IsError == true) + { + // Collect error text from content blocks + string? errorText = null; + if (result.Content is not null) + { + foreach (ContentBlock block in result.Content) + { + if (block is TextContentBlock textBlock) + { + errorText = errorText is null ? textBlock.Text : $"{errorText}\n{textBlock.Text}"; + } + } + } + + resultContent.Output.Add(new TextContent($"Error: {errorText ?? "Unknown error from MCP Server call"}")); + return; + } + + if (result.Content is null || result.Content.Count == 0) + { + return; + } + + // Map each MCP content block to an MEAI AIContent type + foreach (ContentBlock block in result.Content) + { + AIContent content = ConvertContentBlock(block); + if (content is not null) + { + resultContent.Output.Add(content); + } + } + } + + private static AIContent ConvertContentBlock(ContentBlock block) + { + return block switch + { + TextContentBlock text => new TextContent(text.Text), + ImageContentBlock image => CreateDataContentFromBase64(image.Data, image.MimeType ?? "image/*"), + AudioContentBlock audio => CreateDataContentFromBase64(audio.Data, audio.MimeType ?? "audio/*"), + _ => new TextContent(block.ToString() ?? string.Empty), + }; + } + + private static DataContent CreateDataContentFromBase64(string? base64Data, string mediaType) + { + if (string.IsNullOrEmpty(base64Data)) + { + return new DataContent($"data:{mediaType};base64,", mediaType); + } + + // If it's already a data URI, use it directly + if (base64Data.StartsWith("data:", StringComparison.OrdinalIgnoreCase)) + { + return new DataContent(base64Data, mediaType); + } + + // Otherwise, construct a data URI from the base64 data + return new DataContent($"data:{mediaType};base64,{base64Data}", mediaType); + } +} diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/Microsoft.Agents.AI.Workflows.Declarative.Mcp.csproj b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/Microsoft.Agents.AI.Workflows.Declarative.Mcp.csproj new file mode 100644 index 0000000000..f9bf706669 --- /dev/null +++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/Microsoft.Agents.AI.Workflows.Declarative.Mcp.csproj @@ -0,0 +1,33 @@ + + + + true + $(NoWarn);MEAI001;OPENAI001 + + + + true + true + true + + + + + + + Microsoft Agent Framework Declarative Workflows MCP + Provides Microsoft Agent Framework support for MCP (Model Context Protocol) server integration in declarative workflows. + + + + + + + + + + + + + + diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/DeclarativeWorkflowOptions.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/DeclarativeWorkflowOptions.cs index c4808f9311..9e421832d4 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/DeclarativeWorkflowOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/DeclarativeWorkflowOptions.cs @@ -20,6 +20,12 @@ public sealed class DeclarativeWorkflowOptions(ResponseAgentProvider agentProvid /// public ResponseAgentProvider AgentProvider { get; } = Throw.IfNull(agentProvider); + /// + /// Gets or sets the MCP tool handler for invoking MCP tools within workflows. + /// If not set, MCP tool invocations will fail with an appropriate error message. + /// + public IMcpToolHandler? McpToolHandler { get; init; } + /// /// Defines the configuration settings for the workflow. /// diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Extensions/JsonDocumentExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Extensions/JsonDocumentExtensions.cs index e2b038bbd7..a74b9c6eb8 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Extensions/JsonDocumentExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Extensions/JsonDocumentExtensions.cs @@ -42,6 +42,40 @@ internal static class JsonDocumentExtensions }; } + /// + /// Creates a VariableType.List with schema inferred from the first object element in the array. + /// + public static VariableType GetListTypeFromJson(this JsonElement arrayElement) + { + // Find the first object element to infer schema + foreach (JsonElement element in arrayElement.EnumerateArray()) + { + if (element.ValueKind == JsonValueKind.Object) + { + // Build schema from the object's properties + List<(string Key, VariableType Type)> fields = []; + foreach (JsonProperty property in element.EnumerateObject()) + { + VariableType fieldType = property.Value.ValueKind switch + { + JsonValueKind.String => typeof(string), + JsonValueKind.Number => typeof(decimal), + JsonValueKind.True or JsonValueKind.False => typeof(bool), + JsonValueKind.Object => VariableType.RecordType, + JsonValueKind.Array => VariableType.ListType, + _ => typeof(string), + }; + fields.Add((property.Name, fieldType)); + } + + return VariableType.List(fields); + } + } + + // Fallback for arrays of primitives or empty arrays + return VariableType.ListType; + } + private static Dictionary ParseRecord(this JsonElement currentElement, VariableType targetType) { IEnumerable> keyValuePairs = @@ -118,6 +152,7 @@ VariableType DetermineElementType() JsonValueKind.True => typeof(bool), JsonValueKind.False => typeof(bool), JsonValueKind.Number => typeof(decimal), + JsonValueKind.Array => (VariableType)VariableType.ListType, // Add support for nested arrays _ => null, }; @@ -285,9 +320,16 @@ private static bool TryParseObject(JsonElement propertyElement, VariableType? ta private static bool TryParseList(JsonElement propertyElement, VariableType? targetType, out object? value) { + // Handle empty arrays without needing to determine element type + if (propertyElement.GetArrayLength() == 0) + { + value = new List(); + return true; + } + try { - value = ParseTable(propertyElement, targetType ?? VariableType.ListType); + value = ParseTable(propertyElement, targetType ?? GetListTypeFromJson(propertyElement)); return true; } catch diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/IMcpToolHandler.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/IMcpToolHandler.cs new file mode 100644 index 0000000000..56b1c3deb4 --- /dev/null +++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/IMcpToolHandler.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.AI; + +namespace Microsoft.Agents.AI.Workflows.Declarative; + +/// +/// Defines the contract for invoking MCP tools within declarative workflows. +/// +/// +/// This interface allows the MCP tool invocation to be abstracted, enabling +/// different implementations for local development, hosted workflows, and testing scenarios. +/// +public interface IMcpToolHandler +{ + /// + /// Invokes an MCP tool on the specified server. + /// + /// The URL of the MCP server. + /// An optional label identifying the server connection. + /// The name of the tool to invoke. + /// Optional arguments to pass to the tool. + /// Optional headers to include in the request. + /// An optional connection name for managed connections. + /// A token to observe cancellation. + /// + /// A task representing the asynchronous operation. The result contains a + /// with the tool invocation output. + /// + Task InvokeToolAsync( + string serverUrl, + string? serverLabel, + string toolName, + IDictionary? arguments, + IDictionary? headers, + string? connectionName, + CancellationToken cancellationToken = default); +} diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Interpreter/WorkflowActionVisitor.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Interpreter/WorkflowActionVisitor.cs index 7b84e24839..fd818672dd 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Interpreter/WorkflowActionVisitor.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Interpreter/WorkflowActionVisitor.cs @@ -493,6 +493,42 @@ protected override void Visit(SendActivity item) this.ContinueWith(new SendActivityExecutor(item, this._workflowState)); } + protected override void Visit(InvokeMcpTool item) + { + this.Trace(item); + + // Verify MCP handler is configured + if (this._workflowOptions.McpToolHandler is null) + { + throw new DeclarativeModelException("MCP tool handler not configured. Set McpToolHandler in DeclarativeWorkflowOptions to use InvokeMcpTool actions."); + } + + // Entry point to invoke MCP tool - may yield for approval + InvokeMcpToolExecutor action = new(item, this._workflowOptions.McpToolHandler, this._workflowOptions.AgentProvider, this._workflowState); + this.ContinueWith(action); + + // Transition to post action if no external input is required (no approval needed) + string postId = Steps.Post(action.Id); + this._workflowModel.AddLink(action.Id, postId, InvokeMcpToolExecutor.RequiresNothing); + + // If approval is required, define request-port for approval flow + string externalInputPortId = InvokeMcpToolExecutor.Steps.ExternalInput(action.Id); + RequestPortAction externalInputPort = new(RequestPort.Create(externalInputPortId)); + this._workflowModel.AddNode(externalInputPort, action.ParentId); + this._workflowModel.AddLink(action.Id, externalInputPortId, InvokeMcpToolExecutor.RequiresInput); + + // Capture response when external input is received + string resumeId = InvokeMcpToolExecutor.Steps.Resume(action.Id); + this._workflowModel.AddNode(new DelegateActionExecutor(resumeId, this._workflowState, action.CaptureResponseAsync), action.ParentId); + this._workflowModel.AddLink(externalInputPortId, resumeId); + + // After resume, transition to post action + this._workflowModel.AddLink(resumeId, postId); + + // Define post action (completion) + this._workflowModel.AddNode(new DelegateActionExecutor(postId, this._workflowState, action.CompleteAsync), action.ParentId); + } + #region Not supported protected override void Visit(AnswerQuestionWithAI item) => this.NotSupported(item); diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Interpreter/WorkflowTemplateVisitor.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Interpreter/WorkflowTemplateVisitor.cs index 568a38950c..f754c45c62 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Interpreter/WorkflowTemplateVisitor.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Interpreter/WorkflowTemplateVisitor.cs @@ -365,6 +365,8 @@ protected override void Visit(SendActivity item) #region Not supported + protected override void Visit(InvokeMcpTool item) => this.NotSupported(item); + protected override void Visit(InvokeFunctionTool item) => this.NotSupported(item); protected override void Visit(AnswerQuestionWithAI item) => this.NotSupported(item); diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeFunctionToolExecutor.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeFunctionToolExecutor.cs index a5215c283b..0e95bebe63 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeFunctionToolExecutor.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeFunctionToolExecutor.cs @@ -204,7 +204,7 @@ private async ValueTask AssignResultAsync(IWorkflowContext context, FunctionResu object? parsedValue = jsonDocument.RootElement.ValueKind switch { JsonValueKind.Object => jsonDocument.ParseRecord(VariableType.RecordType), - JsonValueKind.Array => jsonDocument.ParseList(CreateListTypeFromJson(jsonDocument.RootElement)), + JsonValueKind.Array => jsonDocument.ParseList(jsonDocument.RootElement.GetListTypeFromJson()), JsonValueKind.String => jsonDocument.RootElement.GetString(), JsonValueKind.Number => jsonDocument.RootElement.TryGetInt64(out long l) ? l : jsonDocument.RootElement.GetDouble(), JsonValueKind.True => true, @@ -224,40 +224,6 @@ private async ValueTask AssignResultAsync(IWorkflowContext context, FunctionResu await this.AssignAsync(this.Model.Output.Result?.Path, resultValue.ToFormula(), context).ConfigureAwait(false); } - /// - /// Creates a VariableType.List with schema inferred from the first object element in the array. - /// - private static VariableType CreateListTypeFromJson(JsonElement arrayElement) - { - // Find the first object element to infer schema - foreach (JsonElement element in arrayElement.EnumerateArray()) - { - if (element.ValueKind == JsonValueKind.Object) - { - // Build schema from the object's properties - List<(string Key, VariableType Type)> fields = []; - foreach (JsonProperty property in element.EnumerateObject()) - { - VariableType fieldType = property.Value.ValueKind switch - { - JsonValueKind.String => typeof(string), - JsonValueKind.Number => typeof(decimal), - JsonValueKind.True or JsonValueKind.False => typeof(bool), - JsonValueKind.Object => VariableType.RecordType, - JsonValueKind.Array => VariableType.ListType, - _ => typeof(string), - }; - fields.Add((property.Name, fieldType)); - } - - return VariableType.List(fields); - } - } - - // Fallback for arrays of primitives or empty arrays - return VariableType.ListType; - } - private string GetFunctionName() => this.Evaluator.GetValue( Throw.IfNull( diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeMcpToolExecutor.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeMcpToolExecutor.cs new file mode 100644 index 0000000000..45929f20f7 --- /dev/null +++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeMcpToolExecutor.cs @@ -0,0 +1,367 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Agents.AI.Workflows.Declarative.Events; +using Microsoft.Agents.AI.Workflows.Declarative.Extensions; +using Microsoft.Agents.AI.Workflows.Declarative.Interpreter; +using Microsoft.Agents.AI.Workflows.Declarative.Kit; +using Microsoft.Agents.AI.Workflows.Declarative.PowerFx; +using Microsoft.Agents.ObjectModel; +using Microsoft.Extensions.AI; +using Microsoft.Shared.Diagnostics; + +namespace Microsoft.Agents.AI.Workflows.Declarative.ObjectModel; + +/// +/// Executor for the action. +/// This executor invokes MCP tools on remote servers and handles approval flows. +/// +internal sealed class InvokeMcpToolExecutor( + InvokeMcpTool model, + IMcpToolHandler mcpToolHandler, + ResponseAgentProvider agentProvider, + WorkflowFormulaState state) : + DeclarativeActionExecutor(model, state) +{ + /// + /// Step identifiers for the MCP tool invocation workflow. + /// + public static class Steps + { + /// + /// Step for waiting for external input (approval or direct response). + /// + public static string ExternalInput(string id) => $"{id}_{nameof(ExternalInput)}"; + + /// + /// Step for resuming after receiving external input. + /// + public static string Resume(string id) => $"{id}_{nameof(Resume)}"; + } + + /// + /// Determines if the message indicates external input is required. + /// + public static bool RequiresInput(object? message) => message is ExternalInputRequest; + + /// + /// Determines if the message indicates no external input is required. + /// + public static bool RequiresNothing(object? message) => message is ActionExecutorResult; + + /// + protected override bool EmitResultEvent => false; + + /// + protected override bool IsDiscreteAction => false; + + /// + [SendsMessage(typeof(ExternalInputRequest))] + protected override async ValueTask ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken = default) + { + string serverUrl = this.GetServerUrl(); + string? serverLabel = this.GetServerLabel(); + string toolName = this.GetToolName(); + bool requireApproval = this.GetRequireApproval(); + Dictionary? arguments = this.GetArguments(); + Dictionary? headers = this.GetHeaders(); + string? connectionName = this.GetConnectionName(); + + if (requireApproval) + { + // Create tool call content for approval request + McpServerToolCallContent toolCall = new(this.Id, toolName, serverLabel ?? serverUrl) + { + Arguments = arguments + }; + + if (headers != null) + { + toolCall.AdditionalProperties ??= []; + toolCall.AdditionalProperties.Add(headers); + } + + McpServerToolApprovalRequestContent approvalRequest = new(this.Id, toolCall); + + ChatMessage requestMessage = new(ChatRole.Assistant, [approvalRequest]); + AgentResponse agentResponse = new([requestMessage]); + + // Yield to the caller for approval + ExternalInputRequest inputRequest = new(agentResponse); + await context.SendMessageAsync(inputRequest, cancellationToken).ConfigureAwait(false); + + return default; + } + + // No approval required - invoke the tool directly + McpServerToolResultContent resultContent = await mcpToolHandler.InvokeToolAsync( + serverUrl, + serverLabel, + toolName, + arguments, + headers, + connectionName, + cancellationToken).ConfigureAwait(false); + + await this.ProcessResultAsync(context, resultContent, cancellationToken).ConfigureAwait(false); + + // Signal completion so the workflow routes via RequiresNothing + await context.SendResultMessageAsync(this.Id, result: null, cancellationToken).ConfigureAwait(false); + + return default; + } + + /// + /// Captures the external input response and processes the MCP tool result. + /// + /// The workflow context. + /// The external input response. + /// A cancellation token. + /// A representing the asynchronous operation. + public async ValueTask CaptureResponseAsync( + IWorkflowContext context, + ExternalInputResponse response, + CancellationToken cancellationToken) + { + // Check for approval response + McpServerToolApprovalResponseContent? approvalResponse = response.Messages + .SelectMany(m => m.Contents) + .OfType() + .FirstOrDefault(r => r.Id == this.Id); + + if (approvalResponse?.Approved != true) + { + // Tool call was rejected + await this.AssignErrorAsync(context, "MCP tool invocation was not approved by user.").ConfigureAwait(false); + return; + } + + // Approved - now invoke the tool + string serverUrl = this.GetServerUrl(); + string? serverLabel = this.GetServerLabel(); + string toolName = this.GetToolName(); + Dictionary? arguments = this.GetArguments(); + Dictionary? headers = this.GetHeaders(); + string? connectionName = this.GetConnectionName(); + + McpServerToolResultContent resultContent = await mcpToolHandler.InvokeToolAsync( + serverUrl, + serverLabel, + toolName, + arguments, + headers, + connectionName, + cancellationToken).ConfigureAwait(false); + + await this.ProcessResultAsync(context, resultContent, cancellationToken).ConfigureAwait(false); + } + + /// + /// Completes the MCP tool invocation by raising the completion event. + /// + public async ValueTask CompleteAsync(IWorkflowContext context, ActionExecutorResult message, CancellationToken cancellationToken) + { + await context.RaiseCompletionEventAsync(this.Model, cancellationToken).ConfigureAwait(false); + } + + private async ValueTask ProcessResultAsync(IWorkflowContext context, McpServerToolResultContent resultContent, CancellationToken cancellationToken) + { + bool autoSend = this.GetAutoSendValue(); + string? conversationId = this.GetConversationId(); + + await this.AssignResultAsync(context, resultContent).ConfigureAwait(false); + ChatMessage resultMessage = new(ChatRole.Tool, resultContent.Output); + + // Store messages if output path is configured + if (this.Model.Output?.Messages is not null) + { + await this.AssignAsync(this.Model.Output.Messages?.Path, resultMessage.ToFormula(), context).ConfigureAwait(false); + } + + // Auto-send the result if configured + if (autoSend) + { + AgentResponse resultResponse = new([resultMessage]); + await context.AddEventAsync(new AgentResponseEvent(this.Id, resultResponse), cancellationToken).ConfigureAwait(false); + } + + // Add messages to conversation if conversationId is provided + if (conversationId is not null) + { + ChatMessage assistantMessage = new(ChatRole.Assistant, resultContent.Output); + await agentProvider.CreateMessageAsync(conversationId, assistantMessage, cancellationToken).ConfigureAwait(false); + } + } + + private async ValueTask AssignResultAsync(IWorkflowContext context, McpServerToolResultContent toolResult) + { + if (this.Model.Output?.Result is null || toolResult.Output is null || toolResult.Output.Count == 0) + { + return; + } + + List parsedResults = []; + foreach (AIContent resultContent in toolResult.Output) + { + object? resultValue = resultContent switch + { + TextContent text => text.Text, + DataContent data => data.Uri, + _ => resultContent.ToString(), + }; + + // Convert JsonElement to its raw JSON string for processing + if (resultValue is JsonElement jsonElement) + { + resultValue = jsonElement.GetRawText(); + } + + // Attempt to parse as JSON if it's a string (or was converted from JsonElement) + if (resultValue is string jsonString) + { + try + { + using JsonDocument jsonDocument = JsonDocument.Parse(jsonString); + + // Handle different JSON value kinds + object? parsedValue = jsonDocument.RootElement.ValueKind switch + { + JsonValueKind.Object => jsonDocument.ParseRecord(VariableType.RecordType), + JsonValueKind.Array => jsonDocument.ParseList(jsonDocument.RootElement.GetListTypeFromJson()), + JsonValueKind.String => jsonDocument.RootElement.GetString(), + JsonValueKind.Number => jsonDocument.RootElement.TryGetInt64(out long l) ? l : jsonDocument.RootElement.GetDouble(), + JsonValueKind.True => true, + JsonValueKind.False => false, + JsonValueKind.Null => null, + _ => jsonString, + }; + + parsedResults.Add(parsedValue); + continue; + } + catch (JsonException) + { + // Not a valid JSON + } + } + + parsedResults.Add(resultValue); + } + + await this.AssignAsync(this.Model.Output.Result?.Path, parsedResults.ToFormula(), context).ConfigureAwait(false); + } + + private async ValueTask AssignErrorAsync(IWorkflowContext context, string errorMessage) + { + // Store error in result if configured (as a simple string) + if (this.Model.Output?.Result is not null) + { + await this.AssignAsync(this.Model.Output.Result?.Path, $"Error: {errorMessage}".ToFormula(), context).ConfigureAwait(false); + } + } + + private string GetServerUrl() => + this.Evaluator.GetValue( + Throw.IfNull( + this.Model.ServerUrl, + $"{nameof(this.Model)}.{nameof(this.Model.ServerUrl)}")).Value; + + private string? GetServerLabel() + { + if (this.Model.ServerLabel is null) + { + return null; + } + + string value = this.Evaluator.GetValue(this.Model.ServerLabel).Value; + return value.Length == 0 ? null : value; + } + + private string GetToolName() => + this.Evaluator.GetValue( + Throw.IfNull( + this.Model.ToolName, + $"{nameof(this.Model)}.{nameof(this.Model.ToolName)}")).Value; + + private string? GetConversationId() + { + if (this.Model.ConversationId is null) + { + return null; + } + + string value = this.Evaluator.GetValue(this.Model.ConversationId).Value; + return value.Length == 0 ? null : value; + } + + private bool GetRequireApproval() + { + if (this.Model.RequireApproval is null) + { + return false; + } + + return this.Evaluator.GetValue(this.Model.RequireApproval).Value; + } + + private bool GetAutoSendValue() + { + if (this.Model.Output?.AutoSend is null) + { + return true; + } + + return this.Evaluator.GetValue(this.Model.Output.AutoSend).Value; + } + + private string? GetConnectionName() + { + if (this.Model.Connection?.Name is null) + { + return null; + } + + string value = this.Evaluator.GetValue(this.Model.Connection.Name).Value; + return value.Length == 0 ? null : value; + } + + private Dictionary? GetArguments() + { + if (this.Model.Arguments is null) + { + return null; + } + + Dictionary result = []; + foreach (KeyValuePair argument in this.Model.Arguments) + { + result[argument.Key] = this.Evaluator.GetValue(argument.Value).Value.ToObject(); + } + + return result; + } + + private Dictionary? GetHeaders() + { + if (this.Model.Headers is null) + { + return null; + } + + Dictionary result = []; + foreach (KeyValuePair header in this.Model.Headers) + { + string value = this.Evaluator.GetValue(header.Value).Value; + if (!string.IsNullOrEmpty(value)) + { + result[header.Key] = value; + } + } + + return result; + } +} diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/README.md b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/README.md index 4202b89b82..4408f0febd 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/README.md +++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/README.md @@ -8,11 +8,9 @@ We've provided a set of [Sample Workflows](../../../workflow-samples/) within th Please refer to the [README](../../../workflow-samples/README.md) for setup instructions to run the sample workflows in your environment. -As part of our [Getting Started with Declarative Workflows](../../samples/GettingStarted/Workflows/Declarative/README.md), +As part of our [Getting Started with Declarative Workflows](../../samples/03-workflows/Declarative/README.md), we've provided a console application that is able to execute any declarative workflow. -Please refer to the [README](../../samples/GettingStarted/Workflows/Declarative/README.md) for configuration instructions. - ## Actions ### ⚙️ Foundry Actions diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/LockstepRunEventStream.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/LockstepRunEventStream.cs index f26f27ad96..506a0d1039 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/LockstepRunEventStream.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/LockstepRunEventStream.cs @@ -18,6 +18,7 @@ internal sealed class LockstepRunEventStream : IRunEventStream private int _isDisposed; private readonly ISuperStepRunner _stepRunner; + private Activity? _sessionActivity; public ValueTask GetStatusAsync(CancellationToken cancellationToken = default) => new(this.RunStatus); @@ -30,7 +31,16 @@ public LockstepRunEventStream(ISuperStepRunner stepRunner) public void Start() { - // No-op for lockstep execution + // Save and restore Activity.Current so the long-lived session activity + // doesn't leak into caller code via AsyncLocal. + Activity? previousActivity = Activity.Current; + + this._sessionActivity = this._stepRunner.TelemetryContext.StartWorkflowSessionActivity(); + this._sessionActivity?.SetTag(Tags.WorkflowId, this._stepRunner.StartExecutorId) + .SetTag(Tags.SessionId, this._stepRunner.SessionId); + this._sessionActivity?.AddEvent(new ActivityEvent(EventNames.SessionStarted)); + + Activity.Current = previousActivity; } public async IAsyncEnumerable TakeEventStreamAsync(bool blockOnPendingRequest, [EnumeratorCancellation] CancellationToken cancellationToken = default) @@ -44,19 +54,23 @@ public async IAsyncEnumerable TakeEventStreamAsync(bool blockOnPe } #endif - CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(this._stopCancellation.Token, cancellationToken); + using CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(this._stopCancellation.Token, cancellationToken); ConcurrentQueue eventSink = []; this._stepRunner.OutgoingEvents.EventRaised += OnWorkflowEventAsync; - using Activity? activity = this._stepRunner.TelemetryContext.StartWorkflowRunActivity(); - activity?.SetTag(Tags.WorkflowId, this._stepRunner.StartExecutorId).SetTag(Tags.SessionId, this._stepRunner.SessionId); + // Re-establish session as parent so the run activity nests correctly. + Activity.Current = this._sessionActivity; + + // Not 'using' — must dispose explicitly in finally for deterministic export. + Activity? runActivity = this._stepRunner.TelemetryContext.StartWorkflowRunActivity(); + runActivity?.SetTag(Tags.WorkflowId, this._stepRunner.StartExecutorId).SetTag(Tags.SessionId, this._stepRunner.SessionId); try { this.RunStatus = RunStatus.Running; - activity?.AddEvent(new ActivityEvent(EventNames.WorkflowStarted)); + runActivity?.AddEvent(new ActivityEvent(EventNames.WorkflowStarted)); do { @@ -65,7 +79,7 @@ public async IAsyncEnumerable TakeEventStreamAsync(bool blockOnPe { // Because we may be yielding out of this function, we need to ensure that the Activity.Current // is set to our activity for the duration of this loop iteration. - Activity.Current = activity; + Activity.Current = runActivity; // Drain SuperSteps while there are steps to run try @@ -75,13 +89,13 @@ public async IAsyncEnumerable TakeEventStreamAsync(bool blockOnPe catch (OperationCanceledException) { } - catch (Exception ex) when (activity is not null) + catch (Exception ex) when (runActivity is not null) { - activity.AddEvent(new ActivityEvent(EventNames.WorkflowError, tags: new() { + runActivity.AddEvent(new ActivityEvent(EventNames.WorkflowError, tags: new() { { Tags.ErrorType, ex.GetType().FullName }, - { Tags.BuildErrorMessage, ex.Message }, + { Tags.ErrorMessage, ex.Message }, })); - activity.CaptureException(ex); + runActivity.CaptureException(ex); throw; } @@ -129,12 +143,16 @@ public async IAsyncEnumerable TakeEventStreamAsync(bool blockOnPe } } while (!ShouldBreak()); - activity?.AddEvent(new ActivityEvent(EventNames.WorkflowCompleted)); + runActivity?.AddEvent(new ActivityEvent(EventNames.WorkflowCompleted)); } finally { this.RunStatus = this._stepRunner.HasUnservicedRequests ? RunStatus.PendingRequests : RunStatus.Idle; this._stepRunner.OutgoingEvents.EventRaised -= OnWorkflowEventAsync; + + // Explicitly dispose the Activity so Activity.Stop fires deterministically, + // regardless of how the async iterator enumerator is disposed. + runActivity?.Dispose(); } ValueTask OnWorkflowEventAsync(object? sender, WorkflowEvent e) @@ -172,6 +190,14 @@ public ValueTask DisposeAsync() { this._stopCancellation.Cancel(); + // Stop the session activity + if (this._sessionActivity is not null) + { + this._sessionActivity.AddEvent(new ActivityEvent(EventNames.SessionCompleted)); + this._sessionActivity.Dispose(); + this._sessionActivity = null; + } + this._stopCancellation.Dispose(); this._inputWaiter.Dispose(); } diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/StreamingRunEventStream.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/StreamingRunEventStream.cs index a6c34f2b9f..a09dedd8ad 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/StreamingRunEventStream.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/StreamingRunEventStream.cs @@ -55,13 +55,20 @@ public void Start() private async Task RunLoopAsync(CancellationToken cancellationToken) { using CancellationTokenSource errorSource = new(); - CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(errorSource.Token, cancellationToken); + using CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(errorSource.Token, cancellationToken); // Subscribe to events - they will flow directly to the channel as they're raised this._stepRunner.OutgoingEvents.EventRaised += OnEventRaisedAsync; - using Activity? activity = this._stepRunner.TelemetryContext.StartWorkflowRunActivity(); - activity?.SetTag(Tags.WorkflowId, this._stepRunner.StartExecutorId).SetTag(Tags.SessionId, this._stepRunner.SessionId); + // Start the session-level activity that spans the entire run loop lifetime. + // Individual run-stage activities are nested within this session activity. + Activity? sessionActivity = this._stepRunner.TelemetryContext.StartWorkflowSessionActivity(); + sessionActivity?.SetTag(Tags.WorkflowId, this._stepRunner.StartExecutorId) + .SetTag(Tags.SessionId, this._stepRunner.SessionId); + + Activity? runActivity = null; + + sessionActivity?.AddEvent(new ActivityEvent(EventNames.SessionStarted)); try { @@ -70,10 +77,15 @@ private async Task RunLoopAsync(CancellationToken cancellationToken) await this._inputWaiter.WaitForInputAsync(cancellationToken: linkedSource.Token).ConfigureAwait(false); this._runStatus = RunStatus.Running; - activity?.AddEvent(new ActivityEvent(EventNames.WorkflowStarted)); while (!linkedSource.Token.IsCancellationRequested) { + // Start a new run-stage activity for this input→processing→halt cycle + runActivity = this._stepRunner.TelemetryContext.StartWorkflowRunActivity(); + runActivity?.SetTag(Tags.WorkflowId, this._stepRunner.StartExecutorId) + .SetTag(Tags.SessionId, this._stepRunner.SessionId); + runActivity?.AddEvent(new ActivityEvent(EventNames.WorkflowStarted)); + // Run all available supersteps continuously // Events are streamed out in real-time as they happen via the event handler while (this._stepRunner.HasUnprocessedMessages && !linkedSource.Token.IsCancellationRequested) @@ -93,6 +105,15 @@ private async Task RunLoopAsync(CancellationToken cancellationToken) RunStatus capturedStatus = this._runStatus; await this._eventChannel.Writer.WriteAsync(new InternalHaltSignal(currentEpoch, capturedStatus), linkedSource.Token).ConfigureAwait(false); + // Close the run-stage activity when processing halts. + // A new run activity will be created when the next input arrives. + if (runActivity is not null) + { + runActivity.AddEvent(new ActivityEvent(EventNames.WorkflowCompleted)); + runActivity.Dispose(); + runActivity = null; + } + // Wait for next input from the consumer // Works for both Idle (no work) and PendingRequests (waiting for responses) await this._inputWaiter.WaitForInputAsync(TimeSpan.FromSeconds(1), linkedSource.Token).ConfigureAwait(false); @@ -107,14 +128,26 @@ private async Task RunLoopAsync(CancellationToken cancellationToken) } catch (Exception ex) { - if (activity != null) + // Record error on the run-stage activity if one is active + if (runActivity is not null) + { + runActivity.AddEvent(new ActivityEvent(EventNames.WorkflowError, tags: new() { + { Tags.ErrorType, ex.GetType().FullName }, + { Tags.ErrorMessage, ex.Message }, + })); + runActivity.CaptureException(ex); + } + + // Record error on the session activity + if (sessionActivity is not null) { - activity.AddEvent(new ActivityEvent(EventNames.WorkflowError, tags: new() { + sessionActivity.AddEvent(new ActivityEvent(EventNames.SessionError, tags: new() { { Tags.ErrorType, ex.GetType().FullName }, - { Tags.BuildErrorMessage, ex.Message }, + { Tags.ErrorMessage, ex.Message }, })); - activity.CaptureException(ex); + sessionActivity.CaptureException(ex); } + await this._eventChannel.Writer.WriteAsync(new WorkflowErrorEvent(ex), linkedSource.Token).ConfigureAwait(false); } finally @@ -124,7 +157,20 @@ private async Task RunLoopAsync(CancellationToken cancellationToken) // Mark as ended when run loop exits this._runStatus = RunStatus.Ended; - activity?.AddEvent(new ActivityEvent(EventNames.WorkflowCompleted)); + + // Stop the run-stage activity if not already stopped (e.g. on cancellation or error) + if (runActivity is not null) + { + runActivity.AddEvent(new ActivityEvent(EventNames.WorkflowCompleted)); + runActivity.Dispose(); + } + + // Stop the session activity — the session always ends when the run loop exits + if (sessionActivity is not null) + { + sessionActivity.AddEvent(new ActivityEvent(EventNames.SessionCompleted)); + sessionActivity.Dispose(); + } } async ValueTask OnEventRaisedAsync(object? sender, WorkflowEvent e) diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Observability/ActivityNames.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Observability/ActivityNames.cs index 9d2912ecd3..1639fc3c3c 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/Observability/ActivityNames.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Observability/ActivityNames.cs @@ -5,7 +5,8 @@ namespace Microsoft.Agents.AI.Workflows.Observability; internal static class ActivityNames { public const string WorkflowBuild = "workflow.build"; - public const string WorkflowRun = "workflow_invoke"; + public const string WorkflowSession = "workflow.session"; + public const string WorkflowInvoke = "workflow_invoke"; public const string MessageSend = "message.send"; public const string ExecutorProcess = "executor.process"; public const string EdgeGroupProcess = "edge_group.process"; diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Observability/EventNames.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Observability/EventNames.cs index 8b9f5bbde8..84540efdc8 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/Observability/EventNames.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Observability/EventNames.cs @@ -8,6 +8,9 @@ internal static class EventNames public const string BuildValidationCompleted = "build.validation_completed"; public const string BuildCompleted = "build.completed"; public const string BuildError = "build.error"; + public const string SessionStarted = "session.started"; + public const string SessionCompleted = "session.completed"; + public const string SessionError = "session.error"; public const string WorkflowStarted = "workflow.started"; public const string WorkflowCompleted = "workflow.completed"; public const string WorkflowError = "workflow.error"; diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Observability/Tags.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Observability/Tags.cs index 88c68eceb9..47ce701794 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/Observability/Tags.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Observability/Tags.cs @@ -11,6 +11,7 @@ internal static class Tags public const string BuildErrorMessage = "build.error.message"; public const string BuildErrorType = "build.error.type"; public const string ErrorType = "error.type"; + public const string ErrorMessage = "error.message"; public const string SessionId = "session.id"; public const string ExecutorId = "executor.id"; public const string ExecutorType = "executor.type"; diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Observability/WorkflowTelemetryContext.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Observability/WorkflowTelemetryContext.cs index e4b8d7a851..974ffce5c5 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/Observability/WorkflowTelemetryContext.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Observability/WorkflowTelemetryContext.cs @@ -88,7 +88,25 @@ public WorkflowTelemetryContext(WorkflowTelemetryOptions options, ActivitySource } /// - /// Starts a workflow run activity if enabled. + /// Starts a workflow session activity if enabled. This is the outer/parent span + /// that represents the entire lifetime of a workflow execution (from start + /// until stop, cancellation, or error) within the current trace. + /// Individual run stages are typically nested within it. + /// + /// An activity if workflow run telemetry is enabled, otherwise null. + public Activity? StartWorkflowSessionActivity() + { + if (!this.IsEnabled || this.Options.DisableWorkflowRun) + { + return null; + } + + return this.ActivitySource.StartActivity(ActivityNames.WorkflowSession); + } + + /// + /// Starts a workflow run activity if enabled. This represents a single + /// input-to-halt cycle within a workflow session. /// /// An activity if workflow run telemetry is enabled, otherwise null. public Activity? StartWorkflowRunActivity() @@ -98,7 +116,7 @@ public WorkflowTelemetryContext(WorkflowTelemetryOptions options, ActivitySource return null; } - return this.ActivitySource.StartActivity(ActivityNames.WorkflowRun); + return this.ActivitySource.StartActivity(ActivityNames.WorkflowInvoke); } /// diff --git a/dotnet/src/Shared/IntegrationTests/AnthropicConfiguration.cs b/dotnet/src/Shared/IntegrationTests/AnthropicConfiguration.cs deleted file mode 100644 index 2230be95ed..0000000000 --- a/dotnet/src/Shared/IntegrationTests/AnthropicConfiguration.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -namespace Shared.IntegrationTests; - -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. -#pragma warning disable CA1812 // Internal class that is apparently never instantiated. - -internal sealed class AnthropicConfiguration -{ - public string? ServiceId { get; set; } - - public string ChatModelId { get; set; } - - public string ChatReasoningModelId { get; set; } - - public string ApiKey { get; set; } -} diff --git a/dotnet/src/Shared/IntegrationTests/AzureAIConfiguration.cs b/dotnet/src/Shared/IntegrationTests/AzureAIConfiguration.cs deleted file mode 100644 index cfdc7aff7b..0000000000 --- a/dotnet/src/Shared/IntegrationTests/AzureAIConfiguration.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -namespace Shared.IntegrationTests; - -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. -#pragma warning disable CA1812 // Internal class that is apparently never instantiated. - -internal sealed class AzureAIConfiguration -{ - public string Endpoint { get; set; } - - public string DeploymentName { get; set; } - - public string BingConnectionId { get; set; } -} diff --git a/dotnet/src/Shared/IntegrationTests/FoundryMemoryConfiguration.cs b/dotnet/src/Shared/IntegrationTests/FoundryMemoryConfiguration.cs deleted file mode 100644 index 957f1bfa4c..0000000000 --- a/dotnet/src/Shared/IntegrationTests/FoundryMemoryConfiguration.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -namespace Shared.IntegrationTests; - -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. -#pragma warning disable CA1812 // Internal class that is apparently never instantiated. - -internal sealed class FoundryMemoryConfiguration -{ - public string Endpoint { get; set; } - public string MemoryStoreName { get; set; } - public string? DeploymentName { get; set; } -} diff --git a/dotnet/src/Shared/IntegrationTests/Mem0Configuration.cs b/dotnet/src/Shared/IntegrationTests/Mem0Configuration.cs deleted file mode 100644 index 052a38f113..0000000000 --- a/dotnet/src/Shared/IntegrationTests/Mem0Configuration.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -namespace Shared.IntegrationTests; - -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. -#pragma warning disable CA1812 // Internal class that is apparently never instantiated. - -internal sealed class Mem0Configuration -{ - public string ServiceUri { get; set; } - public string ApiKey { get; set; } -} diff --git a/dotnet/src/Shared/IntegrationTests/OpenAIConfiguration.cs b/dotnet/src/Shared/IntegrationTests/OpenAIConfiguration.cs deleted file mode 100644 index 34bc08330e..0000000000 --- a/dotnet/src/Shared/IntegrationTests/OpenAIConfiguration.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -namespace Shared.IntegrationTests; - -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. -#pragma warning disable CA1812 // Internal class that is apparently never instantiated. - -internal sealed class OpenAIConfiguration -{ - public string? ServiceId { get; set; } - - public string ChatModelId { get; set; } - - public string ChatReasoningModelId { get; set; } - - public string ApiKey { get; set; } -} diff --git a/dotnet/src/Shared/IntegrationTests/README.md b/dotnet/src/Shared/IntegrationTests/README.md index ea3ed5f3a3..b77a1a2de4 100644 --- a/dotnet/src/Shared/IntegrationTests/README.md +++ b/dotnet/src/Shared/IntegrationTests/README.md @@ -9,3 +9,30 @@ To use this in your project, add the following to your `.csproj` file: true ``` + +## Configuration + +Integration tests use flat environment variable names for configuration. +Use `TestConfiguration.GetValue(key)` or `TestConfiguration.GetRequiredValue(key)` to access values. + +Available keys are defined as constants in `TestSettings.cs`: + +| Key | Description | +|---|---| +| `ANTHROPIC_API_KEY` | API key for Anthropic | +| `ANTHROPIC_CHAT_MODEL_NAME` | Anthropic chat model name | +| `ANTHROPIC_REASONING_MODEL_NAME` | Anthropic reasoning model name | +| `ANTHROPIC_SERVICE_ID` | Anthropic service ID | +| `AZURE_AI_BING_CONNECTION_ID` | Azure AI Bing connection ID | +| `AZURE_AI_MEMORY_STORE_ID` | Azure AI Memory store name | +| `AZURE_AI_MODEL_DEPLOYMENT_NAME` | Azure AI model deployment name | +| `AZURE_AI_PROJECT_ENDPOINT` | Azure AI project endpoint | +| `COPILOTSTUDIO_AGENT_APP_ID` | Copilot Studio agent app ID | +| `COPILOTSTUDIO_DIRECT_CONNECT_URL` | Copilot Studio direct connect URL | +| `COPILOTSTUDIO_TENANT_ID` | Copilot Studio tenant ID | +| `MEM0_API_KEY` | API key for Mem0 | +| `MEM0_ENDPOINT` | Mem0 service endpoint | +| `OPENAI_API_KEY` | API key for OpenAI | +| `OPENAI_CHAT_MODEL_NAME` | OpenAI chat model name | +| `OPENAI_REASONING_MODEL_NAME` | OpenAI reasoning model name | +| `OPENAI_SERVICE_ID` | OpenAI service ID | diff --git a/dotnet/src/Shared/IntegrationTests/TestSettings.cs b/dotnet/src/Shared/IntegrationTests/TestSettings.cs new file mode 100644 index 0000000000..880db9d1cd --- /dev/null +++ b/dotnet/src/Shared/IntegrationTests/TestSettings.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft. All rights reserved. + +namespace Shared.IntegrationTests; + +/// +/// Constants for integration test configuration keys. +/// Values are resolved from environment variables and user secrets. +/// +internal static class TestSettings +{ + // Anthropic + public const string AnthropicApiKey = "ANTHROPIC_API_KEY"; + public const string AnthropicChatModelName = "ANTHROPIC_CHAT_MODEL_NAME"; + public const string AnthropicReasoningModelName = "ANTHROPIC_REASONING_MODEL_NAME"; + public const string AnthropicServiceId = "ANTHROPIC_SERVICE_ID"; + + // Azure AI (Foundry) + public const string AzureAIBingConnectionId = "AZURE_AI_BING_CONNECTION_ID"; + public const string AzureAIMemoryStoreId = "AZURE_AI_MEMORY_STORE_ID"; + public const string AzureAIModelDeploymentName = "AZURE_AI_MODEL_DEPLOYMENT_NAME"; + public const string AzureAIProjectEndpoint = "AZURE_AI_PROJECT_ENDPOINT"; + + // Copilot Studio + public const string CopilotStudioAgentAppId = "COPILOTSTUDIO_AGENT_APP_ID"; + public const string CopilotStudioDirectConnectUrl = "COPILOTSTUDIO_DIRECT_CONNECT_URL"; + public const string CopilotStudioTenantId = "COPILOTSTUDIO_TENANT_ID"; + + // Mem0 + public const string Mem0ApiKey = "MEM0_API_KEY"; + public const string Mem0Endpoint = "MEM0_ENDPOINT"; + + // OpenAI + public const string OpenAIApiKey = "OPENAI_API_KEY"; + public const string OpenAIChatModelName = "OPENAI_CHAT_MODEL_NAME"; + public const string OpenAIReasoningModelName = "OPENAI_REASONING_MODEL_NAME"; + public const string OpenAIServiceId = "OPENAI_SERVICE_ID"; +} diff --git a/dotnet/src/Shared/Workflows/Execution/WorkflowFactory.cs b/dotnet/src/Shared/Workflows/Execution/WorkflowFactory.cs index 1f1570312a..a36c388e73 100644 --- a/dotnet/src/Shared/Workflows/Execution/WorkflowFactory.cs +++ b/dotnet/src/Shared/Workflows/Execution/WorkflowFactory.cs @@ -22,6 +22,9 @@ internal sealed class WorkflowFactory(string workflowFile, Uri foundryEndpoint) // Assign to enable logging public ILoggerFactory LoggerFactory { get; init; } = NullLoggerFactory.Instance; + // Assign to provide MCP tool capabilities + public IMcpToolHandler? McpToolHandler { get; init; } + /// /// Create the workflow from the declarative YAML. Includes definition of the /// and the associated . @@ -42,6 +45,7 @@ public Workflow CreateWorkflow() Configuration = this.Configuration, ConversationId = this.ConversationId, LoggerFactory = this.LoggerFactory, + McpToolHandler = this.McpToolHandler, }; string workflowPath = Path.Combine(AppContext.BaseDirectory, workflowFile); diff --git a/dotnet/src/Shared/Workflows/Settings/Application.cs b/dotnet/src/Shared/Workflows/Settings/Application.cs index de8eb51534..e219b0e92d 100644 --- a/dotnet/src/Shared/Workflows/Settings/Application.cs +++ b/dotnet/src/Shared/Workflows/Settings/Application.cs @@ -12,10 +12,9 @@ internal static class Application /// public static class Settings { - public const string FoundryEndpoint = "FOUNDRY_PROJECT_ENDPOINT"; - public const string FoundryModelMini = "FOUNDRY_MODEL_DEPLOYMENT_NAME"; - public const string FoundryModelFull = "FOUNDRY_MEDIA_DEPLOYMENT_NAME"; - public const string FoundryGroundingTool = "FOUNDRY_CONNECTION_GROUNDING_TOOL"; + public const string FoundryEndpoint = "AZURE_AI_PROJECT_ENDPOINT"; + public const string FoundryModel = "AZURE_AI_MODEL_DEPLOYMENT_NAME"; + public const string FoundryGroundingTool = "AZURE_AI_BING_CONNECTION_ID"; } public static string GetInput(string[] args) diff --git a/dotnet/tests/AgentConformance.IntegrationTests/Support/TestConfiguration.cs b/dotnet/tests/AgentConformance.IntegrationTests/Support/TestConfiguration.cs index e56eeff3cb..dc887e3a1b 100644 --- a/dotnet/tests/AgentConformance.IntegrationTests/Support/TestConfiguration.cs +++ b/dotnet/tests/AgentConformance.IntegrationTests/Support/TestConfiguration.cs @@ -11,30 +11,24 @@ namespace AgentConformance.IntegrationTests.Support; public sealed class TestConfiguration { private static readonly IConfiguration s_configuration = new ConfigurationBuilder() - .AddJsonFile(path: "testsettings.json", optional: true) .AddJsonFile(path: "testsettings.development.json", optional: true) .AddEnvironmentVariables() .AddUserSecrets() .Build(); /// - /// Loads the type of configuration using a section name based on the type name. + /// Gets a configuration value by its flat key name. /// - /// The type of config to load. - /// The loaded configuration section of the specified type. - /// Thrown if the configuration section cannot be loaded. - public static T LoadSection() - { - var configType = typeof(T); - var configTypeName = configType.Name; + /// The configuration key. + /// The configuration value, or if not found. + public static string? GetValue(string key) => s_configuration[key]; - const string TrimText = "Configuration"; - if (configTypeName.EndsWith(TrimText, StringComparison.OrdinalIgnoreCase)) - { - configTypeName = configTypeName.Substring(0, configTypeName.Length - TrimText.Length); - } - - return s_configuration.GetRequiredSection(configTypeName).Get() ?? - throw new InvalidOperationException($"Could not load config for {configTypeName}."); - } + /// + /// Gets a required configuration value by its flat key name. + /// + /// The configuration key. + /// The configuration value. + /// Thrown if the configuration value is not found. + public static string GetRequiredValue(string key) => + s_configuration[key] ?? throw new InvalidOperationException($"Configuration key '{key}' is required but was not found."); } diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs index 4cf232f7d8..466abf3854 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. using System; using System.Collections.Generic; @@ -52,8 +52,9 @@ public Task CreateChatClientAgentAsync( string instructions = "You are a helpful assistant.", IList? aiTools = null) { - AnthropicConfiguration config = TestConfiguration.LoadSection(); - var anthropicClient = new AnthropicClient() { ApiKey = config.ApiKey }; + var anthropicClient = new AnthropicClient() { ApiKey = TestConfiguration.GetRequiredValue(TestSettings.AnthropicApiKey) }; + var chatModelName = TestConfiguration.GetRequiredValue(TestSettings.AnthropicChatModelName); + var reasoningModelName = TestConfiguration.GetRequiredValue(TestSettings.AnthropicReasoningModelName); IChatClient? chatClient = this._useBeta ? anthropicClient @@ -64,7 +65,7 @@ public Task CreateChatClientAgentAsync( => options.RawRepresentationFactory = _ => new Anthropic.Models.Beta.Messages.MessageCreateParams() { - Model = options.ModelId ?? (this._useReasoningModel ? config.ChatReasoningModelId : config.ChatModelId), + Model = options.ModelId ?? (this._useReasoningModel ? reasoningModelName : chatModelName), MaxTokens = options.MaxOutputTokens ?? 4096, Messages = [], Thinking = this._useReasoningModel @@ -79,7 +80,7 @@ public Task CreateChatClientAgentAsync( => options.RawRepresentationFactory = _ => new Anthropic.Models.Messages.MessageCreateParams() { - Model = options.ModelId ?? (this._useReasoningModel ? config.ChatReasoningModelId : config.ChatModelId), + Model = options.ModelId ?? (this._useReasoningModel ? reasoningModelName : chatModelName), MaxTokens = options.MaxOutputTokens ?? 4096, Messages = [], Thinking = this._useReasoningModel diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicSkillsIntegrationTests.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicSkillsIntegrationTests.cs index 105793c709..d171b23ad9 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicSkillsIntegrationTests.cs +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicSkillsIntegrationTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. using System.Threading.Tasks; using AgentConformance.IntegrationTests.Support; @@ -28,9 +28,8 @@ public async Task CreateAgentWithPptxSkillAsync() Assert.SkipWhen(SkipReason is not null, SkipReason ?? string.Empty); // Arrange - AnthropicConfiguration config = TestConfiguration.LoadSection(); - AnthropicClient anthropicClient = new() { ApiKey = config.ApiKey }; - string model = config.ChatModelId; + AnthropicClient anthropicClient = new() { ApiKey = TestConfiguration.GetRequiredValue(TestSettings.AnthropicApiKey) }; + string model = TestConfiguration.GetRequiredValue(TestSettings.AnthropicChatModelName); BetaSkillParams pptxSkill = new() { @@ -60,8 +59,7 @@ public async Task ListAnthropicManagedSkillsAsync() Assert.SkipWhen(SkipReason is not null, SkipReason ?? string.Empty); // Arrange - AnthropicConfiguration config = TestConfiguration.LoadSection(); - AnthropicClient anthropicClient = new() { ApiKey = config.ApiKey }; + AnthropicClient anthropicClient = new() { ApiKey = TestConfiguration.GetRequiredValue(TestSettings.AnthropicApiKey) }; // Act SkillListPage skills = await anthropicClient.Beta.Skills.List( diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientCreateTests.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientCreateTests.cs index d70d3d949d..ec4103f6a8 100644 --- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientCreateTests.cs +++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientCreateTests.cs @@ -17,8 +17,7 @@ namespace AzureAI.IntegrationTests; public class AIProjectClientCreateTests { - private static readonly AzureAIConfiguration s_config = TestConfiguration.LoadSection(); - private readonly AIProjectClient _client = new(new Uri(s_config.Endpoint), new AzureCliCredential()); + private readonly AIProjectClient _client = new(new Uri(TestConfiguration.GetRequiredValue(TestSettings.AzureAIProjectEndpoint)), new AzureCliCredential()); [Theory] [InlineData("CreateWithChatClientAgentOptionsAsync")] @@ -34,7 +33,7 @@ public async Task CreateAgent_CreatesAgentWithCorrectMetadataAsync(string create var agent = createMechanism switch { "CreateWithChatClientAgentOptionsAsync" => await this._client.CreateAIAgentAsync( - model: s_config.DeploymentName, + model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), options: new ChatClientAgentOptions() { Name = AgentName, @@ -43,7 +42,7 @@ public async Task CreateAgent_CreatesAgentWithCorrectMetadataAsync(string create }), "CreateWithFoundryOptionsAsync" => await this._client.CreateAIAgentAsync( name: AgentName, - creationOptions: new AgentVersionCreationOptions(new PromptAgentDefinition(s_config.DeploymentName) { Instructions = AgentInstructions }) { Description = AgentDescription }), + creationOptions: new AgentVersionCreationOptions(new PromptAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName)) { Instructions = AgentInstructions }) { Description = AgentDescription }), _ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}") }; @@ -101,12 +100,12 @@ You are a helpful agent that can help fetch data from files you know about. var agent = createMechanism switch { "CreateWithChatClientAgentOptionsAsync" => await this._client.CreateAIAgentAsync( - model: s_config.DeploymentName, + model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), name: AgentName, instructions: AgentInstructions, tools: [new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreMetadata.Value.Id)] }]), "CreateWithFoundryOptionsAsync" => await this._client.CreateAIAgentAsync( - model: s_config.DeploymentName, + model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), name: AgentName, instructions: AgentInstructions, tools: [ResponseTool.CreateFileSearchTool(vectorStoreIds: [vectorStoreMetadata.Value.Id]).AsAITool()]), @@ -161,13 +160,13 @@ and report the SECRET_NUMBER value it prints. Respond only with the number. { // Hosted tool path (tools supplied via ChatClientAgentOptions) "CreateWithChatClientAgentOptionsAsync" => await this._client.CreateAIAgentAsync( - model: s_config.DeploymentName, + model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), name: AgentName, instructions: AgentInstructions, tools: [new HostedCodeInterpreterTool() { Inputs = [new HostedFileContent(uploadedCodeFile.Id)] }]), // Foundry (definitions + resources provided directly) "CreateWithFoundryOptionsAsync" => await this._client.CreateAIAgentAsync( - model: s_config.DeploymentName, + model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), name: AgentName, instructions: AgentInstructions, tools: [ResponseTool.CreateCodeInterpreterTool(new CodeInterpreterToolContainer(CodeInterpreterToolContainerConfiguration.CreateAutomaticContainerConfiguration([uploadedCodeFile.Id]))).AsAITool()]), @@ -204,7 +203,7 @@ public async Task CreateAgent_CreatesAgentWithAIFunctionToolsAsync(string create ChatClientAgent agent = createMechanism switch { "CreateWithChatClientAgentOptionsAsync" => await this._client.CreateAIAgentAsync( - model: s_config.DeploymentName, + model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), options: new ChatClientAgentOptions() { Name = AgentName, diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs index 3055df899c..192e8b6c9e 100644 --- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs +++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs @@ -18,8 +18,6 @@ namespace AzureAI.IntegrationTests; public class AIProjectClientFixture : IChatClientAgentFixture { - private static readonly AzureAIConfiguration s_config = TestConfiguration.LoadSection(); - private ChatClientAgent _agent = null!; private AIProjectClient _client = null!; @@ -118,14 +116,14 @@ public async Task CreateChatClientAgentAsync( string instructions = "You are a helpful assistant.", IList? aiTools = null) { - return await this._client.CreateAIAgentAsync(GenerateUniqueAgentName(name), model: s_config.DeploymentName, instructions: instructions, tools: aiTools); + return await this._client.CreateAIAgentAsync(GenerateUniqueAgentName(name), model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), instructions: instructions, tools: aiTools); } public async Task CreateChatClientAgentAsync(ChatClientAgentOptions options) { options.Name ??= GenerateUniqueAgentName("HelpfulAssistant"); - return await this._client.CreateAIAgentAsync(model: s_config.DeploymentName, options); + return await this._client.CreateAIAgentAsync(model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), options); } public static string GenerateUniqueAgentName(string baseName) => @@ -172,13 +170,13 @@ public ValueTask DisposeAsync() public virtual async ValueTask InitializeAsync() { - this._client = new(new Uri(s_config.Endpoint), new AzureCliCredential()); + this._client = new(new Uri(TestConfiguration.GetRequiredValue(TestSettings.AzureAIProjectEndpoint)), new AzureCliCredential()); this._agent = await this.CreateChatClientAgentAsync(); } public async Task InitializeAsync(ChatClientAgentOptions options) { - this._client = new(new Uri(s_config.Endpoint), new AzureCliCredential()); + this._client = new(new Uri(TestConfiguration.GetRequiredValue(TestSettings.AzureAIProjectEndpoint)), new AzureCliCredential()); this._agent = await this.CreateChatClientAgentAsync(options); } } diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentCreateTests.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentCreateTests.cs index 05b87539da..ab2e1848a5 100644 --- a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentCreateTests.cs +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentCreateTests.cs @@ -15,8 +15,7 @@ namespace AzureAIAgentsPersistent.IntegrationTests; public class AzureAIAgentsPersistentCreateTests { - private static readonly AzureAIConfiguration s_config = TestConfiguration.LoadSection(); - private readonly PersistentAgentsClient _persistentAgentsClient = new(s_config.Endpoint, new AzureCliCredential()); + private readonly PersistentAgentsClient _persistentAgentsClient = new(TestConfiguration.GetRequiredValue(TestSettings.AzureAIProjectEndpoint), new AzureCliCredential()); [Theory] [InlineData("CreateWithChatClientAgentOptionsAsync")] @@ -32,7 +31,7 @@ public async Task CreateAgent_CreatesAgentWithCorrectMetadataAsync(string create var agent = createMechanism switch { "CreateWithChatClientAgentOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync( - s_config.DeploymentName, + TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), options: new ChatClientAgentOptions() { ChatOptions = new() { Instructions = AgentInstructions }, @@ -40,7 +39,7 @@ public async Task CreateAgent_CreatesAgentWithCorrectMetadataAsync(string create Description = AgentDescription }), "CreateWithFoundryOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync( - s_config.DeploymentName, + TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), instructions: AgentInstructions, name: AgentName, description: AgentDescription), @@ -99,7 +98,7 @@ You are a helpful agent that can help fetch data from files you know about. var agent = createMechanism switch { "CreateWithChatClientAgentOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync( - s_config.DeploymentName, + TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), options: new ChatClientAgentOptions() { ChatOptions = new() @@ -109,7 +108,7 @@ You are a helpful agent that can help fetch data from files you know about. } }), "CreateWithFoundryOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync( - s_config.DeploymentName, + TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), instructions: AgentInstructions, tools: [new FileSearchToolDefinition()], toolResources: new ToolResources() { FileSearch = new([vectorStoreMetadata.Value.Id], null) }), @@ -162,7 +161,7 @@ and report the SECRET_NUMBER value it prints. Respond only with the number. { // Hosted tool path (tools supplied via ChatClientAgentOptions) "CreateWithChatClientAgentOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync( - s_config.DeploymentName, + TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), options: new ChatClientAgentOptions() { ChatOptions = new() @@ -172,7 +171,7 @@ and report the SECRET_NUMBER value it prints. Respond only with the number. } }), "CreateWithFoundryOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync( - s_config.DeploymentName, + TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), instructions: AgentInstructions, tools: [new CodeInterpreterToolDefinition()], toolResources: new ToolResources() { CodeInterpreter = toolResource }), @@ -208,7 +207,7 @@ public async Task CreateAgent_CreatesAgentWithAIFunctionToolsAsync(string create ChatClientAgent agent = createMechanism switch { "CreateWithChatClientAgentOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync( - s_config.DeploymentName, + TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), options: new ChatClientAgentOptions() { ChatOptions = new() diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs index dca9956774..1ebbd2b4f1 100644 --- a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs @@ -16,8 +16,6 @@ namespace AzureAIAgentsPersistent.IntegrationTests; public class AzureAIAgentsPersistentFixture : IChatClientAgentFixture { - private static readonly AzureAIConfiguration s_config = TestConfiguration.LoadSection(); - private ChatClientAgent _agent = null!; private PersistentAgentsClient _persistentAgentsClient = null!; @@ -58,7 +56,7 @@ public async Task CreateChatClientAgentAsync( IList? aiTools = null) { var persistentAgentResponse = await this._persistentAgentsClient.Administration.CreateAgentAsync( - model: s_config.DeploymentName, + model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), name: name, instructions: instructions); @@ -101,7 +99,7 @@ public ValueTask DisposeAsync() public async ValueTask InitializeAsync() { - this._persistentAgentsClient = new(s_config.Endpoint, new AzureCliCredential()); + this._persistentAgentsClient = new(TestConfiguration.GetRequiredValue(TestSettings.AzureAIProjectEndpoint), new AzureCliCredential()); this._agent = await this.CreateChatClientAgentAsync(); } } diff --git a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs index af97b6bcca..567d41a552 100644 --- a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs +++ b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. using System; using System.Collections.Generic; @@ -13,6 +13,7 @@ using Microsoft.Extensions.AI; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; +using Shared.IntegrationTests; namespace CopilotStudio.IntegrationTests; @@ -31,21 +32,21 @@ public ValueTask InitializeAsync() { const string CopilotStudioHttpClientName = nameof(CopilotStudioAgent); - CopilotStudioAgentConfiguration? config = null; + CopilotStudioConnectionSettings? settings = null; try { - config = TestConfiguration.LoadSection(); + settings = new CopilotStudioConnectionSettings( + TestConfiguration.GetRequiredValue(TestSettings.CopilotStudioTenantId), + TestConfiguration.GetRequiredValue(TestSettings.CopilotStudioAgentAppId)) + { + DirectConnectUrl = TestConfiguration.GetRequiredValue(TestSettings.CopilotStudioDirectConnectUrl), + }; } catch (InvalidOperationException ex) { Assert.Skip("CopilotStudio configuration could not be loaded. Error:" + ex.Message); } - var settings = new CopilotStudioConnectionSettings(config!.TenantId, config.AppClientId) - { - DirectConnectUrl = config.DirectConnectUrl, - }; - ServiceCollection services = new(); services diff --git a/dotnet/tests/CopilotStudio.IntegrationTests/Support/CopilotStudioAgentConfiguration.cs b/dotnet/tests/CopilotStudio.IntegrationTests/Support/CopilotStudioAgentConfiguration.cs deleted file mode 100644 index 670ed5d706..0000000000 --- a/dotnet/tests/CopilotStudio.IntegrationTests/Support/CopilotStudioAgentConfiguration.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -namespace CopilotStudio.IntegrationTests.Support; - -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. -#pragma warning disable CA1812 // Internal class that is apparently never instantiated. - -internal sealed class CopilotStudioAgentConfiguration -{ - public string DirectConnectUrl { get; set; } - - public string TenantId { get; set; } - - public string AppClientId { get; set; } -} diff --git a/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatHistoryProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatHistoryProviderTests.cs index 337677419b..cedf37f292 100644 --- a/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatHistoryProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatHistoryProviderTests.cs @@ -18,8 +18,8 @@ namespace Microsoft.Agents.AI.CosmosNoSql.UnitTests; /// - Default Mode: Cleans up all test data after each test run (deletes database) /// - Preserve Mode: Keeps containers and data for inspection in Cosmos DB Emulator Data Explorer /// -/// To enable Preserve Mode, set environment variable: COSMOS_PRESERVE_CONTAINERS=true -/// Example: $env:COSMOS_PRESERVE_CONTAINERS="true"; dotnet test +/// To enable Preserve Mode, set environment variable: COSMOSDB_PRESERVE_CONTAINERS=true +/// Example: $env:COSMOSDB_PRESERVE_CONTAINERS="true"; dotnet test /// /// In Preserve Mode, you can view the data in Cosmos DB Emulator Data Explorer at: /// https://localhost:8081/_explorer/index.html @@ -29,12 +29,12 @@ namespace Microsoft.Agents.AI.CosmosNoSql.UnitTests; /// Environment Variable Reference: /// | Variable | Values | Description | /// |----------|--------|-------------| -/// | COSMOS_PRESERVE_CONTAINERS | true / false | Controls whether to preserve test data after completion | +/// | COSMOSDB_PRESERVE_CONTAINERS | true / false | Controls whether to preserve test data after completion | /// /// Usage Examples: -/// - Run all tests in preserve mode: $env:COSMOS_PRESERVE_CONTAINERS="true"; dotnet test tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/ -/// - Run specific test category in preserve mode: $env:COSMOS_PRESERVE_CONTAINERS="true"; dotnet test tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/ --filter "Category=CosmosDB" -/// - Reset to cleanup mode: $env:COSMOS_PRESERVE_CONTAINERS=""; dotnet test tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/ +/// - Run all tests in preserve mode: $env:COSMOSDB_PRESERVE_CONTAINERS="true"; dotnet test tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/ +/// - Run specific test category in preserve mode: $env:COSMOSDB_PRESERVE_CONTAINERS="true"; dotnet test tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/ --filter "Category=CosmosDB" +/// - Reset to cleanup mode: $env:COSMOSDB_PRESERVE_CONTAINERS=""; dotnet test tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/ /// [Collection("CosmosDB")] public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable @@ -64,8 +64,8 @@ public async ValueTask InitializeAsync() this.SkipIfEmulatorNotAvailable(); // Check environment variable to determine if we should preserve containers - // Set COSMOS_PRESERVE_CONTAINERS=true to keep containers and data for inspection - this._preserveContainer = string.Equals(Environment.GetEnvironmentVariable("COSMOS_PRESERVE_CONTAINERS"), "true", StringComparison.OrdinalIgnoreCase); + // Set COSMOSDB_PRESERVE_CONTAINERS=true to keep containers and data for inspection + this._preserveContainer = string.Equals(Environment.GetEnvironmentVariable("COSMOSDB_PRESERVE_CONTAINERS"), bool.TrueString, StringComparison.OrdinalIgnoreCase); this._connectionString = $"AccountEndpoint={s_emulatorEndpoint};AccountKey={s_emulatorKey}"; @@ -141,9 +141,9 @@ public void Dispose() private void SkipIfEmulatorNotAvailable() { - // In CI: Skip if COSMOS_EMULATOR_AVAILABLE is not set to "true" + // In CI: Skip if COSMOSDB_EMULATOR_AVAILABLE is not set to "true" // Locally: Skip if emulator connection check failed - var ciEmulatorAvailable = string.Equals(Environment.GetEnvironmentVariable("COSMOS_EMULATOR_AVAILABLE"), "true", StringComparison.OrdinalIgnoreCase); + var ciEmulatorAvailable = string.Equals(Environment.GetEnvironmentVariable("COSMOSDB_EMULATOR_AVAILABLE"), bool.TrueString, StringComparison.OrdinalIgnoreCase); Assert.SkipWhen(!ciEmulatorAvailable && !this._emulatorAvailable, "Cosmos DB Emulator is not available"); } diff --git a/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosCheckpointStoreTests.cs b/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosCheckpointStoreTests.cs index 899e037c3d..301b58bc49 100644 --- a/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosCheckpointStoreTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosCheckpointStoreTests.cs @@ -17,8 +17,8 @@ namespace Microsoft.Agents.AI.CosmosNoSql.UnitTests; /// - Default Mode: Cleans up all test data after each test run (deletes database) /// - Preserve Mode: Keeps containers and data for inspection in Cosmos DB Emulator Data Explorer /// -/// To enable Preserve Mode, set environment variable: COSMOS_PRESERVE_CONTAINERS=true -/// Example: $env:COSMOS_PRESERVE_CONTAINERS="true"; dotnet test +/// To enable Preserve Mode, set environment variable: COSMOSDB_PRESERVE_CONTAINERS=true +/// Example: $env:COSMOSDB_PRESERVE_CONTAINERS="true"; dotnet test /// /// In Preserve Mode, you can view the data in Cosmos DB Emulator Data Explorer at: /// https://localhost:8081/_explorer/index.html @@ -61,8 +61,8 @@ public async ValueTask InitializeAsync() this.SkipIfEmulatorNotAvailable(); // Check environment variable to determine if we should preserve containers - // Set COSMOS_PRESERVE_CONTAINERS=true to keep containers and data for inspection - this._preserveContainer = string.Equals(Environment.GetEnvironmentVariable("COSMOS_PRESERVE_CONTAINERS"), "true", StringComparison.OrdinalIgnoreCase); + // Set COSMOSDB_PRESERVE_CONTAINERS=true to keep containers and data for inspection + this._preserveContainer = string.Equals(Environment.GetEnvironmentVariable("COSMOSDB_PRESERVE_CONTAINERS"), bool.TrueString, StringComparison.OrdinalIgnoreCase); this._connectionString = $"AccountEndpoint={s_emulatorEndpoint};AccountKey={s_emulatorKey}"; @@ -122,9 +122,9 @@ public async ValueTask DisposeAsync() private void SkipIfEmulatorNotAvailable() { - // In CI: Skip if COSMOS_EMULATOR_AVAILABLE is not set to "true" + // In CI: Skip if COSMOSDB_EMULATOR_AVAILABLE is not set to "true" // Locally: Skip if emulator connection check failed - var ciEmulatorAvailable = string.Equals(Environment.GetEnvironmentVariable("COSMOS_EMULATOR_AVAILABLE"), "true", StringComparison.OrdinalIgnoreCase); + var ciEmulatorAvailable = string.Equals(Environment.GetEnvironmentVariable("COSMOSDB_EMULATOR_AVAILABLE"), bool.TrueString, StringComparison.OrdinalIgnoreCase); Assert.SkipWhen(!ciEmulatorAvailable && !this._emulatorAvailable, "Cosmos DB Emulator is not available"); } diff --git a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ConsoleAppSamplesValidation.cs b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ConsoleAppSamplesValidation.cs index 44c0fb116d..af14a4c8f4 100644 --- a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ConsoleAppSamplesValidation.cs +++ b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/ConsoleAppSamplesValidation.cs @@ -25,7 +25,7 @@ public sealed class ConsoleAppSamplesValidation(ITestOutputHelper outputHelper) private static bool s_infrastructureStarted; private static readonly string s_samplesPath = Path.GetFullPath( - Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "..", "..", "samples", "Durable", "Agents", "ConsoleApps")); + Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "..", "..", "samples", "04-hosting", "DurableAgents", "ConsoleApps")); private readonly ITestOutputHelper _outputHelper = outputHelper; @@ -863,8 +863,8 @@ private Process StartConsoleApp(string samplePath, BlockingCollection string openAiEndpoint = s_configuration["AZURE_OPENAI_ENDPOINT"] ?? throw new InvalidOperationException("The required AZURE_OPENAI_ENDPOINT env variable is not set."); - string openAiDeployment = s_configuration["AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"] ?? - throw new InvalidOperationException("The required AZURE_OPENAI_CHAT_DEPLOYMENT_NAME env variable is not set."); + string openAiDeployment = s_configuration["AZURE_OPENAI_DEPLOYMENT_NAME"] ?? + throw new InvalidOperationException("The required AZURE_OPENAI_DEPLOYMENT_NAME env variable is not set."); void SetAndLogEnvironmentVariable(string key, string value) { @@ -874,7 +874,7 @@ void SetAndLogEnvironmentVariable(string key, string value) // Set required environment variables for the app SetAndLogEnvironmentVariable("AZURE_OPENAI_ENDPOINT", openAiEndpoint); - SetAndLogEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT", openAiDeployment); + SetAndLogEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME", openAiDeployment); SetAndLogEnvironmentVariable("DURABLE_TASK_SCHEDULER_CONNECTION_STRING", $"Endpoint=http://localhost:{DtsPort};TaskHub={taskHubName};Authentication=None"); SetAndLogEnvironmentVariable("REDIS_CONNECTION_STRING", $"localhost:{RedisPort}"); diff --git a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/Microsoft.Agents.AI.DurableTask.IntegrationTests.csproj b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/Microsoft.Agents.AI.DurableTask.IntegrationTests.csproj index db6aa6d62b..ac4f52e3eb 100644 --- a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/Microsoft.Agents.AI.DurableTask.IntegrationTests.csproj +++ b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/Microsoft.Agents.AI.DurableTask.IntegrationTests.csproj @@ -3,7 +3,6 @@ $(TargetFrameworksCore) enable - b7762d10-e29b-4bb1-8b74-b6d69a667dd4 diff --git a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/TestHelper.cs b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/TestHelper.cs index 171560934b..e092db9e8d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/TestHelper.cs +++ b/dotnet/tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/TestHelper.cs @@ -156,12 +156,12 @@ internal static ChatClient GetAzureOpenAIChatClient(IConfiguration configuration { string azureOpenAiEndpoint = configuration["AZURE_OPENAI_ENDPOINT"] ?? throw new InvalidOperationException("The required AZURE_OPENAI_ENDPOINT env variable is not set."); - string azureOpenAiDeploymentName = configuration["AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"] ?? - throw new InvalidOperationException("The required AZURE_OPENAI_CHAT_DEPLOYMENT_NAME env variable is not set."); + string azureOpenAiDeploymentName = configuration["AZURE_OPENAI_DEPLOYMENT_NAME"] ?? + throw new InvalidOperationException("The required AZURE_OPENAI_DEPLOYMENT_NAME env variable is not set."); - // Check if AZURE_OPENAI_KEY is provided for key-based authentication. + // Check if AZURE_OPENAI_API_KEY is provided for key-based authentication. // NOTE: This is not used for automated tests, but can be useful for local development. - string? azureOpenAiKey = configuration["AZURE_OPENAI_KEY"]; + string? azureOpenAiKey = configuration["AZURE_OPENAI_API_KEY"]; AzureOpenAIClient client = !string.IsNullOrEmpty(azureOpenAiKey) ? new AzureOpenAIClient(new Uri(azureOpenAiEndpoint), new AzureKeyCredential(azureOpenAiKey)) diff --git a/dotnet/tests/Microsoft.Agents.AI.DurableTask.UnitTests/Microsoft.Agents.AI.DurableTask.UnitTests.csproj b/dotnet/tests/Microsoft.Agents.AI.DurableTask.UnitTests/Microsoft.Agents.AI.DurableTask.UnitTests.csproj index b0cf00cae1..d6b34bd6b9 100644 --- a/dotnet/tests/Microsoft.Agents.AI.DurableTask.UnitTests/Microsoft.Agents.AI.DurableTask.UnitTests.csproj +++ b/dotnet/tests/Microsoft.Agents.AI.DurableTask.UnitTests/Microsoft.Agents.AI.DurableTask.UnitTests.csproj @@ -3,7 +3,6 @@ $(TargetFrameworksCore) enable - b7762d10-e29b-4bb1-8b74-b6d69a667dd4 diff --git a/dotnet/tests/Microsoft.Agents.AI.FoundryMemory.IntegrationTests/FoundryMemoryProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.FoundryMemory.IntegrationTests/FoundryMemoryProviderTests.cs index d89001d3b9..4b1838335c 100644 --- a/dotnet/tests/Microsoft.Agents.AI.FoundryMemory.IntegrationTests/FoundryMemoryProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.FoundryMemory.IntegrationTests/FoundryMemoryProviderTests.cs @@ -29,21 +29,21 @@ public sealed class FoundryMemoryProviderTests : IDisposable public FoundryMemoryProviderTests() { IConfigurationRoot configuration = new ConfigurationBuilder() - .AddJsonFile(path: "testsettings.json", optional: true, reloadOnChange: true) .AddJsonFile(path: "testsettings.development.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables() .AddUserSecrets(optional: true) .Build(); - var foundrySettings = configuration.GetSection("FoundryMemory").Get(); + var endpoint = configuration[TestSettings.AzureAIProjectEndpoint]; + var memoryStoreName = configuration[TestSettings.AzureAIMemoryStoreId]; + var deploymentName = configuration[TestSettings.AzureAIModelDeploymentName]; - if (foundrySettings is not null && - !string.IsNullOrWhiteSpace(foundrySettings.Endpoint) && - !string.IsNullOrWhiteSpace(foundrySettings.MemoryStoreName)) + if (!string.IsNullOrWhiteSpace(endpoint) && + !string.IsNullOrWhiteSpace(memoryStoreName)) { - this._client = new AIProjectClient(new Uri(foundrySettings.Endpoint), new AzureCliCredential()); - this._memoryStoreName = foundrySettings.MemoryStoreName; - this._deploymentName = foundrySettings.DeploymentName ?? "gpt-4.1-mini"; + this._client = new AIProjectClient(new Uri(endpoint), new AzureCliCredential()); + this._memoryStoreName = memoryStoreName; + this._deploymentName = deploymentName ?? "gpt-4.1-mini"; } } diff --git a/dotnet/tests/Microsoft.Agents.AI.FoundryMemory.IntegrationTests/Microsoft.Agents.AI.FoundryMemory.IntegrationTests.csproj b/dotnet/tests/Microsoft.Agents.AI.FoundryMemory.IntegrationTests/Microsoft.Agents.AI.FoundryMemory.IntegrationTests.csproj index a28fea3490..4bf96a5b35 100644 --- a/dotnet/tests/Microsoft.Agents.AI.FoundryMemory.IntegrationTests/Microsoft.Agents.AI.FoundryMemory.IntegrationTests.csproj +++ b/dotnet/tests/Microsoft.Agents.AI.FoundryMemory.IntegrationTests/Microsoft.Agents.AI.FoundryMemory.IntegrationTests.csproj @@ -12,7 +12,6 @@ - diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests.csproj b/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests.csproj index fb955c3162..010e9c9650 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests.csproj +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests.csproj @@ -3,7 +3,6 @@ $(TargetFrameworksCore) enable - b7762d10-e29b-4bb1-8b74-b6d69a667dd4 diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs index 919f66f30b..c7004e6ba5 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/SamplesValidation.cs @@ -24,14 +24,14 @@ public sealed class SamplesValidation(ITestOutputHelper outputHelper) : IAsyncLi private static readonly HttpClient s_sharedHttpClient = new(); private static readonly IConfiguration s_configuration = new ConfigurationBuilder() - .AddUserSecrets(Assembly.GetExecutingAssembly()) .AddEnvironmentVariables() + .AddUserSecrets(Assembly.GetExecutingAssembly()) .Build(); private static bool s_infrastructureStarted; private static readonly TimeSpan s_orchestrationTimeout = TimeSpan.FromMinutes(1); private static readonly string s_samplesPath = Path.GetFullPath( - Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "..", "..", "samples", "Durable", "Agents", "AzureFunctions")); + Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "..", "..", "samples", "04-hosting", "DurableAgents", "AzureFunctions")); private readonly ITestOutputHelper _outputHelper = outputHelper; @@ -860,12 +860,12 @@ private Process StartFunctionApp(string samplePath, List logs) string openAiEndpoint = s_configuration["AZURE_OPENAI_ENDPOINT"] ?? throw new InvalidOperationException("The required AZURE_OPENAI_ENDPOINT env variable is not set."); - string openAiDeployment = s_configuration["AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"] ?? - throw new InvalidOperationException("The required AZURE_OPENAI_CHAT_DEPLOYMENT_NAME env variable is not set."); + string openAiDeployment = s_configuration["AZURE_OPENAI_DEPLOYMENT_NAME"] ?? + throw new InvalidOperationException("The required AZURE_OPENAI_DEPLOYMENT_NAME env variable is not set."); // Set required environment variables for the function app (see local.settings.json for required settings) startInfo.EnvironmentVariables["AZURE_OPENAI_ENDPOINT"] = openAiEndpoint; - startInfo.EnvironmentVariables["AZURE_OPENAI_DEPLOYMENT"] = openAiDeployment; + startInfo.EnvironmentVariables["AZURE_OPENAI_DEPLOYMENT_NAME"] = openAiDeployment; startInfo.EnvironmentVariables["DURABLE_TASK_SCHEDULER_CONNECTION_STRING"] = $"Endpoint=http://localhost:{DtsPort};TaskHub=default;Authentication=None"; startInfo.EnvironmentVariables["AzureWebJobsStorage"] = "UseDevelopmentStorage=true"; diff --git a/dotnet/tests/Microsoft.Agents.AI.Mem0.IntegrationTests/Mem0ProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.Mem0.IntegrationTests/Mem0ProviderTests.cs index 72e9f6bdff..f46ca4b651 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Mem0.IntegrationTests/Mem0ProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Mem0.IntegrationTests/Mem0ProviderTests.cs @@ -27,19 +27,20 @@ public sealed class Mem0ProviderTests : IDisposable public Mem0ProviderTests() { IConfigurationRoot configuration = new ConfigurationBuilder() - .AddJsonFile(path: "testsettings.json", optional: true, reloadOnChange: true) .AddJsonFile(path: "testsettings.development.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables() .AddUserSecrets(optional: true) .Build(); - var mem0Settings = configuration.GetSection("Mem0").Get(); + var serviceUri = configuration[TestSettings.Mem0Endpoint]; + var apiKey = configuration[TestSettings.Mem0ApiKey]; + this._httpClient = new HttpClient(); - if (mem0Settings is not null && !string.IsNullOrWhiteSpace(mem0Settings.ServiceUri) && !string.IsNullOrWhiteSpace(mem0Settings.ApiKey)) + if (!string.IsNullOrWhiteSpace(serviceUri) && !string.IsNullOrWhiteSpace(apiKey)) { - this._httpClient.BaseAddress = new Uri(mem0Settings.ServiceUri); - this._httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", mem0Settings.ApiKey); + this._httpClient.BaseAddress = new Uri(serviceUri); + this._httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", apiKey); } } diff --git a/dotnet/tests/Microsoft.Agents.AI.Mem0.IntegrationTests/Microsoft.Agents.AI.Mem0.IntegrationTests.csproj b/dotnet/tests/Microsoft.Agents.AI.Mem0.IntegrationTests/Microsoft.Agents.AI.Mem0.IntegrationTests.csproj index 99b028963a..330671825f 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Mem0.IntegrationTests/Microsoft.Agents.AI.Mem0.IntegrationTests.csproj +++ b/dotnet/tests/Microsoft.Agents.AI.Mem0.IntegrationTests/Microsoft.Agents.AI.Mem0.IntegrationTests.csproj @@ -10,7 +10,6 @@ - diff --git a/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs b/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs index b4749c5624..08910060a2 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs @@ -51,7 +51,7 @@ public async Task ProcessChatContentAsync_WithBlockedPrompt_ReturnsBlockedMessag this._mockProcessor.Setup(x => x.ProcessMessagesAsync( It.IsAny>(), It.IsAny(), - It.IsAny(), + Activity.UploadText, It.IsAny(), It.IsAny(), It.IsAny())) @@ -88,15 +88,24 @@ public async Task ProcessChatContentAsync_WithAllowedPromptAndBlockedResponse_Re It.IsAny())) .ReturnsAsync(innerResponse); - this._mockProcessor.SetupSequence(x => x.ProcessMessagesAsync( + // Prompt check uses UploadText, response check uses DownloadText + this._mockProcessor.Setup(x => x.ProcessMessagesAsync( It.IsAny>(), It.IsAny(), - It.IsAny(), + Activity.UploadText, + It.IsAny(), + It.IsAny(), + It.IsAny())) + .ReturnsAsync((false, "user-123")); // Prompt allowed + + this._mockProcessor.Setup(x => x.ProcessMessagesAsync( + It.IsAny>(), + It.IsAny(), + Activity.DownloadText, It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync((false, "user-123")) // Prompt allowed - .ReturnsAsync((true, "user-123")); // Response blocked + .ReturnsAsync((true, "user-123")); // Response blocked // Act var result = await this._wrapper.ProcessChatContentAsync(messages, null, mockChatClient.Object, CancellationToken.None); @@ -237,14 +246,21 @@ public async Task ProcessChatContentAsync_UsesConversationIdFromOptions_Async() // Act await this._wrapper.ProcessChatContentAsync(messages, options, mockChatClient.Object, CancellationToken.None); - // Assert + // Assert - verify prompt uses UploadText and response uses DownloadText this._mockProcessor.Verify(x => x.ProcessMessagesAsync( It.IsAny>(), "conversation-123", - It.IsAny(), + Activity.UploadText, + It.IsAny(), + It.IsAny(), + It.IsAny()), Times.Once); + this._mockProcessor.Verify(x => x.ProcessMessagesAsync( + It.IsAny>(), + "conversation-123", + Activity.DownloadText, It.IsAny(), It.IsAny(), - It.IsAny()), Times.Exactly(2)); + It.IsAny()), Times.Once); } #endregion @@ -264,7 +280,7 @@ public async Task ProcessAgentContentAsync_WithBlockedPrompt_ReturnsBlockedMessa this._mockProcessor.Setup(x => x.ProcessMessagesAsync( It.IsAny>(), It.IsAny(), - It.IsAny(), + Activity.UploadText, It.IsAny(), It.IsAny(), It.IsAny())) @@ -306,15 +322,24 @@ public async Task ProcessAgentContentAsync_WithAllowedPromptAndBlockedResponse_R ItExpr.IsAny()) .ReturnsAsync(innerResponse); - this._mockProcessor.SetupSequence(x => x.ProcessMessagesAsync( + // Prompt check uses UploadText, response check uses DownloadText + this._mockProcessor.Setup(x => x.ProcessMessagesAsync( It.IsAny>(), It.IsAny(), - It.IsAny(), + Activity.UploadText, It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync((false, "user-123")) // Prompt allowed - .ReturnsAsync((true, "user-123")); // Response blocked + .ReturnsAsync((false, "user-123")); // Prompt allowed + + this._mockProcessor.Setup(x => x.ProcessMessagesAsync( + It.IsAny>(), + It.IsAny(), + Activity.DownloadText, + It.IsAny(), + It.IsAny(), + It.IsAny())) + .ReturnsAsync((true, "user-123")); // Response blocked // Act var result = await this._wrapper.ProcessAgentContentAsync(messages, null, null, mockAgent.Object, CancellationToken.None); @@ -472,10 +497,17 @@ public async Task ProcessAgentContentAsync_ExtractsThreadIdFromMessageAdditional this._mockProcessor.Verify(x => x.ProcessMessagesAsync( It.IsAny>(), "conversation-from-props", - It.IsAny(), + Activity.UploadText, + It.IsAny(), + It.IsAny(), + It.IsAny()), Times.Once); + this._mockProcessor.Verify(x => x.ProcessMessagesAsync( + It.IsAny>(), + "conversation-from-props", + Activity.DownloadText, It.IsAny(), It.IsAny(), - It.IsAny()), Times.Exactly(2)); + It.IsAny()), Times.Once); } [Fact] diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/AgentProvider.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/AgentProvider.cs index 96795cc6a4..a4198d3a4c 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/AgentProvider.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/AgentProvider.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Azure.AI.Projects.OpenAI; using Microsoft.Extensions.Configuration; +using Shared.IntegrationTests; namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Agents; @@ -19,14 +20,6 @@ public static class Names public const string Vision = "VISION"; } - public static class Settings - { - public const string FoundryEndpoint = "FOUNDRY_PROJECT_ENDPOINT"; - public const string FoundryModelMini = "FOUNDRY_MODEL_DEPLOYMENT_NAME"; - public const string FoundryModelFull = "FOUNDRY_MEDIA_DEPLOYMENT_NAME"; - public const string FoundryGroundingTool = "FOUNDRY_CONNECTION_GROUNDING_TOOL"; - } - public static AgentProvider Create(IConfiguration configuration, string providerType) => providerType.ToUpperInvariant() switch { @@ -40,7 +33,7 @@ public static AgentProvider Create(IConfiguration configuration, string provider public async ValueTask CreateAgentsAsync() { - Uri foundryEndpoint = new(this.GetSetting(Settings.FoundryEndpoint)); + Uri foundryEndpoint = new(this.GetSetting(TestSettings.AzureAIProjectEndpoint)); await foreach (AgentVersion agent in this.CreateAgentsAsync(foundryEndpoint)) { diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/FunctionToolAgentProvider.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/FunctionToolAgentProvider.cs index 4ac24c440a..8198618b65 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/FunctionToolAgentProvider.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/FunctionToolAgentProvider.cs @@ -9,6 +9,7 @@ using Microsoft.Extensions.Configuration; using OpenAI.Responses; using Shared.Foundry; +using Shared.IntegrationTests; namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Agents; @@ -36,7 +37,7 @@ await aiProjectClient.CreateAgentAsync( private PromptAgentDefinition DefineMenuAgent(AIFunction[] functions) { PromptAgentDefinition agentDefinition = - new(this.GetSetting(Settings.FoundryModelMini)) + new(this.GetSetting(TestSettings.AzureAIModelDeploymentName)) { Instructions = """ diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/MarketingAgentProvider.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/MarketingAgentProvider.cs index a983794759..f84a40ae23 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/MarketingAgentProvider.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/MarketingAgentProvider.cs @@ -7,6 +7,7 @@ using Azure.Identity; using Microsoft.Extensions.Configuration; using Shared.Foundry; +using Shared.IntegrationTests; namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Agents; @@ -36,7 +37,7 @@ await aiProjectClient.CreateAgentAsync( } private PromptAgentDefinition DefineAnalystAgent() => - new(this.GetSetting(Settings.FoundryModelFull)) + new(this.GetSetting(TestSettings.AzureAIModelDeploymentName)) { Instructions = """ @@ -54,7 +55,7 @@ private PromptAgentDefinition DefineAnalystAgent() => }; private PromptAgentDefinition DefineWriterAgent() => - new(this.GetSetting(Settings.FoundryModelFull)) + new(this.GetSetting(TestSettings.AzureAIModelDeploymentName)) { Instructions = """ @@ -65,7 +66,7 @@ Output should be short (around 150 words), output just the copy as a single text }; private PromptAgentDefinition DefineEditorAgent() => - new(this.GetSetting(Settings.FoundryModelFull)) + new(this.GetSetting(TestSettings.AzureAIModelDeploymentName)) { Instructions = """ diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/MathChatAgentProvider.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/MathChatAgentProvider.cs index 27cdca3515..92cea7d76a 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/MathChatAgentProvider.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/MathChatAgentProvider.cs @@ -7,6 +7,7 @@ using Azure.Identity; using Microsoft.Extensions.Configuration; using Shared.Foundry; +using Shared.IntegrationTests; namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Agents; @@ -30,7 +31,7 @@ await aiProjectClient.CreateAgentAsync( } private PromptAgentDefinition DefineStudentAgent() => - new(this.GetSetting(Settings.FoundryModelMini)) + new(this.GetSetting(TestSettings.AzureAIModelDeploymentName)) { Instructions = """ @@ -42,7 +43,7 @@ You have the math-skills of a 6th grader. }; private PromptAgentDefinition DefineTeacherAgent() => - new(this.GetSetting(Settings.FoundryModelMini)) + new(this.GetSetting(TestSettings.AzureAIModelDeploymentName)) { Instructions = """ diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/PoemAgentProvider.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/PoemAgentProvider.cs index 9706c6227c..8882709a03 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/PoemAgentProvider.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/PoemAgentProvider.cs @@ -7,6 +7,7 @@ using Azure.Identity; using Microsoft.Extensions.Configuration; using Shared.Foundry; +using Shared.IntegrationTests; namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Agents; @@ -24,7 +25,7 @@ await aiProjectClient.CreateAgentAsync( } private PromptAgentDefinition DefinePoemAgent() => - new(this.GetSetting(Settings.FoundryModelMini)) + new(this.GetSetting(TestSettings.AzureAIModelDeploymentName)) { Instructions = """ diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/TestAgentProvider.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/TestAgentProvider.cs index 6cff2c15a0..03b201d440 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/TestAgentProvider.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/TestAgentProvider.cs @@ -7,6 +7,7 @@ using Azure.Identity; using Microsoft.Extensions.Configuration; using Shared.Foundry; +using Shared.IntegrationTests; namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Agents; @@ -24,5 +25,5 @@ await aiProjectClient.CreateAgentAsync( } private PromptAgentDefinition DefineMenuAgent() => - new(this.GetSetting(Settings.FoundryModelFull)); + new(this.GetSetting(TestSettings.AzureAIModelDeploymentName)); } diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/VisionAgentProvider.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/VisionAgentProvider.cs index d9557bd8f9..1c09ea9247 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/VisionAgentProvider.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/VisionAgentProvider.cs @@ -7,6 +7,7 @@ using Azure.Identity; using Microsoft.Extensions.Configuration; using Shared.Foundry; +using Shared.IntegrationTests; namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Agents; @@ -24,7 +25,7 @@ await aiProjectClient.CreateAgentAsync( } private PromptAgentDefinition DefineVisionAgent() => - new(this.GetSetting(Settings.FoundryModelFull)) + new(this.GetSetting(TestSettings.AzureAIModelDeploymentName)) { Instructions = """ diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/IntegrationTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/IntegrationTest.cs index f5b56fac49..59f2dd51ed 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/IntegrationTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/IntegrationTest.cs @@ -5,11 +5,11 @@ using System.Reflection; using System.Threading.Tasks; using Azure.Identity; -using Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Agents; using Microsoft.Agents.AI.Workflows.Declarative.PowerFx; using Microsoft.Agents.ObjectModel; using Microsoft.Extensions.AI; using Microsoft.Extensions.Configuration; +using Shared.IntegrationTests; namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Framework; @@ -29,8 +29,8 @@ protected IntegrationTest(ITestOutputHelper output) this.Output = new TestOutputAdapter(output); this.TestEndpoint = new Uri( - this.Configuration?[AgentProvider.Settings.FoundryEndpoint] ?? - throw new InvalidOperationException($"Undefined configuration setting: {AgentProvider.Settings.FoundryEndpoint}")); + this.Configuration?[TestSettings.AzureAIProjectEndpoint] ?? + throw new InvalidOperationException($"Undefined configuration setting: {TestSettings.AzureAIProjectEndpoint}")); Console.SetOut(this.Output); SetProduct(); } @@ -60,6 +60,11 @@ protected static void SetProduct() internal static string FormatVariablePath(string variableName, string? scope = null) => $"{scope ?? WorkflowFormulaState.DefaultScopeName}.{variableName}"; protected async ValueTask CreateOptionsAsync(bool externalConversation = false, params IEnumerable functionTools) + { + return await this.CreateOptionsAsync(externalConversation, mcpToolProvider: null, functionTools).ConfigureAwait(false); + } + + protected async ValueTask CreateOptionsAsync(bool externalConversation, IMcpToolHandler? mcpToolProvider, params IEnumerable functionTools) { AzureAgentProvider agentProvider = new(this.TestEndpoint, new AzureCliCredential()) @@ -77,7 +82,8 @@ protected async ValueTask CreateOptionsAsync(bool ex new DeclarativeWorkflowOptions(agentProvider) { ConversationId = conversationId, - LoggerFactory = this.Output + LoggerFactory = this.Output, + McpToolHandler = mcpToolProvider }; } diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/InvokeFunctionToolWorkflowTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/InvokeToolWorkflowTest.cs similarity index 51% rename from dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/InvokeFunctionToolWorkflowTest.cs rename to dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/InvokeToolWorkflowTest.cs index 2c5ccbe010..9d5efa6b6d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/InvokeFunctionToolWorkflowTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/InvokeToolWorkflowTest.cs @@ -10,30 +10,47 @@ using Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Agents; using Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Framework; using Microsoft.Agents.AI.Workflows.Declarative.Kit; +using Microsoft.Agents.AI.Workflows.Declarative.Mcp; using Microsoft.Extensions.AI; namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests; /// -/// Integration tests for InvokeFunctionTool action. -/// This test pattern can be extended for other InvokeTool types. +/// Integration tests for InvokeFunctionTool and InvokeMcpTool actions. /// -public sealed class InvokeFunctionToolWorkflowTest(ITestOutputHelper output) : IntegrationTest(output) +public sealed class InvokeToolWorkflowTest(ITestOutputHelper output) : IntegrationTest(output) { + #region InvokeFunctionTool Tests + [Theory] [InlineData("InvokeFunctionTool.yaml", new string[] { "GetSpecials", "GetItemPrice" }, "2.95")] [InlineData("InvokeFunctionToolWithApproval.yaml", new string[] { "GetItemPrice" }, "4.9")] public Task ValidateInvokeFunctionToolAsync(string workflowFileName, string[] expectedFunctionCalls, string? expectedResultContains) => - this.RunInvokeToolTestAsync(workflowFileName, expectedFunctionCalls, expectedResultContains); + this.RunInvokeFunctionToolTestAsync(workflowFileName, expectedFunctionCalls, expectedResultContains); + + #endregion + + #region InvokeMcpTool Tests + + [Theory] + [InlineData("InvokeMcpTool.yaml", "Azure OpenAI")] + public Task ValidateInvokeMcpToolAsync(string workflowFileName, string? expectedResultContains) => + this.RunInvokeMcpToolTestAsync(workflowFileName, expectedResultContains, requireApproval: false); + + [Theory] + [InlineData("InvokeMcpToolWithApproval.yaml", "Azure OpenAI", true)] + [InlineData("InvokeMcpToolWithApproval.yaml", "MCP tool invocation was not approved by user", false)] + public Task ValidateInvokeMcpToolWithApprovalAsync(string workflowFileName, string? expectedResultContains, bool approveRequest) => + this.RunInvokeMcpToolTestAsync(workflowFileName, expectedResultContains, requireApproval: true, approveRequest: approveRequest); + + #endregion + + #region InvokeFunctionTool Test Helpers /// - /// Runs an InvokeTool workflow test with the specified configuration. - /// This method is designed to be generic and reusable for different InvokeTool types. + /// Runs an InvokeFunctionTool workflow test with the specified configuration. /// - /// The workflow YAML file name. - /// Expected function names to be called in order. - /// Expected text to be present in the final result. - private async Task RunInvokeToolTestAsync( + private async Task RunInvokeFunctionToolTestAsync( string workflowFileName, string[] expectedFunctionCalls, string? expectedResultContains = null) @@ -71,7 +88,6 @@ private async Task RunInvokeToolTestAsync( // Continue processing until there are no more pending input events from the resumed workflow if (resumeEvents.InputEvents.Count == 0) { - // No more input events from the last resume - workflow completed break; } } @@ -84,19 +100,12 @@ private async Task RunInvokeToolTestAsync( } // Assert - Verify executor and action events - Assert.NotEmpty(workflowEvents.ExecutorInvokeEvents); - Assert.NotEmpty(workflowEvents.ExecutorCompleteEvents); - Assert.NotEmpty(workflowEvents.ActionInvokeEvents); + AssertWorkflowEventsEmitted(workflowEvents); // Assert - Verify expected result if specified if (expectedResultContains is not null) { - MessageActivityEvent? messageEvent = workflowEvents.Events - .OfType() - .LastOrDefault(); - - Assert.NotNull(messageEvent); - Assert.Contains(expectedResultContains, messageEvent.Message, StringComparison.OrdinalIgnoreCase); + AssertResultContains(workflowEvents, expectedResultContains); } } @@ -149,6 +158,119 @@ private async Task> ProcessFunctionCallsAsync( return results; } + #endregion + + #region InvokeMcpTool Test Helpers + + /// + /// Runs an InvokeMcpTool workflow test with the specified configuration. + /// + private async Task RunInvokeMcpToolTestAsync( + string workflowFileName, + string? expectedResultContains = null, + bool requireApproval = false, + bool approveRequest = true) + { + // Arrange + string workflowPath = GetWorkflowPath(workflowFileName); + DefaultMcpToolHandler mcpToolProvider = new(); + DeclarativeWorkflowOptions workflowOptions = await this.CreateOptionsAsync( + externalConversation: false, + mcpToolProvider: mcpToolProvider); + + Workflow workflow = DeclarativeWorkflowBuilder.Build(workflowPath, workflowOptions); + WorkflowHarness harness = new(workflow, runId: Path.GetFileNameWithoutExtension(workflowPath)); + + // Act - Run workflow and handle MCP tool invocations + WorkflowEvents workflowEvents = await harness.RunWorkflowAsync("start").ConfigureAwait(false); + + while (workflowEvents.InputEvents.Count > 0) + { + RequestInfoEvent inputEvent = workflowEvents.InputEvents[^1]; + ExternalInputRequest? toolRequest = inputEvent.Request.Data.As(); + Assert.NotNull(toolRequest); + + IList mcpResults = this.ProcessMcpToolRequests( + toolRequest, + approveRequest); + + ChatMessage resultMessage = new(ChatRole.Tool, mcpResults); + WorkflowEvents resumeEvents = await harness.ResumeAsync( + inputEvent.Request.CreateResponse(new ExternalInputResponse(resultMessage))).ConfigureAwait(false); + + workflowEvents = new WorkflowEvents([.. workflowEvents.Events, .. resumeEvents.Events]); + + // Continue processing until there are no more pending input events from the resumed workflow + if (resumeEvents.InputEvents.Count == 0) + { + break; + } + } + + // Assert - Verify executor and action events + AssertWorkflowEventsEmitted(workflowEvents); + + // Assert - Verify expected result if specified + if (expectedResultContains is not null) + { + AssertResultContains(workflowEvents, expectedResultContains); + } + + // Cleanup + await mcpToolProvider.DisposeAsync().ConfigureAwait(false); + } + + /// + /// Processes MCP tool requests from an external input request. + /// Handles approval requests for MCP tools. + /// + private List ProcessMcpToolRequests( + ExternalInputRequest toolRequest, + bool approveRequest) + { + List results = []; + + foreach (ChatMessage message in toolRequest.AgentResponse.Messages) + { + // Handle MCP approval requests if present + foreach (McpServerToolApprovalRequestContent approvalRequest in message.Contents.OfType()) + { + this.Output.WriteLine($"MCP APPROVAL REQUEST: {approvalRequest.Id}"); + + // Respond based on test configuration + McpServerToolApprovalResponseContent response = approvalRequest.CreateResponse(approved: approveRequest); + results.Add(response); + + this.Output.WriteLine($"MCP APPROVAL RESPONSE: {(approveRequest ? "Approved" : "Rejected")}"); + } + } + + return results; + } + + #endregion + + #region Shared Helpers + + private static void AssertWorkflowEventsEmitted(WorkflowEvents workflowEvents) + { + Assert.NotEmpty(workflowEvents.ExecutorInvokeEvents); + Assert.NotEmpty(workflowEvents.ExecutorCompleteEvents); + Assert.NotEmpty(workflowEvents.ActionInvokeEvents); + } + + private static void AssertResultContains(WorkflowEvents workflowEvents, string expectedResultContains) + { + MessageActivityEvent? messageEvent = workflowEvents.Events + .OfType() + .LastOrDefault(); + + Assert.NotNull(messageEvent); + Assert.Contains(expectedResultContains, messageEvent.Message, StringComparison.OrdinalIgnoreCase); + } + private static string GetWorkflowPath(string workflowFileName) => Path.Combine(Environment.CurrentDirectory, "Workflows", workflowFileName); + + #endregion } diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.csproj b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.csproj index 309a590b83..92e09fcebb 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.csproj +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.csproj @@ -10,13 +10,13 @@ + - diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Workflows/InvokeMcpTool.yaml b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Workflows/InvokeMcpTool.yaml new file mode 100644 index 0000000000..ff30f7902e --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Workflows/InvokeMcpTool.yaml @@ -0,0 +1,35 @@ +# +# This workflow tests invoking MCP tools directly from a workflow. +# Uses the Microsoft Learn MCP server: search tool +# +kind: Workflow +trigger: + + kind: OnConversationStart + id: workflow_invoke_mcp_tool_test + actions: + + # Set the search query we want to use + - kind: SetVariable + id: set_search_query + variable: Local.SearchQuery + value: Azure OpenAI + + # Invoke MCP search tool on Microsoft Learn server + - kind: InvokeMcpTool + id: invoke_mcp_search + serverUrl: https://learn.microsoft.com/api/mcp + serverLabel: microsoft_docs + toolName: microsoft_docs_search + conversationId: =System.ConversationId + arguments: + query: =Local.SearchQuery + output: + autoSend: true + result: Local.SearchResult + + # Send the result as an activity + - kind: SendMessage + id: show_search_result + message: "Search results: {Local.SearchResult}" + # message: "Search results for {Local.SearchQuery}: {Local.SearchResult}" diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Workflows/InvokeMcpToolWithApproval.yaml b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Workflows/InvokeMcpToolWithApproval.yaml new file mode 100644 index 0000000000..77d62c79ce --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Workflows/InvokeMcpToolWithApproval.yaml @@ -0,0 +1,35 @@ +# +# This workflow tests invoking MCP tools with approval requirement. +# Uses the Microsoft Learn MCP server: search tool with requireApproval: true +# +kind: Workflow +trigger: + + kind: OnConversationStart + id: workflow_invoke_mcp_tool_approval_test + actions: + + # Set the search query we want to use + - kind: SetVariable + id: set_search_query + variable: Local.ContentUrl + value: https://learn.microsoft.com/azure/ai-foundry/openai/concepts/use-your-data + + # Invoke MCP search tool with approval requirement + - kind: InvokeMcpTool + id: invoke_mcp_search + serverUrl: https://learn.microsoft.com/api/mcp + serverLabel: MicrosoftLearn + toolName: microsoft_docs_fetch + requireApproval: true + arguments: + url: =Local.ContentUrl + output: + autoSend: false + result: Local.FetchResult + messages: Local.FetchMessages + + # Send the result as an activity + - kind: SendMessage + id: show_search_result + message: "Content for {Local.ContentUrl}: {Local.FetchResult}" diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests/DefaultMcpToolHandlerTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests/DefaultMcpToolHandlerTests.cs new file mode 100644 index 0000000000..858ea9db14 --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests/DefaultMcpToolHandlerTests.cs @@ -0,0 +1,345 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using FluentAssertions; + +namespace Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests; + +/// +/// Unit tests for . +/// +public sealed class DefaultMcpToolHandlerTests +{ + #region Constructor Tests + + [Fact] + public async Task Constructor_WithNoParameters_ShouldCreateInstanceAsync() + { + // Act + DefaultMcpToolHandler handler = new(); + + // Assert + handler.Should().NotBeNull(); + await handler.DisposeAsync(); + } + + [Fact] + public async Task Constructor_WithNullHttpClientProvider_ShouldCreateInstanceAsync() + { + // Act + DefaultMcpToolHandler handler = new(httpClientProvider: null); + + // Assert + handler.Should().NotBeNull(); + await handler.DisposeAsync(); + } + + [Fact] + public async Task Constructor_WithHttpClientProvider_ShouldCreateInstanceAsync() + { + // Arrange + static Task ProviderAsync(string url, CancellationToken ct) => Task.FromResult(new HttpClient()); + + // Act + DefaultMcpToolHandler handler = new(httpClientProvider: ProviderAsync); + + // Assert + handler.Should().NotBeNull(); + await handler.DisposeAsync(); + } + + #endregion + + #region DisposeAsync Tests + + [Fact] + public async Task DisposeAsync_WhenCalled_ShouldCompleteWithoutErrorAsync() + { + // Arrange + DefaultMcpToolHandler handler = new(); + + // Act + Func act = async () => await handler.DisposeAsync(); + + // Assert + await act.Should().NotThrowAsync(); + } + + [Fact] + public async Task DisposeAsync_WhenCalledMultipleTimes_ShouldHandleGracefullyAsync() + { + // Arrange + DefaultMcpToolHandler handler = new(); + + // Act + await handler.DisposeAsync(); + Func act = async () => await handler.DisposeAsync(); + + // Assert - Second dispose should throw ObjectDisposedException from the semaphore + await act.Should().ThrowAsync(); + } + + #endregion + + #region HttpClientProvider Tests + + [Fact] + public async Task InvokeToolAsync_WithHttpClientProvider_ShouldCallProviderAsync() + { + // Arrange + bool providerCalled = false; + string? capturedServerUrl = null; + + Task ProviderAsync(string url, CancellationToken ct) + { + providerCalled = true; + capturedServerUrl = url; + return Task.FromResult(null); + } + + DefaultMcpToolHandler handler = new(httpClientProvider: ProviderAsync); + + // Act & Assert - The call will fail because there's no real MCP server, but the provider should be called + try + { + await handler.InvokeToolAsync( + serverUrl: "http://localhost:12345/mcp", + serverLabel: "test", + toolName: "testTool", + arguments: null, + headers: null, + connectionName: null); + } + catch + { + // Expected to fail - no real server + } + finally + { + await handler.DisposeAsync(); + } + + // Assert + providerCalled.Should().BeTrue(); + capturedServerUrl.Should().Be("http://localhost:12345/mcp"); + } + + [Fact] + public async Task InvokeToolAsync_WithHttpClientProviderReturningClient_ShouldUseProvidedClientAsync() + { + // Arrange + bool providerCalled = false; + HttpClient? providedClient = null; + + Task ProviderAsync(string url, CancellationToken ct) + { + providerCalled = true; + providedClient = new HttpClient(); + return Task.FromResult(providedClient); + } + + DefaultMcpToolHandler handler = new(httpClientProvider: ProviderAsync); + + // Act & Assert - The call will fail because there's no real MCP server, but the provider should be called + try + { + await handler.InvokeToolAsync( + serverUrl: "http://localhost:12345/mcp", + serverLabel: "test", + toolName: "testTool", + arguments: null, + headers: null, + connectionName: null); + } + catch + { + // Expected to fail - no real server + } + finally + { + await handler.DisposeAsync(); + providedClient?.Dispose(); + } + + // Assert + providerCalled.Should().BeTrue(); + } + + #endregion + + #region Caching Tests + + [Fact] + public async Task InvokeToolAsync_SameServerUrl_ShouldCallProviderOncePerAttemptWhenConnectionFailsAsync() + { + // Arrange + int providerCallCount = 0; + + Task ProviderAsync(string url, CancellationToken ct) + { + providerCallCount++; + return Task.FromResult(null); + } + + DefaultMcpToolHandler handler = new(httpClientProvider: ProviderAsync); + const string ServerUrl = "http://localhost:12345/mcp"; + + try + { + // Act - Call twice with the same server URL + // Since there's no real server, the McpClient.CreateAsync will fail, + // so the client won't be cached and the provider will be called each time + for (int i = 0; i < 2; i++) + { + try + { + await handler.InvokeToolAsync( + serverUrl: ServerUrl, + serverLabel: "test", + toolName: "testTool", + arguments: null, + headers: null, + connectionName: null); + } + catch + { + // Expected to fail - no real server + } + } + + // Assert - Provider is called each time because McpClient creation fails before caching + providerCallCount.Should().Be(2); + } + finally + { + await handler.DisposeAsync(); + } + } + + [Fact] + public async Task InvokeToolAsync_DifferentServerUrls_ShouldCreateSeparateClientsAsync() + { + // Arrange + int providerCallCount = 0; + + Task ProviderAsync(string url, CancellationToken ct) + { + providerCallCount++; + return Task.FromResult(null); + } + + DefaultMcpToolHandler handler = new(httpClientProvider: ProviderAsync); + + try + { + // Act - Call with different server URLs + foreach (string serverUrl in new[] { "http://localhost:12345/mcp1", "http://localhost:12345/mcp2" }) + { + try + { + await handler.InvokeToolAsync( + serverUrl: serverUrl, + serverLabel: "test", + toolName: "testTool", + arguments: null, + headers: null, + connectionName: null); + } + catch + { + // Expected to fail - no real server + } + } + + // Assert - Provider should be called once per unique server URL + providerCallCount.Should().Be(2); + } + finally + { + await handler.DisposeAsync(); + } + } + + [Fact] + public async Task InvokeToolAsync_SameUrlDifferentHeaders_ShouldCreateSeparateClientsAsync() + { + // Arrange + int providerCallCount = 0; + + Task ProviderAsync(string url, CancellationToken ct) + { + providerCallCount++; + return Task.FromResult(null); + } + + DefaultMcpToolHandler handler = new(httpClientProvider: ProviderAsync); + const string ServerUrl = "http://localhost:12345/mcp"; + + try + { + // Act - Call with same URL but different headers + Dictionary[] headerSets = + [ + new() { ["Authorization"] = "Bearer token1" }, + new() { ["Authorization"] = "Bearer token2" } + ]; + + foreach (Dictionary headers in headerSets) + { + try + { + await handler.InvokeToolAsync( + serverUrl: ServerUrl, + serverLabel: "test", + toolName: "testTool", + arguments: null, + headers: headers, + connectionName: null); + } + catch + { + // Expected to fail - no real server + } + } + + // Assert - Different headers should create different cache keys + providerCallCount.Should().Be(2); + } + finally + { + await handler.DisposeAsync(); + } + } + + #endregion + + #region Interface Implementation Tests + + [Fact] + public async Task DefaultMcpToolHandler_ShouldImplementIMcpToolHandlerAsync() + { + // Arrange & Act + DefaultMcpToolHandler handler = new(); + + // Assert + handler.Should().BeAssignableTo(); + await handler.DisposeAsync(); + } + + [Fact] + public async Task DefaultMcpToolHandler_ShouldImplementIAsyncDisposableAsync() + { + // Arrange & Act + DefaultMcpToolHandler handler = new(); + + // Assert + handler.Should().BeAssignableTo(); + await handler.DisposeAsync(); + } + + #endregion +} diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests/Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests.csproj b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests/Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests.csproj new file mode 100644 index 0000000000..057e2cd950 --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests/Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests.csproj @@ -0,0 +1,15 @@ + + + + true + + + + + + + + + + + diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Extensions/JsonDocumentExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Extensions/JsonDocumentExtensionsTests.cs index fc03d17142..6ace20bebb 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Extensions/JsonDocumentExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Extensions/JsonDocumentExtensionsTests.cs @@ -459,4 +459,208 @@ public void ParseList_ArrayOfObjects_NoSchema_PreservesProperties() Assert.Equal("Bob", second["name"]); Assert.Equal("Designer", second["role"]); } + + [Fact] + public void GetListTypeFromJson_EmptyArray_ReturnsFallbackListType() + { + // Arrange + JsonDocument document = JsonDocument.Parse("[]"); + + // Act + VariableType result = document.RootElement.GetListTypeFromJson(); + + // Assert + Assert.Equal(VariableType.ListType, result.Type); + Assert.False(result.HasSchema); + } + + [Fact] + public void GetListTypeFromJson_ArrayOfPrimitives_ReturnsFallbackListType() + { + // Arrange + JsonDocument document = JsonDocument.Parse("[1, 2, 3]"); + + // Act + VariableType result = document.RootElement.GetListTypeFromJson(); + + // Assert + Assert.Equal(VariableType.ListType, result.Type); + Assert.False(result.HasSchema); + } + + [Fact] + public void GetListTypeFromJson_ObjectWithStringField_InfersStringType() + { + // Arrange + JsonDocument document = JsonDocument.Parse( + """ + [{ "name": "hello" }] + """); + + // Act + VariableType result = document.RootElement.GetListTypeFromJson(); + + // Assert + Assert.True(result.HasSchema); + Assert.True(result.Schema!.ContainsKey("name")); + Assert.Equal(typeof(string), result.Schema["name"].Type); + } + + [Fact] + public void GetListTypeFromJson_ObjectWithNumberField_InfersDecimalType() + { + // Arrange + JsonDocument document = JsonDocument.Parse( + """ + [{ "value": 42 }] + """); + + // Act + VariableType result = document.RootElement.GetListTypeFromJson(); + + // Assert + Assert.True(result.HasSchema); + Assert.True(result.Schema!.ContainsKey("value")); + Assert.Equal(typeof(decimal), result.Schema["value"].Type); + } + + [Fact] + public void GetListTypeFromJson_ObjectWithBooleanTrueField_InfersBoolType() + { + // Arrange + JsonDocument document = JsonDocument.Parse( + """ + [{ "flag": true }] + """); + + // Act + VariableType result = document.RootElement.GetListTypeFromJson(); + + // Assert + Assert.True(result.HasSchema); + Assert.True(result.Schema!.ContainsKey("flag")); + Assert.Equal(typeof(bool), result.Schema["flag"].Type); + } + + [Fact] + public void GetListTypeFromJson_ObjectWithBooleanFalseField_InfersBoolType() + { + // Arrange + JsonDocument document = JsonDocument.Parse( + """ + [{ "flag": false }] + """); + + // Act + VariableType result = document.RootElement.GetListTypeFromJson(); + + // Assert + Assert.True(result.HasSchema); + Assert.True(result.Schema!.ContainsKey("flag")); + Assert.Equal(typeof(bool), result.Schema["flag"].Type); + } + + [Fact] + public void GetListTypeFromJson_ObjectWithNestedObjectField_InfersRecordType() + { + // Arrange + JsonDocument document = JsonDocument.Parse( + """ + [{ "child": { "inner": 1 } }] + """); + + // Act + VariableType result = document.RootElement.GetListTypeFromJson(); + + // Assert + Assert.True(result.HasSchema); + Assert.True(result.Schema!.ContainsKey("child")); + Assert.Equal(VariableType.RecordType, result.Schema["child"].Type); + } + + [Fact] + public void GetListTypeFromJson_ObjectWithNestedArrayField_InfersListType() + { + // Arrange + JsonDocument document = JsonDocument.Parse( + """ + [{ "items": [1, 2, 3] }] + """); + + // Act + VariableType result = document.RootElement.GetListTypeFromJson(); + + // Assert + Assert.True(result.HasSchema); + Assert.True(result.Schema!.ContainsKey("items")); + Assert.Equal(VariableType.ListType, result.Schema["items"].Type); + } + + [Fact] + public void GetListTypeFromJson_ObjectWithNullField_InfersStringTypeDefault() + { + // Arrange + JsonDocument document = JsonDocument.Parse( + """ + [{ "missing": null }] + """); + + // Act + VariableType result = document.RootElement.GetListTypeFromJson(); + + // Assert + Assert.True(result.HasSchema); + Assert.True(result.Schema!.ContainsKey("missing")); + Assert.Equal(typeof(string), result.Schema["missing"].Type); + } + + [Fact] + public void GetListTypeFromJson_SkipsNonObjectElements_InfersFromFirstObject() + { + // Arrange + JsonDocument document = JsonDocument.Parse( + """ + [1, "text", { "id": 99 }] + """); + + // Act + VariableType result = document.RootElement.GetListTypeFromJson(); + + // Assert + Assert.True(result.HasSchema); + Assert.True(result.Schema!.ContainsKey("id")); + Assert.Equal(typeof(decimal), result.Schema["id"].Type); + } + + [Fact] + public void GetListTypeFromJson_ObjectWithAllFieldTypes_InfersCorrectTypes() + { + // Arrange + JsonDocument document = JsonDocument.Parse( + """ + [{ + "text": "hello", + "count": 5, + "enabled": true, + "disabled": false, + "nested": { "x": 1 }, + "list": [1, 2], + "empty": null + }] + """); + + // Act + VariableType result = document.RootElement.GetListTypeFromJson(); + + // Assert + Assert.True(result.HasSchema); + Assert.Equal(7, result.Schema!.Count); + Assert.Equal(typeof(string), result.Schema["text"].Type); + Assert.Equal(typeof(decimal), result.Schema["count"].Type); + Assert.Equal(typeof(bool), result.Schema["enabled"].Type); + Assert.Equal(typeof(bool), result.Schema["disabled"].Type); + Assert.Equal(VariableType.RecordType, result.Schema["nested"].Type); + Assert.Equal(VariableType.ListType, result.Schema["list"].Type); + Assert.Equal(typeof(string), result.Schema["empty"].Type); + } } diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/InvokeMcpToolExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/InvokeMcpToolExecutorTest.cs new file mode 100644 index 0000000000..45b0b3c7b7 --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/InvokeMcpToolExecutorTest.cs @@ -0,0 +1,844 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Agents.AI.Workflows.Declarative.Events; +using Microsoft.Agents.AI.Workflows.Declarative.Kit; +using Microsoft.Agents.AI.Workflows.Declarative.ObjectModel; +using Microsoft.Agents.AI.Workflows.Declarative.PowerFx; +using Microsoft.Agents.ObjectModel; +using Microsoft.Extensions.AI; +using Moq; + +namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.ObjectModel; + +/// +/// Tests for . +/// +public sealed class InvokeMcpToolExecutorTest(ITestOutputHelper output) : WorkflowActionExecutorTest(output) +{ + private const string TestServerUrl = "https://mcp.example.com"; + private const string TestServerLabel = "TestMcpServer"; + private const string TestToolName = "test_tool"; + + #region Step Naming Convention Tests + + [Fact] + public void InvokeMcpToolThrowsWhenModelInvalid() + { + // Arrange + Mock mockProvider = new(); + MockAgentProvider mockAgentProvider = new(); + + // Act & Assert + Assert.Throws(() => new InvokeMcpToolExecutor( + new InvokeMcpTool(), + mockProvider.Object, + mockAgentProvider.Object, + this.State)); + } + + [Fact] + public void InvokeMcpToolNamingConvention() + { + // Arrange + string testId = this.CreateActionId().Value; + + // Act + string externalInputStep = InvokeMcpToolExecutor.Steps.ExternalInput(testId); + string resumeStep = InvokeMcpToolExecutor.Steps.Resume(testId); + + // Assert + Assert.Equal($"{testId}_{nameof(InvokeMcpToolExecutor.Steps.ExternalInput)}", externalInputStep); + Assert.Equal($"{testId}_{nameof(InvokeMcpToolExecutor.Steps.Resume)}", resumeStep); + } + + #endregion + + #region RequiresInput and RequiresNothing Tests + + [Fact] + public void RequiresInputReturnsTrueForExternalInputRequest() + { + // Arrange + ExternalInputRequest request = new(new AgentResponse([])); + + // Act + bool result = InvokeMcpToolExecutor.RequiresInput(request); + + // Assert + Assert.True(result); + } + + [Fact] + public void RequiresInputReturnsFalseForOtherTypes() + { + // Act & Assert + Assert.False(InvokeMcpToolExecutor.RequiresInput(null)); + Assert.False(InvokeMcpToolExecutor.RequiresInput("string")); + Assert.False(InvokeMcpToolExecutor.RequiresInput(new ActionExecutorResult("test"))); + } + + [Fact] + public void RequiresNothingReturnsTrueForActionExecutorResult() + { + // Arrange + ActionExecutorResult result = new("test"); + + // Act + bool requiresNothing = InvokeMcpToolExecutor.RequiresNothing(result); + + // Assert + Assert.True(requiresNothing); + } + + [Fact] + public void RequiresNothingReturnsFalseForOtherTypes() + { + // Act & Assert + Assert.False(InvokeMcpToolExecutor.RequiresNothing(null)); + Assert.False(InvokeMcpToolExecutor.RequiresNothing("string")); + Assert.False(InvokeMcpToolExecutor.RequiresNothing(new ExternalInputRequest(new AgentResponse([])))); + } + + #endregion + + #region ExecuteAsync Tests + + [Fact] + public async Task InvokeMcpToolExecuteWithoutApprovalAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithoutApprovalAsync), + serverUrl: TestServerUrl, + toolName: TestToolName, + requireApproval: false); + + // Act and Assert + await this.ExecuteTestAsync(model); + } + + [Fact] + public async Task InvokeMcpToolExecuteWithServerLabelAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithServerLabelAsync), + serverUrl: TestServerUrl, + serverLabel: TestServerLabel, + toolName: TestToolName); + + // Act and Assert + await this.ExecuteTestAsync(model); + } + + [Fact] + public async Task InvokeMcpToolExecuteWithArgumentsAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithArgumentsAsync), + serverUrl: TestServerUrl, + toolName: TestToolName, + argumentKey: "query", + argumentValue: "test query"); + + // Act and Assert + await this.ExecuteTestAsync(model); + } + + [Fact] + public async Task InvokeMcpToolExecuteWithHeadersAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithHeadersAsync), + serverUrl: TestServerUrl, + toolName: TestToolName, + headerKey: "Authorization", + headerValue: "Bearer token123"); + + // Act and Assert + await this.ExecuteTestAsync(model); + } + + [Fact] + public async Task InvokeMcpToolExecuteWithRequireApprovalAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithRequireApprovalAsync), + serverUrl: TestServerUrl, + toolName: TestToolName, + requireApproval: true); + + // Act and Assert + await this.ExecuteTestAsync(model); + } + + [Fact] + public async Task InvokeMcpToolExecuteWithEmptyConversationIdAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithEmptyConversationIdAsync), + serverUrl: TestServerUrl, + toolName: TestToolName, + conversationId: ""); + + // Act and Assert + await this.ExecuteTestAsync(model); + } + + [Fact] + public async Task InvokeMcpToolExecuteWithNullArgumentsAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithNullArgumentsAsync), + serverUrl: TestServerUrl, + toolName: TestToolName, + argumentKey: null); + + // Act and Assert + await this.ExecuteTestAsync(model); + } + + [Fact] + public async Task InvokeMcpToolExecuteWithNullRequireApprovalAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithNullRequireApprovalAsync), + serverUrl: TestServerUrl, + toolName: TestToolName, + requireApproval: null); + + // Act and Assert + await this.ExecuteTestAsync(model); + } + + [Fact] + public async Task InvokeMcpToolExecuteWithNullConversationIdAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithNullConversationIdAsync), + serverUrl: TestServerUrl, + toolName: TestToolName, + conversationId: null); + + // Act and Assert + await this.ExecuteTestAsync(model); + } + + [Fact] + public async Task InvokeMcpToolExecuteWithEmptyServerLabelAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithEmptyServerLabelAsync), + serverUrl: TestServerUrl, + serverLabel: "", + toolName: TestToolName); + + // Act and Assert + await this.ExecuteTestAsync(model); + } + + [Fact] + public async Task InvokeMcpToolExecuteWithConversationIdAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithConversationIdAsync), + serverUrl: TestServerUrl, + toolName: TestToolName, + conversationId: "test-conversation-id"); + + // Act and Assert + await this.ExecuteTestAsync(model); + } + + [Fact] + public async Task InvokeMcpToolExecuteWithRequireApprovalAndHeadersAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithRequireApprovalAndHeadersAsync), + serverUrl: TestServerUrl, + toolName: TestToolName, + requireApproval: true, + headerKey: "X-Custom-Header", + headerValue: "custom-value"); + + // Act and Assert + await this.ExecuteTestAsync(model); + } + + [Fact] + public async Task InvokeMcpToolExecuteWithEmptyHeaderValueAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithEmptyHeaderValueAsync), + serverUrl: TestServerUrl, + toolName: TestToolName, + headerKey: "X-Empty-Header", + headerValue: ""); + + // Act and Assert + await this.ExecuteTestAsync(model); + } + + [Fact] + public async Task InvokeMcpToolExecuteWithJsonObjectResultAsync() + { + // Arrange - Tests JSON object parsing in AssignResultAsync + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithJsonObjectResultAsync), + serverUrl: TestServerUrl, + toolName: TestToolName); + MockMcpToolProvider mockProvider = new(returnJsonObject: true); + MockAgentProvider mockAgentProvider = new(); + InvokeMcpToolExecutor action = new(model, mockProvider.Object, mockAgentProvider.Object, this.State); + + // Act + WorkflowEvent[] events = await this.ExecuteAsync(action, isDiscrete: false); + + // Assert + VerifyModel(model, action); + VerifyInvocationEvent(events); + } + + [Fact] + public async Task InvokeMcpToolExecuteWithJsonArrayResultAsync() + { + // Arrange - Tests JSON array parsing in AssignResultAsync + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithJsonArrayResultAsync), + serverUrl: TestServerUrl, + toolName: TestToolName); + MockMcpToolProvider mockProvider = new(returnJsonArray: true); + MockAgentProvider mockAgentProvider = new(); + InvokeMcpToolExecutor action = new(model, mockProvider.Object, mockAgentProvider.Object, this.State); + + // Act + WorkflowEvent[] events = await this.ExecuteAsync(action, isDiscrete: false); + + // Assert + VerifyModel(model, action); + VerifyInvocationEvent(events); + } + + [Fact] + public async Task InvokeMcpToolExecuteWithInvalidJsonResultAsync() + { + // Arrange - Tests graceful handling of invalid JSON + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithInvalidJsonResultAsync), + serverUrl: TestServerUrl, + toolName: TestToolName); + MockMcpToolProvider mockProvider = new(returnInvalidJson: true); + MockAgentProvider mockAgentProvider = new(); + InvokeMcpToolExecutor action = new(model, mockProvider.Object, mockAgentProvider.Object, this.State); + + // Act + WorkflowEvent[] events = await this.ExecuteAsync(action, isDiscrete: false); + + // Assert - Should handle gracefully + VerifyModel(model, action); + VerifyInvocationEvent(events); + } + + [Fact] + public async Task InvokeMcpToolExecuteWithDataContentResultAsync() + { + // Arrange - Tests DataContent handling (returns URI) + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithDataContentResultAsync), + serverUrl: TestServerUrl, + toolName: TestToolName); + MockMcpToolProvider mockProvider = new(returnDataContent: true); + MockAgentProvider mockAgentProvider = new(); + InvokeMcpToolExecutor action = new(model, mockProvider.Object, mockAgentProvider.Object, this.State); + + // Act + WorkflowEvent[] events = await this.ExecuteAsync(action, isDiscrete: false); + + // Assert + VerifyModel(model, action); + VerifyInvocationEvent(events); + } + + [Fact] + public async Task InvokeMcpToolExecuteWithEmptyOutputAsync() + { + // Arrange - Tests empty output list handling + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithEmptyOutputAsync), + serverUrl: TestServerUrl, + toolName: TestToolName); + MockMcpToolProvider mockProvider = new(returnEmptyOutput: true); + MockAgentProvider mockAgentProvider = new(); + InvokeMcpToolExecutor action = new(model, mockProvider.Object, mockAgentProvider.Object, this.State); + + // Act + WorkflowEvent[] events = await this.ExecuteAsync(action, isDiscrete: false); + + // Assert + VerifyModel(model, action); + VerifyInvocationEvent(events); + } + + [Fact] + public async Task InvokeMcpToolExecuteWithNullOutputAsync() + { + // Arrange - Tests null output handling + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithNullOutputAsync), + serverUrl: TestServerUrl, + toolName: TestToolName); + MockMcpToolProvider mockProvider = new(returnNullOutput: true); + MockAgentProvider mockAgentProvider = new(); + InvokeMcpToolExecutor action = new(model, mockProvider.Object, mockAgentProvider.Object, this.State); + + // Act + WorkflowEvent[] events = await this.ExecuteAsync(action, isDiscrete: false); + + // Assert + VerifyModel(model, action); + VerifyInvocationEvent(events); + } + + [Fact] + public async Task InvokeMcpToolExecuteWithMultipleContentTypesAsync() + { + // Arrange - Tests handling of multiple content types in output + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolExecuteWithMultipleContentTypesAsync), + serverUrl: TestServerUrl, + toolName: TestToolName); + MockMcpToolProvider mockProvider = new(returnMultipleContent: true); + MockAgentProvider mockAgentProvider = new(); + InvokeMcpToolExecutor action = new(model, mockProvider.Object, mockAgentProvider.Object, this.State); + + // Act + WorkflowEvent[] events = await this.ExecuteAsync(action, isDiscrete: false); + + // Assert + VerifyModel(model, action); + VerifyInvocationEvent(events); + } + + #endregion + + #region CaptureResponseAsync Tests + + [Fact] + public async Task InvokeMcpToolCaptureResponseWithApprovalApprovedAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolCaptureResponseWithApprovalApprovedAsync), + serverUrl: TestServerUrl, + toolName: TestToolName, + requireApproval: true); + MockMcpToolProvider mockProvider = new(); + MockAgentProvider mockAgentProvider = new(); + InvokeMcpToolExecutor action = new(model, mockProvider.Object, mockAgentProvider.Object, this.State); + + // Create approval request then response + McpServerToolCallContent toolCall = new(action.Id, TestToolName, TestServerUrl); + McpServerToolApprovalRequestContent approvalRequest = new(action.Id, toolCall); + McpServerToolApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved: true); + ExternalInputResponse response = new(new ChatMessage(ChatRole.User, [approvalResponse])); + + // Act + WorkflowEvent[] events = await this.ExecuteCaptureResponseTestAsync(action, response); + + // Assert + VerifyModel(model, action); + Assert.NotEmpty(events); + } + + [Fact] + public async Task InvokeMcpToolCaptureResponseWithApprovalRejectedAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolCaptureResponseWithApprovalRejectedAsync), + serverUrl: TestServerUrl, + toolName: TestToolName, + requireApproval: true); + MockMcpToolProvider mockProvider = new(); + MockAgentProvider mockAgentProvider = new(); + InvokeMcpToolExecutor action = new(model, mockProvider.Object, mockAgentProvider.Object, this.State); + + // Create approval request then response (rejected) + McpServerToolCallContent toolCall = new(action.Id, TestToolName, TestServerUrl); + McpServerToolApprovalRequestContent approvalRequest = new(action.Id, toolCall); + McpServerToolApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved: false); + ExternalInputResponse response = new(new ChatMessage(ChatRole.User, [approvalResponse])); + + // Act + WorkflowEvent[] events = await this.ExecuteCaptureResponseTestAsync(action, response); + + // Assert + VerifyModel(model, action); + Assert.NotEmpty(events); + } + + [Fact] + public async Task InvokeMcpToolCaptureResponseWithEmptyMessagesAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolCaptureResponseWithEmptyMessagesAsync), + serverUrl: TestServerUrl, + toolName: TestToolName); + MockMcpToolProvider mockProvider = new(); + MockAgentProvider mockAgentProvider = new(); + InvokeMcpToolExecutor action = new(model, mockProvider.Object, mockAgentProvider.Object, this.State); + + // Empty response - no approval found, should treat as rejected + ExternalInputResponse response = new([]); + + // Act + WorkflowEvent[] events = await this.ExecuteCaptureResponseTestAsync(action, response); + + // Assert + VerifyModel(model, action); + Assert.NotEmpty(events); + } + + [Fact] + public async Task InvokeMcpToolCaptureResponseWithNonMatchingApprovalIdAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolCaptureResponseWithNonMatchingApprovalIdAsync), + serverUrl: TestServerUrl, + toolName: TestToolName); + MockMcpToolProvider mockProvider = new(); + MockAgentProvider mockAgentProvider = new(); + InvokeMcpToolExecutor action = new(model, mockProvider.Object, mockAgentProvider.Object, this.State); + + // Create approval with different ID + McpServerToolCallContent toolCall = new("different_id", TestToolName, TestServerUrl); + McpServerToolApprovalRequestContent approvalRequest = new("different_id", toolCall); + McpServerToolApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved: true); + ExternalInputResponse response = new(new ChatMessage(ChatRole.User, [approvalResponse])); + + // Act + WorkflowEvent[] events = await this.ExecuteCaptureResponseTestAsync(action, response); + + // Assert - Should be treated as rejected since no matching approval + VerifyModel(model, action); + Assert.NotEmpty(events); + } + + [Fact] + public async Task InvokeMcpToolCaptureResponseWithApprovedAndArgumentsAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolCaptureResponseWithApprovedAndArgumentsAsync), + serverUrl: TestServerUrl, + toolName: TestToolName, + requireApproval: true, + argumentKey: "query", + argumentValue: "test query"); + MockMcpToolProvider mockProvider = new(); + MockAgentProvider mockAgentProvider = new(); + InvokeMcpToolExecutor action = new(model, mockProvider.Object, mockAgentProvider.Object, this.State); + + // Create approval request then response + McpServerToolCallContent toolCall = new(action.Id, TestToolName, TestServerUrl); + McpServerToolApprovalRequestContent approvalRequest = new(action.Id, toolCall); + McpServerToolApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved: true); + ExternalInputResponse response = new(new ChatMessage(ChatRole.User, [approvalResponse])); + + // Act + WorkflowEvent[] events = await this.ExecuteCaptureResponseTestAsync(action, response); + + // Assert + VerifyModel(model, action); + Assert.NotEmpty(events); + } + + [Fact] + public async Task InvokeMcpToolCaptureResponseWithApprovedAndHeadersAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolCaptureResponseWithApprovedAndHeadersAsync), + serverUrl: TestServerUrl, + serverLabel: TestServerLabel, + toolName: TestToolName, + requireApproval: true, + headerKey: "X-Custom-Header", + headerValue: "custom-value"); + MockMcpToolProvider mockProvider = new(); + MockAgentProvider mockAgentProvider = new(); + InvokeMcpToolExecutor action = new(model, mockProvider.Object, mockAgentProvider.Object, this.State); + + // Create approval request then response + McpServerToolCallContent toolCall = new(action.Id, TestToolName, TestServerLabel); + McpServerToolApprovalRequestContent approvalRequest = new(action.Id, toolCall); + McpServerToolApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved: true); + ExternalInputResponse response = new(new ChatMessage(ChatRole.User, [approvalResponse])); + + // Act + WorkflowEvent[] events = await this.ExecuteCaptureResponseTestAsync(action, response); + + // Assert + VerifyModel(model, action); + Assert.NotEmpty(events); + } + + [Fact] + public async Task InvokeMcpToolCaptureResponseWithApprovedAndConversationIdAsync() + { + // Arrange + this.State.InitializeSystem(); + const string ConversationId = "TestConversationId"; + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolCaptureResponseWithApprovedAndConversationIdAsync), + serverUrl: TestServerUrl, + toolName: TestToolName, + requireApproval: true, + conversationId: ConversationId); + MockMcpToolProvider mockProvider = new(); + MockAgentProvider mockAgentProvider = new(); + InvokeMcpToolExecutor action = new(model, mockProvider.Object, mockAgentProvider.Object, this.State); + + // Create approval request then response + McpServerToolCallContent toolCall = new(action.Id, TestToolName, TestServerUrl); + McpServerToolApprovalRequestContent approvalRequest = new(action.Id, toolCall); + McpServerToolApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved: true); + ExternalInputResponse response = new(new ChatMessage(ChatRole.User, [approvalResponse])); + + // Act + WorkflowEvent[] events = await this.ExecuteCaptureResponseTestAsync(action, response); + + // Assert + VerifyModel(model, action); + Assert.NotEmpty(events); + } + + #endregion + + #region CompleteAsync Tests + + [Fact] + public async Task InvokeMcpToolCompleteAsyncRaisesCompletionEventAsync() + { + // Arrange + this.State.InitializeSystem(); + InvokeMcpTool model = this.CreateModel( + displayName: nameof(InvokeMcpToolCompleteAsyncRaisesCompletionEventAsync), + serverUrl: TestServerUrl, + toolName: TestToolName); + MockMcpToolProvider mockProvider = new(); + MockAgentProvider mockAgentProvider = new(); + InvokeMcpToolExecutor action = new(model, mockProvider.Object, mockAgentProvider.Object, this.State); + ActionExecutorResult result = new(action.Id); + + // Act + WorkflowEvent[] events = await this.ExecuteCompleteTestAsync(action, result); + + // Assert + VerifyModel(model, action); + Assert.NotEmpty(events); + } + + #endregion + + #region Helper Methods + + private async Task ExecuteTestAsync(InvokeMcpTool model) + { + MockMcpToolProvider mockProvider = new(); + MockAgentProvider mockAgentProvider = new(); + InvokeMcpToolExecutor action = new(model, mockProvider.Object, mockAgentProvider.Object, this.State); + + // Act + WorkflowEvent[] events = await this.ExecuteAsync(action, isDiscrete: false); + + // Assert + VerifyModel(model, action); + VerifyInvocationEvent(events); + + // IsDiscreteAction should be false for InvokeMcpTool + VerifyIsDiscrete(action, isDiscrete: false); + } + + private async Task ExecuteCaptureResponseTestAsync( + InvokeMcpToolExecutor action, + ExternalInputResponse response) + { + return await this.ExecuteAsync( + action, + InvokeMcpToolExecutor.Steps.ExternalInput(action.Id), + (context, _, cancellationToken) => action.CaptureResponseAsync(context, response, cancellationToken)); + } + + private async Task ExecuteCompleteTestAsync( + InvokeMcpToolExecutor action, + ActionExecutorResult result) + { + return await this.ExecuteAsync( + action, + InvokeMcpToolExecutor.Steps.Resume(action.Id), + (context, _, cancellationToken) => action.CompleteAsync(context, result, cancellationToken)); + } + + private InvokeMcpTool CreateModel( + string displayName, + string serverUrl, + string toolName, + string? serverLabel = null, + bool? requireApproval = false, + string? conversationId = null, + string? argumentKey = null, + string? argumentValue = null, + string? headerKey = null, + string? headerValue = null) + { + InvokeMcpTool.Builder builder = new() + { + Id = this.CreateActionId(), + DisplayName = this.FormatDisplayName(displayName), + ServerUrl = new StringExpression.Builder(StringExpression.Literal(serverUrl)), + ToolName = new StringExpression.Builder(StringExpression.Literal(toolName)), + RequireApproval = requireApproval != null ? new BoolExpression.Builder(BoolExpression.Literal(requireApproval.Value)) : null + }; + + if (serverLabel is not null) + { + builder.ServerLabel = new StringExpression.Builder(StringExpression.Literal(serverLabel)); + } + + if (conversationId is not null) + { + builder.ConversationId = new StringExpression.Builder(StringExpression.Literal(conversationId)); + } + + if (argumentKey is not null && argumentValue is not null) + { + builder.Arguments.Add(argumentKey, ValueExpression.Literal(new StringDataValue(argumentValue))); + } + + if (headerKey is not null && headerValue is not null) + { + builder.Headers.Add(headerKey, new StringExpression.Builder(StringExpression.Literal(headerValue))); + } + + return AssignParent(builder); + } + + #endregion + + #region Mock MCP Tool Provider + + /// + /// Mock implementation of for unit testing purposes. + /// + private sealed class MockMcpToolProvider : Mock + { + public MockMcpToolProvider( + bool returnJsonObject = false, + bool returnJsonArray = false, + bool returnInvalidJson = false, + bool returnDataContent = false, + bool returnEmptyOutput = false, + bool returnNullOutput = false, + bool returnMultipleContent = false) + { + this.Setup(provider => provider.InvokeToolAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny?>(), + It.IsAny?>(), + It.IsAny(), + It.IsAny())) + .Returns?, IDictionary?, string?, CancellationToken>( + (_, _, _, _, _, _, _) => + { + McpServerToolResultContent result = new("mock-call-id"); + + if (returnNullOutput) + { + result.Output = null; + } + else if (returnEmptyOutput) + { + result.Output = []; + } + else if (returnJsonObject) + { + result.Output = [new TextContent("{\"key\": \"value\", \"number\": 42}")]; + } + else if (returnJsonArray) + { + result.Output = [new TextContent("[1, 2, 3, \"four\"]")]; + } + else if (returnInvalidJson) + { + result.Output = [new TextContent("this is not valid json {")]; + } + else if (returnDataContent) + { + result.Output = [new DataContent("data:image/png;base64,iVBORw0KGgo=", "image/png")]; + } + else if (returnMultipleContent) + { + result.Output = + [ + new TextContent("First text"), + new TextContent("{\"nested\": true}"), + new DataContent("data:audio/mp3;base64,SUQz", "audio/mp3") + ]; + } + else + { + result.Output = [new TextContent("Mock MCP tool result")]; + } + + return Task.FromResult(result); + }); + } + } + + #endregion +} diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/ObservabilityTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/ObservabilityTests.cs index e7a99d5ca2..af8a9d8e0d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/ObservabilityTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/ObservabilityTests.cs @@ -43,15 +43,16 @@ public ObservabilityTests() /// Create a sample workflow for testing. /// /// - /// This workflow is expected to create 8 activities that will be captured by the tests + /// This workflow is expected to create 9 activities that will be captured by the tests /// - ActivityNames.WorkflowBuild - /// - ActivityNames.WorkflowRun - /// -- ActivityNames.EdgeGroupProcess - /// -- ActivityNames.ExecutorProcess (UppercaseExecutor) - /// --- ActivityNames.MessageSend - /// ---- ActivityNames.EdgeGroupProcess - /// -- ActivityNames.ExecutorProcess (ReverseTextExecutor) - /// --- ActivityNames.MessageSend + /// - ActivityNames.WorkflowSession + /// -- ActivityNames.WorkflowInvoke + /// --- ActivityNames.EdgeGroupProcess + /// --- ActivityNames.ExecutorProcess (UppercaseExecutor) + /// ---- ActivityNames.MessageSend + /// ----- ActivityNames.EdgeGroupProcess + /// --- ActivityNames.ExecutorProcess (ReverseTextExecutor) + /// ---- ActivityNames.MessageSend /// /// The created workflow. private static Workflow CreateWorkflow() @@ -74,7 +75,8 @@ private static Dictionary GetExpectedActivityNameCounts() => new() { { ActivityNames.WorkflowBuild, 1 }, - { ActivityNames.WorkflowRun, 1 }, + { ActivityNames.WorkflowSession, 1 }, + { ActivityNames.WorkflowInvoke, 1 }, { ActivityNames.EdgeGroupProcess, 2 }, { ActivityNames.ExecutorProcess, 2 }, { ActivityNames.MessageSend, 2 } @@ -113,7 +115,7 @@ private async Task TestWorkflowEndToEndActivitiesAsync(string executionEnvironme // Assert var capturedActivities = this._capturedActivities.Where(a => a.RootId == testActivity.RootId).ToList(); - capturedActivities.Should().HaveCount(8, "Exactly 8 activities should be created."); + capturedActivities.Should().HaveCount(9, "Exactly 9 activities should be created."); // Make sure all expected activities exist and have the correct count foreach (var kvp in GetExpectedActivityNameCounts()) @@ -125,7 +127,7 @@ private async Task TestWorkflowEndToEndActivitiesAsync(string executionEnvironme } // Verify WorkflowRun activity events include workflow lifecycle events - var workflowRunActivity = capturedActivities.First(a => a.OperationName.StartsWith(ActivityNames.WorkflowRun, StringComparison.Ordinal)); + var workflowRunActivity = capturedActivities.First(a => a.OperationName.StartsWith(ActivityNames.WorkflowInvoke, StringComparison.Ordinal)); var activityEvents = workflowRunActivity.Events.ToList(); activityEvents.Should().Contain(e => e.Name == EventNames.WorkflowStarted, "activity should have workflow started event"); activityEvents.Should().Contain(e => e.Name == EventNames.WorkflowCompleted, "activity should have workflow completed event"); @@ -273,8 +275,11 @@ public async Task DisableWorkflowRun_PreventsWorkflowRunActivityAsync() // Assert var capturedActivities = this._capturedActivities.Where(a => a.RootId == testActivity.RootId).ToList(); capturedActivities.Should().NotContain( - a => a.OperationName.StartsWith(ActivityNames.WorkflowRun, StringComparison.Ordinal), + a => a.OperationName.StartsWith(ActivityNames.WorkflowInvoke, StringComparison.Ordinal), "WorkflowRun activity should be disabled."); + capturedActivities.Should().NotContain( + a => a.OperationName.StartsWith(ActivityNames.WorkflowSession, StringComparison.Ordinal), + "WorkflowSession activity should also be disabled when DisableWorkflowRun is true."); capturedActivities.Should().Contain( a => a.OperationName.StartsWith(ActivityNames.WorkflowBuild, StringComparison.Ordinal), "Other activities should still be created."); @@ -303,7 +308,7 @@ public async Task DisableExecutorProcess_PreventsExecutorProcessActivityAsync() a => a.OperationName.StartsWith(ActivityNames.ExecutorProcess, StringComparison.Ordinal), "ExecutorProcess activity should be disabled."); capturedActivities.Should().Contain( - a => a.OperationName.StartsWith(ActivityNames.WorkflowRun, StringComparison.Ordinal), + a => a.OperationName.StartsWith(ActivityNames.WorkflowInvoke, StringComparison.Ordinal), "Other activities should still be created."); } diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowRunActivityStopTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowRunActivityStopTests.cs new file mode 100644 index 0000000000..f35910f26b --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowRunActivityStopTests.cs @@ -0,0 +1,344 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.Agents.AI.Workflows.Observability; + +namespace Microsoft.Agents.AI.Workflows.UnitTests; + +/// +/// Regression test for https://github.com/microsoft/agent-framework/issues/4155 +/// Verifies that the workflow_invoke Activity is properly stopped/disposed so it gets exported +/// to telemetry backends. The ActivityStopped callback must fire for the workflow_invoke span. +/// +[Collection("ObservabilityTests")] +public sealed class WorkflowRunActivityStopTests : IDisposable +{ + private readonly ActivityListener _activityListener; + private readonly ConcurrentBag _startedActivities = []; + private readonly ConcurrentBag _stoppedActivities = []; + private bool _isDisposed; + + public WorkflowRunActivityStopTests() + { + this._activityListener = new ActivityListener + { + ShouldListenTo = source => source.Name.Contains(typeof(Workflow).Namespace!), + Sample = (ref ActivityCreationOptions options) => ActivitySamplingResult.AllData, + ActivityStarted = activity => this._startedActivities.Add(activity), + ActivityStopped = activity => this._stoppedActivities.Add(activity), + }; + ActivitySource.AddActivityListener(this._activityListener); + } + + public void Dispose() + { + if (!this._isDisposed) + { + this._activityListener?.Dispose(); + this._isDisposed = true; + } + } + + /// + /// Creates a simple sequential workflow with OpenTelemetry enabled. + /// + private static Workflow CreateWorkflow() + { + Func uppercaseFunc = s => s.ToUpperInvariant(); + var uppercase = uppercaseFunc.BindAsExecutor("UppercaseExecutor"); + + Func reverseFunc = s => new string(s.Reverse().ToArray()); + var reverse = reverseFunc.BindAsExecutor("ReverseTextExecutor"); + + WorkflowBuilder builder = new(uppercase); + builder.AddEdge(uppercase, reverse).WithOutputFrom(reverse); + + return builder.WithOpenTelemetry().Build(); + } + + /// + /// Verifies that the workflow_invoke Activity is stopped (and thus exportable) when + /// using the Lockstep execution environment. + /// Bug: The Activity created by LockstepRunEventStream.TakeEventStreamAsync is never + /// disposed because yield break in async iterators does not trigger using disposal. + /// + [Fact] + public async Task WorkflowRunActivity_IsStopped_LockstepAsync() + { + // Arrange + using var testActivity = new Activity("WorkflowRunStopTest_Lockstep").Start(); + + // Act + var workflow = CreateWorkflow(); + Run run = await InProcessExecution.Lockstep.RunAsync(workflow, "Hello, World!"); + await run.DisposeAsync(); + + // Assert - workflow.session should have been started and stopped + var startedSessions = this._startedActivities + .Where(a => a.RootId == testActivity.RootId && + a.OperationName.StartsWith(ActivityNames.WorkflowSession, StringComparison.Ordinal)) + .ToList(); + startedSessions.Should().HaveCount(1, "workflow.session Activity should be started"); + + var stoppedSessions = this._stoppedActivities + .Where(a => a.RootId == testActivity.RootId && + a.OperationName.StartsWith(ActivityNames.WorkflowSession, StringComparison.Ordinal)) + .ToList(); + stoppedSessions.Should().HaveCount(1, + "workflow.session Activity should be stopped/disposed so it is exported to telemetry backends"); + + // Assert - workflow_invoke should have been started and stopped + var startedWorkflowRuns = this._startedActivities + .Where(a => a.RootId == testActivity.RootId && + a.OperationName.StartsWith(ActivityNames.WorkflowInvoke, StringComparison.Ordinal)) + .ToList(); + startedWorkflowRuns.Should().HaveCount(1, "workflow_invoke Activity should be started"); + + var stoppedWorkflowRuns = this._stoppedActivities + .Where(a => a.RootId == testActivity.RootId && + a.OperationName.StartsWith(ActivityNames.WorkflowInvoke, StringComparison.Ordinal)) + .ToList(); + stoppedWorkflowRuns.Should().HaveCount(1, + "workflow_invoke Activity should be stopped/disposed so it is exported to telemetry backends (issue #4155)"); + } + + /// + /// Verifies that the workflow_invoke Activity is stopped when using the OffThread (Default) + /// execution environment (StreamingRunEventStream). + /// + [Fact] + public async Task WorkflowRunActivity_IsStopped_OffThreadAsync() + { + // Arrange + using var testActivity = new Activity("WorkflowRunStopTest_OffThread").Start(); + + // Act + var workflow = CreateWorkflow(); + Run run = await InProcessExecution.OffThread.RunAsync(workflow, "Hello, World!"); + await run.DisposeAsync(); + + // Assert - workflow.session should have been started and stopped + var startedSessions = this._startedActivities + .Where(a => a.RootId == testActivity.RootId && + a.OperationName.StartsWith(ActivityNames.WorkflowSession, StringComparison.Ordinal)) + .ToList(); + startedSessions.Should().HaveCount(1, "workflow.session Activity should be started"); + + var stoppedSessions = this._stoppedActivities + .Where(a => a.RootId == testActivity.RootId && + a.OperationName.StartsWith(ActivityNames.WorkflowSession, StringComparison.Ordinal)) + .ToList(); + stoppedSessions.Should().HaveCount(1, + "workflow.session Activity should be stopped/disposed so it is exported to telemetry backends"); + + // Assert - workflow_invoke should have been started and stopped + var startedWorkflowRuns = this._startedActivities + .Where(a => a.RootId == testActivity.RootId && + a.OperationName.StartsWith(ActivityNames.WorkflowInvoke, StringComparison.Ordinal)) + .ToList(); + startedWorkflowRuns.Should().HaveCount(1, "workflow_invoke Activity should be started"); + + var stoppedWorkflowRuns = this._stoppedActivities + .Where(a => a.RootId == testActivity.RootId && + a.OperationName.StartsWith(ActivityNames.WorkflowInvoke, StringComparison.Ordinal)) + .ToList(); + stoppedWorkflowRuns.Should().HaveCount(1, + "workflow_invoke Activity should be stopped/disposed so it is exported to telemetry backends (issue #4155)"); + } + + /// + /// Verifies that the workflow_invoke Activity is stopped when using the streaming API + /// (StreamingRun.WatchStreamAsync) with the OffThread execution environment. + /// This matches the exact usage pattern described in the issue. + /// + [Fact] + public async Task WorkflowRunActivity_IsStopped_Streaming_OffThreadAsync() + { + // Arrange + using var testActivity = new Activity("WorkflowRunStopTest_Streaming_OffThread").Start(); + + // Act - use streaming path (WatchStreamAsync), which is the pattern from the issue + var workflow = CreateWorkflow(); + StreamingRun run = await InProcessExecution.OffThread.RunStreamingAsync(workflow, "Hello, World!"); + await foreach (WorkflowEvent evt in run.WatchStreamAsync()) + { + // Consume all events + } + + // Dispose the run before asserting — the run Activity is disposed when the + // run loop exits, which happens during DisposeAsync. Without this, assertions + // can race against the background run loop's finally block. + await run.DisposeAsync(); + + // Assert - workflow.session should have been started + var startedSessions = this._startedActivities + .Where(a => a.RootId == testActivity.RootId && + a.OperationName.StartsWith(ActivityNames.WorkflowSession, StringComparison.Ordinal)) + .ToList(); + startedSessions.Should().HaveCount(1, "workflow.session Activity should be started"); + + // Assert - workflow_invoke should have been started + var startedWorkflowRuns = this._startedActivities + .Where(a => a.RootId == testActivity.RootId && + a.OperationName.StartsWith(ActivityNames.WorkflowInvoke, StringComparison.Ordinal)) + .ToList(); + startedWorkflowRuns.Should().HaveCount(1, "workflow_invoke Activity should be started"); + + // Assert - workflow_invoke should have been stopped + var stoppedWorkflowRuns = this._stoppedActivities + .Where(a => a.RootId == testActivity.RootId && + a.OperationName.StartsWith(ActivityNames.WorkflowInvoke, StringComparison.Ordinal)) + .ToList(); + stoppedWorkflowRuns.Should().HaveCount(1, + "workflow_invoke Activity should be stopped/disposed so it is exported to telemetry backends (issue #4155)"); + } + + /// + /// Verifies that a new workflow_invoke activity is started and stopped for each + /// streaming invocation, even when using the same workflow in a multi-turn pattern, + /// and that each session gets its own session activity. + /// + [Fact] + public async Task WorkflowRunActivity_IsStopped_Streaming_OffThread_MultiTurnAsync() + { + // Arrange + using var testActivity = new Activity("WorkflowRunStopTest_Streaming_OffThread_MultiTurn").Start(); + + var workflow = CreateWorkflow(); + + // Act - first streaming run + await using (StreamingRun run1 = await InProcessExecution.OffThread.RunStreamingAsync(workflow, "Hello, World!")) + { + await foreach (WorkflowEvent evt in run1.WatchStreamAsync()) + { + // Consume all events from first turn + } + } + + // Act - second streaming run (multi-turn scenario with same workflow) + await using (StreamingRun run2 = await InProcessExecution.OffThread.RunStreamingAsync(workflow, "Second turn!")) + { + await foreach (WorkflowEvent evt in run2.WatchStreamAsync()) + { + // Consume all events from second turn + } + } + + // Assert - two workflow.session activities should have been started and stopped + var startedSessions = this._startedActivities + .Where(a => a.RootId == testActivity.RootId && + a.OperationName.StartsWith(ActivityNames.WorkflowSession, StringComparison.Ordinal)) + .ToList(); + startedSessions.Should().HaveCount(2, + "each streaming invocation should start its own workflow.session Activity"); + + var stoppedSessions = this._stoppedActivities + .Where(a => a.RootId == testActivity.RootId && + a.OperationName.StartsWith(ActivityNames.WorkflowSession, StringComparison.Ordinal)) + .ToList(); + stoppedSessions.Should().HaveCount(2, + "each workflow.session Activity should be stopped/disposed so it is exported to telemetry backends"); + + // Assert - two workflow_invoke activities should have been started and stopped + var startedWorkflowRuns = this._startedActivities + .Where(a => a.RootId == testActivity.RootId && + a.OperationName.StartsWith(ActivityNames.WorkflowInvoke, StringComparison.Ordinal)) + .ToList(); + startedWorkflowRuns.Should().HaveCount(2, + "each streaming invocation should start its own workflow_invoke Activity"); + + var stoppedWorkflowRuns = this._stoppedActivities + .Where(a => a.RootId == testActivity.RootId && + a.OperationName.StartsWith(ActivityNames.WorkflowInvoke, StringComparison.Ordinal)) + .ToList(); + stoppedWorkflowRuns.Should().HaveCount(2, + "each workflow_invoke Activity should be stopped/disposed so it is exported to telemetry backends in multi-turn scenarios"); + } + + /// + /// Verifies that all started activities (not just workflow_invoke) are properly stopped. + /// This ensures no spans are "leaked" without being exported. + /// + [Fact] + public async Task AllActivities_AreStopped_AfterWorkflowCompletionAsync() + { + // Arrange + using var testActivity = new Activity("AllActivitiesStopTest").Start(); + + // Act + var workflow = CreateWorkflow(); + Run run = await InProcessExecution.Lockstep.RunAsync(workflow, "Hello, World!"); + await run.DisposeAsync(); + + // Assert - every started activity should also be stopped + var started = this._startedActivities + .Where(a => a.RootId == testActivity.RootId) + .Select(a => a.Id) + .ToHashSet(); + + var stopped = this._stoppedActivities + .Where(a => a.RootId == testActivity.RootId) + .Select(a => a.Id) + .ToHashSet(); + + var neverStopped = started.Except(stopped).ToList(); + if (neverStopped.Count > 0) + { + var neverStoppedNames = this._startedActivities + .Where(a => neverStopped.Contains(a.Id)) + .Select(a => a.OperationName) + .ToList(); + neverStoppedNames.Should().BeEmpty( + "all started activities should be stopped so they are exported. " + + $"Activities started but never stopped: [{string.Join(", ", neverStoppedNames)}]"); + } + } + + /// + /// Verifies that Activity.Current is not leaked after lockstep RunAsync. + /// Application code creating activities after RunAsync returns should not + /// be parented under the workflow session span. The run activity should + /// still nest correctly under the session. + /// + [Fact] + public async Task Lockstep_SessionActivity_DoesNotLeak_IntoCaller_ActivityCurrentAsync() + { + // Arrange + using var testActivity = new Activity("SessionLeakTest").Start(); + var workflow = CreateWorkflow(); + + // Act — run the workflow via lockstep (Start + drain happen inside RunAsync) + Run run = await InProcessExecution.Lockstep.RunAsync(workflow, "Hello, World!"); + + // Create an application activity after RunAsync returns. + // If the session leaked into Activity.Current, this would be parented under it. + using var appActivity = new Activity("AppWork").Start(); + appActivity.Stop(); + + await run.DisposeAsync(); + + // Assert — the app activity should be parented under the test root, not the session + var sessionActivities = this._startedActivities + .Where(a => a.RootId == testActivity.RootId && + a.OperationName.StartsWith(ActivityNames.WorkflowSession, StringComparison.Ordinal)) + .ToList(); + sessionActivities.Should().HaveCount(1, "one session activity should exist"); + + appActivity.ParentId.Should().Be(testActivity.Id, + "application activity should be parented under the test root, not the workflow session"); + + // Assert — the run activity should still be parented under the session + var invokeActivities = this._startedActivities + .Where(a => a.RootId == testActivity.RootId && + a.OperationName.StartsWith(ActivityNames.WorkflowInvoke, StringComparison.Ordinal)) + .ToList(); + invokeActivities.Should().HaveCount(1, "one workflow_invoke activity should exist"); + invokeActivities[0].ParentId.Should().Be(sessionActivities[0].Id, + "workflow_invoke activity should be nested under the session activity"); + } +} diff --git a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantClientExtensionsTests.cs b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantClientExtensionsTests.cs index 02f5f36a76..2e92cc6d42 100644 --- a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantClientExtensionsTests.cs +++ b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantClientExtensionsTests.cs @@ -19,9 +19,8 @@ namespace OpenAIAssistant.IntegrationTests; public class OpenAIAssistantClientExtensionsTests { - private static readonly OpenAIConfiguration s_config = TestConfiguration.LoadSection(); - private readonly AssistantClient _assistantClient = new OpenAIClient(s_config.ApiKey).GetAssistantClient(); - private readonly OpenAIFileClient _fileClient = new OpenAIClient(s_config.ApiKey).GetOpenAIFileClient(); + private readonly AssistantClient _assistantClient = new OpenAIClient(TestConfiguration.GetRequiredValue(TestSettings.OpenAIApiKey)).GetAssistantClient(); + private readonly OpenAIFileClient _fileClient = new OpenAIClient(TestConfiguration.GetRequiredValue(TestSettings.OpenAIApiKey)).GetOpenAIFileClient(); [Theory] [InlineData("CreateWithChatClientAgentOptionsAsync")] @@ -39,7 +38,7 @@ public async Task CreateAIAgentAsync_WithAIFunctionTool_InvokesFunctionAsync(str var agent = createMechanism switch { "CreateWithChatClientAgentOptionsAsync" => await this._assistantClient.CreateAIAgentAsync( - model: s_config.ChatModelId!, + model: TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName), options: new ChatClientAgentOptions() { ChatOptions = new() @@ -49,7 +48,7 @@ public async Task CreateAIAgentAsync_WithAIFunctionTool_InvokesFunctionAsync(str } }), "CreateWithChatClientAgentOptionsSync" => await this._assistantClient.CreateAIAgentAsync( - model: s_config.ChatModelId!, + model: TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName), options: new ChatClientAgentOptions() { ChatOptions = new() @@ -59,7 +58,7 @@ public async Task CreateAIAgentAsync_WithAIFunctionTool_InvokesFunctionAsync(str } }), "CreateWithParamsAsync" => await this._assistantClient.CreateAIAgentAsync( - model: s_config.ChatModelId!, + model: TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName), instructions: AgentInstructions, tools: [weatherFunction]), _ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}") @@ -106,7 +105,7 @@ public async Task CreateAIAgentAsync_WithHostedCodeInterpreter_RunsCodeAsync(str var agent = createMechanism switch { "CreateWithChatClientAgentOptionsAsync" => await this._assistantClient.CreateAIAgentAsync( - model: s_config.ChatModelId!, + model: TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName), options: new ChatClientAgentOptions() { ChatOptions = new() @@ -116,7 +115,7 @@ public async Task CreateAIAgentAsync_WithHostedCodeInterpreter_RunsCodeAsync(str } }), "CreateWithChatClientAgentOptionsSync" => await this._assistantClient.CreateAIAgentAsync( - model: s_config.ChatModelId!, + model: TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName), options: new ChatClientAgentOptions() { ChatOptions = new() @@ -126,7 +125,7 @@ public async Task CreateAIAgentAsync_WithHostedCodeInterpreter_RunsCodeAsync(str } }), "CreateWithParamsAsync" => await this._assistantClient.CreateAIAgentAsync( - model: s_config.ChatModelId!, + model: TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName), instructions: Instructions, tools: [codeInterpreterTool]), _ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}") @@ -168,7 +167,7 @@ You are a helpful agent that can help fetch data from files you know about. string uploadedFileId = uploadResult.Value.Id; // Create a vector store backing the file search (HostedFileSearchTool requires a vector store id). - var vectorStoreClient = new OpenAIClient(s_config.ApiKey).GetVectorStoreClient(); + var vectorStoreClient = new OpenAIClient(TestConfiguration.GetRequiredValue(TestSettings.OpenAIApiKey)).GetVectorStoreClient(); var vectorStoreCreate = await vectorStoreClient.CreateVectorStoreAsync(options: new VectorStoreCreationOptions() { Name = "WordCodeLookup_VectorStore", @@ -184,7 +183,7 @@ You are a helpful agent that can help fetch data from files you know about. var agent = createMechanism switch { "CreateWithChatClientAgentOptionsAsync" => await this._assistantClient.CreateAIAgentAsync( - model: s_config.ChatModelId!, + model: TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName), options: new ChatClientAgentOptions() { ChatOptions = new() @@ -194,7 +193,7 @@ You are a helpful agent that can help fetch data from files you know about. } }), "CreateWithChatClientAgentOptionsSync" => await this._assistantClient.CreateAIAgentAsync( - model: s_config.ChatModelId!, + model: TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName), options: new ChatClientAgentOptions() { ChatOptions = new() @@ -204,7 +203,7 @@ You are a helpful agent that can help fetch data from files you know about. } }), "CreateWithParamsAsync" => await this._assistantClient.CreateAIAgentAsync( - model: s_config.ChatModelId!, + model: TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName), instructions: Instructions, tools: [fileSearchTool]), _ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}") diff --git a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs index c59d3cf068..f679da04aa 100644 --- a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs +++ b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs @@ -15,8 +15,6 @@ namespace OpenAIAssistant.IntegrationTests; public class OpenAIAssistantFixture : IChatClientAgentFixture { - private static readonly OpenAIConfiguration s_config = TestConfiguration.LoadSection(); - private AssistantClient? _assistantClient; private ChatClientAgent _agent = null!; @@ -50,7 +48,7 @@ public async Task CreateChatClientAgentAsync( { var assistant = await this._assistantClient!.CreateAssistantAsync( - s_config.ChatModelId!, + TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName), new AssistantCreationOptions() { Name = name, @@ -82,7 +80,7 @@ public Task DeleteSessionAsync(AgentSession session) public async ValueTask InitializeAsync() { - var client = new OpenAIClient(s_config.ApiKey); + var client = new OpenAIClient(TestConfiguration.GetRequiredValue(TestSettings.OpenAIApiKey)); this._assistantClient = client.GetAssistantClient(); this._agent = await this.CreateChatClientAgentAsync(); diff --git a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs index 36ff48dee7..4e3bd7e3b0 100644 --- a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs +++ b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs @@ -15,7 +15,6 @@ namespace OpenAIChatCompletion.IntegrationTests; public class OpenAIChatCompletionFixture : IChatClientAgentFixture { - private static readonly OpenAIConfiguration s_config = TestConfiguration.LoadSection(); private readonly bool _useReasoningModel; private ChatClientAgent _agent = null!; @@ -46,8 +45,8 @@ public Task CreateChatClientAgentAsync( string instructions = "You are a helpful assistant.", IList? aiTools = null) { - var chatClient = new OpenAIClient(s_config.ApiKey) - .GetChatClient(this._useReasoningModel ? s_config.ChatReasoningModelId : s_config.ChatModelId) + var chatClient = new OpenAIClient(TestConfiguration.GetRequiredValue(TestSettings.OpenAIApiKey)) + .GetChatClient(this._useReasoningModel ? TestConfiguration.GetRequiredValue(TestSettings.OpenAIReasoningModelName) : TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName)) .AsIChatClient(); return Task.FromResult(new ChatClientAgent(chatClient, options: new() diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs index 1ab7cb5112..74c7ef9041 100644 --- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs @@ -16,8 +16,6 @@ namespace ResponseResult.IntegrationTests; public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture { - private static readonly OpenAIConfiguration s_config = TestConfiguration.LoadSection(); - private ResponsesClient _openAIResponseClient = null!; private ChatClientAgent _agent = null!; @@ -98,8 +96,8 @@ public Task DeleteSessionAsync(AgentSession session) => public async ValueTask InitializeAsync() { - this._openAIResponseClient = new OpenAIClient(s_config.ApiKey) - .GetResponsesClient(s_config.ChatModelId); + this._openAIResponseClient = new OpenAIClient(TestConfiguration.GetRequiredValue(TestSettings.OpenAIApiKey)) + .GetResponsesClient(TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName)); this._agent = await this.CreateChatClientAgentAsync(); } diff --git a/python/CHANGELOG.md b/python/CHANGELOG.md index b21d8dc0c1..6ae989c0c1 100644 --- a/python/CHANGELOG.md +++ b/python/CHANGELOG.md @@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.0.0rc2] - 2026-02-25 + +### Added + +- **agent-framework-core**: Support Agent Skills ([#4210](https://github.com/microsoft/agent-framework/pull/4210)) +- **agent-framework-core**: Add embedding abstractions and OpenAI implementation (Phase 1) ([#4153](https://github.com/microsoft/agent-framework/pull/4153)) +- **agent-framework-core**: Add Foundry Memory Context Provider ([#3943](https://github.com/microsoft/agent-framework/pull/3943)) +- **agent-framework-core**: Add `max_function_calls` to `FunctionInvocationConfiguration` ([#4175](https://github.com/microsoft/agent-framework/pull/4175)) +- **agent-framework-core**: Add `CreateConversationExecutor`, fix input routing, remove unused handler layer ([#4159](https://github.com/microsoft/agent-framework/pull/4159)) +- **agent-framework-azure-ai-search**: Azure AI Search provider improvements - EmbeddingGenerator, async context manager, KB message handling ([#4212](https://github.com/microsoft/agent-framework/pull/4212)) +- **agent-framework-azure-ai-search**: Enhance Azure AI Search Citations with Document URLs in Foundry V2 ([#4028](https://github.com/microsoft/agent-framework/pull/4028)) +- **agent-framework-ag-ui**: Add Workflow Support, Harden Streaming Semantics, and add Dynamic Handoff Demo ([#3911](https://github.com/microsoft/agent-framework/pull/3911)) + +### Changed + +- **agent-framework-declarative**: [BREAKING] Add `InvokeFunctionTool` action for declarative workflows ([#3716](https://github.com/microsoft/agent-framework/pull/3716)) + +### Fixed + +- **agent-framework-core**: Fix thread corruption when `max_iterations` is reached ([#4234](https://github.com/microsoft/agent-framework/pull/4234)) +- **agent-framework-core**: Fix workflow runner concurrent processing ([#4143](https://github.com/microsoft/agent-framework/pull/4143)) +- **agent-framework-core**: Fix doubled `tool_call` arguments in `MESSAGES_SNAPSHOT` when streaming ([#4200](https://github.com/microsoft/agent-framework/pull/4200)) +- **agent-framework-core**: Fix OpenAI chat client compatibility with third-party endpoints and OTel 0.4.14 ([#4161](https://github.com/microsoft/agent-framework/pull/4161)) +- **agent-framework-claude**: Fix `structured_output` propagation in `ClaudeAgent` ([#4137](https://github.com/microsoft/agent-framework/pull/4137)) + ## [1.0.0rc1] - 2026-02-19 Release candidate for **agent-framework-core** and **agent-framework-azure-ai** packages. @@ -675,7 +700,8 @@ Release candidate for **agent-framework-core** and **agent-framework-azure-ai** For more information, see the [announcement blog post](https://devblogs.microsoft.com/foundry/introducing-microsoft-agent-framework-the-open-source-engine-for-agentic-ai-apps/). -[Unreleased]: https://github.com/microsoft/agent-framework/compare/python-1.0.0rc1...HEAD +[Unreleased]: https://github.com/microsoft/agent-framework/compare/python-1.0.0rc2...HEAD +[1.0.0rc2]: https://github.com/microsoft/agent-framework/compare/python-1.0.0rc1...python-1.0.0rc2 [1.0.0rc1]: https://github.com/microsoft/agent-framework/compare/python-1.0.0b260212...python-1.0.0rc1 [1.0.0b260212]: https://github.com/microsoft/agent-framework/compare/python-1.0.0b260210...python-1.0.0b260212 [1.0.0b260210]: https://github.com/microsoft/agent-framework/compare/python-1.0.0b260130...python-1.0.0b260210 diff --git a/python/packages/a2a/pyproject.toml b/python/packages/a2a/pyproject.toml index 418cfcc827..6a96201ed3 100644 --- a/python/packages/a2a/pyproject.toml +++ b/python/packages/a2a/pyproject.toml @@ -4,7 +4,7 @@ description = "A2A integration for Microsoft Agent Framework." authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0b260219" +version = "1.0.0b260225" license-files = ["LICENSE"] urls.homepage = "https://aka.ms/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -23,7 +23,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", "a2a-sdk>=0.3.5", ] @@ -37,6 +37,7 @@ environments = [ [tool.uv-dynamic-versioning] fallback-version = "0.0.0" + [tool.pytest.ini_options] testpaths = 'tests' addopts = "-ra -q -r fEX" @@ -46,6 +47,9 @@ filterwarnings = [ "ignore:Support for class-based `config` is deprecated:DeprecationWarning:pydantic.*" ] timeout = 120 +markers = [ + "integration: marks tests as integration tests that require external services", +] [tool.ruff] extend = "../../pyproject.toml" @@ -79,6 +83,7 @@ exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" + [tool.poe.tasks] mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_a2a" test = "pytest --cov=agent_framework_a2a --cov-report=term-missing:skip-covered tests" diff --git a/python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py b/python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py index 5afed1ed87..e35f3e4062 100644 --- a/python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py +++ b/python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py @@ -56,6 +56,7 @@ get_conversation_id_from_update, get_role_value, make_json_safe, + normalize_agui_role, ) if TYPE_CHECKING: @@ -450,7 +451,7 @@ async def _resolve_approval_responses( _convert_approval_results_to_tool_messages(messages) -def _convert_approval_results_to_tool_messages(messages: list[Any]) -> None: +def _convert_approval_results_to_tool_messages(messages: list[Message]) -> None: """Convert function_result content in user messages to proper tool messages. After approval processing, tool results end up in user messages. OpenAI and other @@ -462,14 +463,14 @@ def _convert_approval_results_to_tool_messages(messages: list[Any]) -> None: Args: messages: List of Message objects to process """ - result: list[Any] = [] + result: list[Message] = [] for msg in messages: if get_role_value(msg) != "user": result.append(msg) continue - msg_contents = cast(list[Content], getattr(msg, "contents", None) or []) + msg_contents = msg.contents or [] function_results: list[Content] = [content for content in msg_contents if content.type == "function_result"] other_contents: list[Content] = [content for content in msg_contents if content.type != "function_result"] @@ -492,6 +493,68 @@ def _convert_approval_results_to_tool_messages(messages: list[Any]) -> None: messages[:] = result +def _clean_resolved_approvals_from_snapshot( + snapshot_messages: list[dict[str, Any]], + resolved_messages: list[Message], +) -> None: + """Replace approval payloads in snapshot messages with actual tool results. + + After _resolve_approval_responses executes approved tools, the snapshot still + contains the raw approval payload (e.g. ``{"accepted": true}``). When this + snapshot is sent back to CopilotKit via ``MessagesSnapshotEvent``, the approval + payload persists in the conversation history. On the next turn CopilotKit + re-sends the full history and the adapter re-detects the approval, causing the + tool to be re-executed. + + This function replaces approval tool-message content in ``snapshot_messages`` + with the real tool result so the approval payload no longer appears in the + history sent to the client. + + Args: + snapshot_messages: Raw AG-UI snapshot messages (mutated in place). + resolved_messages: Provider messages after approval resolution. + """ + # Build call_id → result text from resolved tool messages + result_by_call_id: dict[str, str] = {} + for msg in resolved_messages: + if get_role_value(msg) != "tool": + continue + for content in msg.contents or []: + if content.type == "function_result" and content.call_id: + result_text = ( + content.result if isinstance(content.result, str) else json.dumps(make_json_safe(content.result)) + ) + result_by_call_id[str(content.call_id)] = result_text + + if not result_by_call_id: + return + + for snap_msg in snapshot_messages: + if normalize_agui_role(snap_msg.get("role", "")) != "tool": + continue + raw_content = snap_msg.get("content") + if not isinstance(raw_content, str): + continue + + # Check if this is an approval payload + try: + parsed = json.loads(raw_content) + except (json.JSONDecodeError, TypeError): + continue + if not isinstance(parsed, dict) or "accepted" not in parsed: + continue + + # Find matching tool result by toolCallId + tool_call_id = snap_msg.get("toolCallId") or snap_msg.get("tool_call_id") or "" + replacement = result_by_call_id.get(str(tool_call_id)) + if replacement is not None: + snap_msg["content"] = replacement + logger.info( + "Replaced approval payload in snapshot for tool_call_id=%s with actual result", + tool_call_id, + ) + + def _build_messages_snapshot( flow: FlowState, snapshot_messages: list[dict[str, Any]], @@ -646,6 +709,10 @@ async def run_agent_stream( tools_for_execution = tools if tools is not None else server_tools await _resolve_approval_responses(messages, tools_for_execution, agent, run_kwargs) + # Defense-in-depth: replace approval payloads in snapshot with actual tool results + # so CopilotKit does not re-send stale approval content on subsequent turns. + _clean_resolved_approvals_from_snapshot(snapshot_messages, messages) + # Feature #3: Emit StateSnapshotEvent for approved state-changing tools before agent runs approved_state_updates = _extract_approved_state_updates(messages, predictive_handler) approved_state_snapshot_emitted = False diff --git a/python/packages/ag-ui/agent_framework_ag_ui_examples/README.md b/python/packages/ag-ui/agent_framework_ag_ui_examples/README.md index fc2b274456..04332dab9c 100644 --- a/python/packages/ag-ui/agent_framework_ag_ui_examples/README.md +++ b/python/packages/ag-ui/agent_framework_ag_ui_examples/README.md @@ -331,10 +331,6 @@ wrapped_agent = AgentFrameworkAgent( orchestrators=[MyCustomOrchestrator(), DefaultOrchestrator()], ) -## Documentation - -For detailed documentation, see [DESIGN.md](DESIGN.md). - ## License MIT diff --git a/python/packages/ag-ui/pyproject.toml b/python/packages/ag-ui/pyproject.toml index 461467d616..460c0a6d1a 100644 --- a/python/packages/ag-ui/pyproject.toml +++ b/python/packages/ag-ui/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "agent-framework-ag-ui" -version = "1.0.0b260219" +version = "1.0.0b260225" description = "AG-UI protocol integration for Agent Framework" readme = "README.md" license-files = ["LICENSE"] @@ -22,7 +22,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", "ag-ui-protocol>=0.1.9", "fastapi>=0.115.0", "uvicorn>=0.30.0" @@ -45,6 +45,9 @@ packages = ["agent_framework_ag_ui", "agent_framework_ag_ui_examples"] asyncio_mode = "auto" testpaths = ["tests/ag_ui"] pythonpath = ["."] +markers = [ + "integration: marks tests as integration tests that require external services", +] [tool.ruff] line-length = 120 diff --git a/python/packages/ag-ui/tests/ag_ui/test_message_adapters.py b/python/packages/ag-ui/tests/ag_ui/test_message_adapters.py index 43bbf48fb2..bc1b95ad7d 100644 --- a/python/packages/ag-ui/tests/ag_ui/test_message_adapters.py +++ b/python/packages/ag-ui/tests/ag_ui/test_message_adapters.py @@ -866,3 +866,45 @@ def test_agui_messages_to_snapshot_format_basic(): assert result[0]["content"] == "Hello" assert result[1]["role"] == "assistant" assert result[1]["content"] == "Hi there" + + +def test_agui_fresh_approval_is_still_processed(): + """A fresh approval (no assistant response after it) must still produce function_approval_response. + + On Turn 2, the approval is fresh (no subsequent assistant message), so it + must be processed normally to execute the tool. + """ + messages_input = [ + # Turn 1: user asks something + {"role": "user", "content": "What time is it?", "id": "msg_1"}, + # Turn 1: assistant calls a tool + { + "role": "assistant", + "content": "", + "tool_calls": [ + { + "id": "call_456", + "type": "function", + "function": {"name": "get_datetime", "arguments": "{}"}, + } + ], + "id": "msg_2", + }, + # Turn 2: user approves (no assistant message after this) + { + "role": "tool", + "content": json.dumps({"accepted": True}), + "toolCallId": "call_456", + "id": "msg_3", + }, + ] + + messages = agui_messages_to_agent_framework(messages_input) + + # The fresh approval SHOULD produce a function_approval_response + approval_contents = [ + content for msg in messages for content in (msg.contents or []) if content.type == "function_approval_response" + ] + assert len(approval_contents) == 1, "Fresh approval should produce function_approval_response" + assert approval_contents[0].approved is True + assert approval_contents[0].function_call.name == "get_datetime" diff --git a/python/packages/ag-ui/tests/ag_ui/test_message_hygiene.py b/python/packages/ag-ui/tests/ag_ui/test_message_hygiene.py index 90e7e35767..a3ccf26d1a 100644 --- a/python/packages/ag-ui/tests/ag_ui/test_message_hygiene.py +++ b/python/packages/ag-ui/tests/ag_ui/test_message_hygiene.py @@ -262,3 +262,141 @@ def test_sanitize_tool_history_filters_confirm_changes_from_assistant_messages() # (the approval response is handled separately by the framework) tool_call_ids = {str(msg.contents[0].call_id) for msg in tool_messages} assert "call_c1" not in tool_call_ids # No synthetic result for confirm_changes + + +# --------------------------------------------------------------------------- +# Tests for _clean_resolved_approvals_from_snapshot +# --------------------------------------------------------------------------- + + +def test_clean_resolved_approvals_from_snapshot() -> None: + """Approval payload in snapshot should be replaced with the actual tool result.""" + import json + + from agent_framework_ag_ui._agent_run import _clean_resolved_approvals_from_snapshot + + # Snapshot still has the approval payload + snapshot_messages = [ + {"role": "user", "content": "What time is it?", "id": "msg_1"}, + { + "role": "assistant", + "content": "", + "tool_calls": [ + {"id": "call_123", "type": "function", "function": {"name": "get_datetime", "arguments": "{}"}} + ], + "id": "msg_2", + }, + { + "role": "tool", + "content": json.dumps({"accepted": True}), + "toolCallId": "call_123", + "id": "msg_3", + }, + ] + + # Resolved provider messages have the actual tool result + resolved_messages = [ + Message(role="user", contents=[Content.from_text(text="What time is it?")]), + Message( + role="assistant", + contents=[Content.from_function_call(call_id="call_123", name="get_datetime", arguments="{}")], + ), + Message( + role="tool", + contents=[Content.from_function_result(call_id="call_123", result="2024-01-01 12:00:00")], + ), + ] + + _clean_resolved_approvals_from_snapshot(snapshot_messages, resolved_messages) + + # The approval payload should now be replaced with the tool result + tool_snap = snapshot_messages[2] + assert tool_snap["content"] == "2024-01-01 12:00:00" + + +def test_clean_resolved_approvals_from_snapshot_no_approvals() -> None: + """When there are no approval payloads, snapshot should be unchanged.""" + from agent_framework_ag_ui._agent_run import _clean_resolved_approvals_from_snapshot # type: ignore + + snapshot_messages = [ + {"role": "user", "content": "Hello", "id": "msg_1"}, + {"role": "assistant", "content": "Hi there", "id": "msg_2"}, + ] + original = [dict(m) for m in snapshot_messages] + + resolved_messages = [ + Message(role="user", contents=[Content.from_text(text="Hello")]), + Message(role="assistant", contents=[Content.from_text(text="Hi there")]), + ] + + _clean_resolved_approvals_from_snapshot(snapshot_messages, resolved_messages) + + # Nothing should have changed + assert snapshot_messages == original + + +def test_cleaned_snapshot_prevents_approval_reprocessing() -> None: + """After snapshot cleaning, approval payload is replaced so it won't re-trigger on next turn. + + Simulates what happens on Turn 2: the approval is processed, the tool executes, + and _clean_resolved_approvals_from_snapshot replaces the approval payload with the + real tool result. On Turn 3, CopilotKit re-sends the cleaned snapshot, which no + longer contains an approval payload — so no function_approval_response is produced. + """ + import json + + from agent_framework_ag_ui._agent_run import _clean_resolved_approvals_from_snapshot + from agent_framework_ag_ui._message_adapters import normalize_agui_input_messages + + # Turn 2 snapshot: still has the raw approval payload + snapshot_messages = [ + {"role": "user", "content": "What time is it?", "id": "msg_1"}, + { + "role": "assistant", + "content": "", + "tool_calls": [ + {"id": "call_789", "type": "function", "function": {"name": "get_datetime", "arguments": "{}"}} + ], + "id": "msg_2", + }, + { + "role": "tool", + "content": json.dumps({"accepted": True}), + "toolCallId": "call_789", + "id": "msg_3", + }, + ] + + # Resolved provider messages after tool execution + resolved_messages = [ + Message(role="user", contents=[Content.from_text(text="What time is it?")]), + Message( + role="assistant", + contents=[Content.from_function_call(call_id="call_789", name="get_datetime", arguments="{}")], + ), + Message( + role="tool", + contents=[Content.from_function_result(call_id="call_789", result="2024-01-01 12:00:00")], + ), + ] + + # Fix B: clean the snapshot + _clean_resolved_approvals_from_snapshot(snapshot_messages, resolved_messages) + + # Snapshot should now have the real tool result + assert snapshot_messages[2]["content"] == "2024-01-01 12:00:00" + + # Simulate Turn 3: CopilotKit re-sends the cleaned snapshot + new messages + turn3_messages = list(snapshot_messages) + [ + {"role": "assistant", "content": "It is 12:00 PM.", "id": "msg_4"}, + {"role": "user", "content": "Thanks!", "id": "msg_5"}, + ] + + provider_messages, _ = normalize_agui_input_messages(turn3_messages) + + # No function_approval_response should exist — the approval payload is gone + for msg in provider_messages: + for content in msg.contents or []: + assert content.type != "function_approval_response", ( + f"Stale approval was re-processed on subsequent turn: {content}" + ) diff --git a/python/packages/anthropic/pyproject.toml b/python/packages/anthropic/pyproject.toml index 48064681c9..3d0b1ab955 100644 --- a/python/packages/anthropic/pyproject.toml +++ b/python/packages/anthropic/pyproject.toml @@ -4,7 +4,7 @@ description = "Anthropic integration for Microsoft Agent Framework." authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0b260219" +version = "1.0.0b260225" license-files = ["LICENSE"] urls.homepage = "https://aka.ms/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -23,7 +23,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", "anthropic>=0.70.0,<1", ] @@ -37,6 +37,7 @@ environments = [ [tool.uv-dynamic-versioning] fallback-version = "0.0.0" + [tool.pytest.ini_options] testpaths = 'tests' addopts = "-ra -q -r fEX" @@ -46,6 +47,9 @@ filterwarnings = [ "ignore:Support for class-based `config` is deprecated:DeprecationWarning:pydantic.*" ] timeout = 120 +markers = [ + "integration: marks tests as integration tests that require external services", +] [tool.ruff] extend = "../../pyproject.toml" @@ -80,6 +84,7 @@ exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" + [tool.poe.tasks] mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_anthropic" test = "pytest --cov=agent_framework_anthropic --cov-report=term-missing:skip-covered -n auto --dist worksteal tests" diff --git a/python/packages/azure-ai-search/pyproject.toml b/python/packages/azure-ai-search/pyproject.toml index 0408bdc423..ce43ddae3a 100644 --- a/python/packages/azure-ai-search/pyproject.toml +++ b/python/packages/azure-ai-search/pyproject.toml @@ -4,7 +4,7 @@ description = "Azure AI Search integration for Microsoft Agent Framework." authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0b260219" +version = "1.0.0b260225" license-files = ["LICENSE"] urls.homepage = "https://aka.ms/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -23,7 +23,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", "azure-search-documents==11.7.0b2", ] @@ -47,6 +47,9 @@ filterwarnings = [ "ignore:Support for class-based `config` is deprecated:DeprecationWarning:pydantic.*" ] timeout = 120 +markers = [ + "integration: marks tests as integration tests that require external services", +] [tool.ruff] extend = "../../pyproject.toml" @@ -82,6 +85,7 @@ exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" + [tool.poe.tasks] mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_azure_ai_search" test = "pytest --cov=agent_framework_azure_ai_search --cov-report=term-missing:skip-covered tests" diff --git a/python/packages/azure-ai-search/tests/test_aisearch_context_provider.py b/python/packages/azure-ai-search/tests/test_aisearch_context_provider.py index 7cc51e4930..3c4fb68fe8 100644 --- a/python/packages/azure-ai-search/tests/test_aisearch_context_provider.py +++ b/python/packages/azure-ai-search/tests/test_aisearch_context_provider.py @@ -1297,7 +1297,6 @@ def test_text_only_messages(self) -> None: assert result[0].content[0].text == "hello" def test_image_uri_content(self) -> None: - from agent_framework import Content img = Content.from_uri(uri="https://example.com/photo.png", media_type="image/png") messages = [Message(role="user", contents=[img])] @@ -1309,7 +1308,6 @@ def test_image_uri_content(self) -> None: assert result[0].content[0].image.url == "https://example.com/photo.png" def test_mixed_text_and_image_content(self) -> None: - from agent_framework import Content text = Content.from_text("describe this image") img = Content.from_uri(uri="https://example.com/img.jpg", media_type="image/jpeg") @@ -1319,7 +1317,6 @@ def test_mixed_text_and_image_content(self) -> None: assert len(result[0].content) == 2 def test_skips_non_text_non_image_content(self) -> None: - from agent_framework import Content error = Content.from_error(message="oops") messages = [Message(role="user", contents=[error])] @@ -1327,7 +1324,6 @@ def test_skips_non_text_non_image_content(self) -> None: assert len(result) == 0 # message had no usable content def test_skips_empty_text(self) -> None: - from agent_framework import Content empty = Content.from_text("") messages = [Message(role="user", contents=[empty])] @@ -1341,7 +1337,6 @@ def test_fallback_to_msg_text_when_no_contents(self) -> None: assert result[0].content[0].text == "fallback text" def test_data_uri_image(self) -> None: - from agent_framework import Content img = Content.from_data(data=b"\x89PNG", media_type="image/png") messages = [Message(role="user", contents=[img])] @@ -1352,7 +1347,6 @@ def test_data_uri_image(self) -> None: assert isinstance(result[0].content[0], KnowledgeBaseMessageImageContent) def test_non_image_uri_skipped(self) -> None: - from agent_framework import Content pdf = Content.from_uri(uri="https://example.com/doc.pdf", media_type="application/pdf") messages = [Message(role="user", contents=[pdf])] @@ -1568,9 +1562,7 @@ def test_references_become_annotations(self) -> None: KnowledgeBaseMessage(role="assistant", content=[KnowledgeBaseMessageTextContent(text="answer")]), ], references=[ - KnowledgeBaseWebReference( - id="ref-1", activity_source=0, url="https://example.com", title="Example" - ), + KnowledgeBaseWebReference(id="ref-1", activity_source=0, url="https://example.com", title="Example"), ], ) result = AzureAISearchContextProvider._parse_messages_from_kb_response(response) diff --git a/python/packages/azure-ai/agent_framework_azure_ai/__init__.py b/python/packages/azure-ai/agent_framework_azure_ai/__init__.py index 1b4c5716d0..46b1ed5b3b 100644 --- a/python/packages/azure-ai/agent_framework_azure_ai/__init__.py +++ b/python/packages/azure-ai/agent_framework_azure_ai/__init__.py @@ -5,6 +5,12 @@ from ._agent_provider import AzureAIAgentsProvider from ._chat_client import AzureAIAgentClient, AzureAIAgentOptions from ._client import AzureAIClient, AzureAIProjectAgentOptions, RawAzureAIClient +from ._embedding_client import ( + AzureAIInferenceEmbeddingClient, + AzureAIInferenceEmbeddingOptions, + AzureAIInferenceEmbeddingSettings, + RawAzureAIInferenceEmbeddingClient, +) from ._foundry_memory_provider import FoundryMemoryProvider from ._project_provider import AzureAIProjectAgentProvider from ._shared import AzureAISettings @@ -19,10 +25,14 @@ "AzureAIAgentOptions", "AzureAIAgentsProvider", "AzureAIClient", + "AzureAIInferenceEmbeddingClient", + "AzureAIInferenceEmbeddingOptions", + "AzureAIInferenceEmbeddingSettings", "AzureAIProjectAgentOptions", "AzureAIProjectAgentProvider", "AzureAISettings", "FoundryMemoryProvider", "RawAzureAIClient", + "RawAzureAIInferenceEmbeddingClient", "__version__", ] diff --git a/python/packages/azure-ai/agent_framework_azure_ai/_embedding_client.py b/python/packages/azure-ai/agent_framework_azure_ai/_embedding_client.py new file mode 100644 index 0000000000..7e6cdfc8b7 --- /dev/null +++ b/python/packages/azure-ai/agent_framework_azure_ai/_embedding_client.py @@ -0,0 +1,396 @@ +# Copyright (c) Microsoft. All rights reserved. + +from __future__ import annotations + +import logging +import sys +from collections.abc import Sequence +from contextlib import suppress +from typing import Any, ClassVar, Generic, TypedDict + +from agent_framework import ( + BaseEmbeddingClient, + Content, + Embedding, + EmbeddingGenerationOptions, + GeneratedEmbeddings, + UsageDetails, + load_settings, +) +from agent_framework.observability import EmbeddingTelemetryLayer +from azure.ai.inference.aio import EmbeddingsClient, ImageEmbeddingsClient +from azure.ai.inference.models import ImageEmbeddingInput +from azure.core.credentials import AzureKeyCredential + +if sys.version_info >= (3, 13): + from typing import TypeVar # type: ignore # pragma: no cover +else: + from typing_extensions import TypeVar # type: ignore # pragma: no cover + + +logger = logging.getLogger("agent_framework.azure_ai") + +_IMAGE_MEDIA_PREFIXES = ("image/",) + + +class AzureAIInferenceEmbeddingOptions(EmbeddingGenerationOptions, total=False): + """Azure AI Inference-specific embedding options. + + Extends EmbeddingGenerationOptions with Azure AI Inference-specific fields. + + Examples: + .. code-block:: python + + from agent_framework_azure_ai import AzureAIInferenceEmbeddingOptions + + options: AzureAIInferenceEmbeddingOptions = { + "model_id": "text-embedding-3-small", + "dimensions": 1536, + "input_type": "document", + "encoding_format": "float", + } + """ + + input_type: str + """Input type hint for the model. Common values: ``"text"``, ``"query"``, ``"document"``.""" + + image_model_id: str + """Override model for image embeddings. Falls back to the client's ``image_model_id``.""" + + encoding_format: str + """Output encoding format. + + Common values: ``"float"``, ``"base64"``, ``"int8"``, ``"uint8"``, + ``"binary"``, ``"ubinary"``. + """ + + extra_parameters: dict[str, Any] + """Additional model-specific parameters passed directly to the API.""" + + +AzureAIInferenceEmbeddingOptionsT = TypeVar( + "AzureAIInferenceEmbeddingOptionsT", + bound=TypedDict, # type: ignore[valid-type] + default="AzureAIInferenceEmbeddingOptions", + covariant=True, +) + + +class AzureAIInferenceEmbeddingSettings(TypedDict, total=False): + """Azure AI Inference embedding settings.""" + + endpoint: str | None + api_key: str | None + embedding_model_id: str | None + image_embedding_model_id: str | None + + +class RawAzureAIInferenceEmbeddingClient( + BaseEmbeddingClient[Content | str, list[float], AzureAIInferenceEmbeddingOptionsT], + Generic[AzureAIInferenceEmbeddingOptionsT], +): + """Raw Azure AI Inference embedding client without telemetry. + + Accepts both text (``str``) and image (``Content``) inputs. Text and image + inputs within a single batch are separated and dispatched to + ``EmbeddingsClient`` and ``ImageEmbeddingsClient`` respectively. Results + are reassembled in the original input order. + + Keyword Args: + model_id: The text embedding model deployment name (e.g. "text-embedding-3-small"). + Can also be set via environment variable AZURE_AI_INFERENCE_EMBEDDING_MODEL_ID. + image_model_id: The image embedding model deployment name (e.g. "Cohere-embed-v3-english"). + Can also be set via environment variable AZURE_AI_INFERENCE_IMAGE_EMBEDDING_MODEL_ID. + Falls back to ``model_id`` if not provided. + endpoint: The Azure AI Inference endpoint URL. + Can also be set via environment variable AZURE_AI_INFERENCE_ENDPOINT. + api_key: API key for authentication. + Can also be set via environment variable AZURE_AI_INFERENCE_API_KEY. + text_client: Optional pre-configured ``EmbeddingsClient``. + image_client: Optional pre-configured ``ImageEmbeddingsClient``. + credential: Optional ``AzureKeyCredential`` or token credential. If not provided, + one is created from ``api_key``. + env_file_path: Path to .env file for settings. + env_file_encoding: Encoding for .env file. + """ + + def __init__( + self, + *, + model_id: str | None = None, + image_model_id: str | None = None, + endpoint: str | None = None, + api_key: str | None = None, + text_client: EmbeddingsClient | None = None, + image_client: ImageEmbeddingsClient | None = None, + credential: AzureKeyCredential | None = None, + env_file_path: str | None = None, + env_file_encoding: str | None = None, + **kwargs: Any, + ) -> None: + """Initialize a raw Azure AI Inference embedding client.""" + settings = load_settings( + AzureAIInferenceEmbeddingSettings, + env_prefix="AZURE_AI_INFERENCE_", + required_fields=["endpoint", "embedding_model_id"], + endpoint=endpoint, + api_key=api_key, + embedding_model_id=model_id, + image_embedding_model_id=image_model_id, + env_file_path=env_file_path, + env_file_encoding=env_file_encoding, + ) + + self.model_id = settings["embedding_model_id"] # type: ignore[reportTypedDictNotRequiredAccess] + self.image_model_id: str = settings.get("image_embedding_model_id") or self.model_id # type: ignore[assignment] + resolved_endpoint = settings["endpoint"] # type: ignore[reportTypedDictNotRequiredAccess] + + if credential is None and settings.get("api_key"): + credential = AzureKeyCredential(settings["api_key"]) # type: ignore[arg-type] + + if credential is None and text_client is None and image_client is None: + raise ValueError("Either 'api_key', 'credential', or pre-configured client(s) must be provided.") + + self._text_client = text_client or EmbeddingsClient( + endpoint=resolved_endpoint, # type: ignore[arg-type] + credential=credential, # type: ignore[arg-type] + ) + self._image_client = image_client or ImageEmbeddingsClient( + endpoint=resolved_endpoint, # type: ignore[arg-type] + credential=credential, # type: ignore[arg-type] + ) + self._endpoint = resolved_endpoint + super().__init__(**kwargs) + + async def close(self) -> None: + """Close the underlying SDK clients and release resources.""" + with suppress(Exception): + await self._text_client.close() + with suppress(Exception): + await self._image_client.close() + + async def __aenter__(self) -> RawAzureAIInferenceEmbeddingClient[AzureAIInferenceEmbeddingOptionsT]: + """Enter the async context manager.""" + return self + + async def __aexit__(self, *args: Any) -> None: + """Exit the async context manager and close clients.""" + await self.close() + + def service_url(self) -> str: + """Get the URL of the service.""" + return self._endpoint or "" + + async def get_embeddings( + self, + values: Sequence[Content | str], + *, + options: AzureAIInferenceEmbeddingOptionsT | None = None, + ) -> GeneratedEmbeddings[list[float]]: + """Generate embeddings for text and/or image inputs. + + Text inputs (``str`` or ``Content`` with ``type="text"``) are sent to the + text embeddings endpoint. Image inputs (``Content`` with an image + ``media_type``) are sent to the image embeddings endpoint. Results are + returned in the same order as the input. + + Args: + values: A sequence of text strings or ``Content`` instances. + options: Optional embedding generation options. + + Returns: + Generated embeddings with usage metadata. + + Raises: + ValueError: If model_id is not provided or an unsupported content type is encountered. + """ + if not values: + return GeneratedEmbeddings([], options=options) # type: ignore[reportReturnType] + + opts: dict[str, Any] = dict(options) if options else {} + + # Separate text and image inputs, tracking original indices. + text_items: list[tuple[int, str]] = [] + image_items: list[tuple[int, ImageEmbeddingInput]] = [] + + for idx, value in enumerate(values): + if isinstance(value, str): + text_items.append((idx, value)) + elif isinstance(value, Content): + if value.type == "text" and value.text is not None: + text_items.append((idx, value.text)) + elif ( + value.type in ("data", "uri") + and value.media_type + and value.media_type.startswith(_IMAGE_MEDIA_PREFIXES[0]) + ): + if not value.uri: + raise ValueError(f"Image Content at index {idx} has no URI.") + image_input = ImageEmbeddingInput(image=value.uri, text=value.text) + image_items.append((idx, image_input)) + else: + raise ValueError( + f"Unsupported Content type '{value.type}' with media_type " + f"'{value.media_type}' at index {idx}. Expected text content or " + f"image content (media_type starting with 'image/')." + ) + else: + raise ValueError(f"Unsupported input type {type(value).__name__} at index {idx}.") + + # Build shared API kwargs (without model, which differs per client). + common_kwargs: dict[str, Any] = {} + if dimensions := opts.get("dimensions"): + common_kwargs["dimensions"] = dimensions + if encoding_format := opts.get("encoding_format"): + common_kwargs["encoding_format"] = encoding_format + if input_type := opts.get("input_type"): + common_kwargs["input_type"] = input_type + if extra_parameters := opts.get("extra_parameters"): + common_kwargs["model_extras"] = extra_parameters + + # Allocate results array. + embeddings: list[Embedding[list[float]] | None] = [None] * len(values) + usage_details: UsageDetails = {} + + # Embed text inputs. + if text_items: + if not (text_model := opts.get("model_id") or self.model_id): + raise ValueError("An model_id is required, either in the client or options, for text inputs.") + text_inputs = [t for _, t in text_items] + response = await self._text_client.embed( + input=text_inputs, + model=text_model, + **common_kwargs, + ) + for i, item in enumerate(response.data): + original_idx = text_items[i][0] + vector: list[float] = [float(v) for v in item.embedding] + embeddings[original_idx] = Embedding( + vector=vector, + dimensions=len(vector), + model_id=response.model or text_model, + ) + if response.usage: + usage_details["input_token_count"] = (usage_details.get("input_token_count") or 0) + ( + response.usage.prompt_tokens or 0 + ) + usage_details["output_token_count"] = (usage_details.get("output_token_count") or 0) + ( + getattr(response.usage, "completion_tokens", 0) or 0 + ) + + # Embed image inputs. + if image_items: + if not (image_model := opts.get("image_model_id") or self.image_model_id): + raise ValueError("An image_model_id is required, either in the client or options, for image inputs.") + image_inputs = [img for _, img in image_items] + response = await self._image_client.embed( + input=image_inputs, + model=image_model, + **common_kwargs, + ) + for i, item in enumerate(response.data): + original_idx = image_items[i][0] + image_vector: list[float] = [float(v) for v in item.embedding] + embeddings[original_idx] = Embedding( + vector=image_vector, + dimensions=len(image_vector), + model_id=response.model or image_model, + ) + if response.usage: + usage_details["input_token_count"] = (usage_details.get("input_token_count") or 0) + ( + response.usage.prompt_tokens or 0 + ) + usage_details["output_token_count"] = (usage_details.get("output_token_count") or 0) + ( + getattr(response.usage, "completion_tokens", 0) or 0 + ) + return GeneratedEmbeddings( + [embedding for embedding in embeddings if embedding is not None], + options=options, + usage=usage_details, + ) # type: ignore[reportReturnType] + + +class AzureAIInferenceEmbeddingClient( + EmbeddingTelemetryLayer[Content | str, list[float], AzureAIInferenceEmbeddingOptionsT], + RawAzureAIInferenceEmbeddingClient[AzureAIInferenceEmbeddingOptionsT], + Generic[AzureAIInferenceEmbeddingOptionsT], +): + """Azure AI Inference embedding client with telemetry support. + + Supports both text and image inputs in a single client. Pass plain strings + or ``Content`` instances created with ``Content.from_text()`` or + ``Content.from_data()``. + + Keyword Args: + model_id: The text embedding model deployment name (e.g. "text-embedding-3-small"). + Can also be set via environment variable AZURE_AI_INFERENCE_EMBEDDING_MODEL_ID. + image_model_id: The image embedding model deployment name + (e.g. "Cohere-embed-v3-english"). Can also be set via environment variable + AZURE_AI_INFERENCE_IMAGE_EMBEDDING_MODEL_ID. Falls back to ``model_id``. + endpoint: The Azure AI Inference endpoint URL. + Can also be set via environment variable AZURE_AI_INFERENCE_ENDPOINT. + api_key: API key for authentication. + Can also be set via environment variable AZURE_AI_INFERENCE_API_KEY. + text_client: Optional pre-configured ``EmbeddingsClient``. + image_client: Optional pre-configured ``ImageEmbeddingsClient``. + credential: Optional ``AzureKeyCredential`` or token credential. + otel_provider_name: Override for the OpenTelemetry provider name. + env_file_path: Path to .env file for settings. + env_file_encoding: Encoding for .env file. + + Examples: + .. code-block:: python + + from agent_framework_azure_ai import AzureAIInferenceEmbeddingClient + + # Using environment variables + # Set AZURE_AI_INFERENCE_ENDPOINT=https://your-endpoint.inference.ai.azure.com + # Set AZURE_AI_INFERENCE_API_KEY=your-key + # Set AZURE_AI_INFERENCE_EMBEDDING_MODEL_ID=text-embedding-3-small + # Set AZURE_AI_INFERENCE_IMAGE_EMBEDDING_MODEL_ID=Cohere-embed-v3-english + client = AzureAIInferenceEmbeddingClient() + + # Text embeddings + result = await client.get_embeddings(["Hello, world!"]) + + # Image embeddings + from agent_framework import Content + + image = Content.from_data(data=image_bytes, media_type="image/png") + result = await client.get_embeddings([image]) + + # Mixed text and image + result = await client.get_embeddings(["hello", image]) + """ + + OTEL_PROVIDER_NAME: ClassVar[str] = "azure.ai.inference" # type: ignore[reportIncompatibleVariableOverride, misc] + + def __init__( + self, + *, + model_id: str | None = None, + image_model_id: str | None = None, + endpoint: str | None = None, + api_key: str | None = None, + text_client: EmbeddingsClient | None = None, + image_client: ImageEmbeddingsClient | None = None, + credential: AzureKeyCredential | None = None, + otel_provider_name: str | None = None, + env_file_path: str | None = None, + env_file_encoding: str | None = None, + **kwargs: Any, + ) -> None: + """Initialize an Azure AI Inference embedding client.""" + super().__init__( + model_id=model_id, + image_model_id=image_model_id, + endpoint=endpoint, + api_key=api_key, + text_client=text_client, + image_client=image_client, + credential=credential, + otel_provider_name=otel_provider_name, + env_file_path=env_file_path, + env_file_encoding=env_file_encoding, + **kwargs, + ) diff --git a/python/packages/azure-ai/pyproject.toml b/python/packages/azure-ai/pyproject.toml index 8fb0adfbd1..af8baf1fb9 100644 --- a/python/packages/azure-ai/pyproject.toml +++ b/python/packages/azure-ai/pyproject.toml @@ -4,7 +4,7 @@ description = "Azure AI Foundry integration for Microsoft Agent Framework." authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0rc1" +version = "1.0.0rc2" license-files = ["LICENSE"] urls.homepage = "https://aka.ms/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -23,8 +23,9 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", "azure-ai-agents == 1.2.0b5", + "azure-ai-inference>=1.0.0b9", "aiohttp", ] @@ -38,6 +39,7 @@ environments = [ [tool.uv-dynamic-versioning] fallback-version = "0.0.0" + [tool.pytest.ini_options] testpaths = 'tests' addopts = "-ra -q -r fEX" @@ -45,6 +47,9 @@ asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" filterwarnings = [] timeout = 120 +markers = [ + "integration: marks tests as integration tests that require external services", +] [tool.ruff] extend = "../../pyproject.toml" @@ -78,6 +83,7 @@ exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" + [tool.poe.tasks] mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_azure_ai" test = "pytest --cov=agent_framework_azure_ai --cov-report=term-missing:skip-covered tests" diff --git a/python/packages/azure-ai/tests/azure_ai/test_azure_ai_inference_embedding_client.py b/python/packages/azure-ai/tests/azure_ai/test_azure_ai_inference_embedding_client.py new file mode 100644 index 0000000000..0ec6a8b811 --- /dev/null +++ b/python/packages/azure-ai/tests/azure_ai/test_azure_ai_inference_embedding_client.py @@ -0,0 +1,316 @@ +# Copyright (c) Microsoft. All rights reserved. + +from __future__ import annotations + +import os +from collections.abc import Sequence +from typing import Any +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest +from agent_framework import Content + +from agent_framework_azure_ai import ( + AzureAIInferenceEmbeddingClient, + AzureAIInferenceEmbeddingOptions, + RawAzureAIInferenceEmbeddingClient, +) + + +def _make_embed_response( + embeddings: Sequence[list[float]], + model: str = "test-model", + prompt_tokens: int = 10, +) -> MagicMock: + """Create a mock EmbeddingsResult.""" + data = [] + for emb in embeddings: + item = MagicMock() + item.embedding = emb + data.append(item) + + usage = MagicMock() + usage.prompt_tokens = prompt_tokens + usage.completion_tokens = 0 + + result = MagicMock() + result.data = data + result.model = model + result.usage = usage + return result + + +@pytest.fixture +def mock_text_client() -> AsyncMock: + """Create a mock text EmbeddingsClient.""" + client = AsyncMock() + client.embed = AsyncMock(return_value=_make_embed_response([[0.1, 0.2, 0.3]])) + return client + + +@pytest.fixture +def mock_image_client() -> AsyncMock: + """Create a mock image ImageEmbeddingsClient.""" + client = AsyncMock() + client.embed = AsyncMock(return_value=_make_embed_response([[0.4, 0.5, 0.6]])) + return client + + +@pytest.fixture +def raw_client(mock_text_client: AsyncMock, mock_image_client: AsyncMock) -> RawAzureAIInferenceEmbeddingClient[Any]: + """Create a RawAzureAIInferenceEmbeddingClient with mocked SDK clients.""" + return RawAzureAIInferenceEmbeddingClient( + model_id="test-model", + endpoint="https://test.inference.ai.azure.com", + api_key="test-key", + text_client=mock_text_client, + image_client=mock_image_client, + ) + + +@pytest.fixture +def client(mock_text_client: AsyncMock, mock_image_client: AsyncMock) -> AzureAIInferenceEmbeddingClient[Any]: + """Create an AzureAIInferenceEmbeddingClient with mocked SDK clients.""" + return AzureAIInferenceEmbeddingClient( + model_id="test-model", + endpoint="https://test.inference.ai.azure.com", + api_key="test-key", + text_client=mock_text_client, + image_client=mock_image_client, + ) + + +class TestRawAzureAIInferenceEmbeddingClient: + """Tests for the raw Azure AI Inference embedding client.""" + + async def test_text_embeddings( + self, raw_client: RawAzureAIInferenceEmbeddingClient[Any], mock_text_client: AsyncMock + ) -> None: + """Text inputs are dispatched to the text client.""" + result = await raw_client.get_embeddings(["hello", "world"]) + assert result is not None + call_kwargs = mock_text_client.embed.call_args + assert call_kwargs.kwargs["input"] == ["hello", "world"] + assert call_kwargs.kwargs["model"] == "test-model" + + async def test_text_content_embeddings( + self, raw_client: RawAzureAIInferenceEmbeddingClient[Any], mock_text_client: AsyncMock + ) -> None: + """Content.from_text() inputs are dispatched to the text client.""" + text_content = Content.from_text("hello") + await raw_client.get_embeddings([text_content]) + + mock_text_client.embed.assert_called_once() + call_kwargs = mock_text_client.embed.call_args + assert call_kwargs.kwargs["input"] == ["hello"] + + async def test_image_content_embeddings( + self, raw_client: RawAzureAIInferenceEmbeddingClient[Any], mock_image_client: AsyncMock + ) -> None: + """Image Content inputs are dispatched to the image client.""" + image_content = Content.from_data(data=b"\x89PNG", media_type="image/png") + await raw_client.get_embeddings([image_content]) + + mock_image_client.embed.assert_called_once() + call_kwargs = mock_image_client.embed.call_args + image_inputs = call_kwargs.kwargs["input"] + assert len(image_inputs) == 1 + assert image_inputs[0].image == image_content.uri + + async def test_mixed_text_and_image( + self, + raw_client: RawAzureAIInferenceEmbeddingClient[Any], + mock_text_client: AsyncMock, + mock_image_client: AsyncMock, + ) -> None: + """Mixed text and image inputs are dispatched to the correct clients.""" + mock_text_client.embed.return_value = _make_embed_response([[0.1, 0.2]]) + mock_image_client.embed.return_value = _make_embed_response([[0.3, 0.4]]) + + image = Content.from_data(data=b"\x89PNG", media_type="image/png") + await raw_client.get_embeddings(["hello", image, "world"]) + + # Text client gets "hello" and "world" + text_call = mock_text_client.embed.call_args + assert text_call.kwargs["input"] == ["hello", "world"] + + # Image client gets the image + image_call = mock_image_client.embed.call_args + assert len(image_call.kwargs["input"]) == 1 + + async def test_empty_input(self, raw_client: RawAzureAIInferenceEmbeddingClient[Any]) -> None: + """Empty input returns empty result.""" + result = await raw_client.get_embeddings([]) + assert len(result) == 0 + + async def test_options_passed_through( + self, raw_client: RawAzureAIInferenceEmbeddingClient[Any], mock_text_client: AsyncMock + ) -> None: + """Options are passed through to the SDK.""" + options: AzureAIInferenceEmbeddingOptions = { + "dimensions": 512, + "input_type": "document", + "encoding_format": "float", + } + await raw_client.get_embeddings(["hello"], options=options) + + call_kwargs = mock_text_client.embed.call_args + assert call_kwargs.kwargs["dimensions"] == 512 + assert call_kwargs.kwargs["input_type"] == "document" + assert call_kwargs.kwargs["encoding_format"] == "float" + + async def test_model_override_in_options( + self, raw_client: RawAzureAIInferenceEmbeddingClient[Any], mock_text_client: AsyncMock + ) -> None: + """model_id in options overrides the default.""" + options: AzureAIInferenceEmbeddingOptions = {"model_id": "custom-model"} + await raw_client.get_embeddings(["hello"], options=options) + + call_kwargs = mock_text_client.embed.call_args + assert call_kwargs.kwargs["model"] == "custom-model" + + async def test_unsupported_content_type_raises(self, raw_client: RawAzureAIInferenceEmbeddingClient[Any]) -> None: + """Non-text, non-image Content raises ValueError.""" + error_content = Content("error", message="fail") + with pytest.raises(ValueError, match="Unsupported Content type"): + await raw_client.get_embeddings([error_content]) + + async def test_usage_metadata( + self, raw_client: RawAzureAIInferenceEmbeddingClient[Any], mock_text_client: AsyncMock + ) -> None: + """Usage metadata is populated from the response.""" + mock_text_client.embed.return_value = _make_embed_response([[0.1, 0.2]], prompt_tokens=42) + result = await raw_client.get_embeddings(["hello"]) + assert result.usage is not None + assert result.usage["input_token_count"] == 42 + + def test_service_url(self, raw_client: RawAzureAIInferenceEmbeddingClient[Any]) -> None: + """service_url returns the configured endpoint.""" + assert raw_client.service_url() == "https://test.inference.ai.azure.com" + + def test_settings_from_env(self) -> None: + """Settings are loaded from environment variables.""" + with ( + patch.dict( + os.environ, + { + "AZURE_AI_INFERENCE_ENDPOINT": "https://env.inference.ai.azure.com", + "AZURE_AI_INFERENCE_API_KEY": "env-key", + "AZURE_AI_INFERENCE_EMBEDDING_MODEL_ID": "env-model", + }, + ), + patch("agent_framework_azure_ai._embedding_client.EmbeddingsClient"), + patch("agent_framework_azure_ai._embedding_client.ImageEmbeddingsClient"), + ): + client = RawAzureAIInferenceEmbeddingClient() + assert client.model_id == "env-model" + assert client.image_model_id == "env-model" # falls back to model_id + + def test_image_model_id_from_env(self) -> None: + """image_model_id is loaded from its own environment variable.""" + with ( + patch.dict( + os.environ, + { + "AZURE_AI_INFERENCE_ENDPOINT": "https://env.inference.ai.azure.com", + "AZURE_AI_INFERENCE_API_KEY": "env-key", + "AZURE_AI_INFERENCE_EMBEDDING_MODEL_ID": "text-model", + "AZURE_AI_INFERENCE_IMAGE_EMBEDDING_MODEL_ID": "image-model", + }, + ), + patch("agent_framework_azure_ai._embedding_client.EmbeddingsClient"), + patch("agent_framework_azure_ai._embedding_client.ImageEmbeddingsClient"), + ): + client = RawAzureAIInferenceEmbeddingClient() + assert client.model_id == "text-model" + assert client.image_model_id == "image-model" + + def test_image_model_id_explicit(self, mock_text_client: AsyncMock, mock_image_client: AsyncMock) -> None: + """image_model_id can be set explicitly.""" + client = RawAzureAIInferenceEmbeddingClient( + model_id="text-model", + image_model_id="image-model", + endpoint="https://test.inference.ai.azure.com", + api_key="test-key", + text_client=mock_text_client, + image_client=mock_image_client, + ) + assert client.model_id == "text-model" + assert client.image_model_id == "image-model" + + async def test_image_model_id_sent_to_image_client( + self, mock_text_client: AsyncMock, mock_image_client: AsyncMock + ) -> None: + """image_model_id is passed to the image client embed call.""" + client = RawAzureAIInferenceEmbeddingClient( + model_id="text-model", + image_model_id="image-model", + endpoint="https://test.inference.ai.azure.com", + api_key="test-key", + text_client=mock_text_client, + image_client=mock_image_client, + ) + image_content = Content.from_data(data=b"\x89PNG", media_type="image/png") + await client.get_embeddings([image_content]) + call_kwargs = mock_image_client.embed.call_args + assert call_kwargs.kwargs["model"] == "image-model" + + +class TestAzureAIInferenceEmbeddingClient: + """Tests for the telemetry-enabled Azure AI Inference embedding client.""" + + async def test_text_embeddings( + self, client: AzureAIInferenceEmbeddingClient[Any], mock_text_client: AsyncMock + ) -> None: + """Text embeddings work through the telemetry layer.""" + result = await client.get_embeddings(["hello"]) + assert len(result) == 1 + assert result[0].vector == [0.1, 0.2, 0.3] + + async def test_otel_provider_name_default(self) -> None: + """Default OTEL provider name is azure.ai.inference.""" + assert AzureAIInferenceEmbeddingClient.OTEL_PROVIDER_NAME == "azure.ai.inference" + + async def test_otel_provider_name_override(self, mock_text_client: AsyncMock, mock_image_client: AsyncMock) -> None: + """OTEL provider name can be overridden.""" + client = AzureAIInferenceEmbeddingClient( + model_id="test-model", + endpoint="https://test.inference.ai.azure.com", + api_key="test-key", + text_client=mock_text_client, + image_client=mock_image_client, + otel_provider_name="custom-provider", + ) + assert client.otel_provider_name == "custom-provider" + + +_SKIP_REASON = "Azure AI Inference integration tests disabled" + + +def _integration_tests_enabled() -> bool: + return bool( + os.environ.get("AZURE_AI_INFERENCE_ENDPOINT") + and os.environ.get("AZURE_AI_INFERENCE_API_KEY") + and os.environ.get("AZURE_AI_INFERENCE_EMBEDDING_MODEL_ID") + ) + + +skip_if_azure_ai_inference_integration_tests_disabled = pytest.mark.skipif( + not _integration_tests_enabled(), + reason=_SKIP_REASON, +) + + +class TestAzureAIInferenceEmbeddingIntegration: + """Integration tests requiring a live Azure AI Inference endpoint.""" + + @pytest.mark.flaky + @pytest.mark.integration + @skip_if_azure_ai_inference_integration_tests_disabled + async def test_text_embedding_live(self) -> None: + """Generate text embeddings against a live endpoint.""" + client = AzureAIInferenceEmbeddingClient() + result = await client.get_embeddings(["Hello, world!"]) + assert len(result) == 1 + assert len(result[0].vector) > 0 + assert result[0].model_id is not None diff --git a/python/packages/azurefunctions/pyproject.toml b/python/packages/azurefunctions/pyproject.toml index c0bda645ec..35f992e400 100644 --- a/python/packages/azurefunctions/pyproject.toml +++ b/python/packages/azurefunctions/pyproject.toml @@ -4,7 +4,7 @@ description = "Azure Functions integration for Microsoft Agent Framework." authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0b260219" +version = "1.0.0b260225" license-files = ["LICENSE"] urls.homepage = "https://aka.ms/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -22,7 +22,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", "agent-framework-durabletask", "azure-functions", "azure-functions-durable", @@ -41,6 +41,7 @@ environments = [ [tool.uv-dynamic-versioning] fallback-version = "0.0.0" + [tool.pytest.ini_options] testpaths = 'tests' pythonpath = ["tests/integration_tests"] @@ -88,6 +89,7 @@ exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" + [tool.poe.tasks] mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_azurefunctions" test = "pytest --cov=agent_framework_azurefunctions --cov-report=term-missing:skip-covered tests" diff --git a/python/packages/bedrock/agent_framework_bedrock/__init__.py b/python/packages/bedrock/agent_framework_bedrock/__init__.py index c33badcb35..3fbf5c15cf 100644 --- a/python/packages/bedrock/agent_framework_bedrock/__init__.py +++ b/python/packages/bedrock/agent_framework_bedrock/__init__.py @@ -3,6 +3,7 @@ import importlib.metadata from ._chat_client import BedrockChatClient, BedrockChatOptions, BedrockGuardrailConfig, BedrockSettings +from ._embedding_client import BedrockEmbeddingClient, BedrockEmbeddingOptions, BedrockEmbeddingSettings try: __version__ = importlib.metadata.version(__name__) @@ -12,6 +13,9 @@ __all__ = [ "BedrockChatClient", "BedrockChatOptions", + "BedrockEmbeddingClient", + "BedrockEmbeddingOptions", + "BedrockEmbeddingSettings", "BedrockGuardrailConfig", "BedrockSettings", "__version__", diff --git a/python/packages/bedrock/agent_framework_bedrock/_embedding_client.py b/python/packages/bedrock/agent_framework_bedrock/_embedding_client.py new file mode 100644 index 0000000000..30be74eed9 --- /dev/null +++ b/python/packages/bedrock/agent_framework_bedrock/_embedding_client.py @@ -0,0 +1,292 @@ +# Copyright (c) Microsoft. All rights reserved. + +from __future__ import annotations + +import asyncio +import json +import logging +import sys +from collections.abc import Sequence +from typing import Any, ClassVar, Generic, TypedDict + +from agent_framework import ( + AGENT_FRAMEWORK_USER_AGENT, + BaseEmbeddingClient, + Embedding, + EmbeddingGenerationOptions, + GeneratedEmbeddings, + SecretString, + UsageDetails, + load_settings, +) +from agent_framework.observability import EmbeddingTelemetryLayer +from boto3.session import Session as Boto3Session +from botocore.client import BaseClient +from botocore.config import Config as BotoConfig + +if sys.version_info >= (3, 13): + from typing import TypeVar # type: ignore # pragma: no cover +else: + from typing_extensions import TypeVar # type: ignore # pragma: no cover + + +logger = logging.getLogger("agent_framework.bedrock") +DEFAULT_REGION = "us-east-1" + + +class BedrockEmbeddingSettings(TypedDict, total=False): + """Bedrock embedding settings.""" + + region: str | None + embedding_model_id: str | None + access_key: SecretString | None + secret_key: SecretString | None + session_token: SecretString | None + + +class BedrockEmbeddingOptions(EmbeddingGenerationOptions, total=False): + """Bedrock-specific embedding options. + + Extends EmbeddingGenerationOptions with Bedrock-specific fields. + + Examples: + .. code-block:: python + + from agent_framework_bedrock import BedrockEmbeddingOptions + + options: BedrockEmbeddingOptions = { + "model_id": "amazon.titan-embed-text-v2:0", + "dimensions": 1024, + "normalize": True, + } + """ + + normalize: bool + + +BedrockEmbeddingOptionsT = TypeVar( + "BedrockEmbeddingOptionsT", + bound=TypedDict, # type: ignore[valid-type] + default="BedrockEmbeddingOptions", + covariant=True, +) + + +class RawBedrockEmbeddingClient( + BaseEmbeddingClient[str, list[float], BedrockEmbeddingOptionsT], + Generic[BedrockEmbeddingOptionsT], +): + """Raw Bedrock embedding client without telemetry. + + Keyword Args: + model_id: The Bedrock embedding model ID (e.g. "amazon.titan-embed-text-v2:0"). + Can also be set via environment variable BEDROCK_EMBEDDING_MODEL_ID. + region: AWS region. Will try to load from BEDROCK_REGION env var, + if not set, the regular Boto3 configuration/loading applies + (which may include other env vars, config files, or instance metadata). + access_key: AWS access key for manual credential injection. + secret_key: AWS secret key paired with access_key. + session_token: AWS session token for temporary credentials. + client: Preconfigured Bedrock runtime client. + boto3_session: Custom boto3 session used to build the runtime client. + env_file_path: Path to .env file for settings. + env_file_encoding: Encoding for .env file. + """ + + def __init__( + self, + *, + region: str | None = None, + model_id: str | None = None, + access_key: str | None = None, + secret_key: str | None = None, + session_token: str | None = None, + client: BaseClient | None = None, + boto3_session: Boto3Session | None = None, + env_file_path: str | None = None, + env_file_encoding: str | None = None, + **kwargs: Any, + ) -> None: + """Initialize a raw Bedrock embedding client.""" + settings = load_settings( + BedrockEmbeddingSettings, + env_prefix="BEDROCK_", + required_fields=["embedding_model_id"], + region=region, + embedding_model_id=model_id, + access_key=access_key, + secret_key=secret_key, + session_token=session_token, + env_file_path=env_file_path, + env_file_encoding=env_file_encoding, + ) + resolved_region = settings.get("region") or DEFAULT_REGION + + if client is None: + if not boto3_session: + session_kwargs: dict[str, Any] = {} + if region := settings.get("region"): + session_kwargs["region_name"] = region + if (access_key := settings.get("access_key")) and (secret_key := settings.get("secret_key")): + session_kwargs["aws_access_key_id"] = access_key.get_secret_value() # type: ignore[union-attr] + session_kwargs["aws_secret_access_key"] = secret_key.get_secret_value() # type: ignore[union-attr] + if session_token := settings.get("session_token"): + session_kwargs["aws_session_token"] = session_token.get_secret_value() # type: ignore[union-attr] + boto3_session = Boto3Session(**session_kwargs) + client = boto3_session.client( + "bedrock-runtime", + region_name=boto3_session.region_name or resolved_region, + config=BotoConfig(user_agent_extra=AGENT_FRAMEWORK_USER_AGENT), + ) + + self._bedrock_client = client + self.model_id = settings["embedding_model_id"] # type: ignore[assignment] + self.region = resolved_region + super().__init__(**kwargs) + + def service_url(self) -> str: + """Get the URL of the service.""" + return str(self._bedrock_client.meta.endpoint_url) + + async def get_embeddings( + self, + values: Sequence[str], + *, + options: BedrockEmbeddingOptionsT | None = None, + ) -> GeneratedEmbeddings[list[float]]: + """Call the Bedrock invoke_model API for embeddings. + + Uses the Amazon Titan Embeddings model format. Each value is embedded + individually since Titan's invoke_model API accepts one input at a time. + + Args: + values: The text values to generate embeddings for. + options: Optional embedding generation options. + + Returns: + Generated embeddings with usage metadata. + + Raises: + ValueError: If model_id is not provided or values is empty. + """ + if not values: + return GeneratedEmbeddings([], options=options) + + opts: dict[str, Any] = dict(options) if options else {} + model = opts.get("model_id") or self.model_id + if not model: + raise ValueError("model_id is required") + + embedding_results = await asyncio.gather( + *(self._generate_embedding_for_text(opts, model, text) for text in values) + ) + embeddings: list[Embedding[list[float]]] = [] + total_input_tokens = 0 + for embedding, input_tokens in embedding_results: + embeddings.append(embedding) + total_input_tokens += input_tokens + + usage_dict: UsageDetails | None = None + if total_input_tokens > 0: + usage_dict = {"input_token_count": total_input_tokens} + + return GeneratedEmbeddings(embeddings, options=options, usage=usage_dict) + + async def _generate_embedding_for_text( + self, + opts: dict[str, Any], + model: str, + text: str, + ) -> tuple[Embedding[list[float]], int]: + body: dict[str, Any] = {"inputText": text} + if dimensions := opts.get("dimensions"): + body["dimensions"] = dimensions + if (normalize := opts.get("normalize")) is not None: + body["normalize"] = normalize + + response = await asyncio.to_thread( + self._bedrock_client.invoke_model, + modelId=model, + contentType="application/json", + accept="application/json", + body=json.dumps(body), + ) + + response_body = json.loads(response["body"].read()) + embedding = Embedding( + vector=response_body["embedding"], + dimensions=len(response_body["embedding"]), + model_id=model, + ) + input_tokens = int(response_body.get("inputTextTokenCount", 0)) + return embedding, input_tokens + + +class BedrockEmbeddingClient( + EmbeddingTelemetryLayer[str, list[float], BedrockEmbeddingOptionsT], + RawBedrockEmbeddingClient[BedrockEmbeddingOptionsT], + Generic[BedrockEmbeddingOptionsT], +): + """Bedrock embedding client with telemetry support. + + Uses the Amazon Titan Embeddings model via Bedrock's invoke_model API. + + Keyword Args: + model_id: The Bedrock embedding model ID (e.g. "amazon.titan-embed-text-v2:0"). + Can also be set via environment variable BEDROCK_EMBEDDING_MODEL_ID. + region: AWS region. Defaults to "us-east-1". + Can also be set via environment variable BEDROCK_REGION. + access_key: AWS access key for manual credential injection. + secret_key: AWS secret key paired with access_key. + session_token: AWS session token for temporary credentials. + client: Preconfigured Bedrock runtime client. + boto3_session: Custom boto3 session used to build the runtime client. + env_file_path: Path to .env file for settings. + env_file_encoding: Encoding for .env file. + + Examples: + .. code-block:: python + + from agent_framework_bedrock import BedrockEmbeddingClient + + # Using default AWS credentials + client = BedrockEmbeddingClient( + model_id="amazon.titan-embed-text-v2:0", + ) + + # Generate embeddings + result = await client.get_embeddings(["Hello, world!"]) + print(result[0].vector) + """ + + OTEL_PROVIDER_NAME: ClassVar[str] = "aws.bedrock" # type: ignore[reportIncompatibleVariableOverride, misc] + + def __init__( + self, + *, + region: str | None = None, + model_id: str | None = None, + access_key: str | None = None, + secret_key: str | None = None, + session_token: str | None = None, + client: BaseClient | None = None, + boto3_session: Boto3Session | None = None, + otel_provider_name: str | None = None, + env_file_path: str | None = None, + env_file_encoding: str | None = None, + **kwargs: Any, + ) -> None: + """Initialize a Bedrock embedding client.""" + super().__init__( + region=region, + model_id=model_id, + access_key=access_key, + secret_key=secret_key, + session_token=session_token, + client=client, + boto3_session=boto3_session, + otel_provider_name=otel_provider_name, + env_file_path=env_file_path, + env_file_encoding=env_file_encoding, + **kwargs, + ) diff --git a/python/packages/bedrock/pyproject.toml b/python/packages/bedrock/pyproject.toml index a7bc169104..a5bd9577a8 100644 --- a/python/packages/bedrock/pyproject.toml +++ b/python/packages/bedrock/pyproject.toml @@ -4,7 +4,7 @@ description = "Amazon Bedrock integration for Microsoft Agent Framework." authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0b260219" +version = "1.0.0b260225" license-files = ["LICENSE"] urls.homepage = "https://aka.ms/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -23,12 +23,11 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", "boto3>=1.35.0,<2.0.0", "botocore>=1.35.0,<2.0.0", ] - [tool.uv] prerelease = "if-necessary-or-explicit" environments = [ @@ -46,6 +45,9 @@ addopts = "-ra -q -r fEX" asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" filterwarnings = [] +markers = [ + "integration: marks tests as integration tests that require external services", +] timeout = 120 [tool.ruff] diff --git a/python/packages/bedrock/tests/bedrock/test_bedrock_embedding_client.py b/python/packages/bedrock/tests/bedrock/test_bedrock_embedding_client.py new file mode 100644 index 0000000000..f6f96254a7 --- /dev/null +++ b/python/packages/bedrock/tests/bedrock/test_bedrock_embedding_client.py @@ -0,0 +1,169 @@ +# Copyright (c) Microsoft. All rights reserved. + +from __future__ import annotations + +import json +import os +from typing import Any +from unittest.mock import MagicMock + +import pytest +from agent_framework import Embedding, GeneratedEmbeddings + +from agent_framework_bedrock import BedrockEmbeddingClient, BedrockEmbeddingOptions + + +class _StubBedrockEmbeddingRuntime: + """Stub for the Bedrock runtime client that handles invoke_model for embeddings.""" + + def __init__(self) -> None: + self.calls: list[dict[str, Any]] = [] + self.meta = MagicMock(endpoint_url="https://bedrock-runtime.us-west-2.amazonaws.com") + + def invoke_model(self, **kwargs: Any) -> dict[str, Any]: + self.calls.append(kwargs) + body = json.loads(kwargs.get("body", "{}")) + # Simulate Titan embedding response + dimensions = body.get("dimensions", 3) + return { + "body": MagicMock( + read=lambda: json.dumps({ + "embedding": [0.1 * (i + 1) for i in range(dimensions)], + "inputTextTokenCount": 5, + }).encode() + ), + } + + +async def test_bedrock_embedding_construction() -> None: + """Test construction with explicit parameters.""" + stub = _StubBedrockEmbeddingRuntime() + client = BedrockEmbeddingClient( + model_id="amazon.titan-embed-text-v2:0", + region="us-west-2", + client=stub, + ) + assert client.model_id == "amazon.titan-embed-text-v2:0" + assert client.region == "us-west-2" + + +async def test_bedrock_embedding_construction_missing_model_raises(monkeypatch: pytest.MonkeyPatch) -> None: + """Test that missing model_id raises an error.""" + monkeypatch.delenv("BEDROCK_EMBEDDING_MODEL_ID", raising=False) + from agent_framework.exceptions import SettingNotFoundError + + with pytest.raises(SettingNotFoundError): + BedrockEmbeddingClient(region="us-west-2") + + +async def test_bedrock_embedding_get_embeddings() -> None: + """Test generating embeddings via the Bedrock invoke_model API.""" + stub = _StubBedrockEmbeddingRuntime() + client = BedrockEmbeddingClient( + model_id="amazon.titan-embed-text-v2:0", + region="us-west-2", + client=stub, + ) + + result = await client.get_embeddings(["hello", "world"]) + + assert isinstance(result, GeneratedEmbeddings) + assert len(result) == 2 + assert len(result[0].vector) == 3 + assert len(result[1].vector) == 3 + assert result[0].model_id == "amazon.titan-embed-text-v2:0" + assert result.usage == {"input_token_count": 10} + + # Two calls since Titan processes one input at a time + assert len(stub.calls) == 2 + call_texts = {json.loads(call["body"])["inputText"] for call in stub.calls} + assert call_texts == {"hello", "world"} + + +async def test_bedrock_embedding_get_embeddings_empty_input() -> None: + """Test generating embeddings with empty input.""" + stub = _StubBedrockEmbeddingRuntime() + client = BedrockEmbeddingClient( + model_id="amazon.titan-embed-text-v2:0", + region="us-west-2", + client=stub, + ) + + result = await client.get_embeddings([]) + + assert isinstance(result, GeneratedEmbeddings) + assert len(result) == 0 + assert len(stub.calls) == 0 + + +async def test_bedrock_embedding_get_embeddings_with_options() -> None: + """Test generating embeddings with custom options.""" + stub = _StubBedrockEmbeddingRuntime() + client = BedrockEmbeddingClient( + model_id="amazon.titan-embed-text-v2:0", + region="us-west-2", + client=stub, + ) + + options: BedrockEmbeddingOptions = { + "dimensions": 5, + "normalize": True, + } + result = await client.get_embeddings(["hello"], options=options) + + assert len(result) == 1 + assert len(result[0].vector) == 5 + + body = json.loads(stub.calls[0]["body"]) + assert body["dimensions"] == 5 + assert body["normalize"] is True + + +async def test_bedrock_embedding_get_embeddings_no_model_raises() -> None: + """Test that missing model_id at call time raises ValueError.""" + stub = _StubBedrockEmbeddingRuntime() + client = BedrockEmbeddingClient( + model_id="amazon.titan-embed-text-v2:0", + region="us-west-2", + client=stub, + ) + client.model_id = None # type: ignore[assignment] + + with pytest.raises(ValueError, match="model_id is required"): + await client.get_embeddings(["hello"]) + + +async def test_bedrock_embedding_default_region() -> None: + """Test that default region is us-east-1.""" + stub = _StubBedrockEmbeddingRuntime() + client = BedrockEmbeddingClient( + model_id="amazon.titan-embed-text-v2:0", + client=stub, + ) + assert client.region == "us-east-1" + + +# region: Integration Tests + +skip_if_bedrock_embedding_integration_tests_disabled = pytest.mark.skipif( + os.getenv("BEDROCK_EMBEDDING_MODEL_ID", "") in ("", "test-model") + or not (os.getenv("AWS_ACCESS_KEY_ID") or os.getenv("BEDROCK_ACCESS_KEY")), + reason="No real Bedrock embedding model or AWS credentials provided; skipping integration tests.", +) + + +@pytest.mark.flaky +@pytest.mark.integration +@skip_if_bedrock_embedding_integration_tests_disabled +async def test_bedrock_embedding_integration() -> None: + """Integration test for Bedrock embedding client.""" + client = BedrockEmbeddingClient() + result = await client.get_embeddings(["Hello, world!", "How are you?"]) + + assert isinstance(result, GeneratedEmbeddings) + assert len(result) == 2 + for embedding in result: + assert isinstance(embedding, Embedding) + assert isinstance(embedding.vector, list) + assert len(embedding.vector) > 0 + assert all(isinstance(v, float) for v in embedding.vector) diff --git a/python/packages/chatkit/pyproject.toml b/python/packages/chatkit/pyproject.toml index e2a58f129a..c39b89f792 100644 --- a/python/packages/chatkit/pyproject.toml +++ b/python/packages/chatkit/pyproject.toml @@ -4,7 +4,7 @@ description = "OpenAI ChatKit integration for Microsoft Agent Framework." authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0b260219" +version = "1.0.0b260225" license-files = ["LICENSE"] urls.homepage = "https://aka.ms/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -22,7 +22,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", "openai-chatkit>=1.4.0,<2.0.0", ] @@ -36,6 +36,7 @@ environments = [ [tool.uv-dynamic-versioning] fallback-version = "0.0.0" + [tool.pytest.ini_options] testpaths = 'tests' addopts = "-ra -q -r fEX" @@ -43,6 +44,9 @@ asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" filterwarnings = [] timeout = 120 +markers = [ + "integration: marks tests as integration tests that require external services", +] [tool.ruff] extend = "../../pyproject.toml" @@ -80,6 +84,7 @@ exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" + [tool.poe.tasks] mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_chatkit" test = "pytest --cov=agent_framework_chatkit --cov-report=term-missing:skip-covered tests" diff --git a/python/packages/claude/pyproject.toml b/python/packages/claude/pyproject.toml index 9d242c1fd5..3c2e37e14e 100644 --- a/python/packages/claude/pyproject.toml +++ b/python/packages/claude/pyproject.toml @@ -4,7 +4,7 @@ description = "Claude Agent SDK integration for Microsoft Agent Framework." authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0b260219" +version = "1.0.0b260225" license-files = ["LICENSE"] urls.homepage = "https://aka.ms/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -23,7 +23,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", "claude-agent-sdk>=0.1.25", ] @@ -37,6 +37,7 @@ environments = [ [tool.uv-dynamic-versioning] fallback-version = "0.0.0" + [tool.pytest.ini_options] testpaths = 'tests' addopts = "-ra -q -r fEX" @@ -46,6 +47,9 @@ filterwarnings = [ "ignore:Support for class-based `config` is deprecated:DeprecationWarning:pydantic.*" ] timeout = 120 +markers = [ + "integration: marks tests as integration tests that require external services", +] [tool.ruff] extend = "../../pyproject.toml" @@ -80,6 +84,7 @@ exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" + [tool.poe.tasks] mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_claude" test = "pytest --cov=agent_framework_claude --cov-report=term-missing:skip-covered tests" diff --git a/python/packages/copilotstudio/pyproject.toml b/python/packages/copilotstudio/pyproject.toml index fbcda0185e..9851dcab30 100644 --- a/python/packages/copilotstudio/pyproject.toml +++ b/python/packages/copilotstudio/pyproject.toml @@ -4,7 +4,7 @@ description = "Copilot Studio integration for Microsoft Agent Framework." authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0b260219" +version = "1.0.0b260225" license-files = ["LICENSE"] urls.homepage = "https://aka.ms/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -23,7 +23,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", "microsoft-agents-copilotstudio-client>=0.3.1", ] @@ -37,6 +37,7 @@ environments = [ [tool.uv-dynamic-versioning] fallback-version = "0.0.0" + [tool.pytest.ini_options] testpaths = 'tests' addopts = "-ra -q -r fEX" @@ -46,6 +47,9 @@ filterwarnings = [ "ignore:Support for class-based `config` is deprecated:DeprecationWarning:pydantic.*" ] timeout = 120 +markers = [ + "integration: marks tests as integration tests that require external services", +] [tool.ruff] extend = "../../pyproject.toml" @@ -58,7 +62,6 @@ omit = [ [tool.pyright] extends = "../../pyproject.toml" - [tool.mypy] plugins = ['pydantic.mypy'] strict = true @@ -80,6 +83,7 @@ exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" + [tool.poe.tasks] mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_copilotstudio" test = "pytest --cov=agent_framework_copilotstudio --cov-report=term-missing:skip-covered tests" diff --git a/python/packages/core/README.md b/python/packages/core/README.md index 1c2433cbc1..ff194ad859 100644 --- a/python/packages/core/README.md +++ b/python/packages/core/README.md @@ -220,7 +220,7 @@ if __name__ == "__main__": - [Getting Started with Agents](../../samples/02-agents): Basic agent creation and tool usage - [Chat Client Examples](../../samples/02-agents/chat_client): Direct chat client usage patterns - [Azure AI Integration](https://github.com/microsoft/agent-framework/tree/main/python/packages/azure-ai): Azure AI integration -- [.NET Workflows Samples](../../../dotnet/samples/GettingStarted/Workflows): Advanced multi-agent patterns (.NET) +- [Workflows Samples](../../samples/03-workflows): Advanced multi-agent patterns ## Agent Framework Documentation diff --git a/python/packages/core/agent_framework/_clients.py b/python/packages/core/agent_framework/_clients.py index 96e8dc0b6a..278657a154 100644 --- a/python/packages/core/agent_framework/_clients.py +++ b/python/packages/core/agent_framework/_clients.py @@ -667,16 +667,12 @@ def get_file_search_tool(**kwargs: Any) -> Any: # region SupportsGetEmbeddings Protocol -# Contravariant/covariant TypeVars for the Protocol +# Contravariant TypeVars for the Protocol EmbeddingInputContraT = TypeVar( "EmbeddingInputContraT", default="str", contravariant=True, ) -EmbeddingCoT = TypeVar( - "EmbeddingCoT", - default="list[float]", -) EmbeddingOptionsContraT = TypeVar( "EmbeddingOptionsContraT", bound=TypedDict, # type: ignore[valid-type] @@ -686,7 +682,7 @@ def get_file_search_tool(**kwargs: Any) -> Any: @runtime_checkable -class SupportsGetEmbeddings(Protocol[EmbeddingInputContraT, EmbeddingCoT, EmbeddingOptionsContraT]): +class SupportsGetEmbeddings(Protocol[EmbeddingInputContraT, EmbeddingT, EmbeddingOptionsContraT]): """Protocol for an embedding client that can generate embeddings. This protocol enables duck-typing for embedding generation. Any class that @@ -714,7 +710,7 @@ def get_embeddings( values: Sequence[EmbeddingInputContraT], *, options: EmbeddingOptionsContraT | None = None, - ) -> Awaitable[GeneratedEmbeddings[EmbeddingCoT]]: + ) -> Awaitable[GeneratedEmbeddings[EmbeddingT]]: """Generate embeddings for the given values. Args: @@ -733,15 +729,15 @@ def get_embeddings( # region BaseEmbeddingClient # Covariant for the BaseEmbeddingClient -EmbeddingOptionsCoT = TypeVar( - "EmbeddingOptionsCoT", +EmbeddingOptionsT = TypeVar( + "EmbeddingOptionsT", bound=TypedDict, # type: ignore[valid-type] default="EmbeddingGenerationOptions", covariant=True, ) -class BaseEmbeddingClient(SerializationMixin, ABC, Generic[EmbeddingInputT, EmbeddingT, EmbeddingOptionsCoT]): +class BaseEmbeddingClient(SerializationMixin, ABC, Generic[EmbeddingInputT, EmbeddingT, EmbeddingOptionsT]): """Abstract base class for embedding clients. Subclasses implement ``get_embeddings`` to provide the actual @@ -785,7 +781,7 @@ async def get_embeddings( self, values: Sequence[EmbeddingInputT], *, - options: EmbeddingOptionsCoT | None = None, + options: EmbeddingOptionsT | None = None, ) -> GeneratedEmbeddings[EmbeddingT]: """Generate embeddings for the given values. diff --git a/python/packages/core/agent_framework/_skills.py b/python/packages/core/agent_framework/_skills.py index 0e132a9336..33d001b6f2 100644 --- a/python/packages/core/agent_framework/_skills.py +++ b/python/packages/core/agent_framework/_skills.py @@ -115,6 +115,7 @@ class _FileAgentSkill: source_path: str resource_names: list[str] = field(default_factory=list) + # endregion # region Private module-level functions (skill discovery, parsing, security) @@ -165,9 +166,7 @@ def _has_symlink_in_path(full_path: str, directory_path: str) -> bool: try: relative = Path(full_path).relative_to(dir_path) except ValueError as exc: - raise ValueError( - f"full_path {full_path!r} does not start with directory_path {directory_path!r}" - ) from exc + raise ValueError(f"full_path {full_path!r} does not start with directory_path {directory_path!r}") from exc current = dir_path for part in relative.parts: @@ -436,6 +435,7 @@ def _build_skills_instruction_prompt( return template.format("\n".join(lines)) + # endregion # region Public API @@ -494,7 +494,9 @@ def __init__( """ super().__init__(source_id or self.DEFAULT_SOURCE_ID) - resolved_paths: Sequence[str] = [str(skill_paths)] if isinstance(skill_paths, (str, Path)) else [str(p) for p in skill_paths] + resolved_paths: Sequence[str] = ( + [str(skill_paths)] if isinstance(skill_paths, (str, Path)) else [str(p) for p in skill_paths] + ) self._skills = _discover_and_load_skills(resolved_paths) self._skills_instruction_prompt = _build_skills_instruction_prompt(skills_instruction_prompt, self._skills) @@ -594,4 +596,5 @@ def _read_skill_resource(self, skill_name: str, resource_name: str) -> str: logger.exception("Failed to read resource '%s' from skill '%s'", resource_name, skill_name) return f"Error: Failed to read resource '{resource_name}' from skill '{skill_name}'." + # endregion diff --git a/python/packages/core/agent_framework/_types.py b/python/packages/core/agent_framework/_types.py index b3d7128d7e..37ee9f1138 100644 --- a/python/packages/core/agent_framework/_types.py +++ b/python/packages/core/agent_framework/_types.py @@ -377,6 +377,12 @@ class UsageDetails(TypedDict, total=False): This is a non-closed dictionary, so any specific provider fields can be added as needed. Whenever they can be mapped to standard fields, they will be. + + Keys: + input_token_count: The number of input tokens used. + output_token_count: The number of output tokens generated. + total_token_count: The total number of tokens (input + output). + """ input_token_count: int | None @@ -3289,7 +3295,7 @@ def __init__( embeddings: Iterable[Embedding[EmbeddingT]] | None = None, *, options: EmbeddingOptionsT | None = None, - usage: dict[str, Any] | None = None, + usage: UsageDetails | None = None, additional_properties: dict[str, Any] | None = None, ) -> None: super().__init__(embeddings or []) diff --git a/python/packages/core/agent_framework/amazon/__init__.py b/python/packages/core/agent_framework/amazon/__init__.py index 42324acf96..e9282b8873 100644 --- a/python/packages/core/agent_framework/amazon/__init__.py +++ b/python/packages/core/agent_framework/amazon/__init__.py @@ -8,6 +8,9 @@ Supported classes: - BedrockChatClient - BedrockChatOptions +- BedrockEmbeddingClient +- BedrockEmbeddingOptions +- BedrockEmbeddingSettings - BedrockGuardrailConfig - BedrockSettings """ @@ -17,7 +20,15 @@ IMPORT_PATH = "agent_framework_bedrock" PACKAGE_NAME = "agent-framework-bedrock" -_IMPORTS = ["BedrockChatClient", "BedrockChatOptions", "BedrockGuardrailConfig", "BedrockSettings"] +_IMPORTS = [ + "BedrockChatClient", + "BedrockChatOptions", + "BedrockEmbeddingClient", + "BedrockEmbeddingOptions", + "BedrockEmbeddingSettings", + "BedrockGuardrailConfig", + "BedrockSettings", +] def __getattr__(name: str) -> Any: diff --git a/python/packages/core/agent_framework/amazon/__init__.pyi b/python/packages/core/agent_framework/amazon/__init__.pyi index c691334da5..a9dd7a9117 100644 --- a/python/packages/core/agent_framework/amazon/__init__.pyi +++ b/python/packages/core/agent_framework/amazon/__init__.pyi @@ -3,6 +3,9 @@ from agent_framework_bedrock import ( BedrockChatClient, BedrockChatOptions, + BedrockEmbeddingClient, + BedrockEmbeddingOptions, + BedrockEmbeddingSettings, BedrockGuardrailConfig, BedrockSettings, ) @@ -10,6 +13,9 @@ from agent_framework_bedrock import ( __all__ = [ "BedrockChatClient", "BedrockChatOptions", + "BedrockEmbeddingClient", + "BedrockEmbeddingOptions", + "BedrockEmbeddingSettings", "BedrockGuardrailConfig", "BedrockSettings", ] diff --git a/python/packages/core/agent_framework/azure/_embedding_client.py b/python/packages/core/agent_framework/azure/_embedding_client.py index 05d6b5d603..13455e78a4 100644 --- a/python/packages/core/agent_framework/azure/_embedding_client.py +++ b/python/packages/core/agent_framework/azure/_embedding_client.py @@ -99,6 +99,7 @@ def __init__( credential: AzureCredentialTypes | AzureTokenProvider | None = None, default_headers: Mapping[str, str] | None = None, async_client: AsyncAzureOpenAI | None = None, + otel_provider_name: str | None = None, env_file_path: str | None = None, env_file_encoding: str | None = None, ) -> None: @@ -133,4 +134,5 @@ def __init__( credential=credential, default_headers=default_headers, client=async_client, + otel_provider_name=otel_provider_name, ) diff --git a/python/packages/core/agent_framework/observability.py b/python/packages/core/agent_framework/observability.py index 11023aef67..9a60053068 100644 --- a/python/packages/core/agent_framework/observability.py +++ b/python/packages/core/agent_framework/observability.py @@ -1279,15 +1279,15 @@ async def _get_response() -> ChatResponse: return _get_response() -EmbeddingOptionsCoT = TypeVar( - "EmbeddingOptionsCoT", +EmbeddingOptionsT = TypeVar( + "EmbeddingOptionsT", bound=TypedDict, # type: ignore[valid-type] default="EmbeddingGenerationOptions", covariant=True, ) -class EmbeddingTelemetryLayer(Generic[EmbeddingInputT, EmbeddingT, EmbeddingOptionsCoT]): +class EmbeddingTelemetryLayer(Generic[EmbeddingInputT, EmbeddingT, EmbeddingOptionsT]): """Layer that wraps embedding client get_embeddings with OpenTelemetry tracing.""" def __init__(self, *args: Any, otel_provider_name: str | None = None, **kwargs: Any) -> None: @@ -1301,7 +1301,7 @@ async def get_embeddings( self, values: Sequence[EmbeddingInputT], *, - options: EmbeddingOptionsCoT | None = None, + options: EmbeddingOptionsT | None = None, ) -> GeneratedEmbeddings[EmbeddingT]: """Trace embedding generation with OpenTelemetry spans and metrics.""" global OBSERVABILITY_SETTINGS diff --git a/python/packages/core/agent_framework/ollama/__init__.py b/python/packages/core/agent_framework/ollama/__init__.py index 1c6ba6820c..38a04f8044 100644 --- a/python/packages/core/agent_framework/ollama/__init__.py +++ b/python/packages/core/agent_framework/ollama/__init__.py @@ -7,6 +7,10 @@ Supported classes: - OllamaChatClient +- OllamaChatOptions +- OllamaEmbeddingClient +- OllamaEmbeddingOptions +- OllamaEmbeddingSettings - OllamaSettings """ @@ -15,7 +19,14 @@ IMPORT_PATH = "agent_framework_ollama" PACKAGE_NAME = "agent-framework-ollama" -_IMPORTS = ["OllamaChatClient", "OllamaSettings"] +_IMPORTS = [ + "OllamaChatClient", + "OllamaChatOptions", + "OllamaEmbeddingClient", + "OllamaEmbeddingOptions", + "OllamaEmbeddingSettings", + "OllamaSettings", +] def __getattr__(name: str) -> Any: diff --git a/python/packages/core/agent_framework/ollama/__init__.pyi b/python/packages/core/agent_framework/ollama/__init__.pyi index ed439f3b36..36415c714c 100644 --- a/python/packages/core/agent_framework/ollama/__init__.pyi +++ b/python/packages/core/agent_framework/ollama/__init__.pyi @@ -2,10 +2,18 @@ from agent_framework_ollama import ( OllamaChatClient, + OllamaChatOptions, + OllamaEmbeddingClient, + OllamaEmbeddingOptions, + OllamaEmbeddingSettings, OllamaSettings, ) __all__ = [ "OllamaChatClient", + "OllamaChatOptions", + "OllamaEmbeddingClient", + "OllamaEmbeddingOptions", + "OllamaEmbeddingSettings", "OllamaSettings", ] diff --git a/python/packages/core/agent_framework/openai/_embedding_client.py b/python/packages/core/agent_framework/openai/_embedding_client.py index 0b59f9bf45..fb479c181c 100644 --- a/python/packages/core/agent_framework/openai/_embedding_client.py +++ b/python/packages/core/agent_framework/openai/_embedding_client.py @@ -12,7 +12,7 @@ from .._clients import BaseEmbeddingClient from .._settings import load_settings -from .._types import Embedding, EmbeddingGenerationOptions, GeneratedEmbeddings +from .._types import Embedding, EmbeddingGenerationOptions, GeneratedEmbeddings, UsageDetails from ..observability import EmbeddingTelemetryLayer from ._shared import OpenAIBase, OpenAIConfigMixin, OpenAISettings @@ -116,11 +116,11 @@ async def get_embeddings( ) ) - usage_dict: dict[str, Any] | None = None + usage_dict: UsageDetails | None = None if response.usage: usage_dict = { - "prompt_tokens": response.usage.prompt_tokens, - "total_tokens": response.usage.total_tokens, + "input_token_count": response.usage.prompt_tokens, + "total_token_count": response.usage.total_tokens, } return GeneratedEmbeddings(embeddings, options=options, usage=usage_dict) @@ -143,6 +143,7 @@ class OpenAIEmbeddingClient( default_headers: Additional HTTP headers. async_client: Pre-configured AsyncOpenAI client. base_url: Custom API base URL. + otel_provider_name: Override the OpenTelemetry provider name for telemetry. env_file_path: Path to .env file for settings. env_file_encoding: Encoding for .env file. @@ -176,6 +177,7 @@ def __init__( default_headers: Mapping[str, str] | None = None, async_client: AsyncOpenAI | None = None, base_url: str | None = None, + otel_provider_name: str | None = None, env_file_path: str | None = None, env_file_encoding: str | None = None, ) -> None: @@ -208,4 +210,5 @@ def __init__( org_id=openai_settings["org_id"], default_headers=default_headers, client=async_client, + otel_provider_name=otel_provider_name, ) diff --git a/python/packages/core/pyproject.toml b/python/packages/core/pyproject.toml index 09ccda3c18..a3c3f53ed6 100644 --- a/python/packages/core/pyproject.toml +++ b/python/packages/core/pyproject.toml @@ -4,7 +4,7 @@ description = "Microsoft Agent Framework for building AI Agents with Python. Thi authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0rc1" +version = "1.0.0rc2" license-files = ["LICENSE"] urls.homepage = "https://aka.ms/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -91,6 +91,9 @@ asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" filterwarnings = [] timeout = 120 +markers = [ + "integration: marks tests as integration tests that require external services", +] [tool.coverage.run] omit = [ @@ -124,6 +127,7 @@ exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" + [tool.poe.tasks] mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework" test = "pytest --cov=agent_framework --cov-report=term-missing:skip-covered -n auto --dist worksteal tests" diff --git a/python/packages/core/tests/core/test_function_invocation_logic.py b/python/packages/core/tests/core/test_function_invocation_logic.py index f278afaeac..319d35f152 100644 --- a/python/packages/core/tests/core/test_function_invocation_logic.py +++ b/python/packages/core/tests/core/test_function_invocation_logic.py @@ -937,8 +937,7 @@ def ai_func(arg1: str) -> str: orphaned_calls = all_call_ids - all_result_ids assert not orphaned_calls, ( - f"Response contains orphaned FunctionCallContent without matching " - f"FunctionResultContent: {orphaned_calls}." + f"Response contains orphaned FunctionCallContent without matching FunctionResultContent: {orphaned_calls}." ) @@ -1123,8 +1122,7 @@ def browser_snapshot(url: str) -> str: orphaned_calls = all_call_ids - all_result_ids assert not orphaned_calls, ( - f"Response contains orphaned function calls {orphaned_calls}. " - f"This would cause API errors on the next call." + f"Response contains orphaned function calls {orphaned_calls}. This would cause API errors on the next call." ) diff --git a/python/packages/core/tests/core/test_observability.py b/python/packages/core/tests/core/test_observability.py index 0e81b7580c..838efcb0e9 100644 --- a/python/packages/core/tests/core/test_observability.py +++ b/python/packages/core/tests/core/test_observability.py @@ -2594,3 +2594,189 @@ async def test_agent_no_instructions_in_default_or_options( span = spans[0] assert OtelAttr.SYSTEM_INSTRUCTIONS not in span.attributes + + +# region Additional coverage tests + + +def test_get_instructions_from_options_none(): + """Test _get_instructions_from_options returns None for None input.""" + from agent_framework.observability import _get_instructions_from_options + + assert _get_instructions_from_options(None) is None + + +def test_get_instructions_from_options_non_dict(): + """Test _get_instructions_from_options returns None for non-dict input.""" + from agent_framework.observability import _get_instructions_from_options + + assert _get_instructions_from_options("not a dict") is None + assert _get_instructions_from_options(42) is None + + +def test_get_instructions_from_options_dict_with_instructions(): + """Test _get_instructions_from_options extracts instructions from dict.""" + from agent_framework.observability import _get_instructions_from_options + + assert _get_instructions_from_options({"instructions": "do stuff"}) == "do stuff" + assert _get_instructions_from_options({"other_key": "value"}) is None + + +def test_get_span_attributes_with_non_dict_options(): + """Test _get_span_attributes handles non-dict options gracefully.""" + from agent_framework.observability import _get_span_attributes + + # Pass options as a non-dict value; should not crash + attrs = _get_span_attributes( + operation_name="chat", + provider_name="test", + all_options="not_a_dict", + ) + assert attrs[OtelAttr.OPERATION] == "chat" + + +def test_capture_response_with_error_type(span_exporter: InMemorySpanExporter): + """Test _capture_response includes error_type in duration histogram attributes.""" + from agent_framework.observability import OtelAttr, _capture_response, get_tracer + + span_exporter.clear() + tracer = get_tracer() + + from agent_framework.observability import _get_duration_histogram, _get_token_usage_histogram + + token_histogram = _get_token_usage_histogram() + duration_histogram = _get_duration_histogram() + + attrs = { + "gen_ai.request.model": "test-model", + OtelAttr.ERROR_TYPE: "ValueError", + } + + with tracer.start_as_current_span("test_span") as span: + _capture_response( + span=span, + attributes=attrs, + token_usage_histogram=token_histogram, + operation_duration_histogram=duration_histogram, + duration=0.5, + ) + + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + assert spans[0].attributes.get(OtelAttr.ERROR_TYPE) == "ValueError" + + +def test_configure_otel_providers_with_env_file_path(monkeypatch, tmp_path): + """Test configure_otel_providers with env_file_path creates new settings.""" + import importlib + + monkeypatch.setenv("ENABLE_INSTRUMENTATION", "false") + for key in [ + "OTEL_EXPORTER_OTLP_ENDPOINT", + "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", + "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", + "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT", + ]: + monkeypatch.delenv(key, raising=False) + + observability = importlib.import_module("agent_framework.observability") + importlib.reload(observability) + + env_file = tmp_path / ".env" + env_file.write_text("ENABLE_INSTRUMENTATION=true\n") + + observability.configure_otel_providers( + env_file_path=str(env_file), + enable_sensitive_data=True, + vs_code_extension_port=None, + ) + + assert observability.OBSERVABILITY_SETTINGS.enable_instrumentation is True + assert observability.OBSERVABILITY_SETTINGS.enable_sensitive_data is True + + +def test_configure_otel_providers_with_env_file_and_vs_code_port(monkeypatch, tmp_path): + """Test configure_otel_providers with env_file_path and vs_code_extension_port.""" + import importlib + + monkeypatch.setenv("ENABLE_INSTRUMENTATION", "false") + for key in [ + "OTEL_EXPORTER_OTLP_ENDPOINT", + "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", + "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", + "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT", + ]: + monkeypatch.delenv(key, raising=False) + + observability = importlib.import_module("agent_framework.observability") + importlib.reload(observability) + + env_file = tmp_path / ".env" + env_file.write_text("ENABLE_INSTRUMENTATION=true\n") + + observability.configure_otel_providers( + env_file_path=str(env_file), + env_file_encoding="utf-8", + vs_code_extension_port=4317, + ) + + assert observability.OBSERVABILITY_SETTINGS.enable_instrumentation is True + assert observability.OBSERVABILITY_SETTINGS.vs_code_extension_port == 4317 + + +def test_get_exporters_from_env_with_env_file_path(monkeypatch, tmp_path): + """Test _get_exporters_from_env loads dotenv when env_file_path is provided.""" + from agent_framework.observability import _get_exporters_from_env + + for key in [ + "OTEL_EXPORTER_OTLP_ENDPOINT", + "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", + "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", + "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT", + ]: + monkeypatch.delenv(key, raising=False) + + # Create a .env file with no OTEL endpoints so it returns empty + env_file = tmp_path / ".env" + env_file.write_text("SOME_VAR=value\n") + + exporters = _get_exporters_from_env(env_file_path=str(env_file)) + assert exporters == [] + + +def test_create_resource_with_env_file_path(monkeypatch, tmp_path): + """Test create_resource loads dotenv when env_file_path is provided.""" + from agent_framework.observability import create_resource + + monkeypatch.delenv("OTEL_SERVICE_NAME", raising=False) + monkeypatch.delenv("OTEL_SERVICE_VERSION", raising=False) + monkeypatch.delenv("OTEL_RESOURCE_ATTRIBUTES", raising=False) + + env_file = tmp_path / ".env" + env_file.write_text("OTEL_SERVICE_NAME=my_test_service\n") + + resource = create_resource(env_file_path=str(env_file)) + assert resource.attributes.get("service.name") == "my_test_service" + + +def test_get_meter_typeerror_fallback(): + """Test get_meter falls back when TypeError is raised (old OTel versions).""" + from unittest.mock import patch as mock_patch + + from agent_framework.observability import get_meter + + call_count = 0 + + def mock_get_meter(*args, **kwargs): + nonlocal call_count + call_count += 1 + if "attributes" in kwargs: + raise TypeError("unexpected keyword argument 'attributes'") + from opentelemetry import metrics + + return metrics.get_meter_provider().get_meter(*args, **{k: v for k, v in kwargs.items() if k != "attributes"}) + + with mock_patch("agent_framework.observability.metrics.get_meter", side_effect=mock_get_meter): + meter = get_meter(name="test", attributes={"key": "val"}) + assert meter is not None + assert call_count == 2 diff --git a/python/packages/core/tests/core/test_skills.py b/python/packages/core/tests/core/test_skills.py index 571abeaa21..a77f214718 100644 --- a/python/packages/core/tests/core/test_skills.py +++ b/python/packages/core/tests/core/test_skills.py @@ -38,6 +38,7 @@ def _symlinks_supported(tmp: Path) -> bool: test_link.unlink(missing_ok=True) test_target.unlink(missing_ok=True) + # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- diff --git a/python/packages/core/tests/openai/test_openai_embedding_client.py b/python/packages/core/tests/openai/test_openai_embedding_client.py index f6fbbad6aa..c606b67e31 100644 --- a/python/packages/core/tests/openai/test_openai_embedding_client.py +++ b/python/packages/core/tests/openai/test_openai_embedding_client.py @@ -100,8 +100,8 @@ async def test_openai_get_embeddings_usage(openai_unit_test_env: None) -> None: result = await client.get_embeddings(["test"]) assert result.usage is not None - assert result.usage["prompt_tokens"] == 10 - assert result.usage["total_tokens"] == 10 + assert result.usage["input_token_count"] == 10 + assert result.usage["total_token_count"] == 10 async def test_openai_options_passthrough_dimensions(openai_unit_test_env: None) -> None: @@ -284,7 +284,7 @@ async def test_integration_openai_get_embeddings() -> None: assert all(isinstance(v, float) for v in result[0].vector) assert result[0].model_id is not None assert result.usage is not None - assert result.usage["prompt_tokens"] > 0 + assert result.usage["input_token_count"] > 0 @skip_if_openai_integration_tests_disabled @@ -327,7 +327,7 @@ async def test_integration_azure_openai_get_embeddings() -> None: assert all(isinstance(v, float) for v in result[0].vector) assert result[0].model_id is not None assert result.usage is not None - assert result.usage["prompt_tokens"] > 0 + assert result.usage["input_token_count"] > 0 @skip_if_azure_openai_integration_tests_disabled diff --git a/python/packages/declarative/pyproject.toml b/python/packages/declarative/pyproject.toml index 192111cc85..f8cb556d26 100644 --- a/python/packages/declarative/pyproject.toml +++ b/python/packages/declarative/pyproject.toml @@ -4,7 +4,7 @@ description = "Declarative specification support for Microsoft Agent Framework." authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0b260219" +version = "1.0.0b260225" license-files = ["LICENSE"] urls.homepage = "https://aka.ms/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -22,7 +22,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", "powerfx>=0.0.31; python_version < '3.14'", "pyyaml>=6.0,<7.0", ] @@ -39,9 +39,9 @@ environments = [ "sys_platform == 'win32'" ] - [tool.uv-dynamic-versioning] fallback-version = "0.0.0" + [tool.pytest.ini_options] testpaths = 'tests' addopts = "-ra -q -r fEX" @@ -51,6 +51,9 @@ filterwarnings = [ "ignore:Support for class-based `config` is deprecated:DeprecationWarning:pydantic.*" ] timeout = 120 +markers = [ + "integration: marks tests as integration tests that require external services", +] [tool.ruff] extend = "../../pyproject.toml" @@ -88,6 +91,7 @@ exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" + [tool.poe.tasks] mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_declarative" test = "pytest --cov=agent_framework_declarative --cov-report=term-missing:skip-covered tests" diff --git a/python/packages/devui/pyproject.toml b/python/packages/devui/pyproject.toml index 9eaa5ece83..5987fb0ea1 100644 --- a/python/packages/devui/pyproject.toml +++ b/python/packages/devui/pyproject.toml @@ -4,7 +4,7 @@ description = "Debug UI for Microsoft Agent Framework with OpenAI-compatible API authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0b260219" +version = "1.0.0b260225" license-files = ["LICENSE"] urls.homepage = "https://github.com/microsoft/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -23,7 +23,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", "fastapi>=0.104.0", "uvicorn[standard]>=0.24.0", "python-dotenv>=1.0.0", @@ -54,6 +54,9 @@ addopts = "-ra -q -r fEX" asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" filterwarnings = [] +markers = [ + "integration: marks tests as integration tests that require external services", +] [tool.ruff] extend = "../../pyproject.toml" @@ -66,7 +69,6 @@ omit = [ [tool.pyright] extends = "../../pyproject.toml" - [tool.mypy] plugins = ['pydantic.mypy'] strict = true @@ -89,6 +91,7 @@ exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" + [tool.poe.tasks] mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_devui" test = "pytest --cov=agent_framework_devui --cov-report=term-missing:skip-covered tests" diff --git a/python/packages/durabletask/pyproject.toml b/python/packages/durabletask/pyproject.toml index aa119e1ee1..431b5f32f0 100644 --- a/python/packages/durabletask/pyproject.toml +++ b/python/packages/durabletask/pyproject.toml @@ -4,7 +4,7 @@ description = "Durable Task integration for Microsoft Agent Framework." authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0b260219" +version = "1.0.0b260225" license-files = ["LICENSE"] urls.homepage = "https://aka.ms/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -22,7 +22,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", "durabletask>=1.3.0", "durabletask-azuremanaged>=1.3.0", "python-dateutil>=2.8.0", @@ -43,6 +43,7 @@ environments = [ [tool.uv-dynamic-versioning] fallback-version = "0.0.0" + [tool.pytest.ini_options] testpaths = 'tests' pythonpath = ["tests/integration_tests"] @@ -94,6 +95,7 @@ exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" + [tool.poe.tasks] mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_durabletask" test = "pytest --cov=agent_framework_durabletask --cov-report=term-missing:skip-covered tests" diff --git a/python/packages/foundry_local/pyproject.toml b/python/packages/foundry_local/pyproject.toml index 7d6ea0625f..6235bd5866 100644 --- a/python/packages/foundry_local/pyproject.toml +++ b/python/packages/foundry_local/pyproject.toml @@ -4,7 +4,7 @@ description = "Foundry Local integration for Microsoft Agent Framework." authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0b260219" +version = "1.0.0b260225" license-files = ["LICENSE"] urls.homepage = "https://aka.ms/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -23,7 +23,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", "foundry-local-sdk>=0.5.1,<1", ] @@ -37,6 +37,7 @@ environments = [ [tool.uv-dynamic-versioning] fallback-version = "0.0.0" + [tool.pytest.ini_options] testpaths = 'tests' addopts = "-ra -q -r fEX" @@ -44,6 +45,9 @@ asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" filterwarnings = [] timeout = 120 +markers = [ + "integration: marks tests as integration tests that require external services", +] [tool.ruff] extend = "../../pyproject.toml" @@ -78,6 +82,7 @@ exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" + [tool.poe.tasks] mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_foundry_local" test = "pytest --cov=agent_framework_foundry_local --cov-report=term-missing:skip-covered tests" diff --git a/python/packages/github_copilot/pyproject.toml b/python/packages/github_copilot/pyproject.toml index e34132f2bc..eba4f0519f 100644 --- a/python/packages/github_copilot/pyproject.toml +++ b/python/packages/github_copilot/pyproject.toml @@ -4,7 +4,7 @@ description = "GitHub Copilot integration for Microsoft Agent Framework." authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0b260219" +version = "1.0.0b260225" license-files = ["LICENSE"] urls.homepage = "https://aka.ms/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -23,7 +23,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", "github-copilot-sdk>=0.1.0", ] @@ -37,6 +37,7 @@ environments = [ [tool.uv-dynamic-versioning] fallback-version = "0.0.0" + [tool.pytest.ini_options] testpaths = 'tests' addopts = "-ra -q -r fEX" @@ -46,6 +47,9 @@ filterwarnings = [ "ignore:Support for class-based `config` is deprecated:DeprecationWarning:pydantic.*" ] timeout = 120 +markers = [ + "integration: marks tests as integration tests that require external services", +] [tool.ruff] extend = "../../pyproject.toml" @@ -58,7 +62,6 @@ omit = [ [tool.pyright] extends = "../../pyproject.toml" - [tool.mypy] plugins = ['pydantic.mypy'] strict = true @@ -80,6 +83,7 @@ exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" + [tool.poe.tasks] mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_github_copilot" test = "pytest --cov=agent_framework_github_copilot --cov-report=term-missing:skip-covered tests" diff --git a/python/packages/lab/pyproject.toml b/python/packages/lab/pyproject.toml index d6cea73d01..137c47b0ff 100644 --- a/python/packages/lab/pyproject.toml +++ b/python/packages/lab/pyproject.toml @@ -4,7 +4,7 @@ description = "Experimental modules for Microsoft Agent Framework" authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0b260219" +version = "1.0.0b260225" license-files = ["LICENSE"] urls.homepage = "https://aka.ms/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -22,7 +22,7 @@ classifiers = [ "Programming Language :: Python :: 3.14", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", ] [project.optional-dependencies] diff --git a/python/packages/mem0/pyproject.toml b/python/packages/mem0/pyproject.toml index 438c879e78..406da3ab88 100644 --- a/python/packages/mem0/pyproject.toml +++ b/python/packages/mem0/pyproject.toml @@ -4,7 +4,7 @@ description = "Mem0 integration for Microsoft Agent Framework." authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0b260219" +version = "1.0.0b260225" license-files = ["LICENSE"] urls.homepage = "https://aka.ms/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -23,7 +23,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", "mem0ai>=1.0.0", ] @@ -37,6 +37,7 @@ environments = [ [tool.uv-dynamic-versioning] fallback-version = "0.0.0" + [tool.pytest.ini_options] testpaths = 'tests' addopts = "-ra -q -r fEX" @@ -46,6 +47,9 @@ filterwarnings = [ "ignore:Support for class-based `config` is deprecated:DeprecationWarning:pydantic.*" ] timeout = 120 +markers = [ + "integration: marks tests as integration tests that require external services", +] [tool.ruff] extend = "../../pyproject.toml" @@ -58,7 +62,6 @@ omit = [ [tool.pyright] extends = "../../pyproject.toml" - [tool.mypy] plugins = ['pydantic.mypy'] strict = true @@ -80,6 +83,7 @@ exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" + [tool.poe.tasks] mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_mem0" test = "pytest --cov=agent_framework_mem0 --cov-report=term-missing:skip-covered tests" diff --git a/python/packages/ollama/agent_framework_ollama/__init__.py b/python/packages/ollama/agent_framework_ollama/__init__.py index d1bd699e1a..4b6e4c6ba0 100644 --- a/python/packages/ollama/agent_framework_ollama/__init__.py +++ b/python/packages/ollama/agent_framework_ollama/__init__.py @@ -3,6 +3,7 @@ import importlib.metadata from ._chat_client import OllamaChatClient, OllamaChatOptions, OllamaSettings +from ._embedding_client import OllamaEmbeddingClient, OllamaEmbeddingOptions, OllamaEmbeddingSettings try: __version__ = importlib.metadata.version(__name__) @@ -12,6 +13,9 @@ __all__ = [ "OllamaChatClient", "OllamaChatOptions", + "OllamaEmbeddingClient", + "OllamaEmbeddingOptions", + "OllamaEmbeddingSettings", "OllamaSettings", "__version__", ] diff --git a/python/packages/ollama/agent_framework_ollama/_embedding_client.py b/python/packages/ollama/agent_framework_ollama/_embedding_client.py new file mode 100644 index 0000000000..4fcf75b465 --- /dev/null +++ b/python/packages/ollama/agent_framework_ollama/_embedding_client.py @@ -0,0 +1,230 @@ +# Copyright (c) Microsoft. All rights reserved. + +from __future__ import annotations + +import logging +import sys +from collections.abc import Sequence +from typing import Any, ClassVar, Generic, TypedDict + +from agent_framework import ( + BaseEmbeddingClient, + Embedding, + EmbeddingGenerationOptions, + GeneratedEmbeddings, + UsageDetails, + load_settings, +) +from agent_framework.observability import EmbeddingTelemetryLayer +from ollama import AsyncClient + +if sys.version_info >= (3, 13): + from typing import TypeVar # type: ignore # pragma: no cover +else: + from typing_extensions import TypeVar # type: ignore # pragma: no cover + + +logger = logging.getLogger("agent_framework.ollama") + + +class OllamaEmbeddingOptions(EmbeddingGenerationOptions, total=False): + """Ollama-specific embedding options. + + Extends EmbeddingGenerationOptions with Ollama-specific fields. + + Examples: + .. code-block:: python + + from agent_framework_ollama import OllamaEmbeddingOptions + + options: OllamaEmbeddingOptions = { + "model_id": "nomic-embed-text", + "dimensions": 768, + "truncate": True, + } + """ + + truncate: bool + """Whether to truncate input text that exceeds the model's context length. + + When True, input that is too long will be silently truncated. + When False (default), the request will fail if input exceeds the context length. + """ + + keep_alive: float | str + """How long to keep the model loaded in memory (e.g. ``"5m"``, ``300``).""" + + +OllamaEmbeddingOptionsT = TypeVar( + "OllamaEmbeddingOptionsT", + bound=TypedDict, # type: ignore[valid-type] + default="OllamaEmbeddingOptions", + covariant=True, +) + + +class OllamaEmbeddingSettings(TypedDict, total=False): + """Ollama embedding settings.""" + + host: str | None + embedding_model_id: str | None + + +class RawOllamaEmbeddingClient( + BaseEmbeddingClient[str, list[float], OllamaEmbeddingOptionsT], + Generic[OllamaEmbeddingOptionsT], +): + """Raw Ollama embedding client without telemetry. + + Keyword Args: + model_id: The Ollama embedding model ID (e.g. "nomic-embed-text"). + Can also be set via environment variable OLLAMA_EMBEDDING_MODEL_ID. + host: Ollama server URL. Defaults to http://localhost:11434. + Can also be set via environment variable OLLAMA_HOST. + client: Optional pre-configured Ollama AsyncClient. + env_file_path: Path to .env file for settings. + env_file_encoding: Encoding for .env file. + """ + + def __init__( + self, + *, + model_id: str | None = None, + host: str | None = None, + client: AsyncClient | None = None, + env_file_path: str | None = None, + env_file_encoding: str | None = None, + **kwargs: Any, + ) -> None: + """Initialize a raw Ollama embedding client.""" + ollama_settings = load_settings( + OllamaEmbeddingSettings, + env_prefix="OLLAMA_", + required_fields=["embedding_model_id"], + host=host, + embedding_model_id=model_id, + env_file_path=env_file_path, + env_file_encoding=env_file_encoding, + ) + + self.model_id = ollama_settings["embedding_model_id"] + self.client = client or AsyncClient(host=ollama_settings.get("host")) + self.host = str(self.client._client.base_url) # pyright: ignore[reportUnknownMemberType,reportPrivateUsage,reportUnknownArgumentType] + super().__init__(**kwargs) + + def service_url(self) -> str: + """Get the URL of the service.""" + return self.host + + async def get_embeddings( + self, + values: Sequence[str], + *, + options: OllamaEmbeddingOptionsT | None = None, + ) -> GeneratedEmbeddings[list[float]]: + """Call the Ollama embed API. + + Args: + values: The text values to generate embeddings for. + options: Optional embedding generation options. + + Returns: + Generated embeddings with usage metadata. + + Raises: + ValueError: If model_id is not provided or values is empty. + """ + if not values: + return GeneratedEmbeddings([], options=options) + + opts: dict[str, Any] = dict(options) if options else {} + model = opts.get("model_id") or self.model_id + if not model: + raise ValueError("model_id is required") + + kwargs: dict[str, Any] = {"model": model, "input": list(values)} + if (truncate := opts.get("truncate")) is not None: + kwargs["truncate"] = truncate + if keep_alive := opts.get("keep_alive"): + kwargs["keep_alive"] = keep_alive + if dimensions := opts.get("dimensions"): + kwargs["dimensions"] = dimensions + + response = await self.client.embed(**kwargs) + + embeddings = [ + Embedding( + vector=list(emb), + dimensions=len(emb), + model_id=response.get("model") or model, + ) + for emb in response.get("embeddings", []) + ] + + usage_dict: UsageDetails | None = None + prompt_eval_count = response.get("prompt_eval_count") + if prompt_eval_count is not None: + usage_dict = {"input_token_count": prompt_eval_count} + + return GeneratedEmbeddings(embeddings, options=options, usage=usage_dict) + + +class OllamaEmbeddingClient( + EmbeddingTelemetryLayer[str, list[float], OllamaEmbeddingOptionsT], + RawOllamaEmbeddingClient[OllamaEmbeddingOptionsT], + Generic[OllamaEmbeddingOptionsT], +): + """Ollama embedding client with telemetry support. + + Keyword Args: + model_id: The Ollama embedding model ID (e.g. "nomic-embed-text"). + Can also be set via environment variable OLLAMA_EMBEDDING_MODEL_ID. + host: Ollama server URL. Defaults to http://localhost:11434. + Can also be set via environment variable OLLAMA_HOST. + client: Optional pre-configured Ollama AsyncClient. + env_file_path: Path to .env file for settings. + env_file_encoding: Encoding for .env file. + + Examples: + .. code-block:: python + + from agent_framework_ollama import OllamaEmbeddingClient + + # Using environment variables + # Set OLLAMA_EMBEDDING_MODEL_ID=nomic-embed-text + client = OllamaEmbeddingClient() + + # Or passing parameters directly + client = OllamaEmbeddingClient( + model_id="nomic-embed-text", + host="http://localhost:11434", + ) + + # Generate embeddings + result = await client.get_embeddings(["Hello, world!"]) + print(result[0].vector) + """ + + OTEL_PROVIDER_NAME: ClassVar[str] = "ollama" + + def __init__( + self, + *, + model_id: str | None = None, + host: str | None = None, + client: AsyncClient | None = None, + otel_provider_name: str | None = None, + env_file_path: str | None = None, + env_file_encoding: str | None = None, + **kwargs: Any, + ) -> None: + """Initialize an Ollama embedding client.""" + super().__init__( + model_id=model_id, + host=host, + client=client, + otel_provider_name=otel_provider_name, + env_file_path=env_file_path, + env_file_encoding=env_file_encoding, + **kwargs, + ) diff --git a/python/packages/ollama/pyproject.toml b/python/packages/ollama/pyproject.toml index 47568ecfc6..686dbe2c8f 100644 --- a/python/packages/ollama/pyproject.toml +++ b/python/packages/ollama/pyproject.toml @@ -4,7 +4,7 @@ description = "Ollama integration for Microsoft Agent Framework." authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0b260219" +version = "1.0.0b260225" license-files = ["LICENSE"] urls.homepage = "https://learn.microsoft.com/en-us/agent-framework/" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -23,7 +23,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", "ollama >= 0.5.3", ] @@ -37,12 +37,16 @@ environments = [ [tool.uv-dynamic-versioning] fallback-version = "0.0.0" + [tool.pytest.ini_options] testpaths = 'tests' addopts = "-ra -q -r fEX" asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" filterwarnings = [] +markers = [ + "integration: marks tests as integration tests that require external services", +] timeout = 120 [tool.ruff] @@ -82,6 +86,7 @@ exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" + [tool.poe.tasks] mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_ollama" test = "pytest --cov=agent_framework_ollama --cov-report=term-missing:skip-covered tests" diff --git a/python/packages/ollama/tests/ollama/test_ollama_embedding_client.py b/python/packages/ollama/tests/ollama/test_ollama_embedding_client.py new file mode 100644 index 0000000000..c585f9c481 --- /dev/null +++ b/python/packages/ollama/tests/ollama/test_ollama_embedding_client.py @@ -0,0 +1,150 @@ +# Copyright (c) Microsoft. All rights reserved. + +import os +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest +from agent_framework import Embedding, GeneratedEmbeddings + +from agent_framework_ollama import OllamaEmbeddingClient, OllamaEmbeddingOptions + +# region: Unit Tests + + +def test_ollama_embedding_construction(monkeypatch: pytest.MonkeyPatch) -> None: + """Test construction with explicit parameters.""" + monkeypatch.setenv("OLLAMA_EMBEDDING_MODEL_ID", "nomic-embed-text") + with patch("agent_framework_ollama._embedding_client.AsyncClient") as mock_client_cls: + mock_client_cls.return_value = MagicMock() + client = OllamaEmbeddingClient() + assert client.model_id == "nomic-embed-text" + + +def test_ollama_embedding_construction_with_params() -> None: + """Test construction with explicit parameters.""" + with patch("agent_framework_ollama._embedding_client.AsyncClient") as mock_client_cls: + mock_client_cls.return_value = MagicMock() + client = OllamaEmbeddingClient( + model_id="nomic-embed-text", + host="http://localhost:11434", + ) + assert client.model_id == "nomic-embed-text" + + +def test_ollama_embedding_construction_missing_model_raises(monkeypatch: pytest.MonkeyPatch) -> None: + """Test that missing model_id raises an error.""" + monkeypatch.delenv("OLLAMA_EMBEDDING_MODEL_ID", raising=False) + monkeypatch.delenv("OLLAMA_MODEL_ID", raising=False) + from agent_framework.exceptions import SettingNotFoundError + + with pytest.raises(SettingNotFoundError): + OllamaEmbeddingClient() + + +async def test_ollama_embedding_get_embeddings() -> None: + """Test generating embeddings via the Ollama API.""" + mock_response = { + "model": "nomic-embed-text", + "embeddings": [[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]], + "prompt_eval_count": 10, + } + + with patch("agent_framework_ollama._embedding_client.AsyncClient") as mock_client_cls: + mock_client = MagicMock() + mock_client.embed = AsyncMock(return_value=mock_response) + mock_client_cls.return_value = mock_client + + client = OllamaEmbeddingClient(model_id="nomic-embed-text") + result = await client.get_embeddings(["hello", "world"]) + + assert isinstance(result, GeneratedEmbeddings) + assert len(result) == 2 + assert result[0].vector == [0.1, 0.2, 0.3] + assert result[1].vector == [0.4, 0.5, 0.6] + assert result[0].model_id == "nomic-embed-text" + assert result.usage == {"input_token_count": 10} + + mock_client.embed.assert_called_once_with( + model="nomic-embed-text", + input=["hello", "world"], + ) + + +async def test_ollama_embedding_get_embeddings_empty_input() -> None: + """Test generating embeddings with empty input.""" + with patch("agent_framework_ollama._embedding_client.AsyncClient") as mock_client_cls: + mock_client = MagicMock() + mock_client_cls.return_value = mock_client + + client = OllamaEmbeddingClient(model_id="nomic-embed-text") + result = await client.get_embeddings([]) + + assert isinstance(result, GeneratedEmbeddings) + assert len(result) == 0 + mock_client.embed.assert_not_called() + + +async def test_ollama_embedding_get_embeddings_with_options() -> None: + """Test generating embeddings with custom options.""" + mock_response = { + "model": "nomic-embed-text", + "embeddings": [[0.1, 0.2, 0.3]], + } + + with patch("agent_framework_ollama._embedding_client.AsyncClient") as mock_client_cls: + mock_client = MagicMock() + mock_client.embed = AsyncMock(return_value=mock_response) + mock_client_cls.return_value = mock_client + + client = OllamaEmbeddingClient(model_id="nomic-embed-text") + options: OllamaEmbeddingOptions = { + "truncate": True, + "dimensions": 512, + } + result = await client.get_embeddings(["hello"], options=options) + + assert len(result) == 1 + mock_client.embed.assert_called_once_with( + model="nomic-embed-text", + input=["hello"], + truncate=True, + dimensions=512, + ) + + +async def test_ollama_embedding_get_embeddings_no_model_raises() -> None: + """Test that missing model_id at call time raises ValueError.""" + with patch("agent_framework_ollama._embedding_client.AsyncClient") as mock_client_cls: + mock_client = MagicMock() + mock_client_cls.return_value = mock_client + + client = OllamaEmbeddingClient(model_id="nomic-embed-text") + client.model_id = None # type: ignore[assignment] + + with pytest.raises(ValueError, match="model_id is required"): + await client.get_embeddings(["hello"]) + + +# region: Integration Tests + +skip_if_ollama_embedding_integration_tests_disabled = pytest.mark.skipif( + os.getenv("OLLAMA_EMBEDDING_MODEL_ID", "") in ("", "test-model"), + reason="No real Ollama embedding model provided; skipping integration tests.", +) + + +@pytest.mark.flaky +@pytest.mark.integration +@skip_if_ollama_embedding_integration_tests_disabled +async def test_ollama_embedding_integration() -> None: + """Integration test for Ollama embedding client.""" + client = OllamaEmbeddingClient() + result = await client.get_embeddings(["Hello, world!", "How are you?"]) + + assert isinstance(result, GeneratedEmbeddings) + assert len(result) == 2 + for embedding in result: + assert isinstance(embedding, Embedding) + assert isinstance(embedding.vector, list) + assert len(embedding.vector) > 0 + assert all(isinstance(v, float) for v in embedding.vector) diff --git a/python/packages/orchestrations/pyproject.toml b/python/packages/orchestrations/pyproject.toml index d4bd40c411..f1cc4bfb45 100644 --- a/python/packages/orchestrations/pyproject.toml +++ b/python/packages/orchestrations/pyproject.toml @@ -4,7 +4,7 @@ description = "Orchestration patterns for Microsoft Agent Framework. Includes Se authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0b260219" +version = "1.0.0b260225" license-files = ["LICENSE"] urls.homepage = "https://aka.ms/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -23,7 +23,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", ] [tool.uv] @@ -44,6 +44,9 @@ asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" filterwarnings = [] timeout = 120 +markers = [ + "integration: marks tests as integration tests that require external services", +] [tool.ruff] extend = "../../pyproject.toml" @@ -78,6 +81,7 @@ exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" + [tool.poe.tasks] mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_orchestrations" test = "pytest --cov=agent_framework_orchestrations --cov-report=term-missing:skip-covered -n auto --dist worksteal tests" diff --git a/python/packages/purview/agent_framework_purview/_middleware.py b/python/packages/purview/agent_framework_purview/_middleware.py index 2296122135..55619d0a39 100644 --- a/python/packages/purview/agent_framework_purview/_middleware.py +++ b/python/packages/purview/agent_framework_purview/_middleware.py @@ -106,7 +106,7 @@ async def process( if context.result and not context.stream: should_block_response, _ = await self._processor.process_messages( context.result.messages, # type: ignore[union-attr] - Activity.UPLOAD_TEXT, + Activity.DOWNLOAD_TEXT, session_id=session_id, user_id=resolved_user_id, ) @@ -210,7 +210,7 @@ async def process( messages = getattr(result_obj, "messages", None) if messages: should_block_response, _ = await self._processor.process_messages( - messages, Activity.UPLOAD_TEXT, session_id=session_id_response, user_id=resolved_user_id + messages, Activity.DOWNLOAD_TEXT, session_id=session_id_response, user_id=resolved_user_id ) if should_block_response: from agent_framework import ChatResponse, Message diff --git a/python/packages/purview/agent_framework_purview/_processor.py b/python/packages/purview/agent_framework_purview/_processor.py index a7fc030cbf..e911fae7a5 100644 --- a/python/packages/purview/agent_framework_purview/_processor.py +++ b/python/packages/purview/agent_framework_purview/_processor.py @@ -158,7 +158,8 @@ async def _map_messages( name=f"Agent Framework Message {message_id}", is_truncated=False, correlation_id=correlation_id, - sequence_number=time.time_ns(), + # This would be c# ticks equivalent and needs to fit inside c# long + sequence_number=time.time_ns() // 100 + 621355968000000000, ) activity_meta = ActivityMetadata(activity=activity) diff --git a/python/packages/purview/pyproject.toml b/python/packages/purview/pyproject.toml index c9a65870cb..3481b27618 100644 --- a/python/packages/purview/pyproject.toml +++ b/python/packages/purview/pyproject.toml @@ -4,7 +4,7 @@ description = "Microsoft Purview (Graph dataSecurityAndGovernance) integration f authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0b260219" +version = "1.0.0b260225" license-files = ["LICENSE"] urls.homepage = "https://github.com/microsoft/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -24,7 +24,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", "azure-core>=1.30.0", "httpx>=0.27.0", ] @@ -39,12 +39,16 @@ environments = [ [tool.uv-dynamic-versioning] fallback-version = "0.0.0" + [tool.pytest.ini_options] testpaths = 'tests' addopts = "-ra -q -r fEX" asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" filterwarnings = [] +markers = [ + "integration: marks tests as integration tests that require external services", +] [tool.ruff] extend = "../../pyproject.toml" @@ -57,7 +61,6 @@ omit = [ [tool.pyright] extends = "../../pyproject.toml" - [tool.mypy] plugins = ['pydantic.mypy'] strict = true @@ -79,6 +82,7 @@ exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" + [tool.poe.tasks] mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_purview" test = "pytest --cov=agent_framework_purview --cov-report=term-missing:skip-covered tests" diff --git a/python/packages/purview/tests/purview/test_middleware.py b/python/packages/purview/tests/purview/test_middleware.py index 3a34c48344..b49b5d46a0 100644 --- a/python/packages/purview/tests/purview/test_middleware.py +++ b/python/packages/purview/tests/purview/test_middleware.py @@ -148,8 +148,10 @@ async def mock_next() -> None: await middleware.process(context, mock_next) assert mock_process.call_count == 2 - for call in mock_process.call_args_list: - assert call[0][1] == Activity.UPLOAD_TEXT + # First call (pre-check) should be UPLOAD_TEXT for user prompt + assert mock_process.call_args_list[0][0][1] == Activity.UPLOAD_TEXT + # Second call (post-check) should be DOWNLOAD_TEXT for agent response + assert mock_process.call_args_list[1][0][1] == Activity.DOWNLOAD_TEXT async def test_middleware_streaming_skips_post_check( self, middleware: PurviewPolicyMiddleware, mock_agent: MagicMock diff --git a/python/packages/redis/pyproject.toml b/python/packages/redis/pyproject.toml index 58a6a6a9e4..ab05066471 100644 --- a/python/packages/redis/pyproject.toml +++ b/python/packages/redis/pyproject.toml @@ -4,7 +4,7 @@ description = "Redis integration for Microsoft Agent Framework." authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0b260219" +version = "1.0.0b260225" license-files = ["LICENSE"] urls.homepage = "https://aka.ms/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -23,7 +23,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core>=1.0.0rc1", + "agent-framework-core>=1.0.0rc2", "redis>=6.4.0", "redisvl>=0.8.2", "numpy>=2.2.6" @@ -39,6 +39,7 @@ environments = [ [tool.uv-dynamic-versioning] fallback-version = "0.0.0" + [tool.pytest.ini_options] testpaths = 'tests' addopts = "-ra -q -r fEX" @@ -48,6 +49,9 @@ filterwarnings = [ "ignore:Support for class-based `config` is deprecated:DeprecationWarning:pydantic.*" ] timeout = 120 +markers = [ + "integration: marks tests as integration tests that require external services", +] [tool.ruff] extend = "../../pyproject.toml" @@ -81,6 +85,7 @@ exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" + [tool.poe.tasks] mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_redis" test = "pytest --cov=agent_framework_redis --cov-report=term-missing:skip-covered tests" diff --git a/python/pyproject.toml b/python/pyproject.toml index 259caffaf9..a03123e9a2 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -4,7 +4,7 @@ description = "Microsoft Agent Framework for building AI Agents with Python. Thi authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "1.0.0rc1" +version = "1.0.0rc2" license-files = ["LICENSE"] urls.homepage = "https://aka.ms/agent-framework" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -23,7 +23,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core[all]==1.0.0rc1", + "agent-framework-core[all]==1.0.0rc2", ] [dependency-groups] diff --git a/python/samples/02-agents/embeddings/azure_ai_inference_embeddings.py b/python/samples/02-agents/embeddings/azure_ai_inference_embeddings.py new file mode 100644 index 0000000000..70d60983e9 --- /dev/null +++ b/python/samples/02-agents/embeddings/azure_ai_inference_embeddings.py @@ -0,0 +1,87 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "agent-framework-azure-ai", +# ] +# /// +# Run with: uv run samples/02-agents/embeddings/azure_ai_inference_embeddings.py + +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +import pathlib + +from agent_framework import Content +from agent_framework_azure_ai import AzureAIInferenceEmbeddingClient +from dotenv import load_dotenv + +load_dotenv() + +"""Azure AI Inference Image Embedding Example + +This sample demonstrates how to generate image embeddings using the +Azure AI Inference embedding client with the Cohere-embed-v3-english model. +Images are passed as ``Content`` objects created with ``Content.from_data()``. + +Prerequisites: + Set the following environment variables or add them to a .env file: + - AZURE_AI_INFERENCE_ENDPOINT: Your Azure AI model inference endpoint URL + - AZURE_AI_INFERENCE_API_KEY: Your API key + - AZURE_AI_INFERENCE_EMBEDDING_MODEL_ID: The text embedding model name + (e.g. "text-embedding-3-small") + - AZURE_AI_INFERENCE_IMAGE_EMBEDDING_MODEL_ID: The image embedding model name + (e.g. "Cohere-embed-v3-english") +""" + +SAMPLE_IMAGE_PATH = pathlib.Path(__file__).parent.parent.parent / "shared" / "sample_assets" / "sample_image.jpg" + + +async def main() -> None: + """Generate image embeddings with Azure AI Inference.""" + async with AzureAIInferenceEmbeddingClient() as client: + # 1. Generate an image embedding. + image_bytes = SAMPLE_IMAGE_PATH.read_bytes() + image_content = Content.from_data(data=image_bytes, media_type="image/jpeg") + result = await client.get_embeddings([image_content]) + print(f"Image embedding dimensions: {result[0].dimensions}") + print(f"First 5 values: {result[0].vector[:5]}") + print(f"Model: {result[0].model_id}") + print(f"Usage: {result.usage}") + print() + + # 2. Generate image and text embeddings separately in one call. + # The client dispatches text to the text endpoint and images to the image + # endpoint, then reassembles results in the original input order. + result = await client.get_embeddings(["A half-timbered house in a forested valley", image_content]) + print(f"Text embedding dimensions: {result[0].dimensions}") + print(f"First 5 values: {result[0].vector[:5]}") + print(f"Image embedding dimensions: {result[1].dimensions}") + print(f"First 5 values: {result[1].vector[:5]}") + print() + + # 3. Generate image embeddings with input_type option. + result = await client.get_embeddings( + [image_content], + options={"input_type": "document"}, + ) + print(f"Document embedding dimensions: {result[0].dimensions}") + print(f"First 5 values: {result[0].vector[:5]}") + + +if __name__ == "__main__": + asyncio.run(main()) + + +""" +Sample output (using Cohere-embed-v3-english): +Image embedding dimensions: 1024 +First 5 values: [0.023, -0.045, 0.067, -0.089, 0.011] +Model: Cohere-embed-v3-english +Usage: {'prompt_tokens': 1, 'total_tokens': 1} + +Image+text (separate) results: +Text embedding dimensions: 1536 +Image embedding dimensions: 1024 + +Document embedding dimensions: 1024 +""" diff --git a/python/samples/03-workflows/README.md b/python/samples/03-workflows/README.md index c5abe202ac..4dfdd68157 100644 --- a/python/samples/03-workflows/README.md +++ b/python/samples/03-workflows/README.md @@ -160,6 +160,14 @@ Sequential orchestration uses a few small adapter nodes for plumbing: These may appear in event streams (executor_invoked/executor_completed). They're analogous to concurrent’s dispatcher and aggregator and can be ignored if you only care about agent activity. +### AzureOpenAIResponsesClient vs AzureAIAgent + +Workflow and orchestration samples use `AzureOpenAIResponsesClient` rather than the CRUD-style `AzureAIAgent` client. The key difference: + +- **`AzureOpenAIResponsesClient`** — A lightweight client that uses the underlying Agent Service V2 (Responses API) for non-CRUD-style agents. Orchestrations use this client because agents are created locally and do not require server-side lifecycle management (create/update/delete). This is the recommended client for orchestration patterns (Sequential, Concurrent, Handoff, GroupChat, Magentic). + +- **`AzureAIAgent`** — A CRUD-style client for server-managed agents. Use this when you need persistent, server-side agent definitions with features like file search, code interpreter sessions, or thread management provided by the Azure AI Agent Service. + ### Environment Variables Workflow samples that use `AzureOpenAIResponsesClient` expect: diff --git a/python/samples/03-workflows/declarative/agent_to_function_tool/main.py b/python/samples/03-workflows/declarative/agent_to_function_tool/main.py index 55ba77c073..d4346d826e 100644 --- a/python/samples/03-workflows/declarative/agent_to_function_tool/main.py +++ b/python/samples/03-workflows/declarative/agent_to_function_tool/main.py @@ -18,10 +18,11 @@ """ import asyncio +import os from pathlib import Path from typing import Any -from agent_framework.azure import AzureOpenAIChatClient +from agent_framework.azure import AzureOpenAIResponsesClient from agent_framework.declarative import WorkflowFactory from azure.identity import AzureCliCredential from pydantic import BaseModel, Field @@ -196,8 +197,12 @@ def format_order_confirmation(order_data: dict[str, Any], order_calculation: dic async def main(): """Run the agent to function tool workflow.""" - # Create Azure OpenAI client - chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) + # Create Azure OpenAI Responses client + chat_client = AzureOpenAIResponsesClient( + project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=AzureCliCredential(), + ) # Create the order analysis agent with structured output order_analysis_agent = chat_client.as_agent( diff --git a/python/samples/03-workflows/declarative/function_tools/README.md b/python/samples/03-workflows/declarative/function_tools/README.md index 78e7cf361e..e831ba51d7 100644 --- a/python/samples/03-workflows/declarative/function_tools/README.md +++ b/python/samples/03-workflows/declarative/function_tools/README.md @@ -6,7 +6,7 @@ This sample demonstrates an agent with function tools responding to user queries The workflow showcases: - **Function Tools**: Agent equipped with tools to query menu data -- **Real Azure OpenAI Agent**: Uses `AzureOpenAIChatClient` to create an agent with tools +- **Real Azure OpenAI Agent**: Uses `AzureOpenAIResponsesClient` to create an agent with tools - **Agent Registration**: Shows how to register agents with the `WorkflowFactory` ## Tools @@ -72,7 +72,11 @@ Session Complete ```python # Create the agent with tools -client = AzureOpenAIChatClient(credential=AzureCliCredential()) +client = AzureOpenAIResponsesClient( + project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=AzureCliCredential(), +) menu_agent = client.as_agent( name="MenuAgent", instructions="You are a helpful restaurant menu assistant...", diff --git a/python/samples/03-workflows/orchestrations/README.md b/python/samples/03-workflows/orchestrations/README.md index 3ce85cd1ab..527a414295 100644 --- a/python/samples/03-workflows/orchestrations/README.md +++ b/python/samples/03-workflows/orchestrations/README.md @@ -92,6 +92,10 @@ from agent_framework.orchestrations import ( These may appear in event streams (executor_invoked/executor_completed). They're analogous to concurrent's dispatcher and aggregator and can be ignored if you only care about agent activity. +## Why AzureOpenAIResponsesClient? + +Orchestration samples use `AzureOpenAIResponsesClient` rather than the CRUD-style `AzureAIAgent` client. Orchestrations create agents locally and do not require server-side lifecycle management (create/update/delete). `AzureOpenAIResponsesClient` is a lightweight client that uses the underlying Agent Service V2 (Responses API) for non-CRUD-style agents, which is ideal for orchestration patterns like Sequential, Concurrent, Handoff, GroupChat, and Magentic. + ## Environment Variables Orchestration samples that use `AzureOpenAIResponsesClient` expect: diff --git a/python/samples/03-workflows/orchestrations/concurrent_agents.py b/python/samples/03-workflows/orchestrations/concurrent_agents.py index f6aae589a6..78e56b38bf 100644 --- a/python/samples/03-workflows/orchestrations/concurrent_agents.py +++ b/python/samples/03-workflows/orchestrations/concurrent_agents.py @@ -1,10 +1,11 @@ # Copyright (c) Microsoft. All rights reserved. import asyncio +import os from typing import Any from agent_framework import Message -from agent_framework.azure import AzureOpenAIChatClient +from agent_framework.azure import AzureOpenAIResponsesClient from agent_framework.orchestrations import ConcurrentBuilder from azure.identity import AzureCliCredential from dotenv import load_dotenv @@ -26,14 +27,20 @@ - Workflow completion when idle with no pending work Prerequisites: -- Azure OpenAI access configured for AzureOpenAIChatClient (use az login + env vars) +- AZURE_AI_PROJECT_ENDPOINT must be your Azure AI Foundry Agent Service (V2) project endpoint. +- Azure OpenAI configured for AzureOpenAIResponsesClient with required environment variables. +- Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample. - Familiarity with Workflow events (WorkflowEvent) """ async def main() -> None: - # 1) Create three domain agents using AzureOpenAIChatClient - client = AzureOpenAIChatClient(credential=AzureCliCredential()) + # 1) Create three domain agents using AzureOpenAIResponsesClient + client = AzureOpenAIResponsesClient( + project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=AzureCliCredential(), + ) researcher = client.as_agent( instructions=( diff --git a/python/samples/03-workflows/orchestrations/concurrent_custom_agent_executors.py b/python/samples/03-workflows/orchestrations/concurrent_custom_agent_executors.py index 82df710435..3e0a9b63c6 100644 --- a/python/samples/03-workflows/orchestrations/concurrent_custom_agent_executors.py +++ b/python/samples/03-workflows/orchestrations/concurrent_custom_agent_executors.py @@ -1,6 +1,7 @@ # Copyright (c) Microsoft. All rights reserved. import asyncio +import os from typing import Any from agent_framework import ( @@ -12,7 +13,7 @@ WorkflowContext, handler, ) -from agent_framework.azure import AzureOpenAIChatClient +from agent_framework.azure import AzureOpenAIResponsesClient from agent_framework.orchestrations import ConcurrentBuilder from azure.identity import AzureCliCredential from dotenv import load_dotenv @@ -29,21 +30,23 @@ ConcurrentBuilder API and the default aggregator. Demonstrates: -- Executors that create their Agent in __init__ (via AzureOpenAIChatClient) +- Executors that create their Agent in __init__ (via AzureOpenAIResponsesClient) - A @handler that converts AgentExecutorRequest -> AgentExecutorResponse - ConcurrentBuilder(participants=[...]) to build fan-out/fan-in - Default aggregator returning list[Message] (one user + one assistant per agent) - Workflow completion when all participants become idle Prerequisites: -- Azure OpenAI configured for AzureOpenAIChatClient (az login + required env vars) +- AZURE_AI_PROJECT_ENDPOINT must be your Azure AI Foundry Agent Service (V2) project endpoint. +- Azure OpenAI configured for AzureOpenAIResponsesClient with required environment variables. +- Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample. """ class ResearcherExec(Executor): agent: Agent - def __init__(self, client: AzureOpenAIChatClient, id: str = "researcher"): + def __init__(self, client: AzureOpenAIResponsesClient, id: str = "researcher"): self.agent = client.as_agent( instructions=( "You're an expert market and product researcher. Given a prompt, provide concise, factual insights," @@ -63,7 +66,7 @@ async def run(self, request: AgentExecutorRequest, ctx: WorkflowContext[AgentExe class MarketerExec(Executor): agent: Agent - def __init__(self, client: AzureOpenAIChatClient, id: str = "marketer"): + def __init__(self, client: AzureOpenAIResponsesClient, id: str = "marketer"): self.agent = client.as_agent( instructions=( "You're a creative marketing strategist. Craft compelling value propositions and target messaging" @@ -83,7 +86,7 @@ async def run(self, request: AgentExecutorRequest, ctx: WorkflowContext[AgentExe class LegalExec(Executor): agent: Agent - def __init__(self, client: AzureOpenAIChatClient, id: str = "legal"): + def __init__(self, client: AzureOpenAIResponsesClient, id: str = "legal"): self.agent = client.as_agent( instructions=( "You're a cautious legal/compliance reviewer. Highlight constraints, disclaimers, and policy concerns" @@ -101,7 +104,11 @@ async def run(self, request: AgentExecutorRequest, ctx: WorkflowContext[AgentExe async def main() -> None: - client = AzureOpenAIChatClient(credential=AzureCliCredential()) + client = AzureOpenAIResponsesClient( + project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=AzureCliCredential(), + ) researcher = ResearcherExec(client) marketer = MarketerExec(client) diff --git a/python/samples/03-workflows/orchestrations/concurrent_custom_aggregator.py b/python/samples/03-workflows/orchestrations/concurrent_custom_aggregator.py index 3014a0e3cf..b622e0f6b7 100644 --- a/python/samples/03-workflows/orchestrations/concurrent_custom_aggregator.py +++ b/python/samples/03-workflows/orchestrations/concurrent_custom_aggregator.py @@ -1,10 +1,11 @@ # Copyright (c) Microsoft. All rights reserved. import asyncio +import os from typing import Any from agent_framework import Message -from agent_framework.azure import AzureOpenAIChatClient +from agent_framework.azure import AzureOpenAIResponsesClient from agent_framework.orchestrations import ConcurrentBuilder from azure.identity import AzureCliCredential from dotenv import load_dotenv @@ -17,7 +18,7 @@ Build a concurrent workflow with ConcurrentBuilder that fans out one prompt to multiple domain agents and fans in their responses. Override the default -aggregator with a custom async callback that uses AzureOpenAIChatClient.get_response() +aggregator with a custom async callback that uses AzureOpenAIResponsesClient.get_response() to synthesize a concise, consolidated summary from the experts' outputs. The workflow completes when all participants become idle. @@ -28,12 +29,18 @@ - Workflow output yielded with the synthesized summary string Prerequisites: -- Azure OpenAI configured for AzureOpenAIChatClient (az login + required env vars) +- AZURE_AI_PROJECT_ENDPOINT must be your Azure AI Foundry Agent Service (V2) project endpoint. +- Azure OpenAI configured for AzureOpenAIResponsesClient with required environment variables. +- Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample. """ async def main() -> None: - client = AzureOpenAIChatClient(credential=AzureCliCredential()) + client = AzureOpenAIResponsesClient( + project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=AzureCliCredential(), + ) researcher = client.as_agent( instructions=( diff --git a/python/samples/03-workflows/orchestrations/group_chat_agent_manager.py b/python/samples/03-workflows/orchestrations/group_chat_agent_manager.py index d057756160..2fecc34282 100644 --- a/python/samples/03-workflows/orchestrations/group_chat_agent_manager.py +++ b/python/samples/03-workflows/orchestrations/group_chat_agent_manager.py @@ -1,6 +1,7 @@ # Copyright (c) Microsoft. All rights reserved. import asyncio +import os from typing import cast from agent_framework import ( @@ -8,7 +9,7 @@ AgentResponseUpdate, Message, ) -from agent_framework.azure import AzureOpenAIChatClient +from agent_framework.azure import AzureOpenAIResponsesClient from agent_framework.orchestrations import GroupChatBuilder from azure.identity import AzureCliCredential from dotenv import load_dotenv @@ -25,7 +26,9 @@ - Coordinates a researcher and writer agent to solve tasks collaboratively Prerequisites: -- OpenAI environment variables configured for OpenAIChatClient +- AZURE_AI_PROJECT_ENDPOINT must be your Azure AI Foundry Agent Service (V2) project endpoint. +- Azure OpenAI configured for AzureOpenAIResponsesClient with required environment variables. +- Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample. """ ORCHESTRATOR_AGENT_INSTRUCTIONS = """ @@ -39,8 +42,12 @@ async def main() -> None: - # Create a chat client using Azure OpenAI and Azure CLI credentials for all agents - client = AzureOpenAIChatClient(credential=AzureCliCredential()) + # Create a Responses client using Azure OpenAI and Azure CLI credentials for all agents + client = AzureOpenAIResponsesClient( + project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=AzureCliCredential(), + ) # Orchestrator agent that manages the conversation # Note: This agent (and the underlying chat client) must support structured outputs. diff --git a/python/samples/03-workflows/orchestrations/group_chat_philosophical_debate.py b/python/samples/03-workflows/orchestrations/group_chat_philosophical_debate.py index 5e55fe9204..a8dd2aebfe 100644 --- a/python/samples/03-workflows/orchestrations/group_chat_philosophical_debate.py +++ b/python/samples/03-workflows/orchestrations/group_chat_philosophical_debate.py @@ -2,6 +2,7 @@ import asyncio import logging +import os from typing import cast from agent_framework import ( @@ -9,7 +10,7 @@ AgentResponseUpdate, Message, ) -from agent_framework.azure import AzureOpenAIChatClient +from agent_framework.azure import AzureOpenAIResponsesClient from agent_framework.orchestrations import GroupChatBuilder from azure.identity import AzureCliCredential from dotenv import load_dotenv @@ -38,15 +39,21 @@ - Doctor from Scandinavia (public health, equity, societal support) Prerequisites: -- OpenAI environment variables configured for OpenAIChatClient +- AZURE_AI_PROJECT_ENDPOINT must be your Azure AI Foundry Agent Service (V2) project endpoint. +- Azure OpenAI configured for AzureOpenAIResponsesClient with required environment variables. +- Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample. """ # Load environment variables from .env file load_dotenv() -def _get_chat_client() -> AzureOpenAIChatClient: - return AzureOpenAIChatClient(credential=AzureCliCredential()) +def _get_chat_client() -> AzureOpenAIResponsesClient: + return AzureOpenAIResponsesClient( + project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=AzureCliCredential(), + ) async def main() -> None: diff --git a/python/samples/03-workflows/orchestrations/group_chat_simple_selector.py b/python/samples/03-workflows/orchestrations/group_chat_simple_selector.py index cf9b6aa8ec..984b46c6a4 100644 --- a/python/samples/03-workflows/orchestrations/group_chat_simple_selector.py +++ b/python/samples/03-workflows/orchestrations/group_chat_simple_selector.py @@ -1,6 +1,7 @@ # Copyright (c) Microsoft. All rights reserved. import asyncio +import os from typing import cast from agent_framework import ( @@ -8,7 +9,7 @@ AgentResponseUpdate, Message, ) -from agent_framework.azure import AzureOpenAIChatClient +from agent_framework.azure import AzureOpenAIResponsesClient from agent_framework.orchestrations import GroupChatBuilder, GroupChatState from azure.identity import AzureCliCredential from dotenv import load_dotenv @@ -24,7 +25,9 @@ - Uses a pure Python function to control speaker selection based on conversation state Prerequisites: -- OpenAI environment variables configured for OpenAIChatClient +- AZURE_AI_PROJECT_ENDPOINT must be your Azure AI Foundry Agent Service (V2) project endpoint. +- Azure OpenAI configured for AzureOpenAIResponsesClient with required environment variables. +- Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample. """ @@ -36,8 +39,12 @@ def round_robin_selector(state: GroupChatState) -> str: async def main() -> None: - # Create a chat client using Azure OpenAI and Azure CLI credentials for all agents - client = AzureOpenAIChatClient(credential=AzureCliCredential()) + # Create a Responses client using Azure OpenAI and Azure CLI credentials for all agents + client = AzureOpenAIResponsesClient( + project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=AzureCliCredential(), + ) # Participant agents expert = Agent( diff --git a/python/samples/03-workflows/orchestrations/handoff_autonomous.py b/python/samples/03-workflows/orchestrations/handoff_autonomous.py index d97006df12..7b86e73cf8 100644 --- a/python/samples/03-workflows/orchestrations/handoff_autonomous.py +++ b/python/samples/03-workflows/orchestrations/handoff_autonomous.py @@ -2,6 +2,7 @@ import asyncio import logging +import os from typing import cast from agent_framework import ( @@ -10,7 +11,7 @@ Message, resolve_agent_id, ) -from agent_framework.azure import AzureOpenAIChatClient +from agent_framework.azure import AzureOpenAIResponsesClient from agent_framework.orchestrations import HandoffBuilder from azure.identity import AzureCliCredential from dotenv import load_dotenv @@ -28,8 +29,9 @@ User -> Coordinator -> Specialist (iterates N times) -> Handoff -> Final Output Prerequisites: - - `az login` (Azure CLI authentication) - - Environment variables for AzureOpenAIChatClient (AZURE_OPENAI_ENDPOINT, etc.) + - AZURE_AI_PROJECT_ENDPOINT must be your Azure AI Foundry Agent Service (V2) project endpoint. + - Azure OpenAI configured for AzureOpenAIResponsesClient with required environment variables. + - Authentication via azure-identity. Use AzureCliCredential and run `az login` before executing the sample. Key Concepts: - Autonomous interaction mode: agents iterate until they handoff @@ -41,7 +43,7 @@ def create_agents( - client: AzureOpenAIChatClient, + client: AzureOpenAIResponsesClient, ) -> tuple[Agent, Agent, Agent]: """Create coordinator and specialists for autonomous iteration.""" coordinator = client.as_agent( @@ -77,7 +79,11 @@ def create_agents( async def main() -> None: """Run an autonomous handoff workflow with specialist iteration enabled.""" - client = AzureOpenAIChatClient(credential=AzureCliCredential()) + client = AzureOpenAIResponsesClient( + project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=AzureCliCredential(), + ) coordinator, research_agent, summary_agent = create_agents(client) # Build the workflow with autonomous mode diff --git a/python/samples/03-workflows/orchestrations/handoff_simple.py b/python/samples/03-workflows/orchestrations/handoff_simple.py index 5a2319d4d2..1b4820ccbe 100644 --- a/python/samples/03-workflows/orchestrations/handoff_simple.py +++ b/python/samples/03-workflows/orchestrations/handoff_simple.py @@ -1,6 +1,7 @@ # Copyright (c) Microsoft. All rights reserved. import asyncio +import os from typing import Annotated, cast from agent_framework import ( @@ -11,7 +12,7 @@ WorkflowRunState, tool, ) -from agent_framework.azure import AzureOpenAIChatClient +from agent_framework.azure import AzureOpenAIResponsesClient from agent_framework.orchestrations import HandoffAgentUserRequest, HandoffBuilder from azure.identity import AzureCliCredential from dotenv import load_dotenv @@ -25,8 +26,9 @@ them to transfer control to each other based on the conversation context. Prerequisites: - - `az login` (Azure CLI authentication) - - Environment variables configured for AzureOpenAIChatClient (AZURE_OPENAI_ENDPOINT, etc.) + - AZURE_AI_PROJECT_ENDPOINT must be your Azure AI Foundry Agent Service (V2) project endpoint. + - Azure OpenAI configured for AzureOpenAIResponsesClient with required environment variables. + - Authentication via azure-identity. Use AzureCliCredential and run `az login` before executing the sample. Key Concepts: - Auto-registered handoff tools: HandoffBuilder automatically creates handoff tools @@ -58,11 +60,11 @@ def process_return(order_number: Annotated[str, "Order number to process return return f"Return initiated successfully for order {order_number}. You will receive return instructions via email." -def create_agents(client: AzureOpenAIChatClient) -> tuple[Agent, Agent, Agent, Agent]: +def create_agents(client: AzureOpenAIResponsesClient) -> tuple[Agent, Agent, Agent, Agent]: """Create and configure the triage and specialist agents. Args: - client: The AzureOpenAIChatClient to use for creating agents. + client: The AzureOpenAIResponsesClient to use for creating agents. Returns: Tuple of (triage_agent, refund_agent, order_agent, return_agent) @@ -192,8 +194,12 @@ async def main() -> None: the demo reproducible and testable. In a production application, you would replace the scripted_responses with actual user input collection. """ - # Initialize the Azure OpenAI chat client - client = AzureOpenAIChatClient(credential=AzureCliCredential()) + # Initialize the Azure OpenAI Responses client + client = AzureOpenAIResponsesClient( + project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=AzureCliCredential(), + ) # Create all agents: triage + specialists triage, refund, order, support = create_agents(client) diff --git a/python/samples/03-workflows/orchestrations/magentic.py b/python/samples/03-workflows/orchestrations/magentic.py index 2420b74245..b412fd0b9d 100644 --- a/python/samples/03-workflows/orchestrations/magentic.py +++ b/python/samples/03-workflows/orchestrations/magentic.py @@ -3,6 +3,7 @@ import asyncio import json import logging +import os from typing import cast from agent_framework import ( @@ -11,8 +12,9 @@ Message, WorkflowEvent, ) -from agent_framework.openai import OpenAIChatClient, OpenAIResponsesClient +from agent_framework.azure import AzureOpenAIResponsesClient from agent_framework.orchestrations import GroupChatRequestSentEvent, MagenticBuilder, MagenticProgressLedger +from azure.identity import AzureCliCredential from dotenv import load_dotenv logging.basicConfig(level=logging.WARNING) @@ -40,7 +42,9 @@ events, and prints the final answer. The workflow completes when idle. Prerequisites: -- OpenAI credentials configured for `OpenAIChatClient` and `OpenAIResponsesClient`. +- AZURE_AI_PROJECT_ENDPOINT must be your Azure AI Foundry Agent Service (V2) project endpoint. +- Azure OpenAI configured for AzureOpenAIResponsesClient with required environment variables. +- Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample. """ # Load environment variables from .env file @@ -48,25 +52,29 @@ async def main() -> None: + client = AzureOpenAIResponsesClient( + project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=AzureCliCredential(), + ) + researcher_agent = Agent( name="ResearcherAgent", description="Specialist in research and information gathering", instructions=( "You are a Researcher. You find information without additional computation or quantitative analysis." ), - # This agent requires the gpt-4o-search-preview model to perform web searches. - client=OpenAIChatClient(model_id="gpt-4o-search-preview"), + client=client, ) # Create code interpreter tool using instance method - coder_client = OpenAIResponsesClient() - code_interpreter_tool = coder_client.get_code_interpreter_tool() + code_interpreter_tool = client.get_code_interpreter_tool() coder_agent = Agent( name="CoderAgent", description="A helpful assistant that writes and executes code to process and analyze data.", instructions="You solve questions using code. Please provide detailed analysis and computation process.", - client=coder_client, + client=client, tools=code_interpreter_tool, ) @@ -75,7 +83,7 @@ async def main() -> None: name="MagenticManager", description="Orchestrator that coordinates the research and coding workflow", instructions="You coordinate a team to complete complex tasks efficiently.", - client=OpenAIChatClient(), + client=client, ) print("\nBuilding Magentic Workflow...") diff --git a/python/samples/03-workflows/orchestrations/magentic_checkpoint.py b/python/samples/03-workflows/orchestrations/magentic_checkpoint.py index 940e47759f..ab3da11c1d 100644 --- a/python/samples/03-workflows/orchestrations/magentic_checkpoint.py +++ b/python/samples/03-workflows/orchestrations/magentic_checkpoint.py @@ -2,6 +2,7 @@ import asyncio import json +import os from datetime import datetime from pathlib import Path from typing import cast @@ -14,9 +15,9 @@ WorkflowEvent, WorkflowRunState, ) -from agent_framework.azure import AzureOpenAIChatClient +from agent_framework.azure import AzureOpenAIResponsesClient from agent_framework.orchestrations import MagenticBuilder, MagenticPlanReviewRequest -from azure.identity._credentials import AzureCliCredential +from azure.identity import AzureCliCredential from dotenv import load_dotenv # Load environment variables from .env file @@ -38,7 +39,9 @@ `responses` mapping so we can inject the stored human reply during restoration. Prerequisites: -- OpenAI environment variables configured for `OpenAIChatClient`. +- AZURE_AI_PROJECT_ENDPOINT must be your Azure AI Foundry Agent Service (V2) project endpoint. +- Azure OpenAI configured for AzureOpenAIResponsesClient with required environment variables. +- Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample. """ TASK = ( @@ -61,14 +64,22 @@ def build_workflow(checkpoint_storage: FileCheckpointStorage): name="ResearcherAgent", description="Collects background facts and references for the project.", instructions=("You are the research lead. Gather crisp bullet points the team should know."), - client=AzureOpenAIChatClient(credential=AzureCliCredential()), + client=AzureOpenAIResponsesClient( + project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=AzureCliCredential(), + ), ) writer = Agent( name="WriterAgent", description="Synthesizes the final brief for stakeholders.", instructions=("You convert the research notes into a structured brief with milestones and risks."), - client=AzureOpenAIChatClient(credential=AzureCliCredential()), + client=AzureOpenAIResponsesClient( + project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=AzureCliCredential(), + ), ) # Create a manager agent for orchestration @@ -76,7 +87,11 @@ def build_workflow(checkpoint_storage: FileCheckpointStorage): name="MagenticManager", description="Orchestrator that coordinates the research and writing workflow", instructions="You coordinate a team to complete complex tasks efficiently.", - client=AzureOpenAIChatClient(credential=AzureCliCredential()), + client=AzureOpenAIResponsesClient( + project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=AzureCliCredential(), + ), ) # The builder wires in the Magentic orchestrator, sets the plan review path, and diff --git a/python/samples/03-workflows/orchestrations/magentic_human_plan_review.py b/python/samples/03-workflows/orchestrations/magentic_human_plan_review.py index d9234026aa..61f1cd412d 100644 --- a/python/samples/03-workflows/orchestrations/magentic_human_plan_review.py +++ b/python/samples/03-workflows/orchestrations/magentic_human_plan_review.py @@ -2,6 +2,7 @@ import asyncio import json +import os from collections.abc import AsyncIterable from typing import cast @@ -11,8 +12,9 @@ Message, WorkflowEvent, ) -from agent_framework.openai import OpenAIChatClient +from agent_framework.azure import AzureOpenAIResponsesClient from agent_framework.orchestrations import MagenticBuilder, MagenticPlanReviewRequest, MagenticPlanReviewResponse +from azure.identity import AzureCliCredential from dotenv import load_dotenv # Load environment variables from .env file @@ -35,7 +37,9 @@ - revise(feedback): Provide textual feedback to modify the plan Prerequisites: -- OpenAI credentials configured for `OpenAIChatClient`. +- AZURE_AI_PROJECT_ENDPOINT must be your Azure AI Foundry Agent Service (V2) project endpoint. +- Azure OpenAI configured for AzureOpenAIResponsesClient with required environment variables. +- Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample. """ # Keep track of the last response to format output nicely in streaming mode @@ -96,25 +100,31 @@ async def process_event_stream(stream: AsyncIterable[WorkflowEvent]) -> dict[str async def main() -> None: + client = AzureOpenAIResponsesClient( + project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=AzureCliCredential(), + ) + researcher_agent = Agent( name="ResearcherAgent", description="Specialist in research and information gathering", instructions="You are a Researcher. You find information and gather facts.", - client=OpenAIChatClient(model_id="gpt-4o"), + client=client, ) analyst_agent = Agent( name="AnalystAgent", description="Data analyst who processes and summarizes research findings", instructions="You are an Analyst. You analyze findings and create summaries.", - client=OpenAIChatClient(model_id="gpt-4o"), + client=client, ) manager_agent = Agent( name="MagenticManager", description="Orchestrator that coordinates the workflow", instructions="You coordinate a team to complete tasks efficiently.", - client=OpenAIChatClient(model_id="gpt-4o"), + client=client, ) print("\nBuilding Magentic Workflow with Human Plan Review...") diff --git a/python/samples/03-workflows/orchestrations/sequential_agents.py b/python/samples/03-workflows/orchestrations/sequential_agents.py index 6eda11ece9..916ecbee9c 100644 --- a/python/samples/03-workflows/orchestrations/sequential_agents.py +++ b/python/samples/03-workflows/orchestrations/sequential_agents.py @@ -1,10 +1,11 @@ # Copyright (c) Microsoft. All rights reserved. import asyncio +import os from typing import cast from agent_framework import Message -from agent_framework.azure import AzureOpenAIChatClient +from agent_framework.azure import AzureOpenAIResponsesClient from agent_framework.orchestrations import SequentialBuilder from azure.identity import AzureCliCredential from dotenv import load_dotenv @@ -28,13 +29,19 @@ You can safely ignore them when focusing on agent progress. Prerequisites: -- Azure OpenAI access configured for AzureOpenAIChatClient (use az login + env vars) +- AZURE_AI_PROJECT_ENDPOINT must be your Azure AI Foundry Agent Service (V2) project endpoint. +- Azure OpenAI configured for AzureOpenAIResponsesClient with required environment variables. +- Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample. """ async def main() -> None: # 1) Create agents - client = AzureOpenAIChatClient(credential=AzureCliCredential()) + client = AzureOpenAIResponsesClient( + project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=AzureCliCredential(), + ) writer = client.as_agent( instructions=("You are a concise copywriter. Provide a single, punchy marketing sentence based on the prompt."), diff --git a/python/samples/03-workflows/orchestrations/sequential_custom_executors.py b/python/samples/03-workflows/orchestrations/sequential_custom_executors.py index 56308d2b5f..b46971cffe 100644 --- a/python/samples/03-workflows/orchestrations/sequential_custom_executors.py +++ b/python/samples/03-workflows/orchestrations/sequential_custom_executors.py @@ -1,6 +1,7 @@ # Copyright (c) Microsoft. All rights reserved. import asyncio +import os from typing import Any from agent_framework import ( @@ -10,7 +11,7 @@ WorkflowContext, handler, ) -from agent_framework.azure import AzureOpenAIChatClient +from agent_framework.azure import AzureOpenAIResponsesClient from agent_framework.orchestrations import SequentialBuilder from azure.identity import AzureCliCredential from dotenv import load_dotenv @@ -32,7 +33,9 @@ - Emit the updated conversation via ctx.send_message([...]) Prerequisites: -- Azure OpenAI access configured for AzureOpenAIChatClient (use az login + env vars) +- AZURE_AI_PROJECT_ENDPOINT must be your Azure AI Foundry Agent Service (V2) project endpoint. +- Azure OpenAI configured for AzureOpenAIResponsesClient with required environment variables. +- Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample. """ @@ -62,7 +65,11 @@ async def summarize(self, agent_response: AgentExecutorResponse, ctx: WorkflowCo async def main() -> None: # 1) Create a content agent - client = AzureOpenAIChatClient(credential=AzureCliCredential()) + client = AzureOpenAIResponsesClient( + project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=AzureCliCredential(), + ) content = client.as_agent( instructions="Produce a concise paragraph answering the user's request.", name="content", diff --git a/python/samples/04-hosting/a2a/README.md b/python/samples/04-hosting/a2a/README.md index dd09aaaa1a..2ede8b8a3d 100644 --- a/python/samples/04-hosting/a2a/README.md +++ b/python/samples/04-hosting/a2a/README.md @@ -25,7 +25,7 @@ Make sure to set the following environment variables before running the example: For quick testing and demonstration, you can use the pre-built .NET A2A servers from this repository: **Quick Testing Reference**: Use the .NET A2A Client Server sample at: -`..\agent-framework\dotnet\samples\A2AClientServer` +`..\agent-framework\dotnet\samples\05-end-to-end\A2AClientServer` ### Run Python A2A Sample ```powershell diff --git a/python/samples/README.md b/python/samples/README.md index 148ace320d..1f353fbc52 100644 --- a/python/samples/README.md +++ b/python/samples/README.md @@ -21,7 +21,7 @@ Start with `01-get-started/` and work through the numbered files: 3. **[03_multi_turn.py](./01-get-started/03_multi_turn.py)** — Multi-turn conversations with `AgentThread` 4. **[04_memory.py](./01-get-started/04_memory.py)** — Agent memory with `ContextProvider` 5. **[05_first_workflow.py](./01-get-started/05_first_workflow.py)** — Build a workflow with executors and edges -6. **[06_host_your_agent.py](./01-get-started/06_host_your_agent.py)** — Host your agent via A2A +6. **[06_host_your_agent.py](./01-get-started/06_host_your_agent.py)** — Host your agent via Azure Functions ## Prerequisites diff --git a/python/samples/shared/sample_assets/sample_image.jpg b/python/samples/shared/sample_assets/sample_image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ea6486656fd5b603af043e29b941c99845baea7a GIT binary patch literal 182161 zcmeGF2Ut_j@&F8VVI$bqWf~w2CQs(&2ap9!fedp37!Ok?!%P4^2Is*ziA-)8|wgXu1r|!=|5FJRSmQzraJ4dymd#c!T z)T{8F^ROiv7@P_^4_}pE8cPi^0$%Wxsj6aWR`Jhc>6Yb#Cm&1yvkb8e%kVmYXI!Ok zj6F`44=j$VBla9QUn*swwAk|$aO_X`=1Q66<>YR{mSuuc+=Q<@f3Av~R4Xv6#Z8(O zn40$XhGlwScS%cfU?gKrTB;)qh=G#e8I;QUwicNlVmn z;3)tB;DVD9VK7qvKvE;+O$|(E<)Q@&C&gf-(){cns1ttn57f6Q`v*8|r4H2h;H%G= zPia}85m^=lk^e4I^kbkPMkMUwD8x)|ac;V%5NvS_VkO5Q%s~?8VkdMIVy363XP{?h zU|?ovVq{|HVq<1zo{3iSvg4oE9Vjm z`+p)}@gc-c58*Z;5EKwZ7{)ZH3InUq9*&!mz zW%X#UqEf)&*Sd6DVy!>F|AHnUxNqkk5!1V1gVC=_3Pf&bwC%-y%{%(IiTL50f%wJb zWBHe!G=H2(DX4Dgo3wKeIv#hYu%@+tO4-ofBRJ}EYEf<5z%)AqhlA2mlgmU$OM@U6 zVYB>tYES}yVGbIFL+3e3C3s}@`m>_w-uD5AV|A^$&;+F~TWCoIpc6w>HG)DUZNTW; zA_K9#t3~+NB@q8vgvAbsnTnhzJERSbf02npR`mw61%6rBCpqGE2)fs90~?C+>2)gheB4)Km}^5x zK~C!~`AXy3`{MW;-{J(y-BPbQD>$C3jmsIs?uq`G*ZHHTaTa@{eY=sN)%M|gXUh~3 z#S4NbJGeJk4riurX>Vp(IMO-f(1t6yKI|F%H6rGs>?(^?d2y@}&oc5K1Ls&&3og|~L*pDlTmQ?;w;iSgEr8`}%sW3W+72CC9TyompO z_19bs^TU;fC5Up}J9`VuGM{;*`1z;QwFPx7G``bTp2=j9ikn+4h<(HGGqT7404>zP`wY9bue#v>C%@$d{c>MJCipIsk`H{LjJ&D(mJR@oq@UI-pCmE|QloajGRTI$JN zewK*u8fCyO1djfwe%?PRlX4!rci2c}Zg*>;6IDmgF7+9ij`5TQE^Ap98GjkVOU3DY z-m=-s85(Xg_iA19_2=2%27L>ywHV^MdLW%C4P73_`+7aTYut>kG08W#vTI(r!Rxfk zfI}l6`n1lgxm{(mbshuhng;1Pb+!)@rNeD1(N4NVwA)~5?O0I9G`?tl%c-;5)4iN} zB}I=EbS5;q-lFrA$RC@x50iaGlf|udxK?2Npl zk;$fhwxqGhnIZAbHD9O7u(o4e?_TvJ!w^rrBkI!9LeL4}>^AkcS#t!Fd|gH-dB3=JrwhC5wZ_1(Jc3(q5ju{G=Q=U{Wo*{X za4=gr*y3gGBky=wuXGmQdp$lq^~Xm$Cw7mAvf4&Tbo!LmR!qkiw#AE^H)vlg`*!g{ z1JeYRTkGC4*xmOkhel>^suYz8Hr=;(H_^W`#$Qs=R8m%Pqp)Tn+Ff~A)-Z3|_cv}g z-*}}F>t{vw_@-fR+{kMVzVl$FsE>(fs=dE#Q+a{9KOc25-eYXEXD@w+6EAbSunSS} z!3V*sS3-_Bv89=_W;L!`^ITNSJO~8yumL9mMz$yR&jfn>Ch?1(MH#n=|4SJxzlA6{~}Z$ zQL8-1cpVYY$-j%%+jGE*dHBb*#JIS1sCmT>q+Dnn%d}R+F|*FEQxTzO9gW&!iw!#D zuMsl$%Y>ibDBX*Fy!oY{dSqf`{~}~b+vB5YCT{2_dd>X2^E!DsmTb>3Z~xq<2aQC2 zh|kJ;d`H;6%<9e}P)E12(sn=o7UI)tw+MNZ3_idNg{2-#I(hQKmGRf~cj2xmI)j4| zQIpq3?K=;}#2jZT(iZztQa8K1F+94X$4g)lqR+A~X4O1`+lbL zQk;fEIGsSxWUR#MdV5CL5WIW=H8EK2hrZ^@(mvjZ zX~TJR@OOsu`!+_Rc6Y{S98GzZppz^WS`RlrO%pflw~~CTDzl z#ICfDhig(u%^7-9a;YM3EYC0Os%A~CR;S=d=dAwjm%9!Wp4zlYpe*QYvkDu5r=$FA zR}Z=gwf%^z!AW_f&ZoD-*CLtjPP*HgDW`5n8w_8qWm$xhwMLWYGSkrx)v@orTX|x6 zjl^TXNa*=m#vSPMVpULpIMlxP!hv49| zXUk~UfN8|Q@rZ#3MX$rO3va|dE4^)KQRs}j$8=KrW;(BG2h(HBHM+>U`G(K?Sj*N& zzswm$vh#Vde)Jl<9Cxf z%fhs|A4%0kb=Xx9W|A>6O z2-yZ|Y7b2kb26WmgBKyJc{ur^S3)SSRZgERK)ABZF{r zSE;DKR%^po8zx1WlJ@U)$hO^CA9gH4VUMRHhTf#6@{0_)=OZ2^VV#r1*amW98oz3wH)|0 ztAZoqUe%3y!cO6@=vMO$0_8B8TBnfExtdn5ec8h34Wg@A@6uQq}N^UgHC4s z%Ae-7jjuNh-;2|R6|}Re&*M+Sa>h1~2fv=BR_>@#9qlpMIC$4k5i`rMp;-GxW0nxV zI0jRZiiyJ6dWoni!N9+ra3X5$AXw z2V}?A%)(|9eOCMn9#@tL=abGnb@kw7ssE$$^S-jsE#JF!*{?Z{y7MV`VQu1$U^9`* z-rmXDtwo>0+NUFt8Sh2JpGy=>QlEtD7W&U3ZlaJ0@?Uv_Lb+5geJmJTgz{UpijbXx z&OH-Si_j6LxH7@@=en!zrQx+k9~H#HCf%G93+flZi|Kvk)$I0?`ts6@2O6cbURurj z2wN+r*$Pz$_>gC6lH>Rq{9+zRQXO^(!c?H`$ zN4~CXT7;qtoTv@^c)zo9v3r(Yc1cRN89Cs=s76$?Hpcm8=&)eNeR@8%yjh@{d9E2> z6xvhQX3dw1?4;TC4Erja*QGWzM%D|TQ=(*<5;vdJ?N-^8IuV)iW-xQZM8a@wR$)$E z&Nb~#ohLhjP7++SjoL!0gk9vwSAv=3=f<1`2D7p{3zymzbzt!hD?5y=8qLsb<`FeB2pzeyQ)=>g+ z9<=&bIJ7h&9Jhau41s?;t|Rc}qMAb8sg*3pI3JhbDF zZ0k@-pR75@^8V7kzOkU2@<+=H){VZs(|2``j;4kkM`lrG#S34l(oJ)MV>G=^uz8Mz zD$PXAMM$^NF1@rg{@hT9rt5LMP}ByFg4l?dyhTVeqsaLvO`+|>sIJeAU-4aSZRtUI z6Q(^QdEYqrMnWDGw=0)>U+vQjV?kDU&$4>STvb`u`y0|RHa_Osdqbc zEm^>5b3<2ET`Qt~N^)K-!{xTaXDP0H)pzTAo!&%`PAa>WYD&-TLoY(FGnl@H7tUG_ z=SNXZTqTZmM`};(?w_qpwx10tI9wZ*Ti$o*{aBQ;E1yeA;>P)%8Ri%1k4GI%O-gP4 z8qj&Zynrc7h6Tr-6ds)s=9XLLGBqa^rhWIWGqz{noFs36f)7Do@4@|oFL^!aU~Ml= z^TvqFgSS4qdNj)1Yttqq-d9qJV+$Tk?ix>>d%9m$wx(!ws!!pvy^H0LoOTbh48bd+ zzhfwJ@Xm)Ch#U6lR&kIv3WI3(soXrJ()GNWNQb;h+s1gBC)jZ)w9Iulv~Urs%G~9a zJ|1j2I3sq%wKxP%z(;(?(`x(Fmb`82oV$K~vR+XzeNs|almExCG;R3|>zt-3d&%2t zyQXYswF8>o$SlPlCnw2|l=q_~Fqi^2BQklLz z^u3+cTc)6{uwz7eq5~$K;V|v(tyIvu5I=5r0~^(fb?A#4$xf?&pMU&_`5+$6H>BCw zV_H8kFBZ0tklZo*ZSYnl&KIuFB@T7)?Tx4VWQG~mR@VtQDP*3a^ z)1uQdPh}GIndj-=d~%oR>}XxM9Qi3dE2dH0apayrtAL{LOLLnxN8-Ht#t&&%dk%e=^b$#8WlkvkYXPkmT|)XkD?42P)<_2~V!g~ZTj!7WaMkAn1rJDPUW zY!pJYG9Zf!MyKC+1&3)}@A+Q1<@9TUPf1J5rOPLDrTt_|!^`+14QDGxM<*5`li=^B zlQ$WX&q-&uKAgYRlvdFcW9|RRtJCX>$(I+t*nrX2aKU_xZCTJFWL6oPq|a4(r@!FX z^`tLgbQpJdz&G_16h3A>ELixt^tueoMiGf08ERvD3Yl&e&horQVV(EgDf69LgbvMm z?I;UsOxRG~y3&DzQyR92xnchd#kTOu_u2`IP-c_;?X2TZ)1|v+Hy(TSb-3%* zcXWo=_Gy)m>X+wwJU)44cY>bR<;6%=E3%7FdYhh27P>KHH(&QAdv=a{5o(pW6;^8C z7+ze&ytnp^ow{_^D`&JraMx&7-b762tgj{L*w5rmyog6%-zwa3L?OfLw9|K_m$pt! z@X$!!JdJeTJns?hdpm5Mthse;Ge33LqWD}dvEZSO0-3n$!3C*yv-9H8f79o{uMb`S3#wvFOQ%OMUP5S8XNL4FkUYbCKsJ)1nP`-s|s0o( z6IGff96LMdx(G$)(8;`0))wKwGJEmXqKgPG=LG9()Pu(&5-L9_ys19PnNyMj|x^hwYR0; z@GyVwuG{{>tZC2TT4d3cxSS$HMdG#K0ld}VHHldlx7f$I*`FpI^nJTn7omQylD8?| zcAbZ=v?{6Hl+JmX)pn3S965$`YP(i(q2uwVrdj=c4>nzMoya{kwr9)72WR<4J36(m zc2_POwtBwrkf8ZcR{2zS{qqcThodX3)j@f^0LvkD$DC}RMJOa>!O9b7R;n9QLm7k@pPzjh8Hp-3+QGMdSsVz=C#y8#_ocq+$hUIl@QwxJr+4%_u+Et z_F%_T#%@2q(mhq_n>MT6WAHL1D_v65()T8=(mUJxY*6t##pduPpPtWe)8FQVqMp>w z37Q9))rQI-i>w?_Xlc-w zX!GONgVi#*k4qB-tmoTDQbs+}Jbt)lEJCL(v)NQzjz=^ld8K3b%+9Ry%DAx$-iD~d z`p#~>CUOVs^772RF_nuJ1E&>e;^I_uX6Z2PvowFcX zbbPY943DljQm&oiTBdpW#M383lck;4^y3u1Jt*4rYEb>iRe{0r(&rtcJ@K!sm-?Xdlre59XKvh^C@l4*t52{oW5b|(7&0LdvC6*=%*VOfR$l1Q^%_78P zUpdWHR_d;w=yt|w+AH;L71c?;hT@3e@tmD!FI;@(U6^_r;;>MIM_vp!?Fv^SxO%H^ z)GPT?{!Sp1I=;Owb9yc!Hd89~#5tJYi`i%Vx=D*IpWZZX-7oymsfV`g#OLFSP*3c9 zRAZ8z=35N=B4pK?Nf*98-SKe+W7?GLZl*6-E$vfOO=`A!H}o_gTXdJj+yLLz_e@5v zROi{Tggx+{8tg}INayLPQ~xmk^buQ2r1fA`=C+qA%7SyIQQy2Ok5~z))ob#j`X#kz zXfxeEbC%s%NO4kS9Ym_@8mM}nfTwc8j_mPgsjUgbRpLAqzew(DxE&$>U7}@rM(l=1 z%Dnc3Xs8p*V3yidmq?x{!@gPLS|haCm3+lo6d`hGcYaLMLgV?52O2lE5Er3@Z1W?= z#p{r>BEDboL6vW0^aa#;)I)0TwjHDOKJy2g0#7~1GL+!*+;%dzFdS#T+_Kx zdvlT|5KqtZZPFf}v(_xvyisrSaDVIoY}zL@&37V9`<}hF+lI&*?S(XvN3+6$*7K({ zc5B84=zH6i;xb+aiZZ- z&n45I#%uF#y*F=cDvLZ+^<;l1+b}#`lS7xNp5XKviU{h~yk|>PM~)FpM1!E1w z{l$#>kN<5#ogW?wG5(z9=7-}TUDM?m{N-u-mAQF;A}Szobt+%#|D34jhi`?p=mua> zfh1P|3@Q+-CPbP_m6k(DE2u~<&7}&VWV~hg;TFFZz!u`v@dgaU03kop#JxuV%Fh!U zzz_HNm5u<>n+N*{1^Whg@xy7L?f+}3`QgeCJ*kXx2n7{>xD&+j2TufkxEsW<>bJlT z$3k?=t_l3`jS!PgK%kJBFEI#<%I<*zy*2=nc=*Erpay){O~}C_1B5!B}>~vvNUL2 z>7}oQn9}F(EDxDhm}S-qlB|$wbSaJGk3yP#UinM5P%&~ED>)YGmt|Nz?Qg3v*IAK6a(HGq>GU}k4=K$+TLx@E|13sgp!3 zVQ$4ItYQ?%cqb69&>+Yd>7N*~%E>h{h_j%Gfxd=VA8Y{VtuO!q*@XCE zmx&pdh)Ebyim|tc5cu=|@A^{_1MoKb4mKdGW#y8T|0~9n;DP-GLyN)(+Mqms!Lg!& z7nTinf1okJ$i{TnUSD54Ng(|S`WIpbPv3x09X!rsMN@1`l{Z>J15P>&)*VH}2Lg~G z2pbUi2Xea=^snShdp&f0@xB2R%4J`Yzr%1f0kA-3zCI*fGX(njnG*vESW3fT!~;Y6 z2bOuSZy+$Jf8d$GBli5AoZO1Hk}`sT_ZB)AY{0eJ#A5ZnYZA*f5 ziz*`Pzc(5e_=Z7mi?js0r-z5TqgfheK<-gqB^ z+I~%8vUD{7lhCrlLS&LaFHK=Z8F?9bX?b~hVZh0t5rA=5v(htIW&|-!;pHL*2M5ap zE6Vr=c*x4Ds;bJ$DaZo98$cmV2=NI-?U(i;h>&G03DLt6&;d9*-2e+;0**us zTw+$~|B*n!It+^}z*dDJ*C5 z8=8W_6gcE){bP@kWj1mmC9y9Ci(jTDJMOH|k?|CSWn{vilUT)DpE>OPF_k;UIpB$QV0cbD=GjSkfSUI zZa}L{iX%WexD}NY71ULA74#L9Q~rq@J!mQdxgh-M^HKRALQT zk`YlD70FuvBTECVY1M{*F|?Itwqj=>u4d|s!MTU%0mln1vRj$FoV1*(&B{J5l5(UC zT{K7yl#+5uqa-BRGYz;D0JjD$@*Wj`-Xms70wwWpBC!8S3CR3Qlt0-`LGuP~aj5@9 zz%R69b^g~%#)55z{;XwE$}i2Cgis@GO9cEfNE`ZSk+!bUko^aGsZM0H0obp{(+{_Q zl>@ZQWscvID7eT@9M+%%#gcjevLFi7FNEHH0ayY7i`jt-Bv@brEO7fl15%Rx$@WJA z3K3)u2R)oOmb^)k76O4pF6a4s((hc3Ke-fpFEbwFn?;OyNV6_m-&l0q=pGXSgT_kTHYhPjju+Wv- zP~s>^4Sdlk{4T7IN1&&cyb7hFC=w{>tx0wWih&LE^aYP}xgft{DaZ|Qc>iWV%EG@PD$x`d~;~cdb_LH#7yq%F`h0ZABNqASsCLJ#m59 z9Vk4?heC0`ASsB+W{Sc)`GWG0h7VfutIhf!Nfc}p3ODvqM0k4xeRyjrD6eSer`jo! zDfr21(M9=@Oz5BbqD-J*GDP8gbbWmS1HkKJMG2HR3Q{r|@Y!9ZTSZV1S))MqOJk-L zaTF*@LQhnHpAYB)SH)ScVkpUod-q|{fqFPXAW8RDC?AiXmTUc|L<(j@3+v_n+gMLa zK?b;~%d8aYmm~_7Uq%6nNEM`#l#-k(QjQ#gk%zLp!cwR}#w>-30FRJgqEV6}aVe8S zFiHU<5P$(8z<@D`961Cd6aa(q2?)W6Mp+SzcSs?i2caq{Q~{JAB;}otQb-g^zk5&2XD*6 zH*2i_a&&1phsS9tDgetMjrRad7P372TP0m?g@2T)@pH%^M3Ui$3Luc1pr$Zsd`;?Y zmq&aI%cM9`A7y~T6R<0-n~Yh`nJmr<3tAEwsw@*MM=2@DJ=cmF$Pvm^1ELR_{B7|| zHY-Rd8xH8uV7;0+{^$8O%KflRhCm$Me2j*NFFYmno{yD>0n8mvCmR>v?@;ZKNCm6M=4GZysLw-7=r*` z?|*AQWM!z~ek*{1lDwimD8Igvk{qc%DhO4io*Wnws^}>q6y(4V?;m{27O;fJ{H7lS zQdtq9j6ftR1 z<>*SkK(atJ@YeUx6y7hr?Ds9K^%rZJ{?}bX|JW(5mXln$e^f>T)G!bih{ygnb^Bk} z9)Yb*|AR796pvI|G--L2ZvbU^S3BP2J{$DQKi78sF^5$SvQ~Z(Ny>w;G+3a_V+YFP zx96=zvFz!$Ojb-nYgt48D*4yNQU8jHaueDAo917o{$WYizam{-Y4xuN{=E3=H|nM6 zs?Nw-{za)IEt8iU{PMD0HP+P!`R7LU>ng85=DjM4oHs>QOVt0(Pc`Iqd87?>S9_UX zey~|h{^j?CRjeBM>z@4P1RpVfLN9eWKU0i#b-~x9JNWp~B3K#j5Hi-+Marupk z;M4z)bia$xA%ZWg0K&@8jaq+NVn`|KclzJuk-iMTk0SN~;1eg{mqzxpIKR^TE`jv5 zzx2}(<+nU#87L`!=QhHkFnF*uX{BMU?5X_UEY_sVb5-u|8S3H!L$v|}pJe&V>*(s&e zU$}o44WwbfPrsDiC`E;r5`X7e{zP9{o=Tb5a?U7;P0 z($+dcN~G`0KYT?{bX@;3&;MRk#(sflB{2P>>l=Vw)fSisL6Zkdgg}feB?Vb+y_$%Ty>snLSUuod4 zh}W!ZOb^VnF{)%|by4KY7R~q;$;x+49Q`cW<;ID|+tZPkOf2D!HB3`quHFf=!2L6h8 z&AQgq^;a7BE8;clT2t3wY2dGj*R1RRJ?i?ouN&AQMN>RjTw80gm)QXH^yFw%Kbilmjz06q#}d~l#23I7?uoO``U zFr0Ko6=wh_Ab@#Du*VWiI`@xr?-IP71Y^8?z@cVvQknfQ-WU@61i&YPh@`{LsCEJP zIO!ll05=0z6i@U9XIxQ{&e-Gh#-hLhY}BMvutWo~Xiosk0hl?!#!45!8sH=`W)BK@ zF9jS3P8|cZkgl&^2zjBJ5NVMs*e*jw$Os#Z#|8#UTY$~>PyrYrU0-iMuyh)Nmg-F6 zg4X|Ig(w-l6#Sow6mEA5L&kV->@+a;m=TDsPZE(&XIM0yk+)v!zcnB&w z20=U>KXIZrA&BiL1Qj+=)Q6j7FBDt*;jm~Kl0sLne@n1R`L6+r{A5V^t*)*W(px&V zO^9UGXmAW0IF*w?+Nw)P`gbG#uNf(dMNtkZupb53ln88JBV-4x46HQ+kJ|?<#U!ol z1SgYHt^uZy@L#e~NFaerb`1cI?>b>mUWT`rLLg*nw>) zA;?~EV9D+Q4ARNrF9AFTMBxM+`6xh0*UCl+O$-QHBB3Vzp#i60vO%2CMracx0&NB7 zJ1RiRkUF#-(ua0}Ga79mM`$;M1{+&?gA*Ntp##t%=s0v5ItN{X5}+GUGL!~oLAg*7 z^Z2HCpnPyLyCfrf>KpGJ~KjmDV9fyRR-nC3W59L*h?0-9=?7MebqDOy@u zZd!3#Wm+RzN7{X~VYFvxuhC}HKBRq3`+;_nj*f04og|$mojDzfE|BgxT|8X|T_s&3 zT_4?ddM0{7dIfp|dMA1x`or{b^!Mm1=o{$==;s*LF>GN_V=!mHFoZC~FeEdSGBhys zG0ZWtGm0~6GTJciV?4xog)xhS6Ak!75`%F)n-ZRZGuVCdIXXRj(VKrvOutu^bvKF#7 zvW~Gaux(+}VcX3X$`;3#$JW3$vW{V$*gD;HsC5zR64#ZiYg;$T&cQCvZqDw_eun)X z`!n`W>*>~ut=C_VS$}kW()!2iKX6cSh;Zm~pgE3kBy&`A^l{R1igOxs?&CbonaNqt z`I&1ymlBs9*M6=o`Qald4~uLQ3pZ!qt5-fG?vK2AP$J~ZDczFfWzemK7b zzZHKNe-i&o{z-vN0)_&90`UTm1x7Y;Z`!_T-=>S3DmD!YatLY(;sh@WRtgRYaS7=N z;f1aUJrVl6nSZmCGuHRP}EfPfM~jC z#}C*3IxMg<99F{4P z8Iu*2b&@?VTPwRDhmga|CChcnub1B`e^|a${;Pt70$L$ap;?hd(Lga$u~_kolB5z= z>AF%of&*cSh(c5$=8?+C0Av<&P+3%YkMdRJHWf}43zah}PgQADcc@0HK2V)eL#hR; z<*JRVORIaS-&6mjA+CYdNY?nEDXfXoyrub0OGs;v)=jN<+MBge+PAfPw~K7YY`?R8 zKxdoIKAjAmu^nh6uwj z!-qyRMkYq*joOR_jM2tv#-Dd8?+oAh#Dv+z+9biGcbCMjfL*1gaMPWp7fid%wwU>t z6`4ck#^x8yyDh{m{4L5XX)P@*6D|9#5kK+v%d2s=ROyN%W;=hS8><wSNDEK>!L5ChcVkR7cqlaO>8W7z+KZl z*8P)*rpE=3K~HVZIL{HBE-nH0WuMW$8~dic%)L^*7V-A@Y;St+J>Dfg>wUa@p7?I^ z-S7L_PtxzW-#dRb|4aU#155($5MTsXLUAA`7)-q+ZY3Tgz7Nt0N(}lQY!{rjpMAg2 z{+A&VAtyryLJdMw!l=X0VO0k-aW*4$m>wu zVY$PxhbNBMA1OU5aP;8O5629Tr5|TK9&o%ZNb3W%<&ugE*8_OOW8vEgb>4n0JLKjb6oVet6 z=~OhOmx-6(UDOs&&xDr59f{dj`LCY5I(-dut?~Ny>sdDhZk)a` zdlPrF{g&aa!rNlE%I3~KeIL3{yg!t)C#N;nB)2M0B`+&~Oa9dYj)GH#u)>hS z&qba^?}{CZUzhAGsVY@3Ehv*IOMS5UL1H;qc}xXk#nFnz%FxQmhkg%79(g?aP~}$D z`Pkuc^ApP__0=ZTPiqWn9@pyBRzB5y`rw(`vy$g3&x>9lUlhJXyez0g)D_et>kAu{ z8;W14zAAmK@w%c>yYW$zUQP~WiM@Z8ApQTEZRV_V1a$G4Ba{Os_#?@Pd!#jj_+ZTyxxp)^r7X)*bB%6n>V z`qcN0-|x<-%+$`>&koE5|DgMEWq#Xy>4M2Z*P=H#b-EL5Lrnz-FIG4exTwK@8hRQU zYHAurIyzcUf`dWMlvD3re!QW89gdsRPjEWt$_yiIJV;UMTd;r(dkOjnl9^VQG z3f6&R!>Op?)KsKWgVb=44pFnytluoJL(5@_q7(M#R5*107QM)hoJU+%uRn__?hQE1 zz{tIUXCvmT?uIP~S~w~5KA>F+bMqv4eWSsi~=`=}7g0!GlQ^ zXQ!swEKj>$$C3`^&mpXEh@NxD`CB=U7(^7UK6C92c+JQys`P%#7gE*8HT%yhcKE-l z*=ogp)~f?zrUJ+7vQx1`+R!|;x7k}yWsjf`W{HFzi8Q7~s1;G;IeW3uAg#V*2tV;g zEN1`U?xdJ)kLzCg4t5O2Ffz>YeiDh7>7|BD{pC80na)m@qty-1Mid;qU1V_H{b7cF#sWV79kpczJ99=yw~MN}owq}KxA&*d?0;14 zaLo9V#7?C>SMKsRH=2~4%H};#F@f}wKYN}^w%j^a^TDb75}yM8Q@ESL$13nPnN^c_ z{R^Y3(@vZ+R%>w-O-r$2L+o_#zf0{gpp>RuXVaI+wV~s1Qx0wRRYf~H{8>r5(Rj^$ zFE=E$o@VW@j5qENYvC$kR(mCuZ0VnTXQse5xgiv7V)oD>7T+wkH?#GYqK)a+I(5dr zlgYz2Z+BUl%9h)OJnBu*%k2uFsy5xc$;|L=Msx9_Ve9ald#@ey=c@mbP|jbTF8Q)+ zCf)K`<8k_-{P_BZcM2{QewDn|*h$Za$Psu*e9CpS2&osGczsuIJa^DQiet@qiv0c4 z+fQ>BU3a6+Vmk4$Uagh7X&{yMns=abfM=gTonvyR<2U1HX}i5!cJ%7h(eKI(BlgX| zWpOu+6n%_RZQvXptUx}yRu-DBXC#x!0KAZ3R;Jd^+XPpbSIg+Js4&Cq* zy;7UnG~1TZz092L}h($vbEQZ%iQ85!j`lXvUlvlNz1w6zayOzu06flFBnXogF)V zYfiH{=TLNp0h?vR&Us&%npf32_xNO+f21lsvahP!nOAn+jq{%8?Q+7S;`a%mv%Gs! zX~YhP97-!YRjBW9$-*EpVoLQcvXSF;0Ykk;V`3-!u4 zm}M+WnDVjFMz1Z7(A-Tz>w^P<tP-b&@YuS9mEt@xNFE1LX-u+hP4QyJ;b zoDMbBj2q6<5VbgSi_Xp|vhw9*$2_sIdJt3cxT!DS-2AhPv_G2jY*|3kk=C-X{ahA% z2o7q()m)`gd-gDX+I$Y3c>s$e80URTOP5?<3u)>z;gt!EccM+gC7u>Flw^4&wr75f z*=eZnRMdCrE+!^;U*aL1CIt2A{Fdn$Z`*78nnZ6Yg_SjX@$fU|51W9GT>nA)0K6#D zz|7yNuux2nRnpC*QCH%lT{>>xJ(Ucs*6~wfuKCzk{1KO=tFxb~+r?ZwcTT=qU#!MX zHKXKI&dVOdzCL{qo|w?C@}nD-*=r;v-W|GX_bfh7q1W^QYem7Ozz+$pE2_>)t{*aE zmBe74D+ds(v8J2T?X8FzWhd3z^mbvMrd&X{CM(5qB%Jx;l*D(U&bB+xs;F)k_F*Qw zf{=(3K6NyWOVWD1P=05cU1O_P6m_yuPn;`o!2CbM0o7iTbynua=_A5Xv@akV-;9fBXJ4yiv9)H=9mUsJiMld+@3uj9kmlWX5*eACDfa8w&zsb^ZV77g;$zK9VC2sH>e60$i_fVk-Y&Z5A7&)BYg8C=8O5C{qV~=R zOG938;xKu{buwIbx0jx-P;tWD7lfj6eS&{mLVZGSp;2A#BGfT6Y=IJtp@Mzxc1$7;)siX6usBZA9SvFZ1ti5G2O z1a+T!a#q*LE+DkIoi)+)avzg3`+ZyZeW`#9`ax&Q16^$z(U$%Wqqp^+_Qq-SUac!k z&eIAbJi8~8&4&rhEIjW(uobuHmdG+O%I11BZsGFmQF3D6M}J$fE4c~D#s*)_jK%VJ zec$q4pEWBuFItVMBUW!jy2R(QDUEV}8S#zN8-NZ=T)DhoqxJaZT*=@dm$(m~GdDd+ zn4K!vly|GEqiUG?YnsdX^9eIly>ugAOOYpu_Y4+@Pkb65?34d`)xMcsr{9&CS%zS_ z|MBAwPOrzMpZVq19u!e`KGfcJ>rjn36=J0OLg-b8m(m&-X)EP$w)z_*t;LDYZBSy0 z2Nl#XHps#gjoU7ar5_v~z3w&0paJtub1YA}9;2CFWuv0&fR{ z#h)I%k;lH}k34^xeB@Fs z`g?Fzcm^i=)Tp}uv+RrmH1EQ^o%Lf|;1ue2VDU_l(ax9bqAl?mnGu ztJ+XwqEaOPk*Y3e>V{l4%XdO=YcuzjbNGR)Uv{_L={hMwZC{1nre=-1;T(1Mp1zWA z=ew=xlH$6KId!#8S4?+1cEksdyWhIdH*2l&-l9(24+rD_8gwsK+37&^^@;Wm3P`BV zQ3Q1;iwBZ)s;TYgx0!W4rvaPHI$LXQRO1+O_u`1nUSkiuNVK1Pwetl&KMB>3Uu)?& zt2%G1{20FRvN^>+&V3-p=`O~CjbPPPr~u0->1XtJ65jFU&;Zw=pr9N?y1!UohWax$ zM8>X!Q%w49H>Ep_ zxy(~exHAc_=d=a3#Y*{&d=s!xq;aj?D1LLcr7JGW%E8~FXSZ&uZiPFBnqMyZ2G7Qi zTg3|PUd`H{I&k1i^X!JpcbpK7d0G8oUIiyu4OOZ6Cuhw0BV5`uY~=D?jRR$_iCgX5 z`i+H9>l8ILeM&xLs#4=P=CCR|ru`7z({{;op~R1&%}#C6sb8w!UQ6k^Ju~%13Kw+q z*u~Kq+oAv?@zm@A7i5}JX;DY&`MQF>#tffPKA9lYh7)9;417}Kxk z77|jS{??>A7CvX+Z(VCy(a0A^n*ydqN%B9;wIiOiynpW){}*2 z{p3_9wH`Rk1?4P4IxVM)^PMtw>MkKfz;QNKP4VD%h*xdU`Hml>Qstm1P7`i}fK~UaI zPLsDJo7rr7x6s#!`tvTSGVjoS^^%jHQdG*WBXeRk9o;3(anVTipsxm@hpe`wD`67e zsTpC;Whh>0HZBGezelp`$JB9~mnq7zyr&CY~qk;R3WDj32h@OfGMHP#b z%UE7x>3&|f!D+x(%dTOk^#L2=z1(iv`cPW~BnSGy^8*0{+XoMEpNqsY>=E7(_)xp* ztC4cm=#E0ky>;KJ-D{$UL#WYonK&o23Wu3=hG`|#qZ$d7fz8MHh8>;yFRS$x^SoYTXQkqjkbv;rLkU!$uyvZ-lOl5_OcebX)SaE-HO@s(my>B0vM>8lnhaE7hwpCvL zSitMZ7vvFanv1J04lZ|vMoRm{raL)59KSfdDezP};d8oSn}mq&+Y9CoN){o#SUSvk zXXXsWV)t?U>MHLlAqm7#3&~SC2HJYy_q~0jw#wg0C z(lbFraGwKlTLHC@ST*`F>tq9Bo0hvbpS~l1YPBzO_r4B|BD?cjC62~XUE)!lZL99+ zyWH-hQIVAq5Xn2gsVL7bzx_i%(Oa%}ix9UKRr}Be>$eVf4?8AGy}cTged}4C3hLva zZTrl~fK;3U|HQQ6(*Rl3Vb{pVoxTd+&30TmW^#4GW)a$O0NJ9kMST3jw|9~^kn#@~ zAq_kC^NNG%B2^{^gU1CV$5K^#x4um*Z)o`R`Z~wD@_cXfgJxOx(&Kh>+qWu&ew*M* zOzo@_e%N^>qt^53M1~RZL`Pd6jTqe+u`YNwS5v3OgQm~Cj_ovzJw(kfyG@Pe=+$rN z8{Doobr_4_@|+&O%Q9TJF2H!)&2aFhF5{s=m#C;8TBYIt4**v{sK1mTE+kROuCCuJ z7!~=>I}!DYoX6hbX!{s)noBpmPLozNmtI?7nw~qam#uk@CvD9$H@z{Ws-I{cwCLN+ zJXBaNPQNv1>9FpT)YVAgV7bO?M^8enyR}Ox8}=~>RC?Bro+DnhksZ+Ey-c!i9Gd55 zsG4ca+*3TltBmtcRobTlt4vuMkQMG2ob!swnodnzx|FJuSu;(7YgmxSTuuU39rWFK zs~1;Yd8})zjOUu#D9y6H)SI(eb9bvUNxMANSsUf`u85ejoWiU5C5>BU+-P91!tyqD zs4Q?ZT3*IhAxmzRI@;#2{u_9Zc*{q+y%MauFRmZ}0h9|N3a13~j8|8wxAtzIEP+eQ zv6)qHSYUnZ5;6xFsEtJ^^R0uIp^>OuTIlvOUSCfgt*k0p<&}b{A5&f-@h{^gviO5e z(=1vmizx1G<+w4ac?d5X0fT@zCp@3<^Zx)Ae0aU_H->J!J*Zemb#}K`4D+&q<;adk z&8M*pG3Yw`aM}t>W#WqsW@#k4jV@HRys?8Eg$B}{ImSmkj91HI^Jh7GDq1e5qely; zG>@--EqKz~z`h54L8Mrwx{R&5mcte*P86^40iSL<*TcH(I-ieYXv~sGt+}_#$0_q? zab`S_2>gDv=wBQ@DqCyb8qu^HZF2JX+oCN#%*DLI<2fL4@{A61)7QOu8KyDfCl?2?+lRyQLtWU6+MblrSUk&c%74}t1eFKo zZZrHRwP|XW*7G!R$ul#qTuhQ}8&r}NxHvrHr#bIRx_!Q-bl3MW?TMgpCr9~q>?0T_ zxg4D5j+I)+L$}l+w6(Slt#KnWnQ+_Vz&IrI$;YoauMY8)Z?UYq1@+V#t*dD;qDa3a ztm*@YUUH+S%yZKn{c8uyvi|^}1i$y`tF~P}&d%o6)#6+4v*sV){JwLLa*7lUr)W5A zo`bz&e{EQQ(7R{(xA<2*IYr-={{Yv_NnHIwR$1ZN)r}5?c^0fEf>a#m%;>||`&QCI z(Y6S|tB|Jcqz;C^VN-F~@p*5#+vv9Kb#E7#%FIRyUs}+T!|eGO&!MV*9h3KHF^=`W zbfQRe(ANx}r%GBJmZfR)791MMlPTAwbeEC!2C%Lph4bIb6qP+E~WhQAx-<4d3O|_Vlik>+) za5~hKYAx5jJl$>>&1Y?8=8ep?79Go`TsK;Rd9o_2PH|Oax-?iWU0aQ<)|N|v)sZF9 z^`@=&y*mo!tI0UcbDD!^HK%cLxlc8Xc_!@Eks+AZeNAUcCfd7n-D@i5OlG!dt`DtMh`FowgHKD0_pEihJr$-5 zibY(z=hAt`t0QjfQ^y`?7_7vKuOphcsy5V9MmFYvAc?(d!Y~G?d4*b~S$*n+Yuib; zrE6JEe85S?bJo`bfNNG6g5w98$&{JZTc0s-PX?*YCy_B3szbF(R_|EtFb0$DSnpL~T-9l*R_nN|hP=;uXuR9rorQB&TaS9BJpFm9l3$Nn zr#0qt%~C?TCApY3Yc@$Xnrzou?^NDwn%*eNOx3i!+`&y;^T5qu!#39juKD#=1kFz~ za`mK^-|dlkLxRT{tlzYi?^diXkJ=NzG}ZS5=WmW41Mv=z;|Oo;EXjrxS)-aR4s-W^ z59?j`!M$U|`lLQiv25_Hc_7OPQ}`bB?l&5H>QStd1a(w6EIv_>TBRN2I!(L~TuU66 z3^xUdcp2v(t$Py8ooqa4N(r^sspZ2jRIw7T8MS_$42??MKG@}vcCp++Jl36+nn`XW zhwg!#isO7^aV$5BG-GHu3R|}|)98AamkJfjZUZ28t|`H)YDn&kmorCMrNZB3Y_>}A zgHY<4DAg_679~OC5!SRc_|wVUINiystG9~s+?-`}c3CxMc*l!;4AHFR;ErTv!+DF5 z_5Ca5PaAk{KMCr=p&PBP6|je*oSvT5`U$MsrOmSfGsS%6@zdeT>Y9D6)x1r$}YmcQ;l&Qt)c^OV! zR)s}UTQkz_c6nnJV|LX!;<4elFt|gSmr%VB!X29ez5|uq`-|v4;$@Ovy(we6U8e_y zu5xWkR>ZM$?dwmsx}HP}w=Q`*MQiNcjQPzQ-C|{RR6?UXvvkFDdLH>9S96~AfoE>l zQldz?UiH@5TdW8bMg?P0q^ygL?{j9|m&pr?mNp!Yl}SET7&R)$S0tL_X6U`Yk%Ly@ zljZ=PYZdPTk&rimQxsRP_ByIin?8CYJ8&a^kVJrQbt7 z>u6J&=I>_O#<#AYf=zRJi*k6a;$T}Iy%_ieg>X(q|_V00XD#HTfF2{~e0Q!K0OJuZOkkZxLy>lg7a(ngpGo&&+{< zJdU{MjP)GWpo5yyGEH3>m6jUhekp2KI%kIU1V|ycwrgT#K4STibHi*TZ&T9%j)uF7 zmirx?*9#L|c`Lo6kM9sUIQPeH_4Bvw3Go-hek#&ov(ujJ>QJ(nq-gOIF(edqJd6?7 zj{R%T$Ks=hqT6&w_tTwlEHT;`mAlb*Ar+XYC50sI~>%cv< zJazj(Xu6(<;+;8_XTDiL(;qCVM$j@3%F244;E;NX@SPXM(OGzJQMmrk+pR2rzL0~I zRaU`K(44Tr_N)yi^)+ZDX>OvL2p4CV2hOZ9fKGb<04yJ+d{pyVijsvrExGB&gn6#| znD+X$-Sqb}M(B*q_nI8;+)m@ysXMzh0UTgO zyArEpys|07H@bu9I^wc?JvHU8i41peY_z&2DdSU;yULBgXXQTQ-i>ouhTmVB2*H@G zym2IL%8Z@PNIgbye+u>8Hz=r>-p#YE(si4^5NN6%+CZ`^giIWbs=sWdW3 z$8pH7)=}iMXF$|8%!on3uC%a{F@szktTRO-kbY5JexAl?7n~Z(YpJYR9ppo4Ju43K zF8HneL0N6wrWt_fYa9l+gmJaIk4jPQ4|5twsxK!ajPYn0BTGt7djl!L556c5Y22lsJt;sN2}q zt+XI8#bGHLq%+E6)NxIc+O+(kwxg0UwzK9g#vIWVtm)USZCxnItjmPXc&!l(#<*g& zNv;Z<=CrP2CbF)hE1K0Aus1}lQn`~kqDCUATu6SEX%besZnc+j^JcAFNmM3!)ey}4 zjx$uAOjX&Jby1o#n$XRWw6yr4k{0{XT25}$(PcjSvK%$NhTFiNjWt{ zmt(lWtqWVMuoZ(GnQT?tcV%vPq|jXH?e2SKqMGRLKQ(5>G3%Zxy!W3m=AlHpnlfHv zy+GH#^_3;wbIm|*PdwGAkmPJN_q{ORd)7N#lTur&OxOmUiMgQnp7mBs!$~!}Mmpx3 z9k%XCstrW*nZ0pM+*^vZ<%AUjkfhZnD+sJ}R^yX!sw&?$D$IW^Y9w1l-+@|ocAj0* zWOU@!Ej4`EFnF$sV;?E*2U>|cXj)e+Yg1gECE&I!ON_4xmt@9C=CkhSg@X)%RT`4m98twx>14pt0fww=h}uhStTJ(3w30({x)qQU z(2B#NwlWInFBn+V^!Hals zE139!<4C*{;hWuBW{)wc5s4cL&x3$__4F0XD=W#HF2`4KbO<7{F28w`Ow)CXU2?`d zt2yJig@8MvV89Xq#(jAe=l=j0zA0OLJn;SNMLcV(hPAmEshvns zgQ+K?6`Y-s$^O!Jw-#O>(-zpt48LrNgpM<7Al?+kDFhYHMjO<173SX@J}z3?_-|40 zbb6Xy32in_JT{(M#^OaDRBMs61O)CovPi}&%)Tsqa@Txe;36Mfjy8rhN3qyb&R|pL z9Q?!|n+@1!+PJGZZZBi9c^ov-M8fe@d5SqFaU_g(I0Na$e5Nlnl&>l}CU#SxwwCDh zy$9oq_&-|k;D*xTG>Y0dE~L1aIlDXpbMk-!gN&1d(~9;#9)8hQo*TUI?3S@Y>3wSR z$p@GoL}CDqNCb0}&!_2MB3o+aO@GQugABU~iZqYrUVE-_-}zI#Pp9f$F4Z843)wE^ zP$PN3Sj+-713VGb06z-ghG$-kT5{j;Dy0a?rjMxnCGqD?ytVNy&c51|5M1gJMlLk% z$sM@z)o@=tH$q3aHQ|2{?(_{OSh~B>bsa+P&PVds?ah$npPBdrkV!pzRX+{Jnq)5x zsclF`-{~cYK6wOhcKNahB;$^Q&{ig**7sVpl1B4E4YQD|A{X4fNdT$tI6ZkM*0^h9 zYGUIWbJp72>8DOK?<7^YzPXWJD=`e#lkLb7F7|8;kh$o6`e5|pvNYzBIPR_OgWkmq z=gsot1yhn7U=TMA#ASi#M{4ObO;XoTid(s3Qf=@M2RK33e?Nu?8LD?09R42h7OOU& zC)kpEy}|%+SZ8yE#&ga(k80*tQERCD(W|D*4fKLJU0`KpjT$qwC^^GPvyI36I#(~_ z9X4ML$#XTHrE#`N4qjqeN|Y_N6b=u}2RI`=O>}y_?yok2@1o2LZya zbIAHvnD~nNYn#au`sR6Hk~USDw+u)C1xOvbpKkqWVW~>@dlk*5vrF$kn5NwmgzE{f;+S_D1eOCy%^)e!tSqyqbQsbsda%aa!5J z14kRK31lRZxL}e-7Z~K4n#aR&t<}b*t6zPtBKy_|K~?9HojUW-SFsn#4M~#b*t_8Q zEH0$EFKA}5jC|XU=NKT29Aq5&8tyzr;MuRDn)^;`%Zr9Zf3wWXwG$Y?&ulk6_&-YG zbiWYUO@9PYSzC=F-wx3-d7n0TQU_7SfBjY1*!Y_7QNFrap}|+=!7kj!JFy=s@(ASc z4>jc1=Z#Kvp_`O;GxP}V-o@=Efnd0cB74xFp~wItmHz;Dr}Q;b`xZ~`zy11O@U1OU z;M{$k6^K@pCA4AnHd-i_0q@t+umHxX7rau~`!!V2XUg zIjtylBEK$iSjieAaU12PO~|ToY5}+=m9Gb_L02o? zbu~gxwQ6a@W~xZSv_@>HcNpp`BH=;EBDJn080N9>BplX=mSxYQ-Df$h`KHZik(*?4zAC(g8f24X)MfMDtb#!pCZ+p6+!}^A z#c08XQJMiOjJCHD9CxiKYzp-h+gLWZU~56H3h|7JhRqRG^z$c5Mbq9PmFg=--syX< zHEmU9T#gM#GbrY!Suv8Q6aAde%D}Q=^;Tnr&Q3n$lkoq-TnyE!iinK+R|H(vCf ze(v=|xu*G;sB~W<-s*#Ikq-w4I5I;Pn#qhVyVb>)P_LImE$#9XEjJoi{^K!7U0%RuRFJ#?iJ`B4!BPxx4sGJJ63fSmCb3rPPaw0^C5Cq<_8r^#abe28goS~VVAE= zn%UE5MYkIm9o(Ezc_@)iYmPE;ozc?boNRpSr|C24ek+n%O-i^nnGnOi>}pK zm`J;eU=HS@)h{iy_>8wRrMv)zX=QDy2{;)irz8sSFBbe*zt^MHv~4|=?yYr1+V)Zc zg4uTc;=|`G#~9jF@yNz%s#S#;-b5(5Jr8x)JV6hIQ%{!VGac33cDuI)5$`zMbJ%}T zT~lh7)^S4f%#f^(v8MBl%*P6&J;^;QUTR6BH}--`)>jsWMYnWf z0C@|pNf_LyHsy28N&7;4e7e%)@b;OYYb~h7cW|;?U0HBV}BW#d3C^V_t9Z5936>E%Cmm6fP#y@9e}=Mt*mTZ5xv>p5y|~5acPqPDgRZab6W{!B%dX(m$onnA6cS z=`AngZMTAaFQG@MYuco{rHvu6x`12#o-}oEvJwe$%g0;{^V1c^{Ce?rrQ*+s8pB+p z9}n2XqSiZ^6}A@nvH%=>+~v6gis61cUrX@|#|C?CJ_R;TzJ2ASyNk^lfsyHxgUIMH z#w(uHF0XY>GS^hNzMb!ws|E8g=oAH2JAowa=eI$QmEvP@(yJFr4@>@E=5@w`ic;!j zc!T>#S~@Mfx^R<4U#jo=cQM@YsYY5+jS7%=EZj9RFc4! z$l6r)_04$_m$izD(J#cSYjcKe9_vT7zST-HTty>DpJ!qOh*)GQ=aGZh06EQRcp+!| z9G4SGD@$?n#IBu)9m7o=*2OOVQCp+8`W|fJpTX>^UZY) zshf-Eb1GWT9~+|ip;RCZpx^+1`t{R3c||F87M%re?Iw{;rubo!P3GQa!I47%T$~K& zsm6FX_N`A9TrBW;@l3_!gsDg!fo!M>aC%^nfBkiv<-@N{=V~a+4Tk$kkC@0O+!t_M zpSnBX=Ze?z{@ryQil9qbq74&B6nP#18qBgn~dBSMO#ypl$DV{pg=k~?!*QCTD!#P2Pw!g+`H z)7-f@%M1cSoaB`Q*!^n9hje&~#zyqKjYvhcQg(nia@aha<0SUaHQ6}9#mk|c+0a5y^GbI|6xdnHXl zu9_h6U8dcx?|~AY7e0(Y>OBTUVEBVY)?u@sP-a_)jRLDEc4OU+2;(?ks2tZl87_3B z%Z{kvwM$s^o6A@&r&;7I(uR$8u$!hmx&!!o*Fx4hj;Cof8`XZ!c8qR(6Up7l+mghQ z)MWlV_n8&##1=A(nJ*;1FC1_H9hi_*l6gIGkA9-PA5ifAo}NX*U8wWdV|l2<&1FYW z91d5mNyc-Y{e4>16*U{~WzQ7s_B{U2!}_k3;y9zWSjC!@dF}!aD+Rjq=zDwg=Dk8s zvf224>Rl!k5(OBR;4aWIPtHAC2ab3=W~tff^6JHO_@fZqC^=)7h@mHUY-D_=@#N%Y zu{=?(THRV++xfx`Szu_@ZOLS1z~CNma(nt$1xGAI;=6xZk?!t}noG?^Y;7##lFBRN z7ndX93$$mfK#UQC*mIHXT=(`h@BI2*f8W_EZwPDpL@~n?M)KUqHix%D2Ukw#AQh%r9iU<;PF-@Z+`AU2LtvV;x{mehT)oB64V5)4 z7saE_c^zuGNx9M+pnr;)CE&+e=bKW;dShyN9cglhF{yKT*P6(?yX#O(eY-r?ea*&m z&02{qjk>t&&0}3$a(St4t}80ye!SM{8Oe;EdZjGKnkJYxdaE;Y*0e@KJefJB2`UL2 zZAIr*6LU&AD)bX^ipGvn>r=yY6}r-}(>hCgkCz6nEW6ZJ3|FHA2ChSQyS-;)TbfHD z-Njvo^JoIG*5C{r^GYv1ZaP$0cQ)jhOtnsKM?BR;y4v4*rE7(g9Mae>Q@XbKcP(W~ zrnlP;gjQY6xl_`uG9-+~=A+~uD%(fxOlWHEW#tt3pndNtrfQppmnXiw4<>ciJoTG zGwG1Kf_%j}$@HzAA6FL^qjCAGrqwifWQES$WMJ2u&2a{ge=383cweZln6+Is)XGcQ zv#o1@&46=IEQ}`kj}^yh{wjO>=R{H?Q-I`fYo_~htYO&o;)zj}H)bf*Qq>o38+bg` z80|vb88OXs{u%Mrw~9ZsTHJP+cu(V|kK$X) zNTrlXdcovHg?8>=Fskr*&H((6rEeKSN>h5hjHKH4k@Qu>=IYhZHaOz2V~*k%0H2V5 z7p-Mj_{uqFVdcrRfO1!iR?K>}z35P~u1^7T$>TISGo_u&Uvq!&2b8&w01|Mg@%`BXC|X_ABAscwu03mvA_F7clwpi z=~Z1CCQ%vX_+=%Oe7iHAayZR9UehgZC7M)`CTN5as&C4(U@^fTaDd*p&lQ&1)Z$HQ z(&A`Ut{@vcxo{nN?Bw+MTh#CerCsn{>+17c%=W1Sk(CS}R@^byJb*_%6m;V?^Vq5L z(@m`r)fAIxp3g-yS;W>B{!QD4mAuD0SwSkf+(|gYHap;Z*7~#>szCx?MfUQo70Si9 z!VXIvtAUai2d*-`t2QZaf8iv!GRO_gajF=|42C=qfcD5I2k`W-LGW&bkobWhxJzv} zNn~?#buvpULgp6X7bA8_0|0Y@gI;YnDwB*G+;&S-$#sk4aPU~^(n+XH*D8+a*lnSD zmFx!Zz3N9b)abeu#+w*Pt~~hwJ3PcH?Z>@fc(cJeo|QBgHZ~U)O>of( z*bpQELE1KwGNk2;WBaw<>9?0wdMxj#NF%z6J;h1NfMigkckyHqk~(Ddu9r`hSkcQ{ zn6vnb$zZtCtxFkg@T%Bp2y(crIg58L4}WY(mlcB1CIXyopsuU z_M3YWiRPA7wSD=uUL<~V)4vC)BxfAgFSyvrb$e!tiz!%#5#bkUC3}Dl6myK@9M)9g ztz?R6oc6cjcavT%Z*Os?+WCHTT7v>q{LA;U+%Usu9Q5P0be29Gzk=c`J86E|r(F=! zKH}}UPV9_l9Wl@0)~#CWlTS2p-X)7p@i&sP$+|EB>J)SX{EFJswT)KZdzYR+Ib%+d zGexzAa=UUk?hilAQ=H{(dKp<<;&sc2Ce)E_?DsM0Z8jDbmoZydi8CaK#!oO{@G^Q3-8*w!uCe2b%cV$-x0PzEgKzhh z#_mVcr+U|GTPx+6tssi&rZ|;Q?r7K+8%b_gC!ogzKT7A4oK>fyVsbg1BJHm{S*tdW zC;B9?uJ5!j%Lr??F`uCWy=dz~ zH5WFP@7K(TREtc^weVeyjh&_Ep9S3T0s}fTz^}U}0Oa(?B=hN1S5R*gc$rAFw~{E? zyoHuBsLVkDPB1f-WAE?MrMS2r9@p+{XVe}IK0>m_8*4T;?H~cusUH6I&uBg(X}m#w zJ=u~ASjz=me5--O?c+T`2A@YjHF!{jSq7v4+cdK2c!OIaS&= zHclN9Q0NOBr&$EBy)tw7#^KJpB)FIuQXfmk~ z`Ll=TaUh-tun*Bz{oM^AHtG4Bo20be}-cK{96{l`c#fD%FODMe+#_Uy=y2|yeUpzwC4V9^u z;4<*lor}4kw}*BrPdGJYBW1D;S>2xWni?+11F5XrqubJ~26bG6kyS1qlbR4SK6AFR z?j+j8jtyPBn4IFVuOuS2iID#Qw;21>6KV%>!K~@-J?f;FSnpbLiL+nrn~165xZ9jI zSG{w>@OL!-0JnUk1J;KS=0>#FqbHiK*Z%d5CE>~DtI2uqPn4cxeYFAWRPHbO;MPN5 zA6l;^=5x(85_yZ$T^8?Dt|S|J)n>c*s?y!n*oMb6oYWIf?@coX?@VP@@6AyJZ!YSK z%*7)wI26sMorFo}d()C*>s4@bPE(Hb#%RZI!@XX&ydxEp8P7FgTW@-c6|u0_n~w&U z?Vg6Rgp}bWPbgf}Th40CS46j3#aF8_F+J*91kIJYBfUS&5-tx~$`0L)IcAgl)ix^S z=8xK-boQlNf;l|XmACX10ac;qqLL48D@CK-gFv<{y(!#AB$0Xwhsn)r%>qXvD>AD& z!l^hcahwmpAIhuEY*mgr)uu_Fv7GT!Mx~A_SpHmswJdAU;;cmWsuQnDxVdG>JXBCc z=U@R^w)%eM=}_3r_Rj*WMhfQ@Vp}sC3<_$-e(ot;ELOTuz})iSo$;<9f0o1;?BZ!I!U zHDzzOIW>nJ>~dT^vdV^d)H}u)ZdEzKBhcXfM!Ag_;x(qL@fTL`$~vjBw1#0L9ByWI zRv-ER=NZo#?Tfck?s^n+?tGrLv1c&A8Lx-FD*o2fXulUUoi^4cw$n75($hJSg50Tv z)P}}#GE|HM>N<+j_}%+izlZ!u;QM_Z^9D6dLgpwJK5Dm^g*Owl<7w%TcjO)`j#!FX z>#<7b+4queRs*GZ#=YTJxofE-ZH7R4j-LM2!u&Y>r?kCW!uoCAxhmU4@};!b)3Trs z_GOjH1Cxd&cmub3;Xi19+O0ert4pHkQT?JjyBMZfBtjF*aK)G~InD^^KZN~jqMj-= zS2|?Q9Qq!!XC|9#r0Mb-%W-dT%C_##2bjbX04KS@uRHjW;@I_%hEZ#fUp<|ZT(zUe zZrGH{B?^xl2}% zGDi#iqSaIYFi%xw!O6$WMn3g?iQ=yrYByS3O8S8^89|aT=Lc>Z83!G4)BB>gejaO| z*m@qJsA~5&8imdD5?VoNV1=bdWCkY~;Ric0jDSut(!9zUoaH62y1ScH!>F$_+J9;P z03BT|jnLCI8$0_JybwbeEgY8aqB9~p0kxwfjD{nUI|}*t!aDu$hBZqoi+j6&?I{(A z7R<7&YBwQFkVbKmI^zJ03a_piV@Q^43%4yL+-&1d3V8;+cF*D_w=afc z(d3a~(iPfbvBsdg@CPTJiaU?H-!+A*YBE{rw^x!$<;=3}R#3TMGxLH*NC5CS&MVTy zaUEKe<3f~d^t-(#^IVnn<>mGDp@wyi;K=bLj7i??0AHIpQI1bx z#(k_wWpUx-Hl=%TWYkOjp3>eeq7)|pmLTIGZag2U_4(O3&hn`J=daB4?4$0jj=Nm( z&Z~7SOL$_IXPeLaY#@2;nCc8l<=LK^qw1EFjuca=_pYdz199Nr^tip~K|C91Le6B(;%{ z?FW#j0}Kv&{Q(ur_;^Bl+X2E#8yk5>NoUMrrDOS`&)=H~X|ZCd8wqqeCd?UlMWndsQu zLCHJ;&1Bo@aZ5GCFkDS{2n0JR;c_}EgPsT{pVqmYQDruk16R|U>3WH7Cq8tnNjVr`0_10b zkF8+uHo3YOTdF+s#On4s3^sRxqnpk$0hEw3mEfFo%XJ6Wn!lx6&wYIvyI5qkwvS|C zvANrWv@a!39OwGh--xuEOG}7E5nG}Vvb4`J3LJD*KCAeF?M=GXtt8bW(`>C;3u|%< zP3E(#l3+G6atSyXC)2h?bjqhRESGbdM@-iGRkUkqa}~LqurSFWStMpdm**Qs3uBIY z`c`F)zL_SqaVxanY*NxU*`IVEU-BM;r{>-9Ueci&kWK?awHP6va{p>7aoJZHRY-^X~{{pa#WO}_a^ZcrDnb` zh8X4hG!e=qXN^G5$_U8=IUh`M-nwl!#z`iy($2zkl?$hxaj|1U!x+!q13y!P(!8_8 z*k(r0l^o<1ogC#sBoNsAdB=Zx=yZEKTQ~y6ZzT6p$af@!ukKWUGnLLUo^#aZyDH(} z>BpPU=hdB8h%^zcO>Z1G(cIiC2R8`He|3U(0Dkc}7{*0%ddGsiM=HT+g}2U0Mn)YaZ{GeXA@F^ykWDOik<1g$%wcfjATT-3 zaLvX>bLuMAm*Ia7+FskmsahYl$XG4UoUTp=0|Ax+jz0`~)kRAUdOPc>i;KAAja%(F zW>y&5Ry=nD955r0J!-6aytca1-^`K;V|CiGppGrek&GUg!1m{geAOj)cYnA!wYk}^ zjh9XraOk#xMhBKY%mDK?sNCZNk}=o)qg+q?EUys%0Dk-Pm;7s}kHuGa7vE${d#SHv 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 UcKRcD&9zq&ZLV4!^WzTS~D|(tTOER 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 B_m}_x literal 0 HcmV?d00001 diff --git a/python/uv.lock b/python/uv.lock index f5c2efd880..9f2e97a91e 100644 --- a/python/uv.lock +++ b/python/uv.lock @@ -96,7 +96,7 @@ wheels = [ [[package]] name = "agent-framework" -version = "1.0.0rc1" +version = "1.0.0rc2" source = { virtual = "." } dependencies = [ { name = "agent-framework-core", extra = ["all"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -145,7 +145,7 @@ dev = [ [[package]] name = "agent-framework-a2a" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/a2a" } dependencies = [ { name = "a2a-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -160,7 +160,7 @@ requires-dist = [ [[package]] name = "agent-framework-ag-ui" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/ag-ui" } dependencies = [ { name = "ag-ui-protocol", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -188,7 +188,7 @@ provides-extras = ["dev"] [[package]] name = "agent-framework-anthropic" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/anthropic" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -203,12 +203,13 @@ requires-dist = [ [[package]] name = "agent-framework-azure-ai" -version = "1.0.0rc1" +version = "1.0.0rc2" source = { editable = "packages/azure-ai" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "aiohttp", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "azure-ai-agents", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-ai-inference", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] [package.metadata] @@ -216,11 +217,12 @@ requires-dist = [ { name = "agent-framework-core", editable = "packages/core" }, { name = "aiohttp" }, { name = "azure-ai-agents", specifier = "==1.2.0b5" }, + { name = "azure-ai-inference", specifier = ">=1.0.0b9" }, ] [[package]] name = "agent-framework-azure-ai-search" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/azure-ai-search" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -235,7 +237,7 @@ requires-dist = [ [[package]] name = "agent-framework-azurefunctions" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/azurefunctions" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -257,7 +259,7 @@ dev = [] [[package]] name = "agent-framework-bedrock" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/bedrock" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -274,7 +276,7 @@ requires-dist = [ [[package]] name = "agent-framework-chatkit" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/chatkit" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -289,7 +291,7 @@ requires-dist = [ [[package]] name = "agent-framework-claude" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/claude" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -304,7 +306,7 @@ requires-dist = [ [[package]] name = "agent-framework-copilotstudio" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/copilotstudio" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -319,7 +321,7 @@ requires-dist = [ [[package]] name = "agent-framework-core" -version = "1.0.0rc1" +version = "1.0.0rc2" source = { editable = "packages/core" } dependencies = [ { name = "azure-ai-projects", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -399,7 +401,7 @@ provides-extras = ["all"] [[package]] name = "agent-framework-declarative" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/declarative" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -424,7 +426,7 @@ dev = [{ name = "types-pyyaml" }] [[package]] name = "agent-framework-devui" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/devui" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -460,7 +462,7 @@ provides-extras = ["dev", "all"] [[package]] name = "agent-framework-durabletask" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/durabletask" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -487,7 +489,7 @@ dev = [{ name = "types-python-dateutil", specifier = ">=2.9.0" }] [[package]] name = "agent-framework-foundry-local" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/foundry_local" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -502,7 +504,7 @@ requires-dist = [ [[package]] name = "agent-framework-github-copilot" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/github_copilot" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -517,7 +519,7 @@ requires-dist = [ [[package]] name = "agent-framework-lab" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/lab" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -596,7 +598,7 @@ dev = [ [[package]] name = "agent-framework-mem0" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/mem0" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -611,7 +613,7 @@ requires-dist = [ [[package]] name = "agent-framework-ollama" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/ollama" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -626,7 +628,7 @@ requires-dist = [ [[package]] name = "agent-framework-orchestrations" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/orchestrations" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -637,7 +639,7 @@ requires-dist = [{ name = "agent-framework-core", editable = "packages/core" }] [[package]] name = "agent-framework-purview" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/purview" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -654,7 +656,7 @@ requires-dist = [ [[package]] name = "agent-framework-redis" -version = "1.0.0b260219" +version = "1.0.0b260225" source = { editable = "packages/redis" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -979,6 +981,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6d/6d/15070d23d7a94833a210da09d5d7ed3c24838bb84f0463895e5d159f1695/azure_ai_agents-1.2.0b5-py3-none-any.whl", hash = "sha256:257d0d24a6bf13eed4819cfa5c12fb222e5908deafb3cbfd5711d3a511cc4e88", size = 217948, upload-time = "2025-09-30T01:55:04.155Z" }, ] +[[package]] +name = "azure-ai-inference" +version = "1.0.0b9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "isodate", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4e/6a/ed85592e5c64e08c291992f58b1a94dab6869f28fb0f40fd753dced73ba6/azure_ai_inference-1.0.0b9.tar.gz", hash = "sha256:1feb496bd84b01ee2691befc04358fa25d7c344d8288e99364438859ad7cd5a4", size = 182408, upload-time = "2025-02-15T00:37:28.464Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/0f/27520da74769db6e58327d96c98e7b9a07ce686dff582c9a5ec60b03f9dd/azure_ai_inference-1.0.0b9-py3-none-any.whl", hash = "sha256:49823732e674092dad83bb8b0d1b65aa73111fab924d61349eb2a8cdc0493990", size = 124885, upload-time = "2025-02-15T00:37:29.964Z" }, +] + [[package]] name = "azure-ai-projects" version = "2.0.0b3" @@ -1362,7 +1378,7 @@ name = "clr-loader" version = "0.2.10" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "cffi", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/18/24/c12faf3f61614b3131b5c98d3bf0d376b49c7feaa73edca559aeb2aee080/clr_loader-0.2.10.tar.gz", hash = "sha256:81f114afbc5005bafc5efe5af1341d400e22137e275b042a8979f3feb9fc9446", size = 83605, upload-time = "2026-01-03T23:13:06.984Z" } wheels = [ @@ -1841,7 +1857,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, + { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -2307,6 +2323,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/38/3f/9859f655d11901e7b2996c6e3d33e0caa9a1d4572c3bc61ed0faa64b2f4c/greenlet-3.3.2-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9bc885b89709d901859cf95179ec9f6bb67a3d2bb1f0e88456461bd4b7f8fd0d", size = 277747, upload-time = "2026-02-20T20:16:21.325Z" }, { url = "https://files.pythonhosted.org/packages/fb/07/cb284a8b5c6498dbd7cba35d31380bb123d7dceaa7907f606c8ff5993cbf/greenlet-3.3.2-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b568183cf65b94919be4438dc28416b234b678c608cafac8874dfeeb2a9bbe13", size = 579202, upload-time = "2026-02-20T20:47:28.955Z" }, { url = "https://files.pythonhosted.org/packages/ed/45/67922992b3a152f726163b19f890a85129a992f39607a2a53155de3448b8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:527fec58dc9f90efd594b9b700662ed3fb2493c2122067ac9c740d98080a620e", size = 590620, upload-time = "2026-02-20T20:55:55.581Z" }, + { url = "https://files.pythonhosted.org/packages/03/5f/6e2a7d80c353587751ef3d44bb947f0565ec008a2e0927821c007e96d3a7/greenlet-3.3.2-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508c7f01f1791fbc8e011bd508f6794cb95397fdb198a46cb6635eb5b78d85a7", size = 602132, upload-time = "2026-02-20T21:02:43.261Z" }, { url = "https://files.pythonhosted.org/packages/ad/55/9f1ebb5a825215fadcc0f7d5073f6e79e3007e3282b14b22d6aba7ca6cb8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ad0c8917dd42a819fe77e6bdfcb84e3379c0de956469301d9fd36427a1ca501f", size = 591729, upload-time = "2026-02-20T20:20:58.395Z" }, { url = "https://files.pythonhosted.org/packages/24/b4/21f5455773d37f94b866eb3cf5caed88d6cea6dd2c6e1f9c34f463cba3ec/greenlet-3.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:97245cc10e5515dbc8c3104b2928f7f02b6813002770cfaffaf9a6e0fc2b94ef", size = 1551946, upload-time = "2026-02-20T20:49:31.102Z" }, { url = "https://files.pythonhosted.org/packages/00/68/91f061a926abead128fe1a87f0b453ccf07368666bd59ffa46016627a930/greenlet-3.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8c1fdd7d1b309ff0da81d60a9688a8bd044ac4e18b250320a96fc68d31c209ca", size = 1618494, upload-time = "2026-02-20T20:21:06.541Z" }, @@ -2314,6 +2331,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f3/47/16400cb42d18d7a6bb46f0626852c1718612e35dcb0dffa16bbaffdf5dd2/greenlet-3.3.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86", size = 278890, upload-time = "2026-02-20T20:19:39.263Z" }, { url = "https://files.pythonhosted.org/packages/a3/90/42762b77a5b6aa96cd8c0e80612663d39211e8ae8a6cd47c7f1249a66262/greenlet-3.3.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f", size = 581120, upload-time = "2026-02-20T20:47:30.161Z" }, { url = "https://files.pythonhosted.org/packages/bf/6f/f3d64f4fa0a9c7b5c5b3c810ff1df614540d5aa7d519261b53fba55d4df9/greenlet-3.3.2-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55", size = 594363, upload-time = "2026-02-20T20:55:56.965Z" }, + { url = "https://files.pythonhosted.org/packages/9c/8b/1430a04657735a3f23116c2e0d5eb10220928846e4537a938a41b350bed6/greenlet-3.3.2-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2", size = 605046, upload-time = "2026-02-20T21:02:45.234Z" }, { url = "https://files.pythonhosted.org/packages/72/83/3e06a52aca8128bdd4dcd67e932b809e76a96ab8c232a8b025b2850264c5/greenlet-3.3.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358", size = 594156, upload-time = "2026-02-20T20:20:59.955Z" }, { url = "https://files.pythonhosted.org/packages/70/79/0de5e62b873e08fe3cef7dbe84e5c4bc0e8ed0c7ff131bccb8405cd107c8/greenlet-3.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99", size = 1554649, upload-time = "2026-02-20T20:49:32.293Z" }, { url = "https://files.pythonhosted.org/packages/5a/00/32d30dee8389dc36d42170a9c66217757289e2afb0de59a3565260f38373/greenlet-3.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be", size = 1619472, upload-time = "2026-02-20T20:21:07.966Z" }, @@ -2322,6 +2340,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ea/ab/1608e5a7578e62113506740b88066bf09888322a311cff602105e619bd87/greenlet-3.3.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd", size = 280358, upload-time = "2026-02-20T20:17:43.971Z" }, { url = "https://files.pythonhosted.org/packages/a5/23/0eae412a4ade4e6623ff7626e38998cb9b11e9ff1ebacaa021e4e108ec15/greenlet-3.3.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd", size = 601217, upload-time = "2026-02-20T20:47:31.462Z" }, { url = "https://files.pythonhosted.org/packages/f8/16/5b1678a9c07098ecb9ab2dd159fafaf12e963293e61ee8d10ecb55273e5e/greenlet-3.3.2-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac", size = 611792, upload-time = "2026-02-20T20:55:58.423Z" }, + { url = "https://files.pythonhosted.org/packages/5c/c5/cc09412a29e43406eba18d61c70baa936e299bc27e074e2be3806ed29098/greenlet-3.3.2-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae9e21c84035c490506c17002f5c8ab25f980205c3e61ddb3a2a2a2e6c411fcb", size = 626250, upload-time = "2026-02-20T21:02:46.596Z" }, { url = "https://files.pythonhosted.org/packages/50/1f/5155f55bd71cabd03765a4aac9ac446be129895271f73872c36ebd4b04b6/greenlet-3.3.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070", size = 613875, upload-time = "2026-02-20T20:21:01.102Z" }, { url = "https://files.pythonhosted.org/packages/fc/dd/845f249c3fcd69e32df80cdab059b4be8b766ef5830a3d0aa9d6cad55beb/greenlet-3.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79", size = 1571467, upload-time = "2026-02-20T20:49:33.495Z" }, { url = "https://files.pythonhosted.org/packages/2a/50/2649fe21fcc2b56659a452868e695634722a6655ba245d9f77f5656010bf/greenlet-3.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395", size = 1640001, upload-time = "2026-02-20T20:21:09.154Z" }, @@ -2330,6 +2349,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ac/48/f8b875fa7dea7dd9b33245e37f065af59df6a25af2f9561efa8d822fde51/greenlet-3.3.2-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4", size = 279120, upload-time = "2026-02-20T20:19:01.9Z" }, { url = "https://files.pythonhosted.org/packages/49/8d/9771d03e7a8b1ee456511961e1b97a6d77ae1dea4a34a5b98eee706689d3/greenlet-3.3.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab0c7e7901a00bc0a7284907273dc165b32e0d109a6713babd04471327ff7986", size = 603238, upload-time = "2026-02-20T20:47:32.873Z" }, { url = "https://files.pythonhosted.org/packages/59/0e/4223c2bbb63cd5c97f28ffb2a8aee71bdfb30b323c35d409450f51b91e3e/greenlet-3.3.2-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d248d8c23c67d2291ffd47af766e2a3aa9fa1c6703155c099feb11f526c63a92", size = 614219, upload-time = "2026-02-20T20:55:59.817Z" }, + { url = "https://files.pythonhosted.org/packages/94/2b/4d012a69759ac9d77210b8bfb128bc621125f5b20fc398bce3940d036b1c/greenlet-3.3.2-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ccd21bb86944ca9be6d967cf7691e658e43417782bce90b5d2faeda0ff78a7dd", size = 628268, upload-time = "2026-02-20T21:02:48.024Z" }, { url = "https://files.pythonhosted.org/packages/7a/34/259b28ea7a2a0c904b11cd36c79b8cef8019b26ee5dbe24e73b469dea347/greenlet-3.3.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab", size = 616774, upload-time = "2026-02-20T20:21:02.454Z" }, { url = "https://files.pythonhosted.org/packages/0a/03/996c2d1689d486a6e199cb0f1cf9e4aa940c500e01bdf201299d7d61fa69/greenlet-3.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:64970c33a50551c7c50491671265d8954046cb6e8e2999aacdd60e439b70418a", size = 1571277, upload-time = "2026-02-20T20:49:34.795Z" }, { url = "https://files.pythonhosted.org/packages/d9/c4/2570fc07f34a39f2caf0bf9f24b0a1a0a47bc2e8e465b2c2424821389dfc/greenlet-3.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1a9172f5bf6bd88e6ba5a84e0a68afeac9dc7b6b412b245dd64f52d83c81e55b", size = 1640455, upload-time = "2026-02-20T20:21:10.261Z" }, @@ -2338,6 +2358,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3f/ae/8bffcbd373b57a5992cd077cbe8858fff39110480a9d50697091faea6f39/greenlet-3.3.2-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab", size = 279650, upload-time = "2026-02-20T20:18:00.783Z" }, { url = "https://files.pythonhosted.org/packages/d1/c0/45f93f348fa49abf32ac8439938726c480bd96b2a3c6f4d949ec0124b69f/greenlet-3.3.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082", size = 650295, upload-time = "2026-02-20T20:47:34.036Z" }, { url = "https://files.pythonhosted.org/packages/b3/de/dd7589b3f2b8372069ab3e4763ea5329940fc7ad9dcd3e272a37516d7c9b/greenlet-3.3.2-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9", size = 662163, upload-time = "2026-02-20T20:56:01.295Z" }, + { url = "https://files.pythonhosted.org/packages/cd/ac/85804f74f1ccea31ba518dcc8ee6f14c79f73fe36fa1beba38930806df09/greenlet-3.3.2-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e3cb43ce200f59483eb82949bf1835a99cf43d7571e900d7c8d5c62cdf25d2f9", size = 675371, upload-time = "2026-02-20T21:02:49.664Z" }, { url = "https://files.pythonhosted.org/packages/d2/d8/09bfa816572a4d83bccd6750df1926f79158b1c36c5f73786e26dbe4ee38/greenlet-3.3.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506", size = 664160, upload-time = "2026-02-20T20:21:04.015Z" }, { url = "https://files.pythonhosted.org/packages/48/cf/56832f0c8255d27f6c35d41b5ec91168d74ec721d85f01a12131eec6b93c/greenlet-3.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce", size = 1619181, upload-time = "2026-02-20T20:49:36.052Z" }, { url = "https://files.pythonhosted.org/packages/0a/23/b90b60a4aabb4cec0796e55f25ffbfb579a907c3898cd2905c8918acaa16/greenlet-3.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5", size = 1687713, upload-time = "2026-02-20T20:21:11.684Z" }, @@ -2346,6 +2367,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/98/6d/8f2ef704e614bcf58ed43cfb8d87afa1c285e98194ab2cfad351bf04f81e/greenlet-3.3.2-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54", size = 286617, upload-time = "2026-02-20T20:19:29.856Z" }, { url = "https://files.pythonhosted.org/packages/5e/0d/93894161d307c6ea237a43988f27eba0947b360b99ac5239ad3fe09f0b47/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4", size = 655189, upload-time = "2026-02-20T20:47:35.742Z" }, { url = "https://files.pythonhosted.org/packages/f5/2c/d2d506ebd8abcb57386ec4f7ba20f4030cbe56eae541bc6fd6ef399c0b41/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff", size = 658225, upload-time = "2026-02-20T20:56:02.527Z" }, + { url = "https://files.pythonhosted.org/packages/d1/67/8197b7e7e602150938049d8e7f30de1660cfb87e4c8ee349b42b67bdb2e1/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:59b3e2c40f6706b05a9cd299c836c6aa2378cabe25d021acd80f13abf81181cf", size = 666581, upload-time = "2026-02-20T21:02:51.526Z" }, { url = "https://files.pythonhosted.org/packages/8e/30/3a09155fbf728673a1dea713572d2d31159f824a37c22da82127056c44e4/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4", size = 657907, upload-time = "2026-02-20T20:21:05.259Z" }, { url = "https://files.pythonhosted.org/packages/f3/fd/d05a4b7acd0154ed758797f0a43b4c0962a843bedfe980115e842c5b2d08/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727", size = 1618857, upload-time = "2026-02-20T20:49:37.309Z" }, { url = "https://files.pythonhosted.org/packages/6f/e1/50ee92a5db521de8f35075b5eff060dd43d39ebd46c2181a2042f7070385/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e", size = 1680010, upload-time = "2026-02-20T20:21:13.427Z" }, @@ -4567,8 +4589,8 @@ name = "powerfx" version = "0.0.34" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "pythonnet", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "cffi", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, + { name = "pythonnet", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9f/fb/6c4bf87e0c74ca1c563921ce89ca1c5785b7576bca932f7255cdf81082a7/powerfx-0.0.34.tar.gz", hash = "sha256:956992e7afd272657ed16d80f4cad24ec95d9e4a79fb9dfa4a068a09e136af32", size = 3237555, upload-time = "2025-12-22T15:50:59.682Z" } wheels = [ @@ -5231,7 +5253,7 @@ name = "pythonnet" version = "3.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "clr-loader", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "clr-loader", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9a/d6/1afd75edd932306ae9bd2c2d961d603dc2b52fcec51b04afea464f1f6646/pythonnet-3.0.5.tar.gz", hash = "sha256:48e43ca463941b3608b32b4e236db92d8d40db4c58a75ace902985f76dac21cf", size = 239212, upload-time = "2024-12-13T08:30:44.393Z" } wheels = [ diff --git a/workflow-samples/README.md b/workflow-samples/README.md index a7bed697e5..07cbb859e2 100644 --- a/workflow-samples/README.md +++ b/workflow-samples/README.md @@ -10,8 +10,8 @@ Workflow workflow = DeclarativeWorkflowBuilder.Build("Marketing.yaml", options); ``` These example workflows may be executed by the workflow -[Samples](../dotnet/samples/GettingStarted/Workflows/Declarative) +[Samples](../dotnet/samples/03-workflows/Declarative) that are present in this repository. -> See the [README.md](../dotnet/samples/GettingStarted/Workflows/Declarative/README.md) +> See the [README.md](../dotnet/samples/03-workflows/Declarative/README.md) associated with the samples for configuration details. From 6bc5f543413ce2680480bacbc4ddcfb148fc9493 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Thu, 26 Feb 2026 11:06:28 +0000 Subject: [PATCH 17/19] Fix encoding (#4309) --- .../AnthropicChatCompletionFixture.cs | 2 +- .../AnthropicSkillsIntegrationTests.cs | 2 +- .../CopilotStudio.IntegrationTests/CopilotStudioFixture.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs index 466abf3854..af98629237 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. using System; using System.Collections.Generic; diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicSkillsIntegrationTests.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicSkillsIntegrationTests.cs index d171b23ad9..452b0c6cf2 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicSkillsIntegrationTests.cs +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicSkillsIntegrationTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. using System.Threading.Tasks; using AgentConformance.IntegrationTests.Support; diff --git a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs index 567d41a552..c8db0c77d7 100644 --- a/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs +++ b/dotnet/tests/CopilotStudio.IntegrationTests/CopilotStudioFixture.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. using System; using System.Collections.Generic; From 279386dbed311cdeb417a934145ccc707c890d81 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Thu, 26 Feb 2026 12:02:24 +0000 Subject: [PATCH 18/19] Disable Parallelization for WorkflowRunActivityStopTests (#4313) --- .../WorkflowRunActivityStopTests.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowRunActivityStopTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowRunActivityStopTests.cs index f35910f26b..239287da1a 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowRunActivityStopTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowRunActivityStopTests.cs @@ -10,6 +10,9 @@ namespace Microsoft.Agents.AI.Workflows.UnitTests; +[CollectionDefinition("ObservabilityTests", DisableParallelization = true)] +public class NoParallelizationObservabilityTests { } + ///

/// Regression test for https://github.com/microsoft/agent-framework/issues/4155 /// Verifies that the workflow_invoke Activity is properly stopped/disposed so it gets exported From 7cb4e7864a4b2b380d5c819373410ce5fab25d30 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Thu, 26 Feb 2026 14:33:25 +0000 Subject: [PATCH 19/19] Revert parallel disable (#4324) --- .../WorkflowRunActivityStopTests.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowRunActivityStopTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowRunActivityStopTests.cs index 239287da1a..f35910f26b 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowRunActivityStopTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowRunActivityStopTests.cs @@ -10,9 +10,6 @@ namespace Microsoft.Agents.AI.Workflows.UnitTests; -[CollectionDefinition("ObservabilityTests", DisableParallelization = true)] -public class NoParallelizationObservabilityTests { } - /// /// Regression test for https://github.com/microsoft/agent-framework/issues/4155 /// Verifies that the workflow_invoke Activity is properly stopped/disposed so it gets exported