File: Blocks\ContentBlock.cs
Web Access
Project: src\aspnetcore\src\Components\AI\src\Microsoft.AspNetCore.Components.AI.csproj (Microsoft.AspNetCore.Components.AI)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.Extensions.AI;
 
namespace Microsoft.AspNetCore.Components.AI;
 
/// <summary>
/// Base class for a unit of conversation content that the UI renders and that can be
/// updated while the model response is still streaming.
/// </summary>
/// <example>
/// Subscribe to updates while a block streams:
/// <code>
/// using var subscription = block.OnChanged(() => StateHasChanged());
/// </code>
/// </example>
public abstract class ContentBlock
{
    private readonly List<Action> _callbacks = new();
 
    /// <summary>
    /// Gets the identifier of this block. Blocks produced by the same model message share the same identifier.
    /// </summary>
    public string Id { get; internal set; } = string.Empty;
 
    /// <summary>
    /// Gets the current lifecycle state of this block.
    /// </summary>
    public BlockLifecycleState LifecycleState { get; internal set; }
 
    /// <summary>
    /// Gets the role of the conversation participant that produced this block.
    /// </summary>
    public ChatRole? Role { get; internal set; }
 
    /// <summary>
    /// Gets the name of the author that produced this block, when the model provides one.
    /// </summary>
    public string? AuthorName { get; internal set; }
 
    /// <summary>
    /// Registers a callback that runs whenever this block changes.
    /// </summary>
    /// <param name="callback">The callback to invoke when the block changes.</param>
    /// <returns>A subscription that removes the callback when disposed.</returns>
    public ContentBlockChangedSubscription OnChanged(Action callback)
    {
        ArgumentNullException.ThrowIfNull(callback);
        _callbacks.Add(callback);
        return new ContentBlockChangedSubscription(this, callback);
    }
 
    /// <summary>
    /// Notifies subscribers that this block changed.
    /// </summary>
    protected void NotifyChanged()
    {
        // Snapshot the callbacks to allow safe removal during iteration
        var snapshot = _callbacks.ToArray();
        for (var i = 0; i < snapshot.Length; i++)
        {
            snapshot[i]();
        }
    }
 
    internal void InvokeNotifyChanged() => NotifyChanged();
 
    internal void RemoveCallback(Action callback)
    {
        _callbacks.Remove(callback);
    }
}