mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET Workflows - Declarative updated for Question action (#1532)
* Updated * Namespace
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.Events;
|
||||
|
||||
@@ -12,15 +13,24 @@ public sealed class InputResponse
|
||||
/// <summary>
|
||||
/// The response value.
|
||||
/// </summary>
|
||||
public string Value { get; }
|
||||
public ChatMessage Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InputResponse"/> class.
|
||||
/// </summary>
|
||||
/// <param name="value">The response value.</param>
|
||||
[JsonConstructor]
|
||||
public InputResponse(string value)
|
||||
public InputResponse(ChatMessage value)
|
||||
{
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InputResponse"/> class.
|
||||
/// </summary>
|
||||
/// <param name="value">The response value.</param>
|
||||
public InputResponse(string value)
|
||||
{
|
||||
this.Value = new ChatMessage(ChatRole.User, value);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-14
@@ -69,25 +69,20 @@ internal static class IWorkflowContextExtensions
|
||||
await context.AddEventAsync(new ConversationUpdateEvent(conversationId) { IsWorkflow = isExternal }, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static string? GetWorkflowConversation(this IWorkflowContext context) =>
|
||||
context.ReadState(SystemScope.Names.ConversationId, VariableScopeNames.System) switch
|
||||
{
|
||||
StringValue stringValue when stringValue.Value.Length > 0 => stringValue.Value,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
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}.");
|
||||
}
|
||||
workflowConversationId = context.GetWorkflowConversation();
|
||||
return workflowConversationId?.Equals(conversationId, StringComparison.Ordinal) ?? false;
|
||||
}
|
||||
|
||||
private static DeclarativeWorkflowContext DeclarativeContext(IWorkflowContext context)
|
||||
|
||||
+1
-1
@@ -237,7 +237,7 @@ internal sealed class WorkflowActionVisitor : DialogActionVisitor
|
||||
this.Trace(item);
|
||||
|
||||
// Entry point for question
|
||||
QuestionExecutor action = new(item, this._workflowState);
|
||||
QuestionExecutor action = new(item, this._workflowOptions.AgentProvider, this._workflowState);
|
||||
this.ContinueWith(action);
|
||||
// Transition to post action if complete
|
||||
string postId = Steps.Post(action.Id);
|
||||
|
||||
+24
-4
@@ -9,12 +9,13 @@ using Microsoft.Agents.AI.Workflows.Declarative.Interpreter;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Kit;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.PowerFx.Types;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.ObjectModel;
|
||||
|
||||
internal sealed class QuestionExecutor(Question model, WorkflowFormulaState state) :
|
||||
internal sealed class QuestionExecutor(Question model, WorkflowAgentProvider agentProvider, WorkflowFormulaState state) :
|
||||
DeclarativeActionExecutor<Question>(model, state)
|
||||
{
|
||||
public static class Steps
|
||||
@@ -82,21 +83,21 @@ internal sealed class QuestionExecutor(Question model, WorkflowFormulaState stat
|
||||
public async ValueTask CaptureResponseAsync(IWorkflowContext context, InputResponse message, CancellationToken cancellationToken)
|
||||
{
|
||||
FormulaValue? extractedValue = null;
|
||||
if (string.IsNullOrWhiteSpace(message.Value))
|
||||
if (message.Value is null)
|
||||
{
|
||||
string unrecognizedResponse = this.FormatPrompt(this.Model.UnrecognizedPrompt);
|
||||
await context.AddEventAsync(new MessageActivityEvent(unrecognizedResponse.Trim()), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EntityExtractionResult entityResult = EntityExtractor.Parse(this.Model.Entity, message.Value);
|
||||
EntityExtractionResult entityResult = EntityExtractor.Parse(this.Model.Entity, message.Value.Text);
|
||||
if (entityResult.IsValid)
|
||||
{
|
||||
extractedValue = entityResult.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
string invalidResponse = this.FormatPrompt(this.Model.InvalidPrompt);
|
||||
string invalidResponse = this.Model.InvalidPrompt is not null ? this.FormatPrompt(this.Model.InvalidPrompt) : "Invalid response";
|
||||
await context.AddEventAsync(new MessageActivityEvent(invalidResponse.Trim()), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -107,6 +108,25 @@ internal sealed class QuestionExecutor(Question model, WorkflowFormulaState stat
|
||||
}
|
||||
else
|
||||
{
|
||||
bool autoSend = true;
|
||||
|
||||
if (this.Model.ExtensionData?.Properties.TryGetValue("autoSend", out DataValue? autoSendValue) ?? false)
|
||||
{
|
||||
autoSend = autoSendValue.ToObject() is bool value && value;
|
||||
}
|
||||
|
||||
if (autoSend)
|
||||
{
|
||||
string? workflowConversationId = context.GetWorkflowConversation();
|
||||
if (workflowConversationId is not null)
|
||||
{
|
||||
// Input message always defined if values has been extracted.
|
||||
ChatMessage input = message.Value!;
|
||||
await agentProvider.CreateMessageAsync(workflowConversationId, input, cancellationToken).ConfigureAwait(false);
|
||||
await context.SetLastMessageAsync(input).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
await this.AssignAsync(this.Model.Variable?.Path, extractedValue, context).ConfigureAwait(false);
|
||||
await this._hasExecuted.WriteAsync(context, true).ConfigureAwait(false);
|
||||
await context.SendResultMessageAsync(this.Id, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
+10
-2
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Events;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.Events;
|
||||
@@ -11,9 +12,16 @@ namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.Events;
|
||||
public sealed class InputResponseTest(ITestOutputHelper output) : EventTest(output)
|
||||
{
|
||||
[Fact]
|
||||
public void VerifySerialization()
|
||||
public void VerifySerializationText()
|
||||
{
|
||||
InputResponse copy = VerifyEventSerialization(new InputResponse("test response"));
|
||||
Assert.Equal("test response", copy.Value);
|
||||
Assert.Equal("test response", copy.Value.Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifySerializationMessage()
|
||||
{
|
||||
InputResponse copy = VerifyEventSerialization(new InputResponse(new ChatMessage(ChatRole.User, "test response")));
|
||||
Assert.Equal("test response", copy.Value.Text);
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ trigger:
|
||||
- kind: Question
|
||||
id: question_confirm
|
||||
alwaysPrompt: false
|
||||
autoSend: false
|
||||
property: Local.ConfirmedInput
|
||||
prompt:
|
||||
kind: Message
|
||||
|
||||
Reference in New Issue
Block a user