File: Realtime\OpenTelemetryRealtimeClient.cs
Project: ..\..\..\src\Libraries\Microsoft.Extensions.AI\Microsoft.Extensions.AI.csproj (Microsoft.Extensions.AI)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;
 
namespace Microsoft.Extensions.AI;
 
/// <summary>
/// A delegating realtime client that adds OpenTelemetry support, following the OpenTelemetry Semantic Conventions for Generative AI systems.
/// </summary>
/// <remarks>
/// <para>
/// The draft specification this follows is available at <see href="https://opentelemetry.io/docs/specs/semconv/gen-ai/" />.
/// The specification is still experimental and subject to change; as such, the telemetry output by this client is also subject to change.
/// </para>
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AIRealTime, UrlFormat = DiagnosticIds.UrlFormat)]
public sealed class OpenTelemetryRealtimeClient : DelegatingRealtimeClient
{
    private readonly ILogger? _logger;
    private readonly string? _sourceName;
    private JsonSerializerOptions _jsonSerializerOptions;
 
    /// <summary>Initializes a new instance of the <see cref="OpenTelemetryRealtimeClient"/> class.</summary>
    /// <param name="innerClient">The inner <see cref="IRealtimeClient"/>.</param>
    /// <param name="logger">The <see cref="ILogger"/> to use for emitting any logging data from the client.</param>
    /// <param name="sourceName">An optional source name that will be used on the telemetry data.</param>
    public OpenTelemetryRealtimeClient(IRealtimeClient innerClient, ILogger? logger = null, string? sourceName = null)
        : base(innerClient)
    {
        _logger = logger;
        _sourceName = sourceName;
        _jsonSerializerOptions = AIJsonUtilities.DefaultOptions;
    }
 
    /// <summary>
    /// Gets or sets a value indicating whether potentially sensitive information should be included in telemetry.
    /// </summary>
    /// <value>
    /// <see langword="true"/> if potentially sensitive information should be included in telemetry;
    /// <see langword="false"/> if telemetry shouldn't include raw inputs and outputs.
    /// The default value is <see langword="false"/>, unless the <c>OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT</c>
    /// environment variable is set to "true" (case-insensitive).
    /// </value>
    public bool EnableSensitiveData { get; set; } = TelemetryHelpers.EnableSensitiveDataDefault;
 
    /// <summary>Gets or sets JSON serialization options to use when formatting realtime data into telemetry strings.</summary>
    public JsonSerializerOptions JsonSerializerOptions
    {
        get => _jsonSerializerOptions;
        set => _jsonSerializerOptions = Throw.IfNull(value);
    }
 
    /// <inheritdoc />
    public override async Task<IRealtimeClientSession> CreateSessionAsync(
        RealtimeSessionOptions? options = null, CancellationToken cancellationToken = default)
    {
        var innerSession = await base.CreateSessionAsync(options, cancellationToken).ConfigureAwait(false);
        return new OpenTelemetryRealtimeClientSession(innerSession, _logger, _sourceName)
        {
            EnableSensitiveData = EnableSensitiveData,
            JsonSerializerOptions = _jsonSerializerOptions,
        };
    }
}