.NET: NET Workflows - Skip conversation initialization when identifier is provided (#1087)

* Fix

* Code-gen case

* Tests

* Autosend logic

* Build fix

* Namespace

* Validation enhancement
This commit is contained in:
Chris
2025-10-02 07:27:46 -07:00
committed by GitHub
Unverified
parent cb62407fb8
commit 08ea625de0
23 changed files with 102 additions and 31 deletions
@@ -1,7 +1,7 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
// Runtime Version: 17.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -19,7 +19,7 @@ namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "17.0.0.0")]
internal partial class CreateConversationTemplate : ActionTemplate
{
/// <summary>
@@ -59,8 +59,8 @@ namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
string conversationId = await agentProvider.CreateConversationAsync(cancellationToken).ConfigureAwait(false);");
AssignVariable(this.ConversationId, "conversationId");
this.Write("\n return default;\n }\n}\n");
this.Write("\n await context.AddEventAsync(new ConversationUpdateEvent(conversationId))" +
".ConfigureAwait(false);\n\n return default;\n }\n}\n");
return this.GenerationEnvironment.ToString();
}
@@ -10,8 +10,9 @@ internal sealed class <#= this.Name #>Executor(FormulaSession session, WorkflowA
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{
string conversationId = await agentProvider.CreateConversationAsync(cancellationToken).ConfigureAwait(false);<#
AssignVariable(this.ConversationId, "conversationId");
#>
AssignVariable(this.ConversationId, "conversationId");#>
await context.AddEventAsync(new ConversationUpdateEvent(conversationId)).ConfigureAwait(false);
return default;
}
}
@@ -69,7 +69,7 @@ public static class DeclarativeWorkflowBuilder
state.Initialize(workflowElement.WrapWithBot(), options.Configuration);
DeclarativeWorkflowExecutor<TInput> rootExecutor =
new(rootId,
options.AgentProvider,
options,
state,
message => inputTransform?.Invoke(message) ?? DefaultTransform(message));
@@ -12,7 +12,11 @@ public sealed class ConversationUpdateEvent : WorkflowEvent
/// </summary>
public string ConversationId { get; }
internal ConversationUpdateEvent(string conversationId)
/// <summary>
/// Initializes a new instance of <see cref="ConversationUpdateEvent"/>.
/// </summary>
/// <param name="conversationId">The identifier of the associated conversation.</param>
public ConversationUpdateEvent(string conversationId)
: base(conversationId)
{
this.ConversationId = conversationId;
@@ -41,7 +41,7 @@ internal static class AgentProviderExtensions
// Enable "autoSend" behavior if this is the workflow conversation.
bool isWorkflowConversation = context.IsWorkflowConversation(conversationId);
autoSend &= isWorkflowConversation;
autoSend |= isWorkflowConversation;
// Process the agent response updates.
List<AgentRunResponseUpdate> updates = [];
@@ -13,7 +13,7 @@ namespace Microsoft.Agents.AI.Workflows.Declarative.Interpreter;
/// </summary>
internal sealed class DeclarativeWorkflowExecutor<TInput>(
string workflowId,
WorkflowAgentProvider agentProvider,
DeclarativeWorkflowOptions options,
WorkflowFormulaState state,
Func<TInput, ChatMessage> inputTransform) :
Executor<TInput>(workflowId), IModeledAction where TInput : notnull
@@ -26,10 +26,14 @@ internal sealed class DeclarativeWorkflowExecutor<TInput>(
DeclarativeWorkflowContext declarativeContext = new(context, state);
ChatMessage input = inputTransform.Invoke(message);
string conversationId = await agentProvider.CreateConversationAsync(cancellationToken: default).ConfigureAwait(false);
string? conversationId = options.ConversationId;
if (string.IsNullOrWhiteSpace(conversationId))
{
conversationId = await options.AgentProvider.CreateConversationAsync(cancellationToken: default).ConfigureAwait(false);
}
await declarativeContext.QueueConversationUpdateAsync(conversationId).ConfigureAwait(false);
await agentProvider.CreateMessageAsync(conversationId, input, cancellationToken: default).ConfigureAwait(false);
await options.AgentProvider.CreateMessageAsync(conversationId, input, cancellationToken: default).ConfigureAwait(false);
await declarativeContext.SetLastMessageAsync(input).ConfigureAwait(false);
await context.SendResultMessageAsync(this.Id).ConfigureAwait(false);
@@ -23,6 +23,8 @@ public abstract class RootExecutor<TInput> : Executor<TInput> where TInput : not
private readonly WorkflowFormulaState _state;
private readonly Func<TInput, ChatMessage>? _inputTransform;
private string? _conversationId;
/// <summary>
/// Get the shared formula session to provide to workflow <see cref="ActionExecutor"/> instances.
/// </summary>
@@ -39,6 +41,7 @@ public abstract class RootExecutor<TInput> : Executor<TInput> where TInput : not
{
this._configuration = options.Configuration;
this._agentProvider = options.AgentProvider;
this._conversationId = options.ConversationId;
this._inputTransform = inputTransform;
this._state = new WorkflowFormulaState(options.CreateRecalcEngine());
this._state.InitializeSystem();
@@ -53,10 +56,13 @@ public abstract class RootExecutor<TInput> : Executor<TInput> where TInput : not
ChatMessage input = (this._inputTransform ?? DefaultInputTransform).Invoke(message);
string conversationId = await this._agentProvider.CreateConversationAsync(cancellationToken: default).ConfigureAwait(false);
await declarativeContext.QueueConversationUpdateAsync(conversationId).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(this._conversationId))
{
this._conversationId = await this._agentProvider.CreateConversationAsync(cancellationToken: default).ConfigureAwait(false);
}
await declarativeContext.QueueConversationUpdateAsync(this._conversationId).ConfigureAwait(false);
await this._agentProvider.CreateMessageAsync(conversationId, input, cancellationToken: default).ConfigureAwait(false);
await this._agentProvider.CreateMessageAsync(this._conversationId, input, cancellationToken: default).ConfigureAwait(false);
await declarativeContext.SetLastMessageAsync(input).ConfigureAwait(false);
await declarativeContext.SendMessageAsync(new ActionExecutorResult(this.Id)).ConfigureAwait(false);
@@ -2,6 +2,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
using Microsoft.Agents.AI.Workflows.Declarative.Interpreter;
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx;
using Microsoft.Bot.ObjectModel;
@@ -16,6 +17,7 @@ internal sealed class CreateConversationExecutor(CreateConversation model, Workf
{
string conversationId = await agentProvider.CreateConversationAsync(cancellationToken).ConfigureAwait(false);
await this.AssignAsync(this.Model.ConversationId?.Path, FormulaValue.New(conversationId), context).ConfigureAwait(false);
await context.QueueConversationUpdateAsync(conversationId).ConfigureAwait(false);
return default;
}