.NET: Introduce Core implementation methods for session methods on AIAgent (#3699)

* Introduce Core implementation methods for session methods on AIAgent

* Update changelog
This commit is contained in:
westey
2026-02-06 13:25:20 +00:00
committed by GitHub
parent 0f3f4dbcaf
commit ac17adb595
33 changed files with 157 additions and 106 deletions
@@ -54,7 +54,7 @@ public sealed class A2AAgent : AIAgent
}
/// <inheritdoc/>
public sealed override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default)
protected sealed override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
=> new(new A2AAgentSession());
/// <summary>
@@ -66,7 +66,7 @@ public sealed class A2AAgent : AIAgent
=> new(new A2AAgentSession() { ContextId = contextId });
/// <inheritdoc/>
public override JsonElement SerializeSession(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
{
_ = Throw.IfNull(session);
@@ -79,7 +79,7 @@ public sealed class A2AAgent : AIAgent
}
/// <inheritdoc/>
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
=> new(new A2AAgentSession(serializedState, jsonSerializerOptions));
/// <inheritdoc/>
@@ -139,7 +139,18 @@ public abstract class AIAgent
/// may be deferred until first use to optimize performance.
/// </para>
/// </remarks>
public abstract ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default);
public ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default)
=> this.CreateSessionCoreAsync(cancellationToken);
/// <summary>
/// Core implementation of session creation logic.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A value task that represents the asynchronous operation. The task result contains a new <see cref="AgentSession"/> instance ready for use with this agent.</returns>
/// <remarks>
/// This is the primary session creation method that implementations must override.
/// </remarks>
protected abstract ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Serializes an agent session to its JSON representation.
@@ -154,7 +165,19 @@ public abstract class AIAgent
/// allowing conversations to resume across application restarts or be migrated between
/// different agent instances. Use <see cref="DeserializeSessionAsync"/> to restore the session.
/// </remarks>
public abstract JsonElement SerializeSession(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null);
public JsonElement SerializeSession(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
=> this.SerializeSessionCore(session, jsonSerializerOptions);
/// <summary>
/// Core implementation of session serialization logic.
/// </summary>
/// <param name="session">The <see cref="AgentSession"/> to serialize.</param>
/// <param name="jsonSerializerOptions">Optional settings to customize the serialization process.</param>
/// <returns>A <see cref="JsonElement"/> containing the serialized session state.</returns>
/// <remarks>
/// This is the primary session serialization method that implementations must override.
/// </remarks>
protected abstract JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null);
/// <summary>
/// Deserializes an agent session from its JSON serialized representation.
@@ -170,7 +193,20 @@ public abstract class AIAgent
/// allowing conversations to resume across application restarts or be migrated between
/// different agent instances.
/// </remarks>
public abstract ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default);
public ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
=> this.DeserializeSessionCoreAsync(serializedState, jsonSerializerOptions, cancellationToken);
/// <summary>
/// Core implementation of session deserialization logic.
/// </summary>
/// <param name="serializedState">A <see cref="JsonElement"/> containing the serialized session state.</param>
/// <param name="jsonSerializerOptions">Optional settings to customize the deserialization process.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A value task that represents the asynchronous operation. The task result contains a restored <see cref="AgentSession"/> instance with the state from <paramref name="serializedState"/>.</returns>
/// <remarks>
/// This is the primary session deserialization method that implementations must override.
/// </remarks>
protected abstract ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default);
/// <summary>
/// Run the agent with no message assuming that all required instructions are already provided to the agent or on the session.
@@ -74,14 +74,14 @@ public abstract class DelegatingAIAgent : AIAgent
}
/// <inheritdoc />
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default) => this.InnerAgent.CreateSessionAsync(cancellationToken);
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default) => this.InnerAgent.CreateSessionAsync(cancellationToken);
/// <inheritdoc />
public override JsonElement SerializeSession(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
=> this.InnerAgent.SerializeSession(session, jsonSerializerOptions);
/// <inheritdoc />
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
=> this.InnerAgent.DeserializeSessionAsync(serializedState, jsonSerializerOptions, cancellationToken);
/// <inheritdoc />
@@ -42,7 +42,7 @@ public class CopilotStudioAgent : AIAgent
}
/// <inheritdoc/>
public sealed override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default)
protected sealed override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
=> new(new CopilotStudioAgentSession());
/// <summary>
@@ -54,7 +54,7 @@ public class CopilotStudioAgent : AIAgent
=> new(new CopilotStudioAgentSession() { ConversationId = conversationId });
/// <inheritdoc/>
public override JsonElement SerializeSession(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
{
Throw.IfNull(session);
@@ -67,7 +67,7 @@ public class CopilotStudioAgent : AIAgent
}
/// <inheritdoc/>
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
=> new(new CopilotStudioAgentSession(serializedState, jsonSerializerOptions));
/// <inheritdoc/>
@@ -10,6 +10,7 @@
- Renamed AgentThread to AgentSession ([#3430](https://github.com/microsoft/agent-framework/pull/3430))
- Moved AgentSession.Serialize to AIAgent.SerializeSession ([#3650](https://github.com/microsoft/agent-framework/pull/3650))
- Renamed serializedSession parameter to serializedState on DeserializeSessionAsync for consistency ([#3681](https://github.com/microsoft/agent-framework/pull/3681))
- Introduce Core method pattern for Session management methods on AIAgent ([#3699](https://github.com/microsoft/agent-framework/pull/3699))
## v1.0.0-preview.251204.1
@@ -34,7 +34,7 @@ public sealed class DurableAIAgent : AIAgent
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A value task that represents the asynchronous operation. The task result contains a new agent session.</returns>
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
{
AgentSessionId sessionId = this._context.NewAgentSessionId(this._agentName);
return ValueTask.FromResult<AgentSession>(new DurableAgentSession(sessionId));
@@ -46,7 +46,7 @@ public sealed class DurableAIAgent : AIAgent
/// <param name="session">The session to serialize.</param>
/// <param name="jsonSerializerOptions">Optional JSON serializer options.</param>
/// <returns>A <see cref="JsonElement"/> containing the serialized session state.</returns>
public override JsonElement SerializeSession(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
{
if (session is null)
{
@@ -68,7 +68,7 @@ public sealed class DurableAIAgent : AIAgent
/// <param name="jsonSerializerOptions">Optional JSON serializer options.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A value task that represents the asynchronous operation. The task result contains the deserialized agent session.</returns>
public override ValueTask<AgentSession> DeserializeSessionAsync(
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(
JsonElement serializedState,
JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
{
@@ -11,7 +11,7 @@ internal class DurableAIAgentProxy(string name, IDurableAgentClient agentClient)
public override string? Name { get; } = name;
public override JsonElement SerializeSession(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
{
if (session is null)
{
@@ -26,14 +26,14 @@ internal class DurableAIAgentProxy(string name, IDurableAgentClient agentClient)
return durableSession.Serialize(jsonSerializerOptions);
}
public override ValueTask<AgentSession> DeserializeSessionAsync(
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(
JsonElement serializedState,
JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
{
return ValueTask.FromResult<AgentSession>(DurableAgentSession.Deserialize(serializedState, jsonSerializerOptions));
}
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
{
return ValueTask.FromResult<AgentSession>(new DurableAgentSession(AgentSessionId.WithRandomKey(this.Name!)));
}
@@ -86,7 +86,7 @@ public sealed class GitHubCopilotAgent : AIAgent, IAsyncDisposable
}
/// <inheritdoc/>
public sealed override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default)
protected sealed override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
=> new(new GitHubCopilotAgentSession());
/// <summary>
@@ -98,7 +98,7 @@ public sealed class GitHubCopilotAgent : AIAgent, IAsyncDisposable
=> new(new GitHubCopilotAgentSession() { SessionId = sessionId });
/// <inheritdoc/>
public override JsonElement SerializeSession(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
{
_ = Throw.IfNull(session);
@@ -111,7 +111,7 @@ public sealed class GitHubCopilotAgent : AIAgent, IAsyncDisposable
}
/// <inheritdoc/>
public override ValueTask<AgentSession> DeserializeSessionAsync(
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(
JsonElement serializedState,
JsonSerializerOptions? jsonSerializerOptions = null,
CancellationToken cancellationToken = default)
@@ -30,19 +30,19 @@ internal class PurviewAgent : AIAgent, IDisposable
}
/// <inheritdoc/>
public override JsonElement SerializeSession(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
{
return this._innerAgent.SerializeSession(session, jsonSerializerOptions);
}
/// <inheritdoc/>
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
{
return this._innerAgent.DeserializeSessionAsync(serializedState, jsonSerializerOptions, cancellationToken);
}
/// <inheritdoc/>
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
{
return this._innerAgent.CreateSessionAsync(cancellationToken);
}
@@ -65,10 +65,10 @@ internal sealed class WorkflowHostAgent : AIAgent
protocol.ThrowIfNotChatProtocol(allowCatchAll: true);
}
public override ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
=> new(new WorkflowSession(this._workflow, this.GenerateNewId(), this._executionEnvironment, this._checkpointManager, this._includeExceptionDetails, this._includeWorkflowOutputsInResponse));
public override JsonElement SerializeSession(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
{
_ = Throw.IfNull(session);
@@ -80,7 +80,7 @@ internal sealed class WorkflowHostAgent : AIAgent
return workflowSession.Serialize(jsonSerializerOptions);
}
public override ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
=> new(new WorkflowSession(this._workflow, serializedState, this._executionEnvironment, this._checkpointManager, this._includeExceptionDetails, this._includeWorkflowOutputsInResponse, jsonSerializerOptions));
private async ValueTask<WorkflowSession> UpdateSessionAsync(IEnumerable<ChatMessage> messages, AgentSession? session = null, CancellationToken cancellationToken = default)
@@ -302,7 +302,7 @@ public sealed partial class ChatClientAgent : AIAgent
: this.ChatClient.GetService(serviceType, serviceKey));
/// <inheritdoc/>
public override async ValueTask<AgentSession> CreateSessionAsync(CancellationToken cancellationToken = default)
protected override async ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
{
ChatHistoryProvider? chatHistoryProvider = this._agentOptions?.ChatHistoryProviderFactory is not null
? await this._agentOptions.ChatHistoryProviderFactory.Invoke(new() { SerializedState = default, JsonSerializerOptions = null }, cancellationToken).ConfigureAwait(false)
@@ -386,7 +386,7 @@ public sealed partial class ChatClientAgent : AIAgent
}
/// <inheritdoc/>
public override JsonElement SerializeSession(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
protected override JsonElement SerializeSessionCore(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null)
{
_ = Throw.IfNull(session);
@@ -399,7 +399,7 @@ public sealed partial class ChatClientAgent : AIAgent
}
/// <inheritdoc/>
public override async ValueTask<AgentSession> DeserializeSessionAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
protected override async ValueTask<AgentSession> DeserializeSessionCoreAsync(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
{
Func<JsonElement, JsonSerializerOptions?, CancellationToken, ValueTask<ChatHistoryProvider>>? chatHistoryProviderFactory = this._agentOptions?.ChatHistoryProviderFactory is null ?
null :