mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Merge main into feat/durable_task and resolve conflicts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -67,7 +67,7 @@ internal class AgentEntity(IServiceProvider services, CancellationToken cancella
|
||||
// Start the agent response stream
|
||||
IAsyncEnumerable<AgentResponseUpdate> responseStream = agentWrapper.RunStreamingAsync(
|
||||
this.State.Data.ConversationHistory.SelectMany(e => e.Messages).Select(m => m.ToChatMessage()),
|
||||
await agentWrapper.GetNewSessionAsync(cancellationToken).ConfigureAwait(false),
|
||||
await agentWrapper.CreateSessionAsync(cancellationToken).ConfigureAwait(false),
|
||||
options: null,
|
||||
this._cancellationToken);
|
||||
|
||||
|
||||
@@ -4,6 +4,25 @@
|
||||
|
||||
- Added support for durable workflows ([#4436](https://github.com/microsoft/agent-framework/pull/4436))
|
||||
|
||||
## v1.0.0-preview.260219.1
|
||||
|
||||
- [BREAKING] Changed ChatHistory and AIContext Providers to have pipeline semantics ([#3806](https://github.com/microsoft/agent-framework/pull/3806))
|
||||
- Marked all `RunAsync<T>` overloads as `new`, added missing ones, and added support for primitives and arrays ([#3803](https://github.com/microsoft/agent-framework/pull/3803))
|
||||
- Improve session cast error message quality and consistency ([#3973](https://github.com/microsoft/agent-framework/pull/3973))
|
||||
|
||||
## v1.0.0-preview.260212.1
|
||||
|
||||
- [BREAKING] Changed AIAgent.SerializeSession to AIAgent.SerializeSessionAsync ([#3879](https://github.com/microsoft/agent-framework/pull/3879))
|
||||
|
||||
## v1.0.0-preview.260209.1
|
||||
|
||||
- [BREAKING] Introduce Core method pattern for Session management methods on AIAgent ([#3699](https://github.com/microsoft/agent-framework/pull/3699))
|
||||
|
||||
## v1.0.0-preview.260205.1
|
||||
|
||||
- [BREAKING] Moved AgentSession.Serialize to AIAgent.SerializeSession ([#3650](https://github.com/microsoft/agent-framework/pull/3650))
|
||||
- [BREAKING] Renamed serializedSession parameter to serializedState on DeserializeSessionAsync for consistency ([#3681](https://github.com/microsoft/agent-framework/pull/3681))
|
||||
|
||||
## v1.0.0-preview.260127.1
|
||||
|
||||
- [BREAKING] Renamed AgentThread to AgentSession ([#3430](https://github.com/microsoft/agent-framework/pull/3430))
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
using Microsoft.DurableTask;
|
||||
using Microsoft.DurableTask.Entities;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace Microsoft.Agents.AI.DurableTask;
|
||||
|
||||
@@ -34,24 +33,46 @@ 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> GetNewSessionAsync(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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes an agent session to JSON.
|
||||
/// </summary>
|
||||
/// <param name="session">The session to serialize.</param>
|
||||
/// <param name="jsonSerializerOptions">Optional JSON serializer options.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A <see cref="JsonElement"/> containing the serialized session state.</returns>
|
||||
protected override ValueTask<JsonElement> SerializeSessionCoreAsync(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (session is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(session));
|
||||
}
|
||||
|
||||
if (session is not DurableAgentSession durableSession)
|
||||
{
|
||||
throw new InvalidOperationException($"The provided session type '{session.GetType().Name}' is not compatible with this agent. Only sessions of type '{nameof(DurableAgentSession)}' can be serialized by this agent.");
|
||||
}
|
||||
|
||||
return new(durableSession.Serialize(jsonSerializerOptions));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes an agent session from JSON.
|
||||
/// </summary>
|
||||
/// <param name="serializedSession">The serialized session data.</param>
|
||||
/// <param name="serializedState">The serialized session data.</param>
|
||||
/// <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(
|
||||
JsonElement serializedSession,
|
||||
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(
|
||||
JsonElement serializedState,
|
||||
JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return ValueTask.FromResult<AgentSession>(DurableAgentSession.Deserialize(serializedSession, jsonSerializerOptions));
|
||||
return ValueTask.FromResult<AgentSession>(DurableAgentSession.Deserialize(serializedState, jsonSerializerOptions));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -76,12 +97,12 @@ public sealed class DurableAIAgent : AIAgent
|
||||
throw new NotSupportedException("Cancellation is not supported for durable agents.");
|
||||
}
|
||||
|
||||
session ??= await this.GetNewSessionAsync(cancellationToken).ConfigureAwait(false);
|
||||
session ??= await this.CreateSessionAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (session is not DurableAgentSession durableSession)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"The provided session is not valid for a durable agent. " +
|
||||
"Create a new session using GetNewSessionAsync or provide a session previously created by this agent.",
|
||||
"Create a new session using CreateSessionAsync or provide a session previously created by this agent.",
|
||||
paramName: nameof(session));
|
||||
}
|
||||
|
||||
@@ -92,7 +113,6 @@ public sealed class DurableAIAgent : AIAgent
|
||||
{
|
||||
enableToolCalls = durableOptions.EnableToolCalls;
|
||||
enableToolNames = durableOptions.EnableToolNames;
|
||||
responseFormat = durableOptions.ResponseFormat;
|
||||
}
|
||||
else if (options is ChatClientAgentRunOptions chatClientOptions && chatClientOptions.ChatOptions?.Tools != null)
|
||||
{
|
||||
@@ -100,6 +120,12 @@ public sealed class DurableAIAgent : AIAgent
|
||||
responseFormat = chatClientOptions.ChatOptions?.ResponseFormat;
|
||||
}
|
||||
|
||||
// Override the response format if specified in the agent run options
|
||||
if (options?.ResponseFormat is { } format)
|
||||
{
|
||||
responseFormat = format;
|
||||
}
|
||||
|
||||
RunRequest request = new([.. messages], responseFormat, enableToolCalls, enableToolNames)
|
||||
{
|
||||
OrchestrationId = this._context.InstanceId
|
||||
@@ -146,108 +172,125 @@ public sealed class DurableAIAgent : AIAgent
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the agent with a message and returns the deserialized output as an instance of <typeparamref name="T"/>.
|
||||
/// Run the agent with no message assuming that all required instructions are already provided to the agent or on the session, and requesting a response of the specified type <typeparamref name="T"/>.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to send to the agent.</param>
|
||||
/// <param name="session">The agent session to use.</param>
|
||||
/// <param name="serializerOptions">Optional JSON serializer options.</param>
|
||||
/// <param name="options">Optional run options.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <typeparam name="T">The type of the output.</typeparam>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when the provided <paramref name="options"/> already contains a response schema.
|
||||
/// Thrown when the provided <paramref name="options"/> is not a <see cref="DurableAgentRunOptions"/>.
|
||||
/// </exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown when the agent response is empty or cannot be deserialized.
|
||||
/// </exception>
|
||||
/// <returns>The output from the agent.</returns>
|
||||
public async Task<AgentResponse<T>> RunAsync<T>(
|
||||
/// <typeparam name="T">The type of structured output to request.</typeparam>
|
||||
/// <param name="session">
|
||||
/// The conversation session to use for this invocation. If <see langword="null"/>, a new session will be created.
|
||||
/// The session will be updated with any response messages generated during invocation.
|
||||
/// </param>
|
||||
/// <param name="serializerOptions">Optional JSON serializer options to use for deserializing the response.</param>
|
||||
/// <param name="options">Optional configuration parameters for controlling the agent's invocation behavior.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains an <see cref="AgentResponse{T}"/> with the agent's output.</returns>
|
||||
/// <remarks>
|
||||
/// This method is specific to durable agents because the Durable Task Framework uses a custom
|
||||
/// synchronization context for orchestration execution, and all continuations must run on the
|
||||
/// orchestration thread to avoid breaking the durable orchestration and potential deadlocks.
|
||||
/// </remarks>
|
||||
public new Task<AgentResponse<T>> RunAsync<T>(
|
||||
AgentSession? session = null,
|
||||
JsonSerializerOptions? serializerOptions = null,
|
||||
AgentRunOptions? options = null,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
this.RunAsync<T>([], session, serializerOptions, options, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Runs the agent with a text message from the user, requesting a response of the specified type <typeparamref name="T"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of structured output to request.</typeparam>
|
||||
/// <param name="message">The user message to send to the agent.</param>
|
||||
/// <param name="session">
|
||||
/// The conversation session to use for this invocation. If <see langword="null"/>, a new session will be created.
|
||||
/// The session will be updated with the input message and any response messages generated during invocation.
|
||||
/// </param>
|
||||
/// <param name="serializerOptions">Optional JSON serializer options to use for deserializing the response.</param>
|
||||
/// <param name="options">Optional configuration parameters for controlling the agent's invocation behavior.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains an <see cref="AgentResponse{T}"/> with the agent's output.</returns>
|
||||
/// <exception cref="ArgumentException"><paramref name="message"/> is <see langword="null"/>, empty, or contains only whitespace.</exception>
|
||||
/// <remarks>
|
||||
/// <inheritdoc cref="RunAsync{T}(AgentSession?, JsonSerializerOptions?, AgentRunOptions?, CancellationToken)" path="/remarks" />
|
||||
/// </remarks>
|
||||
public new Task<AgentResponse<T>> RunAsync<T>(
|
||||
string message,
|
||||
AgentSession? session = null,
|
||||
JsonSerializerOptions? serializerOptions = null,
|
||||
AgentRunOptions? options = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await this.RunAsync<T>(
|
||||
messages: [new ChatMessage(ChatRole.User, message) { CreatedAt = DateTimeOffset.UtcNow }],
|
||||
session,
|
||||
serializerOptions,
|
||||
options,
|
||||
cancellationToken);
|
||||
_ = Throw.IfNull(message);
|
||||
|
||||
return this.RunAsync<T>(new ChatMessage(ChatRole.User, message), session, serializerOptions, options, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the agent with messages and returns the deserialized output as an instance of <typeparamref name="T"/>.
|
||||
/// Runs the agent with a single chat message, requesting a response of the specified type <typeparamref name="T"/>.
|
||||
/// </summary>
|
||||
/// <param name="messages">The messages to send to the agent.</param>
|
||||
/// <param name="session">The agent session to use.</param>
|
||||
/// <param name="serializerOptions">Optional JSON serializer options.</param>
|
||||
/// <param name="options">Optional run options.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <typeparam name="T">The type of the output.</typeparam>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when the provided <paramref name="options"/> already contains a response schema.
|
||||
/// Thrown when the provided <paramref name="options"/> is not a <see cref="DurableAgentRunOptions"/>.
|
||||
/// </exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown when the agent response is empty or cannot be deserialized.
|
||||
/// </exception>
|
||||
/// <returns>The output from the agent.</returns>
|
||||
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Fallback to reflection-based deserialization is intentional for library flexibility with user-defined types.")]
|
||||
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050", Justification = "Fallback to reflection-based deserialization is intentional for library flexibility with user-defined types.")]
|
||||
public async Task<AgentResponse<T>> RunAsync<T>(
|
||||
/// <typeparam name="T">The type of structured output to request.</typeparam>
|
||||
/// <param name="message">The chat message to send to the agent.</param>
|
||||
/// <param name="session">
|
||||
/// The conversation session to use for this invocation. If <see langword="null"/>, a new session will be created.
|
||||
/// The session will be updated with the input message and any response messages generated during invocation.
|
||||
/// </param>
|
||||
/// <param name="serializerOptions">Optional JSON serializer options to use for deserializing the response.</param>
|
||||
/// <param name="options">Optional configuration parameters for controlling the agent's invocation behavior.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains an <see cref="AgentResponse{T}"/> with the agent's output.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="message"/> is <see langword="null"/>.</exception>
|
||||
/// <remarks>
|
||||
/// <inheritdoc cref="RunAsync{T}(AgentSession?, JsonSerializerOptions?, AgentRunOptions?, CancellationToken)" path="/remarks" />
|
||||
/// </remarks>
|
||||
public new Task<AgentResponse<T>> RunAsync<T>(
|
||||
ChatMessage message,
|
||||
AgentSession? session = null,
|
||||
JsonSerializerOptions? serializerOptions = null,
|
||||
AgentRunOptions? options = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
_ = Throw.IfNull(message);
|
||||
|
||||
return this.RunAsync<T>([message], session, serializerOptions, options, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the agent with a collection of chat messages, requesting a response of the specified type <typeparamref name="T"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of structured output to request.</typeparam>
|
||||
/// <param name="messages">The collection of messages to send to the agent for processing.</param>
|
||||
/// <param name="session">
|
||||
/// The conversation session to use for this invocation. If <see langword="null"/>, a new session will be created.
|
||||
/// The session will be updated with the input messages and any response messages generated during invocation.
|
||||
/// </param>
|
||||
/// <param name="serializerOptions">Optional JSON serializer options to use for deserializing the response.</param>
|
||||
/// <param name="options">Optional configuration parameters for controlling the agent's invocation behavior.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains an <see cref="AgentResponse{T}"/> with the agent's output.</returns>
|
||||
/// <remarks>
|
||||
/// <inheritdoc cref="RunAsync{T}(AgentSession?, JsonSerializerOptions?, AgentRunOptions?, CancellationToken)" path="/remarks" />
|
||||
/// </remarks>
|
||||
public new async Task<AgentResponse<T>> RunAsync<T>(
|
||||
IEnumerable<ChatMessage> messages,
|
||||
AgentSession? session = null,
|
||||
JsonSerializerOptions? serializerOptions = null,
|
||||
AgentRunOptions? options = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
options ??= new DurableAgentRunOptions();
|
||||
if (options is not DurableAgentRunOptions durableOptions)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Response schema is only supported with DurableAgentRunOptions when using durable agents. " +
|
||||
"Cannot specify a response schema when calling RunAsync<T>.",
|
||||
paramName: nameof(options));
|
||||
}
|
||||
serializerOptions ??= AgentAbstractionsJsonUtilities.DefaultOptions;
|
||||
|
||||
if (durableOptions.ResponseFormat is not null)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"A response schema is already defined in the provided DurableAgentRunOptions. " +
|
||||
"Cannot specify a response schema when calling RunAsync<T>.",
|
||||
paramName: nameof(options));
|
||||
}
|
||||
var responseFormat = ChatResponseFormat.ForJsonSchema<T>(serializerOptions);
|
||||
|
||||
// Create the JSON schema for the response type
|
||||
durableOptions.ResponseFormat = ChatResponseFormat.ForJsonSchema<T>();
|
||||
(responseFormat, bool isWrappedInObject) = StructuredOutputSchemaUtilities.WrapNonObjectSchema(responseFormat);
|
||||
|
||||
AgentResponse response = await this.RunAsync(messages, session, durableOptions, cancellationToken);
|
||||
options = options?.Clone() ?? new DurableAgentRunOptions();
|
||||
options.ResponseFormat = responseFormat;
|
||||
|
||||
// Deserialize the response text to the requested type
|
||||
if (string.IsNullOrEmpty(response.Text))
|
||||
{
|
||||
throw new InvalidOperationException("Agent response is empty and cannot be deserialized.");
|
||||
}
|
||||
// ConfigureAwait(false) cannot be used here because the Durable Task Framework uses
|
||||
// a custom synchronization context that requires all continuations to execute on the
|
||||
// orchestration thread. Scheduling the continuation on an arbitrary thread would break
|
||||
// the orchestration.
|
||||
AgentResponse response = await this.RunAsync(messages, session, options, cancellationToken);
|
||||
|
||||
serializerOptions ??= DurableAgentJsonUtilities.DefaultOptions;
|
||||
|
||||
// Prefer source-generated metadata when available to support AOT/trimming scenarios.
|
||||
// Fallback to reflection-based deserialization for types without source-generated metadata.
|
||||
// This is necessary since T is a user-provided type that may not have [JsonSerializable] coverage.
|
||||
JsonTypeInfo? typeInfo = serializerOptions.GetTypeInfo(typeof(T));
|
||||
T? result = (typeInfo is JsonTypeInfo typedInfo
|
||||
? (T?)JsonSerializer.Deserialize(response.Text, typedInfo)
|
||||
: JsonSerializer.Deserialize<T>(response.Text, serializerOptions))
|
||||
?? throw new InvalidOperationException($"Failed to deserialize agent response to type {typeof(T).Name}.");
|
||||
|
||||
return new DurableAIAgentResponse<T>(response, result);
|
||||
}
|
||||
|
||||
private sealed class DurableAIAgentResponse<T>(AgentResponse response, T result)
|
||||
: AgentResponse<T>(response.AsChatResponse())
|
||||
{
|
||||
public override T Result { get; } = result;
|
||||
return new AgentResponse<T>(response, serializerOptions) { IsWrappedInObject = isWrappedInObject };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,14 +11,29 @@ internal class DurableAIAgentProxy(string name, IDurableAgentClient agentClient)
|
||||
|
||||
public override string? Name { get; } = name;
|
||||
|
||||
public override ValueTask<AgentSession> DeserializeSessionAsync(
|
||||
JsonElement serializedSession,
|
||||
JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
|
||||
protected override ValueTask<JsonElement> SerializeSessionCoreAsync(AgentSession session, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return ValueTask.FromResult<AgentSession>(DurableAgentSession.Deserialize(serializedSession, jsonSerializerOptions));
|
||||
if (session is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(session));
|
||||
}
|
||||
|
||||
if (session is not DurableAgentSession durableSession)
|
||||
{
|
||||
throw new InvalidOperationException($"The provided session type '{session.GetType().Name}' is not compatible with this agent. Only sessions of type '{nameof(DurableAgentSession)}' can be serialized by this agent.");
|
||||
}
|
||||
|
||||
return new(durableSession.Serialize(jsonSerializerOptions));
|
||||
}
|
||||
|
||||
public override ValueTask<AgentSession> GetNewSessionAsync(CancellationToken cancellationToken = default)
|
||||
protected override ValueTask<AgentSession> DeserializeSessionCoreAsync(
|
||||
JsonElement serializedState,
|
||||
JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return ValueTask.FromResult<AgentSession>(DurableAgentSession.Deserialize(serializedState, jsonSerializerOptions));
|
||||
}
|
||||
|
||||
protected override ValueTask<AgentSession> CreateSessionCoreAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return ValueTask.FromResult<AgentSession>(new DurableAgentSession(AgentSessionId.WithRandomKey(this.Name!)));
|
||||
}
|
||||
@@ -29,12 +44,12 @@ internal class DurableAIAgentProxy(string name, IDurableAgentClient agentClient)
|
||||
AgentRunOptions? options = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
session ??= await this.GetNewSessionAsync(cancellationToken).ConfigureAwait(false);
|
||||
session ??= await this.CreateSessionAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (session is not DurableAgentSession durableSession)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"The provided session is not valid for a durable agent. " +
|
||||
"Create a new session using GetNewSession or provide a session previously created by this agent.",
|
||||
"Create a new session using CreateSessionAsync or provide a session previously created by this agent.",
|
||||
paramName: nameof(session));
|
||||
}
|
||||
|
||||
@@ -47,7 +62,6 @@ internal class DurableAIAgentProxy(string name, IDurableAgentClient agentClient)
|
||||
{
|
||||
enableToolCalls = durableOptions.EnableToolCalls;
|
||||
enableToolNames = durableOptions.EnableToolNames;
|
||||
responseFormat = durableOptions.ResponseFormat;
|
||||
isFireAndForget = durableOptions.IsFireAndForget;
|
||||
}
|
||||
else if (options is ChatClientAgentRunOptions chatClientOptions)
|
||||
@@ -56,6 +70,12 @@ internal class DurableAIAgentProxy(string name, IDurableAgentClient agentClient)
|
||||
responseFormat = chatClientOptions.ChatOptions?.ResponseFormat;
|
||||
}
|
||||
|
||||
// Override the response format if specified in the agent run options
|
||||
if (options?.ResponseFormat is { } format)
|
||||
{
|
||||
responseFormat = format;
|
||||
}
|
||||
|
||||
RunRequest request = new([.. messages], responseFormat, enableToolCalls, enableToolNames);
|
||||
AgentSessionId sessionId = durableSession.SessionId;
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace Microsoft.Agents.AI.DurableTask;
|
||||
|
||||
/// <summary>
|
||||
@@ -9,6 +7,25 @@ namespace Microsoft.Agents.AI.DurableTask;
|
||||
/// </summary>
|
||||
public sealed class DurableAgentRunOptions : AgentRunOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DurableAgentRunOptions"/> class.
|
||||
/// </summary>
|
||||
public DurableAgentRunOptions()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DurableAgentRunOptions"/> class by copying values from the specified options.
|
||||
/// </summary>
|
||||
/// <param name="options">The options instance from which to copy values.</param>
|
||||
private DurableAgentRunOptions(DurableAgentRunOptions options)
|
||||
: base(options)
|
||||
{
|
||||
this.EnableToolCalls = options.EnableToolCalls;
|
||||
this.EnableToolNames = options.EnableToolNames is not null ? new List<string>(options.EnableToolNames) : null;
|
||||
this.IsFireAndForget = options.IsFireAndForget;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to enable tool calls for this request.
|
||||
/// </summary>
|
||||
@@ -19,11 +36,6 @@ public sealed class DurableAgentRunOptions : AgentRunOptions
|
||||
/// </summary>
|
||||
public IList<string>? EnableToolNames { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the response format for the agent's response.
|
||||
/// </summary>
|
||||
public ChatResponseFormat? ResponseFormat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to fire and forget the agent run request.
|
||||
/// </summary>
|
||||
@@ -33,4 +45,7 @@ public sealed class DurableAgentRunOptions : AgentRunOptions
|
||||
/// long-running tasks where the caller does not need to wait for the agent to complete the run.
|
||||
/// </remarks>
|
||||
public bool IsFireAndForget { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override AgentRunOptions Clone() => new DurableAgentRunOptions(this);
|
||||
}
|
||||
|
||||
@@ -7,17 +7,22 @@ using System.Text.Json.Serialization;
|
||||
namespace Microsoft.Agents.AI.DurableTask;
|
||||
|
||||
/// <summary>
|
||||
/// An agent thread implementation for durable agents.
|
||||
/// An <see cref="AgentSession"/> implementation for durable agents.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{SessionId}")]
|
||||
[DebuggerDisplay("{DebuggerDisplay,nq}")]
|
||||
public sealed class DurableAgentSession : AgentSession
|
||||
{
|
||||
[JsonConstructor]
|
||||
internal DurableAgentSession(AgentSessionId sessionId)
|
||||
{
|
||||
this.SessionId = sessionId;
|
||||
}
|
||||
|
||||
[JsonConstructor]
|
||||
internal DurableAgentSession(AgentSessionId sessionId, AgentSessionStateBag stateBag) : base(stateBag)
|
||||
{
|
||||
this.SessionId = sessionId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the agent session ID.
|
||||
/// </summary>
|
||||
@@ -26,11 +31,10 @@ public sealed class DurableAgentSession : AgentSession
|
||||
internal AgentSessionId SessionId { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null)
|
||||
internal JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null)
|
||||
{
|
||||
return JsonSerializer.SerializeToElement(
|
||||
this,
|
||||
DurableAgentJsonUtilities.DefaultOptions.GetTypeInfo(typeof(DurableAgentSession)));
|
||||
var jso = jsonSerializerOptions ?? DurableAgentJsonUtilities.DefaultOptions;
|
||||
return JsonSerializer.SerializeToElement(this, jso.GetTypeInfo(typeof(DurableAgentSession)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -49,7 +53,11 @@ public sealed class DurableAgentSession : AgentSession
|
||||
|
||||
string sessionIdString = sessionIdElement.GetString() ?? throw new JsonException("sessionId property is null.");
|
||||
AgentSessionId sessionId = AgentSessionId.Parse(sessionIdString);
|
||||
return new DurableAgentSession(sessionId);
|
||||
AgentSessionStateBag stateBag = serializedSession.TryGetProperty("stateBag", out JsonElement stateBagElement)
|
||||
? AgentSessionStateBag.Deserialize(stateBagElement)
|
||||
: new AgentSessionStateBag();
|
||||
|
||||
return new DurableAgentSession(sessionId, stateBag);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -68,4 +76,8 @@ public sealed class DurableAgentSession : AgentSession
|
||||
{
|
||||
return this.SessionId.ToString();
|
||||
}
|
||||
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
||||
private string DebuggerDisplay =>
|
||||
$"SessionId = {this.SessionId}, StateBag Count = {this.StateBag.Count}";
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
<TargetFrameworks>$(TargetFrameworksCore)</TargetFrameworks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<!-- CA2007: This rule should generally be suppressed in Durable Task libraries -->
|
||||
<!-- MEAI001: UserInputRequestContent is experimental but used in source-generated code for AgentResponse -->
|
||||
<NoWarn>$(NoWarn);CA2007;MEAI001</NoWarn>
|
||||
<NoWarn>$(NoWarn);CA2007</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
|
||||
@@ -17,6 +16,11 @@
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<InjectSharedThrow>true</InjectSharedThrow>
|
||||
<InjectSharedStructuredOutput>true</InjectSharedStructuredOutput>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Durable Task dependencies -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.DurableTask.Client" />
|
||||
|
||||
Reference in New Issue
Block a user