.NET Workflows - Fix converation behaviors for declarative worfklows (#1237)

* Updated

* Passing

* Ready

* Update dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Workflows/ConversationMessages.yaml

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Comment

* Code analysis

* Unit-tests/provider signature

* Comment

* Consistent

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Chris
2025-10-06 22:04:41 +00:00
committed by GitHub
co-authored by Copilot
parent f1694b0507
commit f81b4a5abe
28 changed files with 202 additions and 138 deletions
@@ -40,7 +40,7 @@ internal static class AgentProviderExtensions
agent.RunStreamingAsync(null, options, cancellationToken);
// Enable "autoSend" behavior if this is the workflow conversation.
bool isWorkflowConversation = context.IsWorkflowConversation(conversationId);
bool isWorkflowConversation = context.IsWorkflowConversation(conversationId, out string? workflowConversationId);
autoSend |= isWorkflowConversation;
// Process the agent response updates.
@@ -64,7 +64,7 @@ internal static class AgentProviderExtensions
await context.AddEventAsync(new AgentRunResponseEvent(executorId, response)).ConfigureAwait(false);
}
if (autoSend && !isWorkflowConversation && conversationId is not null)
if (autoSend && !isWorkflowConversation && workflowConversationId is not null)
{
// Copy messages with content that aren't function calls or results.
IEnumerable<ChatMessage> messages =
@@ -75,7 +75,7 @@ internal static class AgentProviderExtensions
!message.Contents.OfType<FunctionResultContent>().Any());
foreach (ChatMessage message in messages)
{
await agentProvider.CreateMessageAsync(conversationId, message, cancellationToken).ConfigureAwait(false);
await agentProvider.CreateMessageAsync(workflowConversationId, message, cancellationToken).ConfigureAwait(false);
}
}
@@ -38,25 +38,39 @@ internal static class IWorkflowContextExtensions
public static FormulaValue ReadState(this IWorkflowContext context, string key, string? scopeName = null) =>
DeclarativeContext(context).State.Get(key, scopeName);
public static async ValueTask QueueConversationUpdateAsync(this IWorkflowContext context, string conversationId)
public static async ValueTask QueueConversationUpdateAsync(this IWorkflowContext context, string conversationId, bool isExternal = false)
{
RecordValue conversation = (RecordValue)context.ReadState(SystemScope.Names.Conversation, VariableScopeNames.System);
conversation.UpdateField("Id", FormulaValue.New(conversationId));
await context.QueueSystemUpdateAsync(SystemScope.Names.Conversation, conversation).ConfigureAwait(false);
await context.QueueSystemUpdateAsync(SystemScope.Names.ConversationId, FormulaValue.New(conversationId)).ConfigureAwait(false);
await context.AddEventAsync(new ConversationUpdateEvent(conversationId)).ConfigureAwait(false);
}
public static bool IsWorkflowConversation(this IWorkflowContext context, string? conversationId)
{
if (string.IsNullOrWhiteSpace(conversationId))
if (isExternal)
{
return false;
conversation.UpdateField("Id", FormulaValue.New(conversationId));
await context.QueueSystemUpdateAsync(SystemScope.Names.Conversation, conversation).ConfigureAwait(false);
await context.QueueSystemUpdateAsync(SystemScope.Names.ConversationId, FormulaValue.New(conversationId)).ConfigureAwait(false);
}
StringValue workflowId = (StringValue)context.ReadState(SystemScope.Names.ConversationId, VariableScopeNames.System);
return workflowId.Value.Equals(conversationId, StringComparison.Ordinal);
await context.AddEventAsync(new ConversationUpdateEvent(conversationId) { IsWorkflow = isExternal }).ConfigureAwait(false);
}
public static bool IsWorkflowConversation(
this IWorkflowContext context,
string? conversationId,
out string? workflowConversationId)
{
FormulaValue idValue = context.ReadState(SystemScope.Names.ConversationId, VariableScopeNames.System);
switch (idValue)
{
case BlankValue:
case ErrorValue:
workflowConversationId = null;
return false;
case StringValue stringValue when stringValue.Value.Length > 0:
workflowConversationId = stringValue.Value;
return workflowConversationId.Equals(conversationId, StringComparison.Ordinal);
default:
// Something has gone terribly wrong.
throw new DeclarativeActionException($"Invalid '{SystemScope.Names.ConversationId}' value type: {idValue.GetType().Name}.");
}
}
private static DeclarativeWorkflowContext DeclarativeContext(IWorkflowContext context)