mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Replace Typed Base Providers with Composition (#3988)
This commit is contained in:
committed by
GitHub
Unverified
parent
8015e00f56
commit
cd4e36ebf7
@@ -1,87 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace Microsoft.Agents.AI;
|
||||
|
||||
/// <summary>
|
||||
/// Provides an abstract base class for components that enhance AI context during agent invocations with support for maintaining provider state of type <typeparamref name="TState"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TState">The type of the state to be maintained by the context provider. Must be a reference type.</typeparam>
|
||||
/// <remarks>
|
||||
/// This class extends <see cref="AIContextProvider"/> by introducing a strongly-typed state management mechanism, allowing derived classes to maintain and persist custom state information across invocations.
|
||||
/// The state is stored in the session's StateBag using a configurable key and JSON serialization options, enabling seamless integration with the agent session lifecycle.
|
||||
/// </remarks>
|
||||
public abstract class AIContextProvider<TState> : AIContextProvider
|
||||
where TState : class
|
||||
{
|
||||
private readonly Func<AgentSession?, TState> _stateInitializer;
|
||||
private readonly string _stateKey;
|
||||
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AIContextProvider{TState}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="stateInitializer">A function to initialize the state for the context provider.</param>
|
||||
/// <param name="stateKey">The key used to store the state in the session's StateBag.</param>
|
||||
/// <param name="jsonSerializerOptions">Options for JSON serialization and deserialization of the state.</param>
|
||||
/// <param name="provideInputMessageFilter">An optional filter function to apply to input messages before providing context. If not set, defaults to including only <see cref="AgentRequestMessageSourceType.External"/> messages.</param>
|
||||
/// <param name="storeInputMessageFilter">An optional filter function to apply to request messages before storing context. If not set, defaults to including only <see cref="AgentRequestMessageSourceType.External"/> messages.</param>
|
||||
protected AIContextProvider(
|
||||
Func<AgentSession?, TState> stateInitializer,
|
||||
string? stateKey,
|
||||
JsonSerializerOptions? jsonSerializerOptions,
|
||||
Func<IEnumerable<ChatMessage>, IEnumerable<ChatMessage>>? provideInputMessageFilter,
|
||||
Func<IEnumerable<ChatMessage>, IEnumerable<ChatMessage>>? storeInputMessageFilter)
|
||||
: base(provideInputMessageFilter, storeInputMessageFilter)
|
||||
{
|
||||
this._stateInitializer = stateInitializer;
|
||||
this._jsonSerializerOptions = jsonSerializerOptions ?? AgentAbstractionsJsonUtilities.DefaultOptions;
|
||||
this._stateKey = stateKey ?? this.GetType().Name;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string StateKey => this._stateKey;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the state from the session's StateBag, or initializes it using the state initializer if not present.
|
||||
/// </summary>
|
||||
/// <param name="session">The agent session containing the StateBag.</param>
|
||||
/// <returns>The provider state.</returns>
|
||||
protected virtual TState GetOrInitializeState(AgentSession? session)
|
||||
{
|
||||
if (session?.StateBag.TryGetValue<TState>(this._stateKey, out var state, this._jsonSerializerOptions) is true && state is not null)
|
||||
{
|
||||
return state;
|
||||
}
|
||||
|
||||
state = this._stateInitializer(session);
|
||||
if (session is not null)
|
||||
{
|
||||
session.StateBag.SetValue(this._stateKey, state, this._jsonSerializerOptions);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the specified state to the session's StateBag using the configured state key and JSON serializer options.
|
||||
/// If the session is null, this method does nothing.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method provides a convenient way for derived classes to persist state changes back to the session after processing.
|
||||
/// It abstracts away the details of how state is stored in the session, allowing derived classes to focus on their specific logic.
|
||||
/// </remarks>
|
||||
/// <param name="session">The agent session containing the StateBag.</param>
|
||||
/// <param name="state">The state to be saved.</param>
|
||||
protected virtual void SaveState(AgentSession? session, TState state)
|
||||
{
|
||||
if (session is not null)
|
||||
{
|
||||
session.StateBag.SetValue(this._stateKey, state, this._jsonSerializerOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace Microsoft.Agents.AI;
|
||||
|
||||
/// <summary>
|
||||
/// Provides an abstract base class for fetching chat messages from, and adding chat messages to, chat history for the purposes of agent execution with support for maintaining provider state of type <typeparamref name="TState"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TState">The type of the state to be maintained by the chat history provider. Must be a reference type.</typeparam>
|
||||
/// <remarks>
|
||||
/// This class extends <see cref="ChatHistoryProvider"/> by introducing a strongly-typed state management mechanism, allowing derived classes to maintain and persist custom state information across invocations.
|
||||
/// The state is stored in the session's StateBag using a configurable key and JSON serialization options, enabling seamless integration with the agent session lifecycle.
|
||||
/// </remarks>
|
||||
public abstract class ChatHistoryProvider<TState> : ChatHistoryProvider
|
||||
where TState : class
|
||||
{
|
||||
private readonly Func<AgentSession?, TState> _stateInitializer;
|
||||
private readonly string _stateKey;
|
||||
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ChatHistoryProvider{TState}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="stateInitializer">A function to initialize the state for the chat history provider.</param>
|
||||
/// <param name="stateKey">The key used to store the state in the session's StateBag.</param>
|
||||
/// <param name="jsonSerializerOptions">Options for JSON serialization and deserialization of the state.</param>
|
||||
/// <param name="provideOutputMessageFilter">A filter function to apply to messages when retrieving them from the chat history.</param>
|
||||
/// <param name="storeInputMessageFilter">A filter function to apply to messages before storing them in the chat history.</param>
|
||||
protected ChatHistoryProvider(
|
||||
Func<AgentSession?, TState> stateInitializer,
|
||||
string? stateKey,
|
||||
JsonSerializerOptions? jsonSerializerOptions,
|
||||
Func<IEnumerable<ChatMessage>, IEnumerable<ChatMessage>>? provideOutputMessageFilter,
|
||||
Func<IEnumerable<ChatMessage>, IEnumerable<ChatMessage>>? storeInputMessageFilter)
|
||||
: base(provideOutputMessageFilter, storeInputMessageFilter)
|
||||
{
|
||||
this._stateInitializer = stateInitializer;
|
||||
this._jsonSerializerOptions = jsonSerializerOptions ?? AgentAbstractionsJsonUtilities.DefaultOptions;
|
||||
this._stateKey = stateKey ?? this.GetType().Name;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string StateKey => this._stateKey;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the state from the session's StateBag, or initializes it using the state initializer if not present.
|
||||
/// </summary>
|
||||
/// <param name="session">The agent session containing the StateBag.</param>
|
||||
/// <returns>The provider state, or null if no session is available.</returns>
|
||||
protected virtual TState GetOrInitializeState(AgentSession? session)
|
||||
{
|
||||
if (session?.StateBag.TryGetValue<TState>(this._stateKey, out var state, this._jsonSerializerOptions) is true && state is not null)
|
||||
{
|
||||
return state;
|
||||
}
|
||||
|
||||
state = this._stateInitializer(session);
|
||||
if (session is not null)
|
||||
{
|
||||
session.StateBag.SetValue(this._stateKey, state, this._jsonSerializerOptions);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the specified state to the session's StateBag using the configured state key and JSON serializer options.
|
||||
/// If the session is null, this method does nothing.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method provides a convenient way for derived classes to persist state changes back to the session after processing.
|
||||
/// It abstracts away the details of how state is stored in the session, allowing derived classes to focus on their specific logic.
|
||||
/// </remarks>
|
||||
/// <param name="session">The agent session containing the StateBag.</param>
|
||||
/// <param name="state">The state to be saved.</param>
|
||||
protected virtual void SaveState(AgentSession? session, TState state)
|
||||
{
|
||||
if (session is not null)
|
||||
{
|
||||
session.StateBag.SetValue(this._stateKey, state, this._jsonSerializerOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,10 @@ namespace Microsoft.Agents.AI;
|
||||
/// message reduction strategies or alternative storage implementations.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public sealed class InMemoryChatHistoryProvider : ChatHistoryProvider<InMemoryChatHistoryProvider.State>
|
||||
public sealed class InMemoryChatHistoryProvider : ChatHistoryProvider
|
||||
{
|
||||
private readonly ProviderSessionState<State> _sessionState;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InMemoryChatHistoryProvider"/> class.
|
||||
/// </summary>
|
||||
@@ -35,16 +37,20 @@ public sealed class InMemoryChatHistoryProvider : ChatHistoryProvider<InMemoryCh
|
||||
/// </param>
|
||||
public InMemoryChatHistoryProvider(InMemoryChatHistoryProviderOptions? options = null)
|
||||
: base(
|
||||
options?.StateInitializer ?? (_ => new State()),
|
||||
options?.StateKey,
|
||||
options?.JsonSerializerOptions,
|
||||
options?.ProvideOutputMessageFilter,
|
||||
options?.StorageInputMessageFilter)
|
||||
{
|
||||
this._sessionState = new ProviderSessionState<State>(
|
||||
options?.StateInitializer ?? (_ => new State()),
|
||||
options?.StateKey ?? this.GetType().Name,
|
||||
options?.JsonSerializerOptions);
|
||||
this.ChatReducer = options?.ChatReducer;
|
||||
this.ReducerTriggerEvent = options?.ReducerTriggerEvent ?? InMemoryChatHistoryProviderOptions.ChatReducerTriggerEvent.BeforeMessagesRetrieval;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string StateKey => this._sessionState.StateKey;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chat reducer used to process or reduce chat messages. If null, no reduction logic will be applied.
|
||||
/// </summary>
|
||||
@@ -61,7 +67,7 @@ public sealed class InMemoryChatHistoryProvider : ChatHistoryProvider<InMemoryCh
|
||||
/// <param name="session">The agent session containing the state.</param>
|
||||
/// <returns>A list of chat messages, or an empty list if no state is found.</returns>
|
||||
public List<ChatMessage> GetMessages(AgentSession? session)
|
||||
=> this.GetOrInitializeState(session).Messages;
|
||||
=> this._sessionState.GetOrInitializeState(session).Messages;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the chat messages for the specified session.
|
||||
@@ -73,14 +79,14 @@ public sealed class InMemoryChatHistoryProvider : ChatHistoryProvider<InMemoryCh
|
||||
{
|
||||
_ = Throw.IfNull(messages);
|
||||
|
||||
var state = this.GetOrInitializeState(session);
|
||||
var state = this._sessionState.GetOrInitializeState(session);
|
||||
state.Messages = messages;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask<IEnumerable<ChatMessage>> ProvideChatHistoryAsync(InvokingContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var state = this.GetOrInitializeState(context.Session);
|
||||
var state = this._sessionState.GetOrInitializeState(context.Session);
|
||||
|
||||
if (this.ReducerTriggerEvent is InMemoryChatHistoryProviderOptions.ChatReducerTriggerEvent.BeforeMessagesRetrieval && this.ChatReducer is not null)
|
||||
{
|
||||
@@ -93,7 +99,7 @@ public sealed class InMemoryChatHistoryProvider : ChatHistoryProvider<InMemoryCh
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask StoreChatHistoryAsync(InvokedContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var state = this.GetOrInitializeState(context.Session);
|
||||
var state = this._sessionState.GetOrInitializeState(context.Session);
|
||||
|
||||
// Add request and response messages to the provider
|
||||
var allNewMessages = context.RequestMessages.Concat(context.ResponseMessages ?? []);
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Microsoft.Agents.AI;
|
||||
|
||||
/// <summary>
|
||||
/// Provides strongly-typed state management for providers, enabling reading and writing of provider-specific state
|
||||
/// to and from an <see cref="AgentSession"/>'s <see cref="AgentSessionStateBag"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TState">The type of the state to be maintained. Must be a reference type.</typeparam>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This class encapsulates the logic for initializing, retrieving, and persisting provider state in the session's StateBag
|
||||
/// using a configurable key and JSON serialization options. It is intended to be used as a composed field within provider
|
||||
/// implementations (e.g., <see cref="AIContextProvider"/> or <see cref="ChatHistoryProvider"/> subclasses) to avoid
|
||||
/// duplicating state management logic across provider type hierarchies.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// State is stored in the <see cref="AgentSession.StateBag"/> using the <see cref="StateKey"/> property as the key,
|
||||
/// enabling multiple providers to maintain independent state within the same session.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class ProviderSessionState<TState>
|
||||
where TState : class
|
||||
{
|
||||
private readonly Func<AgentSession?, TState> _stateInitializer;
|
||||
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ProviderSessionState{TState}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="stateInitializer">A function to initialize the state when it is not yet present in the session's StateBag.</param>
|
||||
/// <param name="stateKey">The key used to store the state in the session's StateBag.</param>
|
||||
/// <param name="jsonSerializerOptions">Options for JSON serialization and deserialization of the state.</param>
|
||||
public ProviderSessionState(
|
||||
Func<AgentSession?, TState> stateInitializer,
|
||||
string stateKey,
|
||||
JsonSerializerOptions? jsonSerializerOptions = null)
|
||||
{
|
||||
this._stateInitializer = stateInitializer;
|
||||
this.StateKey = stateKey;
|
||||
this._jsonSerializerOptions = jsonSerializerOptions ?? AgentAbstractionsJsonUtilities.DefaultOptions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the key used to store the provider state in the <see cref="AgentSession.StateBag"/>.
|
||||
/// </summary>
|
||||
public string StateKey { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the state from the session's StateBag, or initializes it using the state initializer if not present.
|
||||
/// </summary>
|
||||
/// <param name="session">The agent session containing the StateBag.</param>
|
||||
/// <returns>The provider state.</returns>
|
||||
public TState GetOrInitializeState(AgentSession? session)
|
||||
{
|
||||
if (session?.StateBag.TryGetValue<TState>(this.StateKey, out var state, this._jsonSerializerOptions) is true && state is not null)
|
||||
{
|
||||
return state;
|
||||
}
|
||||
|
||||
state = this._stateInitializer(session);
|
||||
if (session is not null)
|
||||
{
|
||||
session.StateBag.SetValue(this.StateKey, state, this._jsonSerializerOptions);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the specified state to the session's StateBag using the configured state key and JSON serializer options.
|
||||
/// If the session is null, this method does nothing.
|
||||
/// </summary>
|
||||
/// <param name="session">The agent session containing the StateBag.</param>
|
||||
/// <param name="state">The state to be saved.</param>
|
||||
public void SaveState(AgentSession? session, TState state)
|
||||
{
|
||||
if (session is not null)
|
||||
{
|
||||
session.StateBag.SetValue(this.StateKey, state, this._jsonSerializerOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user