.NET: [BREAKING] Rename from ServiceStoredSimulatingChatClient to PerServiceCallChatHistoryPersistingChatClient (#4993)

* Rename from ServiceStoredSimulatingChatClient to PerServiceCallChatHistoryPersistingChatClient

* Address PR comment
This commit is contained in:
westey
2026-03-31 17:32:05 +00:00
committed by GitHub
parent 9c57680f00
commit 6e7254bba7
13 changed files with 198 additions and 151 deletions
@@ -139,8 +139,8 @@ public sealed partial class ChatClientAgent : AIAgent
this._logger = (loggerFactory ?? chatClient.GetService<ILoggerFactory>() ?? NullLoggerFactory.Instance).CreateLogger<ChatClientAgent>();
// Warn if using a custom chat client stack with simulated service stored persistence but no ServiceStoredSimulatingChatClient.
this.WarnOnMissingServiceStoredSimulatingClient();
// Warn if using a custom chat client stack with simulated service stored persistence but no PerServiceCallChatHistoryPersistingChatClient.
this.WarnOnMissingPerServiceCallChatHistoryPersistingChatClient();
}
/// <summary>
@@ -454,7 +454,7 @@ public sealed partial class ChatClientAgent : AIAgent
/// Notifies the <see cref="ChatHistoryProvider"/> and all <see cref="AIContextProviders"/> of successfully completed messages.
/// </summary>
/// <remarks>
/// This method is also called by <see cref="ServiceStoredSimulatingChatClient"/> to persist messages per-service-call.
/// This method is also called by <see cref="PerServiceCallChatHistoryPersistingChatClient"/> to persist messages per-service-call.
/// </remarks>
internal async Task NotifyProvidersOfNewMessagesAsync(
ChatClientAgentSession session,
@@ -486,7 +486,7 @@ public sealed partial class ChatClientAgent : AIAgent
/// Notifies the <see cref="ChatHistoryProvider"/> and all <see cref="AIContextProviders"/> of a failure during a service call.
/// </summary>
/// <remarks>
/// This method is also called by <see cref="ServiceStoredSimulatingChatClient"/> to report failures per-service-call.
/// This method is also called by <see cref="PerServiceCallChatHistoryPersistingChatClient"/> to report failures per-service-call.
/// </remarks>
internal async Task NotifyProvidersOfFailureAsync(
ChatClientAgentSession session,
@@ -701,7 +701,7 @@ public sealed partial class ChatClientAgent : AIAgent
throw new InvalidOperationException("A session must be provided when continuing a background response with a continuation token.");
}
if ((continuationToken is not null || chatOptions?.AllowBackgroundResponses is true) && this.SimulatesServiceStoredChatHistory && this._logger.IsEnabled(LogLevel.Warning))
if ((continuationToken is not null || chatOptions?.AllowBackgroundResponses is true) && this.RequiresPerServiceCallChatHistoryPersistence && this._logger.IsEnabled(LogLevel.Warning))
{
var warningAgentName = this.GetLoggingAgentName();
this._logger.LogAgentChatClientBackgroundResponseFallback(this.Id, warningAgentName);
@@ -740,10 +740,10 @@ public sealed partial class ChatClientAgent : AIAgent
IEnumerable<ChatMessage> inputMessagesForChatClient = inputMessages;
// Populate the session messages only if we are not continuing an existing response as it's not allowed.
// When SimulateServiceStoredChatHistory is active, the ServiceStoredSimulatingChatClient
// When RequirePerServiceCallChatHistoryPersistence is active, the PerServiceCallChatHistoryPersistingChatClient
// owns the chat history lifecycle — it loads history before each service call. The agent
// must not load history itself, as that would result in duplicate messages.
if (chatOptions?.ContinuationToken is null && !this.SimulatesServiceStoredChatHistory)
if (chatOptions?.ContinuationToken is null && !this.RequiresPerServiceCallChatHistoryPersistence)
{
// Add any existing messages from the session to the messages to be sent to the chat client.
// The ChatHistoryProvider returns the merged result (history + input messages).
@@ -837,14 +837,14 @@ public sealed partial class ChatClientAgent : AIAgent
/// Updates the session conversation ID at the end of an agent run.
/// </summary>
/// <remarks>
/// When a <see cref="ServiceStoredSimulatingChatClient"/> handles per-service-call
/// When a <see cref="PerServiceCallChatHistoryPersistingChatClient"/> handles per-service-call
/// conversation ID updates, this end-of-run update is skipped. When the decorator is
/// absent, the update is performed here. When <paramref name="forceUpdate"/> is <see langword="true"/>
/// (continuation token scenarios), the update is always performed.
/// </remarks>
private void UpdateSessionConversationIdAtEndOfRun(ChatClientAgentSession session, string? responseConversationId, CancellationToken cancellationToken, bool forceUpdate = false)
{
if (!forceUpdate && this.SimulatesServiceStoredChatHistory)
if (!forceUpdate && this.RequiresPerServiceCallChatHistoryPersistence)
{
return;
}
@@ -856,7 +856,7 @@ public sealed partial class ChatClientAgent : AIAgent
/// Notifies providers of successfully completed messages at the end of an agent run.
/// </summary>
/// <remarks>
/// When a <see cref="ServiceStoredSimulatingChatClient"/> handles per-service-call
/// When a <see cref="PerServiceCallChatHistoryPersistingChatClient"/> handles per-service-call
/// notification, this end-of-run notification is skipped. When no decorator is present,
/// all messages are persisted.
/// When <paramref name="forceNotify"/> is <see langword="true"/> (continuation token or
@@ -871,7 +871,7 @@ public sealed partial class ChatClientAgent : AIAgent
CancellationToken cancellationToken,
bool forceNotify = false)
{
if (!forceNotify && this.SimulatesServiceStoredChatHistory)
if (!forceNotify && this.RequiresPerServiceCallChatHistoryPersistence)
{
return Task.CompletedTask;
}
@@ -883,7 +883,7 @@ public sealed partial class ChatClientAgent : AIAgent
/// Notifies providers of a failure at the end of an agent run.
/// </summary>
/// <remarks>
/// When a <see cref="ServiceStoredSimulatingChatClient"/> handles per-service-call
/// When a <see cref="PerServiceCallChatHistoryPersistingChatClient"/> handles per-service-call
/// notification (including failure), this end-of-run notification is skipped to avoid
/// duplicate notification. In all other cases, failure is reported at the end of the run.
/// </remarks>
@@ -894,7 +894,7 @@ public sealed partial class ChatClientAgent : AIAgent
ChatOptions? chatOptions,
CancellationToken cancellationToken)
{
if (this.SimulatesServiceStoredChatHistory)
if (this.RequiresPerServiceCallChatHistoryPersistence)
{
return Task.CompletedTask;
}
@@ -905,14 +905,14 @@ public sealed partial class ChatClientAgent : AIAgent
/// <summary>
/// Gets a value indicating whether the agent is configured to simulate service-stored chat history.
/// When <see langword="true"/>, end-of-run persistence and history loading are skipped because a
/// per-service-call decorator (such as <see cref="ServiceStoredSimulatingChatClient"/> or a
/// per-service-call decorator (such as <see cref="PerServiceCallChatHistoryPersistingChatClient"/> or a
/// user-supplied equivalent) is expected to handle the history lifecycle.
/// </summary>
private bool SimulatesServiceStoredChatHistory
private bool RequiresPerServiceCallChatHistoryPersistence
{
get
{
return this._agentOptions?.SimulateServiceStoredChatHistory is true;
return this._agentOptions?.RequirePerServiceCallChatHistoryPersistence is true;
}
}
@@ -923,7 +923,7 @@ public sealed partial class ChatClientAgent : AIAgent
/// The base class sets <see cref="AIAgent.CurrentRunContext"/> with the raw session parameter
/// (which may be null) and restores it after each yield in streaming scenarios. After
/// <see cref="PrepareSessionAndMessagesAsync"/> resolves or creates a session, we update the
/// context so the <see cref="ServiceStoredSimulatingChatClient"/> decorator always has a valid session.
/// context so the <see cref="PerServiceCallChatHistoryPersistingChatClient"/> decorator always has a valid session.
/// The original agent from the context is preserved to maintain the top-of-stack agent in
/// decorated agent scenarios.
/// </remarks>
@@ -939,19 +939,19 @@ public sealed partial class ChatClientAgent : AIAgent
/// <summary>
/// Checks for potential misconfiguration when using a custom chat client stack and logs warnings.
/// </summary>
private void WarnOnMissingServiceStoredSimulatingClient()
private void WarnOnMissingPerServiceCallChatHistoryPersistingChatClient()
{
if (this._agentOptions?.UseProvidedChatClientAsIs is not true)
{
return;
}
if (this._agentOptions?.SimulateServiceStoredChatHistory is not true)
if (this._agentOptions?.RequirePerServiceCallChatHistoryPersistence is not true)
{
return;
}
var persistingClient = this.ChatClient.GetService<ServiceStoredSimulatingChatClient>();
var persistingClient = this.ChatClient.GetService<PerServiceCallChatHistoryPersistingChatClient>();
if (persistingClient is null && this._logger.IsEnabled(LogLevel.Warning))
{
var loggingAgentName = this.GetLoggingAgentName();
@@ -998,7 +998,7 @@ public sealed partial class ChatClientAgent : AIAgent
/// </summary>
/// <remarks>
/// This method is used by both the agent (during <see cref="PrepareSessionAndMessagesAsync"/>) and by
/// <see cref="ServiceStoredSimulatingChatClient"/> to load history before each service call.
/// <see cref="PerServiceCallChatHistoryPersistingChatClient"/> to load history before each service call.
/// </remarks>
internal async Task<IEnumerable<ChatMessage>> LoadChatHistoryAsync(
ChatClientAgentSession session,
@@ -72,12 +72,12 @@ internal static partial class ChatClientAgentLogMessages
/// <summary>
/// Logs a warning when <see cref="ChatClientAgentOptions.UseProvidedChatClientAsIs"/> is <see langword="true"/>
/// and <see cref="ChatClientAgentOptions.SimulateServiceStoredChatHistory"/> is <see langword="true"/>,
/// but no <see cref="ServiceStoredSimulatingChatClient"/> is found in the custom chat client stack.
/// and <see cref="ChatClientAgentOptions.RequirePerServiceCallChatHistoryPersistence"/> is <see langword="true"/>,
/// but no <see cref="PerServiceCallChatHistoryPersistingChatClient"/> is found in the custom chat client stack.
/// </summary>
[LoggerMessage(
Level = LogLevel.Warning,
Message = "Agent {AgentId}/{AgentName}: SimulateServiceStoredChatHistory is enabled with a custom chat client stack (UseProvidedChatClientAsIs), but no ServiceStoredSimulatingChatClient was found in the pipeline. Chat history will not be persisted by ChatClientAgent. Consider adding a ServiceStoredSimulatingChatClient to the pipeline using the UseServiceStoredChatHistorySimulation extension method if you have not added your own persistence mechanism.")]
Message = "Agent {AgentId}/{AgentName}: RequirePerServiceCallChatHistoryPersistence is enabled with a custom chat client stack (UseProvidedChatClientAsIs), but no PerServiceCallChatHistoryPersistingChatClient was found in the pipeline. Chat history will not be persisted by ChatClientAgent. Consider adding a PerServiceCallChatHistoryPersistingChatClient to the pipeline using the UsePerServiceCallChatHistoryPersistence extension method if you have not added your own persistence mechanism.")]
public static partial void LogAgentChatClientMissingPersistingClient(
this ILogger logger,
string agentId,
@@ -92,7 +92,7 @@ internal static partial class ChatClientAgentLogMessages
/// </summary>
[LoggerMessage(
Level = LogLevel.Warning,
Message = "Agent {AgentId}/{AgentName}: SimulateServiceStoredChatHistory is enabled but we have to fall back to end-of-run persistence because the run involves background responses.")]
Message = "Agent {AgentId}/{AgentName}: RequirePerServiceCallChatHistoryPersistence is enabled but we have to fall back to end-of-run persistence because the run involves background responses.")]
public static partial void LogAgentChatClientBackgroundResponseFallback(
this ILogger logger,
string agentId,
@@ -92,38 +92,56 @@ public sealed class ChatClientAgentOptions
public bool ThrowOnChatHistoryProviderConflict { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether the <see cref="ChatClientAgent"/> should simulate
/// service-stored chat history behavior using its configured <see cref="ChatHistoryProvider"/>.
/// Gets or sets a value indicating whether the <see cref="ChatClientAgent"/> should persist
/// chat history after each individual service call within the <see cref="FunctionInvokingChatClient"/>
/// loop, rather than at the end of the full agent run.
/// </summary>
/// <remarks>
/// <para>
/// When set to <see langword="true"/>, a <see cref="ServiceStoredSimulatingChatClient"/> decorator is
/// injected between the <see cref="FunctionInvokingChatClient"/> and the leaf <see cref="IChatClient"/>
/// in the chat client pipeline. This decorator takes full ownership of the chat history lifecycle:
/// it loads history from the <see cref="ChatHistoryProvider"/> before each service call and persists
/// new messages after each service call. It also returns a sentinel <see cref="ChatOptions.ConversationId"/>
/// on the response, causing the <see cref="FunctionInvokingChatClient"/> to treat the conversation
/// as service-managed — clearing accumulated history and not injecting duplicate
/// <see cref="FunctionCallContent"/> during approval-response processing.
/// </para>
/// <para>
/// This mode aligns the behavior of framework-managed chat history with service-stored chat history,
/// ensuring consistency in how messages are stored and loaded, including during function calling loops
/// and tool-call termination scenarios.
/// When set to <see langword="true"/>, a <see cref="PerServiceCallChatHistoryPersistingChatClient"/>
/// decorator becomes active in the chat client pipeline. It handles two complementary scenarios:
/// </para>
/// <list type="bullet">
/// <item>
/// <term>Framework-managed chat history</term>
/// <description>
/// The decorator loads history from the <see cref="ChatHistoryProvider"/> before each service call
/// and persists new request and response messages after each call. It returns a sentinel
/// <see cref="ChatOptions.ConversationId"/> on the response, causing the
/// <see cref="FunctionInvokingChatClient"/> to treat the conversation as service-managed — clearing
/// accumulated history between iterations and not injecting duplicate <see cref="FunctionCallContent"/>
/// during approval-response processing.
/// </description>
/// </item>
/// <item>
/// <term>AI Service-stored chat history</term>
/// <description>
/// When the service manages its own chat history (returning a real <see cref="ChatOptions.ConversationId"/>),
/// the decorator updates <see cref="ChatClientAgentSession.ConversationId"/> after each service call so
/// that intermediate ConversationId changes are captured immediately. For some services (e.g., the
/// Conversations API with the Responses API), there is only one thread with one ID, so every service
/// call updates it anyway and updating the <see cref="ChatClientAgentSession.ConversationId"/> has little effect
/// since it's the same ID. For other services (e.g., Responses API with Response IDs), a new ID is generated
/// with each service call, so updating the <see cref="ChatClientAgentSession.ConversationId"/> ensures that the
/// latest ID is always captured, even mid-run.
/// Enabling this option ensures consistent per-service-call behavior across all service types.
/// </description>
/// </item>
/// </list>
/// <para>
/// When set to <see langword="false"/> (the default), the <see cref="ChatClientAgent"/> handles
/// chat history persistence at the end of the full agent run via the <see cref="ChatHistoryProvider"/>
/// pipeline.
/// chat history persistence at the end of the full agent run via the <see cref="ChatHistoryProvider"/> if using
/// framework-managed chat history. For AI service-stored chat history, the <see cref="ChatClientAgentSession.ConversationId"/>
/// updates happen only at the end of the run.
/// </para>
/// <para>
/// When setting the <see cref="UseProvidedChatClientAsIs"/> setting to <see langword="true"/> and
/// <see cref="SimulateServiceStoredChatHistory"/> to <see langword="true"/>, ensure that your custom chat client stack includes a
/// <see cref="ServiceStoredSimulatingChatClient"/> to enable per-service-call persistence.
/// If no <see cref="ServiceStoredSimulatingChatClient"/> is provided, and you are not storing chat history via other means,
/// <see cref="RequirePerServiceCallChatHistoryPersistence"/> to <see langword="true"/>, ensure that your custom chat client stack includes a
/// <see cref="PerServiceCallChatHistoryPersistingChatClient"/> to enable per-service-call persistence.
/// If no <see cref="PerServiceCallChatHistoryPersistingChatClient"/> is provided, and you are not storing chat history via other means,
/// no chat history may be stored.
/// When using a custom chat client stack, you can add a <see cref="ServiceStoredSimulatingChatClient"/>
/// manually via the <see cref="ChatClientBuilderExtensions.UseServiceStoredChatHistorySimulation"/>
/// When using a custom chat client stack, you can add a <see cref="PerServiceCallChatHistoryPersistingChatClient"/>
/// manually via the <see cref="ChatClientBuilderExtensions.UsePerServiceCallChatHistoryPersistence"/>
/// extension method.
/// </para>
/// </remarks>
@@ -131,7 +149,7 @@ public sealed class ChatClientAgentOptions
/// Default is <see langword="false"/>.
/// </value>
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public bool SimulateServiceStoredChatHistory { get; set; }
public bool RequirePerServiceCallChatHistoryPersistence { get; set; }
/// <summary>
/// Creates a new instance of <see cref="ChatClientAgentOptions"/> with the same values as this instance.
@@ -149,6 +167,6 @@ public sealed class ChatClientAgentOptions
ClearOnChatHistoryProviderConflict = this.ClearOnChatHistoryProviderConflict,
WarnOnChatHistoryProviderConflict = this.WarnOnChatHistoryProviderConflict,
ThrowOnChatHistoryProviderConflict = this.ThrowOnChatHistoryProviderConflict,
SimulateServiceStoredChatHistory = this.SimulateServiceStoredChatHistory,
RequirePerServiceCallChatHistoryPersistence = this.RequirePerServiceCallChatHistoryPersistence,
};
}
@@ -86,21 +86,21 @@ public static class ChatClientBuilderExtensions
services: services);
/// <summary>
/// Adds a <see cref="ServiceStoredSimulatingChatClient"/> to the chat client pipeline.
/// Adds a <see cref="PerServiceCallChatHistoryPersistingChatClient"/> to the chat client pipeline.
/// </summary>
/// <remarks>
/// <para>
/// This decorator should be positioned between the <see cref="FunctionInvokingChatClient"/> and the leaf
/// <see cref="IChatClient"/> in the pipeline. It simulates service-stored chat history behavior by
/// loading history before each service call, persisting after each call, and returning a sentinel
/// <see cref="ChatOptions.ConversationId"/> on the response.
/// <see cref="IChatClient"/> in the pipeline. It persists chat history after each individual service call
/// and updates the session <see cref="ChatOptions.ConversationId"/> per call for both framework-managed
/// and service-stored chat history scenarios.
/// </para>
/// <para>
/// This extension method is intended for use with custom chat client stacks when
/// <see cref="ChatClientAgentOptions.UseProvidedChatClientAsIs"/> is <see langword="true"/>.
/// When <see cref="ChatClientAgentOptions.UseProvidedChatClientAsIs"/> is <see langword="false"/> (the default),
/// the <see cref="ChatClientAgent"/> automatically injects this decorator when
/// <see cref="ChatClientAgentOptions.SimulateServiceStoredChatHistory"/> is <see langword="true"/>.
/// the <see cref="ChatClientAgent"/> automatically includes this decorator in the pipeline and activates it when
/// <see cref="ChatClientAgentOptions.RequirePerServiceCallChatHistoryPersistence"/> is <see langword="true"/>.
/// </para>
/// <para>
/// This decorator only works within the context of a running <see cref="ChatClientAgent"/> and will throw an
@@ -110,8 +110,8 @@ public static class ChatClientBuilderExtensions
/// <param name="builder">The <see cref="ChatClientBuilder"/> to add the decorator to.</param>
/// <returns>The <paramref name="builder"/> for chaining.</returns>
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public static ChatClientBuilder UseServiceStoredChatHistorySimulation(this ChatClientBuilder builder)
public static ChatClientBuilder UsePerServiceCallChatHistoryPersistence(this ChatClientBuilder builder)
{
return builder.Use(innerClient => new ServiceStoredSimulatingChatClient(innerClient));
return builder.Use(innerClient => new PerServiceCallChatHistoryPersistingChatClient(innerClient));
}
}
@@ -63,16 +63,16 @@ public static class ChatClientExtensions
});
}
// ServiceStoredSimulatingChatClient is only injected when SimulateServiceStoredChatHistory is enabled.
// PerServiceCallChatHistoryPersistingChatClient is only injected when RequirePerServiceCallChatHistoryPersistence is enabled.
// It is registered after FunctionInvokingChatClient so that it sits between FIC and the leaf client.
// ChatClientBuilder.Build applies factories in reverse order, making the first Use() call outermost.
// By adding our decorator second, the resulting pipeline is:
// FunctionInvokingChatClient → ServiceStoredSimulatingChatClient → leaf IChatClient
// FunctionInvokingChatClient → PerServiceCallChatHistoryPersistingChatClient → leaf IChatClient
// This allows the decorator to simulate service-stored chat history by loading history before
// each service call, persisting after each call, and returning a sentinel ConversationId.
if (options?.SimulateServiceStoredChatHistory is true)
if (options?.RequirePerServiceCallChatHistoryPersistence is true)
{
chatBuilder.Use(innerClient => new ServiceStoredSimulatingChatClient(innerClient));
chatBuilder.Use(innerClient => new PerServiceCallChatHistoryPersistingChatClient(innerClient));
}
var agentChatClient = chatBuilder.Build(services);
@@ -11,23 +11,39 @@ using Microsoft.Extensions.AI;
namespace Microsoft.Agents.AI;
/// <summary>
/// A delegating chat client that simulates service-stored chat history behavior using
/// framework-managed <see cref="ChatHistoryProvider"/> instances.
/// A delegating chat client that persists chat history and updates session state after each
/// individual service call within the <see cref="FunctionInvokingChatClient"/> loop.
/// </summary>
/// <remarks>
/// <para>
/// This decorator is intended to operate between the <see cref="FunctionInvokingChatClient"/> and the leaf
/// <see cref="IChatClient"/> in a <see cref="ChatClientAgent"/> pipeline.
/// <see cref="IChatClient"/> in a <see cref="ChatClientAgent"/> pipeline. It is activated when
/// <see cref="ChatClientAgentOptions.RequirePerServiceCallChatHistoryPersistence"/> is <see langword="true"/>.
/// </para>
/// <para>
/// Before each service call, it loads chat history from the agent's <see cref="ChatHistoryProvider"/>
/// and prepends it to the request messages. After each successful service call, it persists
/// new request and response messages to the provider. It also returns a sentinel
/// <see cref="ChatOptions.ConversationId"/> on the response so that the
/// <see cref="FunctionInvokingChatClient"/> treats the conversation as service-managed —
/// clearing accumulated history between iterations and not injecting duplicate
/// <see cref="FunctionCallContent"/> during approval-response processing.
/// When active, it handles two complementary scenarios:
/// </para>
/// <list type="bullet">
/// <item>
/// <term>Framework-managed chat history</term>
/// <description>
/// Before each service call, the decorator loads history from the agent's <see cref="ChatHistoryProvider"/>
/// and prepends it to the request messages. After each successful call, it persists new messages to
/// the provider and returns a sentinel <see cref="ChatOptions.ConversationId"/> so that
/// <see cref="FunctionInvokingChatClient"/> treats the conversation as service-managed — clearing
/// accumulated history between iterations and not injecting duplicate <see cref="FunctionCallContent"/>
/// during approval-response processing.
/// </description>
/// </item>
/// <item>
/// <term>Service-stored chat history</term>
/// <description>
/// When the underlying service manages its own chat history (real <see cref="ChatOptions.ConversationId"/>),
/// the decorator updates <see cref="ChatClientAgentSession.ConversationId"/> after each service call so
/// that intermediate ConversationId changes are captured immediately rather than only at the end of the run.
/// </description>
/// </item>
/// </list>
/// <para>
/// This chat client must be used within the context of a running <see cref="ChatClientAgent"/>. It retrieves the
/// current agent and session from <see cref="AIAgent.CurrentRunContext"/>, which is set automatically when an agent's
@@ -38,7 +54,7 @@ namespace Microsoft.Agents.AI;
/// available or if the agent is not a <see cref="ChatClientAgent"/>.
/// </para>
/// </remarks>
internal sealed class ServiceStoredSimulatingChatClient : DelegatingChatClient
internal sealed class PerServiceCallChatHistoryPersistingChatClient : DelegatingChatClient
{
/// <summary>
/// A sentinel value returned on <see cref="ChatResponse.ConversationId"/> to signal
@@ -59,10 +75,10 @@ internal sealed class ServiceStoredSimulatingChatClient : DelegatingChatClient
internal const string LocalHistoryConversationId = "_agent_local_chat_history";
/// <summary>
/// Initializes a new instance of the <see cref="ServiceStoredSimulatingChatClient"/> class.
/// Initializes a new instance of the <see cref="PerServiceCallChatHistoryPersistingChatClient"/> class.
/// </summary>
/// <param name="innerClient">The underlying chat client that will handle the core operations.</param>
public ServiceStoredSimulatingChatClient(IChatClient innerClient)
public PerServiceCallChatHistoryPersistingChatClient(IChatClient innerClient)
: base(innerClient)
{
}
@@ -237,18 +253,18 @@ internal sealed class ServiceStoredSimulatingChatClient : DelegatingChatClient
{
var runContext = AIAgent.CurrentRunContext
?? throw new InvalidOperationException(
$"{nameof(ServiceStoredSimulatingChatClient)} can only be used within the context of a running AIAgent. " +
$"{nameof(PerServiceCallChatHistoryPersistingChatClient)} can only be used within the context of a running AIAgent. " +
"Ensure that the chat client is being invoked as part of an AIAgent.RunAsync or AIAgent.RunStreamingAsync call.");
var chatClientAgent = runContext.Agent.GetService<ChatClientAgent>()
?? throw new InvalidOperationException(
$"{nameof(ServiceStoredSimulatingChatClient)} can only be used with a {nameof(ChatClientAgent)}. " +
$"{nameof(PerServiceCallChatHistoryPersistingChatClient)} can only be used with a {nameof(ChatClientAgent)}. " +
$"The current agent is of type '{runContext.Agent.GetType().Name}'.");
if (runContext.Session is not ChatClientAgentSession chatClientAgentSession)
{
throw new InvalidOperationException(
$"{nameof(ServiceStoredSimulatingChatClient)} requires a {nameof(ChatClientAgentSession)}. " +
$"{nameof(PerServiceCallChatHistoryPersistingChatClient)} requires a {nameof(ChatClientAgentSession)}. " +
$"The current session is of type '{runContext.Session?.GetType().Name ?? "null"}'.");
}