mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: Update AIContextProviders to use Microsoft.Extensions.Compliance.Redaction (#4854)
* Update providers to use Microsoft.Extensions.Compliance.Redaction * Fix formatting. * Fix readme
This commit is contained in:
@@ -148,11 +148,15 @@ public sealed class Mem0ProviderTests : IDisposable
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false, false, 4)]
|
||||
[InlineData(true, false, 4)]
|
||||
[InlineData(false, true, 2)]
|
||||
[InlineData(true, true, 2)]
|
||||
public async Task InvokingAsync_LogsUserIdBasedOnEnableSensitiveTelemetryDataAsync(bool enableSensitiveTelemetryData, bool requestThrows, int expectedLogInvocations)
|
||||
[InlineData(false, false, false, 4)]
|
||||
[InlineData(false, false, true, 4)]
|
||||
[InlineData(true, false, false, 4)]
|
||||
[InlineData(true, false, true, 4)]
|
||||
[InlineData(false, true, false, 2)]
|
||||
[InlineData(false, true, true, 2)]
|
||||
[InlineData(true, true, false, 2)]
|
||||
[InlineData(true, true, true, 2)]
|
||||
public async Task InvokingAsync_RedactsLogDataBasedOnOptionsAsync(bool enableSensitiveTelemetryData, bool requestThrows, bool useCustomRedactor, int expectedLogInvocations)
|
||||
{
|
||||
// Arrange
|
||||
if (requestThrows)
|
||||
@@ -171,7 +175,11 @@ public sealed class Mem0ProviderTests : IDisposable
|
||||
ThreadId = "session",
|
||||
UserId = "user"
|
||||
};
|
||||
var options = new Mem0ProviderOptions { EnableSensitiveTelemetryData = enableSensitiveTelemetryData };
|
||||
var options = new Mem0ProviderOptions
|
||||
{
|
||||
EnableSensitiveTelemetryData = enableSensitiveTelemetryData,
|
||||
Redactor = useCustomRedactor ? new ReplacingRedactor("***") : null
|
||||
};
|
||||
var mockSession = new TestAgentSession();
|
||||
|
||||
var sut = new Mem0Provider(this._httpClient, _ => new Mem0Provider.State(storageScope), options: options, loggerFactory: this._loggerFactoryMock.Object);
|
||||
@@ -180,7 +188,8 @@ public sealed class Mem0ProviderTests : IDisposable
|
||||
// Act
|
||||
await sut.InvokingAsync(invokingContext, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
// Assert — EnableSensitiveTelemetryData takes precedence over Redactor
|
||||
string expectedRedaction = enableSensitiveTelemetryData ? "user" : (useCustomRedactor ? "***" : "<redacted>");
|
||||
Assert.Equal(expectedLogInvocations, this._loggerMock.Invocations.Count);
|
||||
foreach (var logInvocation in this._loggerMock.Invocations)
|
||||
{
|
||||
@@ -191,18 +200,18 @@ public sealed class Mem0ProviderTests : IDisposable
|
||||
|
||||
var state = Assert.IsType<IReadOnlyList<KeyValuePair<string, object?>>>(logInvocation.Arguments[2], exactMatch: false);
|
||||
var userIdValue = state.First(kvp => kvp.Key == "UserId").Value;
|
||||
Assert.Equal(enableSensitiveTelemetryData ? "user" : "<redacted>", userIdValue);
|
||||
Assert.Equal(expectedRedaction, userIdValue);
|
||||
|
||||
var inputValue = state.FirstOrDefault(kvp => kvp.Key == "Input").Value;
|
||||
if (inputValue != null)
|
||||
{
|
||||
Assert.Equal(enableSensitiveTelemetryData ? "Who am I?" : "<redacted>", inputValue);
|
||||
Assert.Equal(enableSensitiveTelemetryData ? "Who am I?" : expectedRedaction, inputValue);
|
||||
}
|
||||
|
||||
var messageTextValue = state.FirstOrDefault(kvp => kvp.Key == "MessageText").Value;
|
||||
if (messageTextValue != null)
|
||||
{
|
||||
Assert.Equal(enableSensitiveTelemetryData ? "## Memories\nConsider the following memories when answering user questions:\nName is Caoimhe" : "<redacted>", messageTextValue);
|
||||
Assert.Equal(enableSensitiveTelemetryData ? "## Memories\nConsider the following memories when answering user questions:\nName is Caoimhe" : expectedRedaction, messageTextValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,8 @@ public sealed class TextSearchProviderTests
|
||||
{
|
||||
SearchTime = TextSearchProviderOptions.TextSearchBehavior.BeforeAIInvoke,
|
||||
ContextPrompt = overrideContextPrompt,
|
||||
CitationsPrompt = overrideCitationsPrompt
|
||||
CitationsPrompt = overrideCitationsPrompt,
|
||||
EnableSensitiveTelemetryData = true
|
||||
};
|
||||
var provider = new TextSearchProvider(SearchDelegateAsync, options, withLogging ? this._loggerFactoryMock.Object : null);
|
||||
|
||||
@@ -164,6 +165,65 @@ public sealed class TextSearchProviderTests
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false, false)]
|
||||
[InlineData(false, true)]
|
||||
[InlineData(true, false)]
|
||||
[InlineData(true, true)]
|
||||
public async Task InvokingAsync_RedactsLogDataBasedOnOptionsAsync(bool enableSensitiveTelemetryData, bool useCustomRedactor)
|
||||
{
|
||||
// Arrange
|
||||
List<TextSearchProvider.TextSearchResult> results =
|
||||
[
|
||||
new() { SourceName = "Doc1", SourceLink = "http://example.com/doc1", Text = "Content of Doc1" }
|
||||
];
|
||||
|
||||
Task<IEnumerable<TextSearchProvider.TextSearchResult>> SearchDelegateAsync(string input, CancellationToken ct)
|
||||
{
|
||||
return Task.FromResult<IEnumerable<TextSearchProvider.TextSearchResult>>(results);
|
||||
}
|
||||
|
||||
var options = new TextSearchProviderOptions
|
||||
{
|
||||
SearchTime = TextSearchProviderOptions.TextSearchBehavior.BeforeAIInvoke,
|
||||
EnableSensitiveTelemetryData = enableSensitiveTelemetryData,
|
||||
Redactor = useCustomRedactor ? new ReplacingRedactor("***") : null
|
||||
};
|
||||
var provider = new TextSearchProvider(SearchDelegateAsync, options, this._loggerFactoryMock.Object);
|
||||
|
||||
var invokingContext = new AIContextProvider.InvokingContext(
|
||||
s_mockAgent,
|
||||
new TestAgentSession(),
|
||||
new AIContext { Messages = new List<ChatMessage> { new(ChatRole.User, "Sample user question?") } });
|
||||
|
||||
// Act
|
||||
await provider.InvokingAsync(invokingContext, CancellationToken.None);
|
||||
|
||||
// Assert — EnableSensitiveTelemetryData takes precedence over Redactor
|
||||
var traceInvocation = this._loggerMock.Invocations
|
||||
.Where(i => i.Method.Name == nameof(ILogger.Log))
|
||||
.FirstOrDefault(i => (LogLevel)i.Arguments[0]! == LogLevel.Trace);
|
||||
Assert.NotNull(traceInvocation);
|
||||
|
||||
var state = Assert.IsType<IReadOnlyList<KeyValuePair<string, object?>>>(traceInvocation.Arguments[2], exactMatch: false);
|
||||
var inputValue = state.First(kvp => kvp.Key == "Input").Value;
|
||||
var messageTextValue = state.First(kvp => kvp.Key == "MessageText").Value;
|
||||
|
||||
if (enableSensitiveTelemetryData)
|
||||
{
|
||||
// EnableSensitiveTelemetryData=true: raw data passes through regardless of Redactor
|
||||
Assert.Equal("Sample user question?", inputValue);
|
||||
Assert.Contains("Content of Doc1", messageTextValue?.ToString()!);
|
||||
}
|
||||
else
|
||||
{
|
||||
// EnableSensitiveTelemetryData=false: custom redactor or default placeholder
|
||||
string expectedRedaction = useCustomRedactor ? "***" : "<redacted>";
|
||||
Assert.Equal(expectedRedaction, inputValue);
|
||||
Assert.Equal(expectedRedaction, messageTextValue);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, null, "Search", "Allows searching for additional information to help answer the user question.")]
|
||||
[InlineData("CustomSearch", "CustomDescription", "CustomSearch", "CustomDescription")]
|
||||
|
||||
+30
-18
@@ -270,16 +270,21 @@ public class ChatHistoryMemoryProviderTests
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false, false, 0)]
|
||||
[InlineData(true, false, 0)]
|
||||
[InlineData(false, true, 2)]
|
||||
[InlineData(true, true, 2)]
|
||||
public async Task InvokedAsync_LogsUserIdBasedOnEnableSensitiveTelemetryDataAsync(bool enableSensitiveTelemetryData, bool requestThrows, int expectedLogInvocations)
|
||||
[InlineData(false, false, false, 0)]
|
||||
[InlineData(false, false, true, 0)]
|
||||
[InlineData(true, false, false, 0)]
|
||||
[InlineData(true, false, true, 0)]
|
||||
[InlineData(false, true, false, 2)]
|
||||
[InlineData(false, true, true, 2)]
|
||||
[InlineData(true, true, false, 2)]
|
||||
[InlineData(true, true, true, 2)]
|
||||
public async Task InvokedAsync_RedactsLogDataBasedOnOptionsAsync(bool enableSensitiveTelemetryData, bool requestThrows, bool useCustomRedactor, int expectedLogInvocations)
|
||||
{
|
||||
// Arrange
|
||||
var options = new ChatHistoryMemoryProviderOptions
|
||||
{
|
||||
EnableSensitiveTelemetryData = enableSensitiveTelemetryData
|
||||
EnableSensitiveTelemetryData = enableSensitiveTelemetryData,
|
||||
Redactor = useCustomRedactor ? new ReplacingRedactor("***") : null
|
||||
};
|
||||
|
||||
if (requestThrows)
|
||||
@@ -309,7 +314,7 @@ public class ChatHistoryMemoryProviderTests
|
||||
// Act
|
||||
await provider.InvokedAsync(invokedContext, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
// Assert — EnableSensitiveTelemetryData takes precedence over Redactor
|
||||
Assert.Equal(expectedLogInvocations, this._loggerMock.Invocations.Count);
|
||||
foreach (var logInvocation in this._loggerMock.Invocations)
|
||||
{
|
||||
@@ -320,7 +325,8 @@ public class ChatHistoryMemoryProviderTests
|
||||
|
||||
var state = Assert.IsType<IReadOnlyList<KeyValuePair<string, object?>>>(logInvocation.Arguments[2], exactMatch: false);
|
||||
var userIdValue = state.First(kvp => kvp.Key == "UserId").Value;
|
||||
Assert.Equal(enableSensitiveTelemetryData ? "user1" : "<redacted>", userIdValue);
|
||||
string expectedRedaction = enableSensitiveTelemetryData ? "user1" : (useCustomRedactor ? "***" : "<redacted>");
|
||||
Assert.Equal(expectedRedaction, userIdValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,17 +532,22 @@ public class ChatHistoryMemoryProviderTests
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false, false, 2)]
|
||||
[InlineData(true, false, 2)]
|
||||
[InlineData(false, true, 2)]
|
||||
[InlineData(true, true, 2)]
|
||||
public async Task InvokingAsync_LogsUserIdBasedOnEnableSensitiveTelemetryDataAsync(bool enableSensitiveTelemetryData, bool requestThrows, int expectedLogInvocations)
|
||||
[InlineData(false, false, false, 2)]
|
||||
[InlineData(false, false, true, 2)]
|
||||
[InlineData(true, false, false, 2)]
|
||||
[InlineData(true, false, true, 2)]
|
||||
[InlineData(false, true, false, 2)]
|
||||
[InlineData(false, true, true, 2)]
|
||||
[InlineData(true, true, false, 2)]
|
||||
[InlineData(true, true, true, 2)]
|
||||
public async Task InvokingAsync_RedactsLogDataBasedOnOptionsAsync(bool enableSensitiveTelemetryData, bool requestThrows, bool useCustomRedactor, int expectedLogInvocations)
|
||||
{
|
||||
// Arrange
|
||||
var options = new ChatHistoryMemoryProviderOptions
|
||||
{
|
||||
SearchTime = ChatHistoryMemoryProviderOptions.SearchBehavior.BeforeAIInvoke,
|
||||
EnableSensitiveTelemetryData = enableSensitiveTelemetryData
|
||||
EnableSensitiveTelemetryData = enableSensitiveTelemetryData,
|
||||
Redactor = useCustomRedactor ? new ReplacingRedactor("***") : null
|
||||
};
|
||||
|
||||
var scope = new ChatHistoryMemoryProviderScope
|
||||
@@ -578,7 +589,8 @@ public class ChatHistoryMemoryProviderTests
|
||||
// Act
|
||||
await provider.InvokingAsync(invokingContext, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
// Assert — EnableSensitiveTelemetryData takes precedence over Redactor
|
||||
string expectedRedaction = enableSensitiveTelemetryData ? "user1" : (useCustomRedactor ? "***" : "<redacted>");
|
||||
Assert.Equal(expectedLogInvocations, this._loggerMock.Invocations.Count);
|
||||
foreach (var logInvocation in this._loggerMock.Invocations)
|
||||
{
|
||||
@@ -589,18 +601,18 @@ public class ChatHistoryMemoryProviderTests
|
||||
|
||||
var state = Assert.IsType<IReadOnlyList<KeyValuePair<string, object?>>>(logInvocation.Arguments[2], exactMatch: false);
|
||||
var userIdValue = state.First(kvp => kvp.Key == "UserId").Value;
|
||||
Assert.Equal(enableSensitiveTelemetryData ? "user1" : "<redacted>", userIdValue);
|
||||
Assert.Equal(expectedRedaction, userIdValue);
|
||||
|
||||
var inputValue = state.FirstOrDefault(kvp => kvp.Key == "Input").Value;
|
||||
if (inputValue != null)
|
||||
{
|
||||
Assert.Equal(enableSensitiveTelemetryData ? "Who am I?" : "<redacted>", inputValue);
|
||||
Assert.Equal(enableSensitiveTelemetryData ? "Who am I?" : expectedRedaction, inputValue);
|
||||
}
|
||||
|
||||
var messageTextValue = state.FirstOrDefault(kvp => kvp.Key == "MessageText").Value;
|
||||
if (messageTextValue != null)
|
||||
{
|
||||
Assert.Equal(enableSensitiveTelemetryData ? "## Memories\nConsider the following memories when answering user questions:\nName is Caoimhe" : "<redacted>", messageTextValue);
|
||||
Assert.Equal(enableSensitiveTelemetryData ? "## Memories\nConsider the following memories when answering user questions:\nName is Caoimhe" : expectedRedaction, messageTextValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user