File: ProtectedMaterialEvaluator.cs
Web Access
Project: src\src\Libraries\Microsoft.Extensions.AI.Evaluation.Safety\Microsoft.Extensions.AI.Evaluation.Safety.csproj (Microsoft.Extensions.AI.Evaluation.Safety)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Shared.Diagnostics;
 
namespace Microsoft.Extensions.AI.Evaluation.Safety;
 
/// <summary>
/// An <see cref="IEvaluator"/> that utilizes the Azure AI Content Safety service to evaluate responses produced by an
/// AI model for presence of protected material.
/// </summary>
/// <remarks>
/// <para>
/// Protected material includes any text that is under copyright, including song lyrics, recipes, and articles. Note
/// that <see cref="ProtectedMaterialEvaluator"/> can also detect protected material present within image content in
/// the evaluated responses. Supported file formats include JPG/JPEG, PNG and GIF and the evaluation can detect
/// copyrighted artwork, fictional characters, and logos and branding that are registered trademarks. Other modalities
/// such as audio and video are currently not supported.
/// </para>
/// <para>
/// <see cref="ProtectedMaterialEvaluator"/> returns a <see cref="BooleanMetric"/> with a value of
/// <see langword="true"/> indicating the presence of protected material in the response, and a value of
/// <see langword="false"/> indicating the absence of protected material.
/// </para>
/// </remarks>
public sealed class ProtectedMaterialEvaluator()
    : ContentSafetyEvaluator(
        contentSafetyServiceAnnotationTask: "protected material",
        metricNames:
            new Dictionary<string, string>
            {
                ["protected_material"] = ProtectedMaterialMetricName,
                ["artwork"] = ProtectedArtworkMetricName,
                ["fictional_characters"] = ProtectedFictionalCharactersMetricName,
                ["logos_and_brands"] = ProtectedLogosAndBrandsMetricName
            })
{
    /// <summary>
    /// Gets the <see cref="EvaluationMetric.Name"/> of the <see cref="BooleanMetric"/> returned by
    /// <see cref="ProtectedMaterialEvaluator"/> for indicating presence of protected material in responses.
    /// </summary>
    public static string ProtectedMaterialMetricName => "Protected Material";
 
    /// <summary>
    /// Gets the <see cref="EvaluationMetric.Name"/> of the <see cref="BooleanMetric"/> returned by
    /// <see cref="ProtectedMaterialEvaluator"/> for indicating presence of protected material in artwork in images.
    /// </summary>
    public static string ProtectedArtworkMetricName => "Protected Artwork";
 
    /// <summary>
    /// Gets the <see cref="EvaluationMetric.Name"/> of the <see cref="BooleanMetric"/> returned by
    /// <see cref="ProtectedMaterialEvaluator"/> for indicating presence of protected fictional characters in images.
    /// </summary>
    public static string ProtectedFictionalCharactersMetricName => "Protected Fictional Characters";
 
    /// <summary>
    /// Gets the <see cref="EvaluationMetric.Name"/> of the <see cref="BooleanMetric"/> returned by
    /// <see cref="ProtectedMaterialEvaluator"/> for indicating presence of protected logos and brands in images.
    /// </summary>
    public static string ProtectedLogosAndBrandsMetricName => "Protected Logos And Brands";
 
    /// <inheritdoc/>
    public override async ValueTask<EvaluationResult> EvaluateAsync(
        IEnumerable<ChatMessage> messages,
        ChatResponse modelResponse,
        ChatConfiguration? chatConfiguration = null,
        IEnumerable<EvaluationContext>? additionalContext = null,
        CancellationToken cancellationToken = default)
    {
        _ = Throw.IfNull(chatConfiguration);
        _ = Throw.IfNull(modelResponse);
 
        IChatClient chatClient = chatConfiguration.ChatClient;
 
        // First evaluate the text content in the conversation for protected material.
        EvaluationResult result =
            await EvaluateContentSafetyAsync(
                chatClient,
                messages,
                modelResponse,
                contentSafetyServicePayloadFormat: ContentSafetyServicePayloadFormat.HumanSystem.ToString(),
                includeMetricNamesInContentSafetyServicePayload: false,
                cancellationToken: cancellationToken).ConfigureAwait(false);
 
        // If images are present in the conversation, do a second evaluation for protected material in images.
        // The content safety service does not support evaluating both text and images in the same request currently.
        if (messages.ContainsImageWithSupportedFormat() || modelResponse.ContainsImageWithSupportedFormat())
        {
            EvaluationResult imageResult =
                await EvaluateContentSafetyAsync(
                    chatClient,
                    messages,
                    modelResponse,
                    contentSafetyServicePayloadFormat: ContentSafetyServicePayloadFormat.Conversation.ToString(),
                    includeMetricNamesInContentSafetyServicePayload: false,
                    cancellationToken: cancellationToken).ConfigureAwait(false);
 
            foreach (EvaluationMetric imageMetric in imageResult.Metrics.Values)
            {
                result.Metrics[imageMetric.Name] = imageMetric;
            }
        }
 
        return result;
    }
}