From a151f10cc24f65deb8c56e23da13bbd64220bb3f Mon Sep 17 00:00:00 2001 From: eoindoherty1 Date: Fri, 16 Jan 2026 11:20:35 -0800 Subject: [PATCH] .NET Purview Middleware: Improve Background Job Runner Injection (#3256) * Clean up background job dependency injection * Fix xml documentation grammar --- .../BackgroundJobRunner.cs | 10 +++++++++- .../IBackgroundJobRunner.cs | 16 ++++++++++++++++ .../PurviewExtensions.cs | 2 +- .../PurviewWrapper.cs | 10 +++++----- .../PurviewWrapperTests.cs | 10 +++++----- 5 files changed, 36 insertions(+), 12 deletions(-) create mode 100644 dotnet/src/Microsoft.Agents.AI.Purview/IBackgroundJobRunner.cs diff --git a/dotnet/src/Microsoft.Agents.AI.Purview/BackgroundJobRunner.cs b/dotnet/src/Microsoft.Agents.AI.Purview/BackgroundJobRunner.cs index efe376718b..85a4fa54c3 100644 --- a/dotnet/src/Microsoft.Agents.AI.Purview/BackgroundJobRunner.cs +++ b/dotnet/src/Microsoft.Agents.AI.Purview/BackgroundJobRunner.cs @@ -12,7 +12,7 @@ namespace Microsoft.Agents.AI.Purview; /// /// Service that runs jobs in background threads. /// -internal sealed class BackgroundJobRunner +internal sealed class BackgroundJobRunner : IBackgroundJobRunner { private readonly IChannelHandler _channelHandler; private readonly IPurviewClient _purviewClient; @@ -70,4 +70,12 @@ internal sealed class BackgroundJobRunner break; } } + + /// + /// Shutdown the job runners. + /// + public async Task ShutdownAsync() + { + await this._channelHandler.StopAndWaitForCompletionAsync().ConfigureAwait(false); + } } diff --git a/dotnet/src/Microsoft.Agents.AI.Purview/IBackgroundJobRunner.cs b/dotnet/src/Microsoft.Agents.AI.Purview/IBackgroundJobRunner.cs new file mode 100644 index 0000000000..e9c3d0d54e --- /dev/null +++ b/dotnet/src/Microsoft.Agents.AI.Purview/IBackgroundJobRunner.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Threading.Tasks; + +namespace Microsoft.Agents.AI.Purview; + +/// +/// An interface for a class that manages background jobs. +/// +internal interface IBackgroundJobRunner +{ + /// + /// Shutdown the background jobs. + /// + Task ShutdownAsync(); +} diff --git a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewExtensions.cs index 4095345d99..2458db94a3 100644 --- a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewExtensions.cs @@ -41,7 +41,7 @@ public static class PurviewExtensions services.AddSingleton(); services.AddSingleton(Channel.CreateBounded(purviewSettings.PendingBackgroundJobLimit)); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); ServiceProvider serviceProvider = services.BuildServiceProvider(); return serviceProvider.GetRequiredService(); diff --git a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs index 835b0146a8..14cddbe5c4 100644 --- a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs +++ b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs @@ -18,7 +18,7 @@ internal sealed class PurviewWrapper : IDisposable private readonly ILogger _logger; private readonly IScopedContentProcessor _scopedProcessor; private readonly PurviewSettings _purviewSettings; - private readonly IChannelHandler _channelHandler; + private readonly IBackgroundJobRunner _backgroundJobRunner; /// /// Creates a new instance. @@ -26,13 +26,13 @@ internal sealed class PurviewWrapper : IDisposable /// The scoped processor used to orchestrate the calls to Purview. /// The settings for Purview integration. /// The logger used for logging. - /// The channel handler used to queue background jobs and add job runners. - public PurviewWrapper(IScopedContentProcessor scopedProcessor, PurviewSettings purviewSettings, ILogger logger, IChannelHandler channelHandler) + /// The runner used to manage background jobs. + public PurviewWrapper(IScopedContentProcessor scopedProcessor, PurviewSettings purviewSettings, ILogger logger, IBackgroundJobRunner backgroundJobRunner) { this._scopedProcessor = scopedProcessor; this._purviewSettings = purviewSettings; this._logger = logger; - this._channelHandler = channelHandler; + this._backgroundJobRunner = backgroundJobRunner; } private static string GetThreadIdFromAgentThread(AgentThread? thread, IEnumerable messages) @@ -203,7 +203,7 @@ internal sealed class PurviewWrapper : IDisposable public void Dispose() { #pragma warning disable VSTHRD002 // Need to wait for pending jobs to complete. - this._channelHandler.StopAndWaitForCompletionAsync().GetAwaiter().GetResult(); + this._backgroundJobRunner.ShutdownAsync().GetAwaiter().GetResult(); #pragma warning restore VSTHRD002 // Need to wait for pending jobs to complete. } } diff --git a/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs b/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs index ed012669d6..316ea14057 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs @@ -18,14 +18,13 @@ namespace Microsoft.Agents.AI.Purview.UnitTests; public sealed class PurviewWrapperTests : IDisposable { private readonly Mock _mockProcessor; - private readonly IChannelHandler _channelHandler; + private readonly IBackgroundJobRunner _backgroundJobRunner; private readonly PurviewSettings _settings; private readonly PurviewWrapper _wrapper; public PurviewWrapperTests() { this._mockProcessor = new Mock(); - this._channelHandler = Mock.Of(); this._settings = new PurviewSettings("TestApp") { TenantId = "tenant-123", @@ -33,7 +32,8 @@ public sealed class PurviewWrapperTests : IDisposable BlockedPromptMessage = "Prompt blocked by policy", BlockedResponseMessage = "Response blocked by policy" }; - this._wrapper = new PurviewWrapper(this._mockProcessor.Object, this._settings, NullLogger.Instance, this._channelHandler); + this._backgroundJobRunner = Mock.Of(); + this._wrapper = new PurviewWrapper(this._mockProcessor.Object, this._settings, NullLogger.Instance, this._backgroundJobRunner); } #region ProcessChatContentAsync Tests @@ -151,7 +151,7 @@ public sealed class PurviewWrapperTests : IDisposable IgnoreExceptions = true, PurviewAppLocation = new PurviewAppLocation(PurviewLocationType.Application, "app-123") }; - var wrapper = new PurviewWrapper(this._mockProcessor.Object, settingsWithIgnore, NullLogger.Instance, this._channelHandler); + var wrapper = new PurviewWrapper(this._mockProcessor.Object, settingsWithIgnore, NullLogger.Instance, this._backgroundJobRunner); var messages = new List { @@ -371,7 +371,7 @@ public sealed class PurviewWrapperTests : IDisposable IgnoreExceptions = true, PurviewAppLocation = new PurviewAppLocation(PurviewLocationType.Application, "app-123") }; - var wrapper = new PurviewWrapper(this._mockProcessor.Object, settingsWithIgnore, NullLogger.Instance, this._channelHandler); + var wrapper = new PurviewWrapper(this._mockProcessor.Object, settingsWithIgnore, NullLogger.Instance, this._backgroundJobRunner); var messages = new List {