diff --git a/Directory.Packages.props b/Directory.Packages.props
index 412780b628..05ded62687 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -51,6 +51,9 @@
+
+
+
diff --git a/Microsoft.Testing.Platform.slnf b/Microsoft.Testing.Platform.slnf
index d29d82d5b1..8508da1581 100644
--- a/Microsoft.Testing.Platform.slnf
+++ b/Microsoft.Testing.Platform.slnf
@@ -4,6 +4,7 @@
"projects": [
"samples\\Playground\\Playground.csproj",
"src\\Platform\\Microsoft.Testing.Extensions.AzureDevOpsReport\\Microsoft.Testing.Extensions.AzureDevOpsReport.csproj",
+ "src\\Platform\\Microsoft.Testing.Extensions.AzureFoundry\\Microsoft.Testing.Extensions.AzureFoundry.csproj",
"src\\Platform\\Microsoft.Testing.Extensions.CrashDump\\Microsoft.Testing.Extensions.CrashDump.csproj",
"src\\Platform\\Microsoft.Testing.Extensions.HangDump\\Microsoft.Testing.Extensions.HangDump.csproj",
"src\\Platform\\Microsoft.Testing.Extensions.HotReload\\Microsoft.Testing.Extensions.HotReload.csproj",
@@ -13,6 +14,7 @@
"src\\Platform\\Microsoft.Testing.Extensions.TrxReport.Abstractions\\Microsoft.Testing.Extensions.TrxReport.Abstractions.csproj",
"src\\Platform\\Microsoft.Testing.Extensions.TrxReport\\Microsoft.Testing.Extensions.TrxReport.csproj",
"src\\Platform\\Microsoft.Testing.Extensions.VSTestBridge\\Microsoft.Testing.Extensions.VSTestBridge.csproj",
+ "src\\Platform\\Microsoft.Testing.Platform.AI\\Microsoft.Testing.Platform.AI.csproj",
"src\\Platform\\Microsoft.Testing.Platform.MSBuild\\Microsoft.Testing.Platform.MSBuild.csproj",
"src\\Platform\\Microsoft.Testing.Platform\\Microsoft.Testing.Platform.csproj",
"test\\IntegrationTests\\Microsoft.Testing.Platform.Acceptance.IntegrationTests\\Microsoft.Testing.Platform.Acceptance.IntegrationTests.csproj",
diff --git a/TestFx.slnx b/TestFx.slnx
index 321210072b..6b71a7412a 100644
--- a/TestFx.slnx
+++ b/TestFx.slnx
@@ -12,7 +12,7 @@
-
+
@@ -23,7 +23,7 @@
-
+
@@ -31,8 +31,9 @@
-
+
+
@@ -43,19 +44,20 @@
+
-
+
-
+
-
+
@@ -64,7 +66,7 @@
-
+
@@ -73,14 +75,14 @@
-
+
-
+
@@ -110,15 +112,15 @@
-
+
-
+
-
+
diff --git a/docs/microsoft.testing.platform/001-AI-Extensibility.md b/docs/microsoft.testing.platform/001-AI-Extensibility.md
new file mode 100644
index 0000000000..a666410881
--- /dev/null
+++ b/docs/microsoft.testing.platform/001-AI-Extensibility.md
@@ -0,0 +1,298 @@
+# RFC 001 - AI-Powered Extensibility for Testing Platform
+
+- [ ] Approved in principle
+- [ ] Under discussion
+- [ ] Implementation
+- [ ] Shipped
+
+## Summary
+
+This RFC details the Microsoft.Testing.Platform extensibility model for integrating Large Language Models (LLMs) and AI capabilities into test frameworks and extensions. The goal is to provide a standardized way for any extensibility point in the testing platform to leverage AI for intelligent testing activities such as flaky test analysis, crash dump analysis, hang analysis, test failure root cause analysis, and more.
+
+## Motivation
+
+Modern testing scenarios increasingly benefit from AI-powered analysis and automation. Various testing extensibility points could leverage LLMs to provide intelligent insights:
+
+1. **Test Frameworks** - Analyze flaky tests, suggest test improvements, generate test cases, analyze test failures for root causes
+2. **Crash Dump Extensions** - Automatically analyze crash dumps to identify root causes, stack trace analysis, memory corruption detection
+3. **Hang Analysis Extensions** - Analyze hanging processes, identify deadlocks, suggest fixes
+4. **Code Coverage Extensions** - Suggest areas needing more test coverage, identify critical untested paths
+5. **Performance Extensions** - Analyze performance regressions, suggest optimizations
+6. **Test Result Extensions** - Summarize test results, identify patterns in failures
+
+However, each extension implementing its own AI integration would lead to:
+
+- **Duplication** - Multiple extensions reimplementing the same AI client initialization logic
+- **Inconsistency** - Different configuration patterns across extensions
+- **Complexity** - Users having to configure AI settings multiple times for different extensions
+- **Limited Flexibility** - Difficulty in switching between different AI providers (Azure OpenAI, OpenAI, local models, etc.)
+
+The platform needs a unified, extensible approach to AI integration that allows extensions to access AI capabilities in a standardized way.
+
+## Detailed Design
+
+### Requirements
+
+1. Extensions should be able to access AI chat clients without implementing provider-specific logic
+2. The platform should support multiple AI providers (Azure OpenAI, OpenAI, local models, etc.)
+3. Only one AI provider should be active at a time to avoid conflicts and unnecessary resource consumption
+4. AI provider availability should be detectable at runtime (e.g., if required environment variables are missing)
+5. Extensions should be able to determine if the AI provider supports advanced features like tool calling
+6. The design should leverage existing industry-standard abstractions (Microsoft.Extensions.AI)
+7. Configuration should be simple and consistent across all extensions
+
+### Proposed Solution
+
+The solution introduces a **Chat Client Provider** abstraction that allows AI providers to be registered with the testing platform and consumed by any extension.
+
+#### Core Components
+
+##### 1. IChatClientProvider Interface
+
+This interface defines the contract for AI provider implementations:
+
+```csharp
+namespace Microsoft.Testing.Platform.AI;
+
+public interface IChatClientProvider
+{
+ ///
+ /// Gets a value indicating whether the provider is available and can be used.
+ /// A provider may be registered but not available if required configuration
+ /// (e.g., environment variables) is missing.
+ ///
+ bool IsAvailable { get; }
+
+ ///
+ /// Gets a value indicating whether the chat client has tool capability
+ /// (e.g., MCP tools or functions).
+ ///
+ bool HasToolsCapability { get; }
+
+ ///
+ /// Gets the name of the model being used by the chat client.
+ ///
+ string ModelName { get; }
+
+ ///
+ /// Creates a new instance of IChatClient.
+ ///
+ Task CreateChatClientAsync(CancellationToken cancellationToken);
+}
+```
+
+**Design Decisions:**
+
+- Uses `Microsoft.Extensions.AI.IChatClient` as the return type, leveraging the industry-standard abstraction
+- `IsAvailable` allows providers to validate configuration before being used
+- `HasToolsCapability` enables extensions to use advanced features when available
+- `ModelName` provides transparency about which AI model is being used
+
+##### 2. ChatClientManager (Internal)
+
+The platform includes an internal manager that handles provider registration:
+
+```csharp
+internal interface IChatClientManager
+{
+ void SetChatClientProviderFactory(Func chatClientProviderFactory);
+ void InstantiateChatClientProvider(ServiceProvider serviceProvider);
+}
+```
+
+**Design Decisions:**
+
+- `SetChatClientProviderFactory`: Stores the factory function that will create the provider instance
+- `InstantiateChatClientProvider`: Invokes the factory function and registers the provider instance to the service provider
+- Only one provider can be registered at a time (enforced with an exception)
+- Provider registration is deferred until the service provider is fully built
+- The manager integrates with the existing platform service provider pattern
+
+##### 3. Extension Methods for Registration
+
+Test application builders can register AI providers using extension methods:
+
+```csharp
+namespace Microsoft.Testing.Platform.AI;
+
+public static class ChatClientProviderExtensions
+{
+ ///
+ /// Adds a chat client provider to the test application builder.
+ ///
+ public static void AddChatClientProvider(
+ this ITestApplicationBuilder testApplicationBuilder,
+ Func chatClientProvider);
+
+ ///
+ /// Gets a chat client from the service provider.
+ ///
+ public static async Task GetChatClientAsync(
+ this IServiceProvider serviceProvider,
+ CancellationToken cancellationToken);
+}
+```
+
+**Design Decisions:**
+
+- `AddChatClientProvider` follows the platform's existing builder pattern
+- `GetChatClientAsync` provides a simple way for extensions to access the chat client
+- Returns `null` if no provider is registered, allowing graceful degradation
+
+##### 4. Reference Implementation - Azure OpenAI Provider
+
+The platform includes a reference implementation for Azure OpenAI:
+
+```csharp
+namespace Microsoft.Testing.Extensions.AzureFoundry;
+
+public static class TestApplicationBuilderExtensions
+{
+ public static void AddAzureOpenAIChatClientProvider(
+ this ITestApplicationBuilder testApplicationBuilder)
+ => testApplicationBuilder.AddChatClientProvider(
+ sp => new AzureOpenAIChatClientProvider(sp));
+}
+```
+
+The Azure OpenAI provider:
+
+- Reads configuration from environment variables (`AZURE_OPENAI_ENDPOINT`, `AZURE_OPENAI_DEPLOYMENT_NAME`, `AZURE_OPENAI_API_KEY`)
+- Validates availability based on configuration presence
+- Supports tool calling
+- Uses the official Azure OpenAI SDK
+
+### Usage Example
+
+#### Test Framework with Flaky Test Analysis
+
+```csharp
+public class MyTestFramework : ITestFramework
+{
+ private readonly IServiceProvider _serviceProvider;
+
+ public async Task ExecuteRequestAsync(ExecuteRequestContext context)
+ {
+ // Execute tests
+ var results = await ExecuteTestsAsync(context);
+
+ // Analyze flaky tests using AI
+ IChatClientProvider? provider = _serviceProvider
+ .GetServiceInternal();
+
+ if (provider?.IsAvailable == true)
+ {
+ IChatClient chatClient = await provider
+ .CreateChatClientAsync(context.CancellationToken);
+
+ var flakyTests = results.Where(r => r.IsFlaky);
+ if (flakyTests.Any())
+ {
+ var analysis = await AnalyzeFlakyTestsAsync(
+ chatClient,
+ flakyTests,
+ context.CancellationToken);
+
+ // Report analysis
+ await ReportFlakyAnalysisAsync(analysis);
+ }
+ }
+ }
+
+ private async Task AnalyzeFlakyTestsAsync(
+ IChatClient chatClient,
+ IEnumerable flakyTests,
+ CancellationToken cancellationToken)
+ {
+ var prompt = $"""
+ Analyze these flaky tests and suggest potential root causes:
+ {string.Join("\n", flakyTests.Select(t => t.ToString()))}
+ """;
+
+ var response = await chatClient.GetResponseAsync(
+ prompt,
+ cancellationToken: cancellationToken);
+
+ return response.Text;
+ }
+}
+```
+
+### Benefits
+
+#### For Extension Authors
+
+1. **No AI-specific infrastructure** - Extensions only need to call `GetChatClientAsync()` and use the standard `IChatClient` interface
+2. **Works with any provider** - Extension works automatically with Azure OpenAI, OpenAI, local models, or any custom provider
+3. **Graceful degradation** - Extension can detect when AI is unavailable and skip AI-powered features
+4. **Feature detection** - Extensions can check `SupportsToolCalling` and use advanced features when available
+
+#### For Test Authors
+
+1. **Single configuration point** - Configure AI provider once, all extensions use it
+2. **Easy provider switching** - Change from Azure OpenAI to OpenAI by swapping one line
+3. **Clear availability** - Know which features are available based on configuration
+4. **Consistent experience** - All AI-powered features use the same underlying provider
+
+#### For the Ecosystem
+
+1. **Reusable providers** - AI providers can be packaged and shared as NuGet packages
+2. **Standardization** - All extensions follow the same AI integration pattern
+3. **Interoperability** - Leverages `Microsoft.Extensions.AI` for broad ecosystem compatibility
+4. **Extensibility** - New providers can be added without changing existing extensions
+
+### Integration with Testing Platform
+
+The AI provider is registered during test host initialization:
+
+```csharp
+// In TestHostBuilder.BuildAsync()
+public async Task BuildAsync()
+{
+ // ... existing initialization ...
+
+ // Register AI provider if configured
+ ChatClientManager.RegisterChatClientProvider(serviceProvider);
+
+ // ... continue with normal flow ...
+}
+```
+
+This ensures:
+
+- The provider is available when any extension needs it
+- Registration happens after all services are configured
+- The service provider pattern is consistent with the rest of the platform
+
+## Architecture: Keeping the Core Platform Dependency-Free
+
+A critical design principle of this feature is maintaining the independence of the core `Microsoft.Testing.Platform` library. The platform serves as the foundational pillar for the entire ecosystem and must remain lightweight and free from external dependencies.
+
+### Separation of Concerns
+
+The AI extensibility is implemented through a separate `Microsoft.Testing.Platform.AI` library that acts as a bridge between:
+
+1. **AI Provider Implementations** - Concrete implementations like `Microsoft.Testing.Extensions.AzureFoundry` that integrate with specific AI services
+2. **Core Platform Service Provider** - The platform's service provider mechanism that makes services available to extensions
+
+This architectural separation provides several key benefits:
+
+**For the Core Platform:**
+
+- `Microsoft.Testing.Platform` remains dependency-free and does not reference AI-related packages like `Microsoft.Extensions.AI`
+- The core platform stays lean, fast to load, and suitable for all testing scenarios (including those that don't need AI)
+- No breaking changes or version conflicts from AI SDK updates
+
+**For AI Capabilities:**
+
+- The `Microsoft.Testing.Platform.AI` library defines the public contract (`IChatClientProvider`) without coupling to specific AI implementations
+- Extensions can opt-in to AI capabilities by referencing only the packages they need
+- Provider implementations (e.g., Azure OpenAI, OpenAI, local models) can evolve independently
+
+**For the Integration:**
+
+- Internal management components in the core platform (`ChatClientManager`) handle provider registration
+- The AI library bridges provider implementations to the service provider
+- Extensions receive a fully configured `IChatClient` through the standard service provider pattern
+
+This design ensures that the core testing platform remains a stable, dependency-free foundation while enabling rich AI-powered extensibility for those who need it.
diff --git a/samples/Playground/Playground.csproj b/samples/Playground/Playground.csproj
index ba8afed52d..c7d96bb63d 100644
--- a/samples/Playground/Playground.csproj
+++ b/samples/Playground/Playground.csproj
@@ -21,6 +21,8 @@
+
+
diff --git a/samples/Playground/Program.cs b/samples/Playground/Program.cs
index 8e12c8794c..fca420c9ec 100644
--- a/samples/Playground/Program.cs
+++ b/samples/Playground/Program.cs
@@ -8,8 +8,14 @@
using Microsoft.Testing.Platform.Messages;
#if NETCOREAPP
using Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100;
+
using MSTest.Acceptance.IntegrationTests.Messages.V100;
+
#endif
+using Microsoft.Extensions.AI;
+using Microsoft.Testing.Extensions.AzureFoundry;
+using Microsoft.Testing.Platform.AI;
+using Microsoft.Testing.Platform.Capabilities.TestFramework;
using Microsoft.Testing.Platform.TestHost;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -34,8 +40,11 @@ public static async Task Main(string[] args)
// Test MSTest
testApplicationBuilder.AddMSTest(() => [Assembly.GetEntryAssembly()!]);
+ // Add Chat client provider
+ // testApplicationBuilder.AddAzureOpenAIChatClientProvider();
+
// Test a custom local test framework
- // testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, _) => new DummyAdapter());
+ // testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, s) => new DummyAdapter(s));
// Custom test host controller extension
// testApplicationBuilder.TestHostControllers.AddProcessLifetimeHandler(s => new OutOfProc(s.GetMessageBus()));
@@ -69,15 +78,19 @@ public static async Task Main(string[] args)
await runRequest.WaitCompletionAsync();
await client.ExitAsync();
-
- return 0;
#endif
}
+
+ return 0;
}
}
internal sealed class DummyAdapter : ITestFramework, IDataProducer
{
+ private readonly IServiceProvider _serviceProvider;
+
+ public DummyAdapter(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider;
+
public string Uid => nameof(DummyAdapter);
public string Version => string.Empty;
@@ -96,6 +109,12 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context)
{
try
{
+ IChatClient? chatClient = await _serviceProvider.GetChatClientAsync(context.CancellationToken);
+ if (chatClient != null)
+ {
+ ChatResponse response = await chatClient.GetResponseAsync(chatMessage: "Hello, world!", cancellationToken: context.CancellationToken);
+ }
+
MyService.DoSomething();
}
catch (Exception e)
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Microsoft.Testing.Extensions.AzureFoundry.csproj b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Microsoft.Testing.Extensions.AzureFoundry.csproj
new file mode 100644
index 0000000000..e8626e6050
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Microsoft.Testing.Extensions.AzureFoundry.csproj
@@ -0,0 +1,32 @@
+
+
+
+ netstandard2.0;$(SupportedNetFrameworks)
+ false
+
+
+
+
+
+
+ true
+ buildMultiTargeting
+
+
+ buildTransitive/$(TargetFramework)
+
+
+ build/$(TargetFramework)
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/OpenAIChatClientProvider.cs b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/OpenAIChatClientProvider.cs
new file mode 100644
index 0000000000..b1984c7328
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/OpenAIChatClientProvider.cs
@@ -0,0 +1,70 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.ClientModel;
+
+using Azure.AI.OpenAI;
+
+using Microsoft.Extensions.AI;
+using Microsoft.Testing.Extensions.AzureFoundry.Resources;
+using Microsoft.Testing.Platform;
+using Microsoft.Testing.Platform.AI;
+using Microsoft.Testing.Platform.Helpers;
+
+namespace Microsoft.Testing.Extensions.AzureFoundry;
+
+///
+/// Provider for creating Azure OpenAI chat clients.
+///
+internal sealed class AzureOpenAIChatClientProvider : IChatClientProvider
+{
+ private readonly IEnvironment _environment;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The environment service.
+ internal AzureOpenAIChatClientProvider(IEnvironment environment)
+ => _environment = environment;
+
+ ///
+ public bool IsAvailable =>
+ !RoslynString.IsNullOrEmpty(_environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")) &&
+ !RoslynString.IsNullOrEmpty(_environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME")) &&
+ !RoslynString.IsNullOrEmpty(_environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY"));
+
+ ///
+ public bool HasToolsCapability => true;
+
+ ///
+ public string ModelName => _environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "unknown";
+
+ ///
+ public Task CreateChatClientAsync(CancellationToken cancellationToken)
+ {
+ string? endpoint = _environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT");
+ string? deploymentName = _environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME");
+ string? apiKey = _environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY");
+
+ if (RoslynString.IsNullOrEmpty(endpoint))
+ {
+ throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, ExtensionResources.EnvironmentVariableNotSet, "AZURE_OPENAI_ENDPOINT"));
+ }
+
+ if (RoslynString.IsNullOrEmpty(deploymentName))
+ {
+ throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, ExtensionResources.EnvironmentVariableNotSet, "AZURE_OPENAI_DEPLOYMENT_NAME"));
+ }
+
+ if (RoslynString.IsNullOrEmpty(apiKey))
+ {
+ throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, ExtensionResources.EnvironmentVariableNotSet, "AZURE_OPENAI_API_KEY"));
+ }
+
+ var client = new AzureOpenAIClient(
+ new Uri(endpoint!),
+ new ApiKeyCredential(apiKey!));
+
+ return Task.FromResult(client.GetChatClient(deploymentName).AsIChatClient());
+ }
+}
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/ExtensionResources.resx b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/ExtensionResources.resx
new file mode 100644
index 0000000000..dc0946744c
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/ExtensionResources.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Environment variable '{0}' is not set.
+
+
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.cs.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.cs.xlf
new file mode 100644
index 0000000000..011d21eeda
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.cs.xlf
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Environment variable '{0}' is not set.
+ Environment variable '{0}' is not set.
+
+
+
+
+
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.de.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.de.xlf
new file mode 100644
index 0000000000..ee6b892b33
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.de.xlf
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Environment variable '{0}' is not set.
+ Environment variable '{0}' is not set.
+
+
+
+
+
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.es.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.es.xlf
new file mode 100644
index 0000000000..9c84abc121
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.es.xlf
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Environment variable '{0}' is not set.
+ Environment variable '{0}' is not set.
+
+
+
+
+
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.fr.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.fr.xlf
new file mode 100644
index 0000000000..66eb696594
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.fr.xlf
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Environment variable '{0}' is not set.
+ Environment variable '{0}' is not set.
+
+
+
+
+
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.it.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.it.xlf
new file mode 100644
index 0000000000..9854aaa0ad
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.it.xlf
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Environment variable '{0}' is not set.
+ Environment variable '{0}' is not set.
+
+
+
+
+
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.ja.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.ja.xlf
new file mode 100644
index 0000000000..688eb21768
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.ja.xlf
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Environment variable '{0}' is not set.
+ Environment variable '{0}' is not set.
+
+
+
+
+
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.ko.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.ko.xlf
new file mode 100644
index 0000000000..cf11a6f7ac
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.ko.xlf
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Environment variable '{0}' is not set.
+ Environment variable '{0}' is not set.
+
+
+
+
+
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.pl.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.pl.xlf
new file mode 100644
index 0000000000..3134982c18
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.pl.xlf
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Environment variable '{0}' is not set.
+ Environment variable '{0}' is not set.
+
+
+
+
+
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.pt-BR.xlf
new file mode 100644
index 0000000000..cb0d7edb69
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.pt-BR.xlf
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Environment variable '{0}' is not set.
+ Environment variable '{0}' is not set.
+
+
+
+
+
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.ru.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.ru.xlf
new file mode 100644
index 0000000000..05e9d1acbb
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.ru.xlf
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Environment variable '{0}' is not set.
+ Environment variable '{0}' is not set.
+
+
+
+
+
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.tr.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.tr.xlf
new file mode 100644
index 0000000000..df3e59afe4
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.tr.xlf
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Environment variable '{0}' is not set.
+ Environment variable '{0}' is not set.
+
+
+
+
+
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.zh-Hans.xlf
new file mode 100644
index 0000000000..3249d41d04
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.zh-Hans.xlf
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Environment variable '{0}' is not set.
+ Environment variable '{0}' is not set.
+
+
+
+
+
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.zh-Hant.xlf
new file mode 100644
index 0000000000..454168beac
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/Resources/xlf/ExtensionResources.zh-Hant.xlf
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Environment variable '{0}' is not set.
+ Environment variable '{0}' is not set.
+
+
+
+
+
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/TestApplicationBuilderExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/TestApplicationBuilderExtensions.cs
new file mode 100644
index 0000000000..0e4c65e7d5
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/TestApplicationBuilderExtensions.cs
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Testing.Platform.AI;
+using Microsoft.Testing.Platform.Builder;
+using Microsoft.Testing.Platform.Helpers;
+using Microsoft.Testing.Platform.Services;
+
+namespace Microsoft.Testing.Extensions.AzureFoundry;
+
+///
+/// Extension methods for configuring Azure Foundry services.
+///
+public static class TestApplicationBuilderExtensions
+{
+ ///
+ /// Adds the Azure OpenAI chat client provider to the test application.
+ ///
+ /// The test application builder.
+ public static void AddAzureOpenAIChatClientProvider(this ITestApplicationBuilder testApplicationBuilder)
+ => testApplicationBuilder.AddChatClientProvider(sp => new AzureOpenAIChatClientProvider(sp.GetRequiredService()));
+}
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/TestingPlatformBuilderHook.cs b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/TestingPlatformBuilderHook.cs
new file mode 100644
index 0000000000..9122137a8b
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/TestingPlatformBuilderHook.cs
@@ -0,0 +1,20 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Testing.Platform.Builder;
+
+namespace Microsoft.Testing.Extensions.AzureFoundry;
+
+///
+/// This class is used by Microsoft.Testing.Platform.MSBuild to hook into the Testing Platform Builder to add Azure Foundry AI support.
+///
+public static class TestingPlatformBuilderHook
+{
+ ///
+ /// Adds Azure Foundry AI support to the Testing Platform Builder.
+ ///
+ /// The test application builder.
+ /// The command line arguments.
+ public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] _)
+ => testApplicationBuilder.AddAzureOpenAIChatClientProvider();
+}
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/build/Microsoft.Testing.Extensions.AzureFoundry.props b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/build/Microsoft.Testing.Extensions.AzureFoundry.props
new file mode 100644
index 0000000000..de499ce943
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/build/Microsoft.Testing.Extensions.AzureFoundry.props
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/buildMultiTargeting/Microsoft.Testing.Extensions.AzureFoundry.props b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/buildMultiTargeting/Microsoft.Testing.Extensions.AzureFoundry.props
new file mode 100644
index 0000000000..b3dbed5c82
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/buildMultiTargeting/Microsoft.Testing.Extensions.AzureFoundry.props
@@ -0,0 +1,9 @@
+
+
+
+
+ Microsoft.Testing.Extensions.AzureFoundry
+ Microsoft.Testing.Extensions.AzureFoundry.TestingPlatformBuilderHook
+
+
+
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/buildTransitive/Microsoft.Testing.Extensions.AzureFoundry.props b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/buildTransitive/Microsoft.Testing.Extensions.AzureFoundry.props
new file mode 100644
index 0000000000..de499ce943
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Extensions.AzureFoundry/buildTransitive/Microsoft.Testing.Extensions.AzureFoundry.props
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/Platform/Microsoft.Testing.Platform.AI/ChatClientProviderExtensions.cs b/src/Platform/Microsoft.Testing.Platform.AI/ChatClientProviderExtensions.cs
new file mode 100644
index 0000000000..32b63732d1
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Platform.AI/ChatClientProviderExtensions.cs
@@ -0,0 +1,44 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Extensions.AI;
+using Microsoft.Testing.Platform.Builder;
+using Microsoft.Testing.Platform.Resources;
+using Microsoft.Testing.Platform.Services;
+
+namespace Microsoft.Testing.Platform.AI;
+
+///
+/// Extension methods for chat client provider.
+///
+public static class ChatClientProviderExtensions
+{
+ ///
+ /// Adds a chat client provider to the test application builder.
+ ///
+ /// The test application builder.
+ /// The factory function to create chat client providers.
+ public static void AddChatClientProvider(this ITestApplicationBuilder testApplicationBuilder, Func chatClientProvider)
+ {
+ if (testApplicationBuilder is not TestApplicationBuilder builder)
+ {
+ throw new InvalidOperationException(PlatformResources.InvalidTestApplicationBuilderTypeForAI);
+ }
+
+ builder.ChatClientManager.AddChatClientProvider(chatClientProvider);
+ }
+
+ ///
+ /// Gets a chat client from the service provider.
+ ///
+ /// The service provider.
+ /// The cancellation token.
+ /// A task representing the asynchronous operation that returns an instance of .
+ public static async Task GetChatClientAsync(this IServiceProvider serviceProvider, CancellationToken cancellationToken)
+ {
+ IChatClientProvider? provider = serviceProvider.GetServiceInternal();
+ return provider is null
+ ? null
+ : await provider.CreateChatClientAsync(cancellationToken).ConfigureAwait(false);
+ }
+}
diff --git a/src/Platform/Microsoft.Testing.Platform.AI/IChatClientProvider.cs b/src/Platform/Microsoft.Testing.Platform.AI/IChatClientProvider.cs
new file mode 100644
index 0000000000..72b766f9f9
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Platform.AI/IChatClientProvider.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Extensions.AI;
+
+namespace Microsoft.Testing.Platform.AI;
+
+///
+/// Provider interface for creating and configuring chat clients.
+///
+[Experimental("TPEXP", UrlFormat = "https://aka.ms/testingplatform/experimental")]
+public interface IChatClientProvider
+{
+ ///
+ /// Gets a value indicating whether the provider is available and can be used.
+ /// A provider may be registered but not available if required configuration (e.g., environment variables) is missing.
+ ///
+ bool IsAvailable { get; }
+
+ ///
+ /// Gets a value indicating whether the chat client has tool capability (e.g., MCP tools or functions).
+ ///
+ bool HasToolsCapability { get; }
+
+ ///
+ /// Gets the name of the model being used by the chat client.
+ ///
+ string ModelName { get; }
+
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The cancellation token.
+ /// A task representing the asynchronous operation that returns an instance of .
+ Task CreateChatClientAsync(CancellationToken cancellationToken);
+}
diff --git a/src/Platform/Microsoft.Testing.Platform.AI/Microsoft.Testing.Platform.AI.csproj b/src/Platform/Microsoft.Testing.Platform.AI/Microsoft.Testing.Platform.AI.csproj
new file mode 100644
index 0000000000..fbf171f863
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Platform.AI/Microsoft.Testing.Platform.AI.csproj
@@ -0,0 +1,17 @@
+
+
+
+ $(SupportedNetFrameworks);netstandard2.0
+ false
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Platform/Microsoft.Testing.Platform/AI/ChatClientManager.cs b/src/Platform/Microsoft.Testing.Platform/AI/ChatClientManager.cs
new file mode 100644
index 0000000000..1d0810da42
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Platform/AI/ChatClientManager.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Testing.Platform.Resources;
+using Microsoft.Testing.Platform.Services;
+
+namespace Microsoft.Testing.Platform.AI;
+
+internal sealed class ChatClientManager : IChatClientManager
+{
+ private Func? _chatClientProviderFactory;
+
+ public void AddChatClientProvider(Func chatClientProviderFactory)
+ {
+ if (chatClientProviderFactory is null)
+ {
+ throw new ArgumentNullException(nameof(chatClientProviderFactory));
+ }
+
+ if (_chatClientProviderFactory is not null)
+ {
+ throw new InvalidOperationException(PlatformResources.ChatClientProviderAlreadyRegistered);
+ }
+
+ _chatClientProviderFactory = chatClientProviderFactory;
+ }
+
+ public void BuildChatClients(ServiceProvider serviceProvider)
+ {
+ if (_chatClientProviderFactory is not null)
+ {
+ serviceProvider.AddService(_chatClientProviderFactory(serviceProvider));
+ }
+ }
+}
diff --git a/src/Platform/Microsoft.Testing.Platform/AI/IChatClientManager.cs b/src/Platform/Microsoft.Testing.Platform/AI/IChatClientManager.cs
new file mode 100644
index 0000000000..5e2e09dd2c
--- /dev/null
+++ b/src/Platform/Microsoft.Testing.Platform/AI/IChatClientManager.cs
@@ -0,0 +1,13 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Testing.Platform.Services;
+
+namespace Microsoft.Testing.Platform.AI;
+
+internal interface IChatClientManager
+{
+ void AddChatClientProvider(Func chatClientProviderFactory);
+
+ void BuildChatClients(ServiceProvider serviceProvider);
+}
diff --git a/src/Platform/Microsoft.Testing.Platform/Builder/TestApplicationBuilder.cs b/src/Platform/Microsoft.Testing.Platform/Builder/TestApplicationBuilder.cs
index 77aadd626b..9701dcd041 100644
--- a/src/Platform/Microsoft.Testing.Platform/Builder/TestApplicationBuilder.cs
+++ b/src/Platform/Microsoft.Testing.Platform/Builder/TestApplicationBuilder.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.Testing.Internal.Framework;
+using Microsoft.Testing.Platform.AI;
using Microsoft.Testing.Platform.Capabilities.TestFramework;
using Microsoft.Testing.Platform.CommandLine;
using Microsoft.Testing.Platform.Configurations;
@@ -46,6 +47,8 @@ internal TestApplicationBuilder(
_unhandledExceptionsHandler = unhandledExceptionsHandler;
}
+ public IChatClientManager ChatClientManager => _testHostBuilder.ChatClientManager;
+
public ITestHostManager TestHost => _testHostBuilder.TestHost;
public ITestHostControllersManager TestHostControllers => _testHostBuilder.TestHostControllers;
diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.cs
index 76e68664cc..1add332506 100644
--- a/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.cs
+++ b/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.Testing.Internal.Framework;
+using Microsoft.Testing.Platform.AI;
using Microsoft.Testing.Platform.Builder;
using Microsoft.Testing.Platform.Capabilities.TestFramework;
using Microsoft.Testing.Platform.CommandLine;
@@ -34,6 +35,8 @@ internal sealed class TestHostBuilder(IFileSystem fileSystem, IRuntimeFeature ru
private readonly ITestApplicationModuleInfo _testApplicationModuleInfo = testApplicationModuleInfo;
private readonly PlatformOutputDeviceManager _outputDisplay = new();
+ public IChatClientManager ChatClientManager { get; } = new ChatClientManager();
+
public ITestFrameworkManager? TestFramework { get; set; }
public ITestHostManager TestHost { get; } = new TestHostManager();
@@ -310,6 +313,9 @@ public async Task BuildAsync(
policiesService);
serviceProvider.AddService(testApplicationResult);
+ // Add Chat Client if AI capabilities are enabled
+ ChatClientManager.BuildChatClients(serviceProvider);
+
// ============= SETUP COMMON SERVICE USED IN ALL MODES END ===============//
// ============= SELECT AND RUN THE ACTUAL MODE ===============//
diff --git a/src/Platform/Microsoft.Testing.Platform/Microsoft.Testing.Platform.csproj b/src/Platform/Microsoft.Testing.Platform/Microsoft.Testing.Platform.csproj
index 67deb6020c..2fde16409a 100644
--- a/src/Platform/Microsoft.Testing.Platform/Microsoft.Testing.Platform.csproj
+++ b/src/Platform/Microsoft.Testing.Platform/Microsoft.Testing.Platform.csproj
@@ -39,6 +39,7 @@ This package provides the core platform and the .NET implementation of the proto
+
@@ -52,6 +53,7 @@ This package provides the core platform and the .NET implementation of the proto
+
diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx b/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx
index 5b7a17db4f..87f9857b19 100644
--- a/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx
+++ b/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx
@@ -1,17 +1,17 @@
-
@@ -501,12 +501,12 @@ Read more about Microsoft Testing Platform telemetry: https://aka.ms/testingplat
Diagnostic file (level '{0}' with async flush): {1}
- 0 level such as verbose,
+ 0 level such as verbose,
1 path to fileDiagnostic file (level '{0}' with sync flush): {1}
- 0 level such as verbose,
+ 0 level such as verbose,
1 path to file
@@ -725,4 +725,10 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is
succeeded
+
+ A chat client provider has already been registered.
+
+
+ AI extensions only work with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder'
+
\ No newline at end of file
diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.cs.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.cs.xlf
index 70924b66dc..79c7549bdb 100644
--- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.cs.xlf
+++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.cs.xlf
@@ -205,13 +205,13 @@
Diagnostic file (level '{0}' with async flush): {1}Diagnostický soubor (úroveň {0} s asynchronním vyprázdněním): {1}
- 0 level such as verbose,
+ 0 level such as verbose,
1 path to fileDiagnostic file (level '{0}' with sync flush): {1}Diagnostický soubor (úroveň {0} se synchronním vyprázdněním): {1}
- 0 level such as verbose,
+ 0 level such as verbose,
1 path to file
@@ -992,6 +992,16 @@ Platné hodnoty jsou Normal a Detailed. Výchozí hodnota je Normal.
celkem
+
+ A chat client provider has already been registered.
+ A chat client provider has already been registered.
+
+
+
+ AI extensions only work with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder'
+ AI extensions only work with builders of type 'Microsoft.Testing.Platform.Builder.TestApplicationBuilder'
+
+