From 76616d8e39c6732138412ad76311bff7156c2484 Mon Sep 17 00:00:00 2001 From: Eoin Doherty Date: Thu, 15 Jan 2026 18:16:20 -0800 Subject: [PATCH 1/2] Clean up background job dependency injection --- .../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 @@ private async Task RunJobAsync(BackgroundJobBase job) 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..d211cc463a --- /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 managed 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 @@ private static PurviewWrapper CreateWrapper(TokenCredential tokenCredential, Pur 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..666c767dfd 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 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 @@ public async Task ProcessAgentContentAsync(IEnumerable _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 PurviewWrapperTests() 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 async Task ProcessChatContentAsync_WithIgnoreExceptions_ContinuesOnPrompt 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 async Task ProcessAgentContentAsync_WithIgnoreExceptions_ContinuesOnError 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 { From f6990e6ac6e6e85fff4af57ee9c432b54048cf5a Mon Sep 17 00:00:00 2001 From: Eoin Doherty Date: Fri, 16 Jan 2026 10:50:58 -0800 Subject: [PATCH 2/2] Fix xml documentation grammar --- dotnet/src/Microsoft.Agents.AI.Purview/IBackgroundJobRunner.cs | 2 +- dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.AI.Purview/IBackgroundJobRunner.cs b/dotnet/src/Microsoft.Agents.AI.Purview/IBackgroundJobRunner.cs index d211cc463a..e9c3d0d54e 100644 --- a/dotnet/src/Microsoft.Agents.AI.Purview/IBackgroundJobRunner.cs +++ b/dotnet/src/Microsoft.Agents.AI.Purview/IBackgroundJobRunner.cs @@ -5,7 +5,7 @@ namespace Microsoft.Agents.AI.Purview; /// -/// An interface for a class that managed background jobs. +/// An interface for a class that manages background jobs. /// internal interface IBackgroundJobRunner { diff --git a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs index 666c767dfd..14cddbe5c4 100644 --- a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs +++ b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewWrapper.cs @@ -26,7 +26,7 @@ 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 runner used manage background jobs. + /// The runner used to manage background jobs. public PurviewWrapper(IScopedContentProcessor scopedProcessor, PurviewSettings purviewSettings, ILogger logger, IBackgroundJobRunner backgroundJobRunner) { this._scopedProcessor = scopedProcessor;