diff --git a/app/MindWork AI Studio/Assistants/BeyondFactsLab/BflChat.razor b/app/MindWork AI Studio/Assistants/BeyondFactsLab/BflChat.razor index 8c218f8a8..684099f13 100644 --- a/app/MindWork AI Studio/Assistants/BeyondFactsLab/BflChat.razor +++ b/app/MindWork AI Studio/Assistants/BeyondFactsLab/BflChat.razor @@ -16,4 +16,5 @@ AllowsAttachments="false" ShowsContextWindow="true" SystemPromptForCountAsync="@this.systemPromptForCountAsync" - ContextVersion="@this.ContextVersion"/> \ No newline at end of file + ContextVersion="@this.ContextVersion" + QuickMessage="@this.QuickMessage"/> \ No newline at end of file diff --git a/app/MindWork AI Studio/Assistants/BeyondFactsLab/BflChat.razor.cs b/app/MindWork AI Studio/Assistants/BeyondFactsLab/BflChat.razor.cs index 5ae181552..724a6d8b5 100644 --- a/app/MindWork AI Studio/Assistants/BeyondFactsLab/BflChat.razor.cs +++ b/app/MindWork AI Studio/Assistants/BeyondFactsLab/BflChat.razor.cs @@ -109,6 +109,14 @@ public partial class BflChat : MSGComponentBase _ => TA("Delete this conversation and start it over?") }; + /// + /// Gets the button with which the person ends an exercise of the step "Test situation" and asks for its evaluation, + /// see Prompts/steps/06-test.md; the other chats have none. + /// + private EmbeddedChatQuickMessage? QuickMessage => this.Location is BflStepThreadLocation { Step: BflStep.TEST_SITUATION } + ? new(T("End and evaluate the exercise"), AppIcons.END_EXERCISE, T("I would like to end the exercise now and get its evaluation."), TA("This button ends the exercise, and the lab evaluates it. While the lab simulates people, it gives feedback only then; write 'Pause' to get brief feedback in between.")) + : null; + /// protected override async Task OnInitializedAsync() { diff --git a/app/MindWork AI Studio/Assistants/BeyondFactsLab/Prompts/steps/06-test.md b/app/MindWork AI Studio/Assistants/BeyondFactsLab/Prompts/steps/06-test.md index 257adb059..ac8466ed7 100644 --- a/app/MindWork AI Studio/Assistants/BeyondFactsLab/Prompts/steps/06-test.md +++ b/app/MindWork AI Studio/Assistants/BeyondFactsLab/Prompts/steps/06-test.md @@ -31,6 +31,10 @@ Nutze für die Fragen, was die Person über ihre Kernthesen, Unsicherheiten und Mit generischen Gegenübern: Erkläre in zwei Sätzen den Ablauf und nenne Art und Schwierigkeit, mit denen du beginnst. Stelle dann die erste kritische Frage. +### Übung beenden + +Unter dem Eingabefeld hat die Person einen Button, der die Übung beendet: Er sendet in der Sprache der App die Bitte, die Übung zu beenden und auszuwerten. Diese Bitte, oder dieselbe in eigenen Worten, beendet die Übung; folge dann „Nach einer Übung“. + ### Nach einer Übung Hat die Person einige Runden gespielt oder möchte sie aufhören, frage: „Ändert sich Ihre Position, ihre Begründung oder nur ihre Darstellung?“ und „Was hat Ihre Reaktion geprägt, und möchten Sie an Ihrer Antwort oder Position etwas ändern?“ Weise darauf hin, dass sie zu „Werte und Perspektiven“ zurückkehren kann. diff --git a/app/MindWork AI Studio/Chat/Embedded/EmbeddedChatQuickMessage.cs b/app/MindWork AI Studio/Chat/Embedded/EmbeddedChatQuickMessage.cs new file mode 100644 index 000000000..01115dd41 --- /dev/null +++ b/app/MindWork AI Studio/Chat/Embedded/EmbeddedChatQuickMessage.cs @@ -0,0 +1,16 @@ +namespace AIStudio.Chat.Embedded; + +/// +/// A message which the user of an embedded chat sends with one click, such as asking for the evaluation at the end of +/// an exercise. +/// +/// +/// The message goes into the chat as the user's own and stays visible there, so that the conversation reads the same +/// whether the user clicked or typed it. It is written in the language of the app; the model reads it like any other +/// message, so its prompt names what the message asks for, not its words. +/// +/// The label of the button. +/// The icon of the button. +/// The message the button sends. +/// What the chat says beside the button, such as when to use it; empty for nothing. +public sealed record EmbeddedChatQuickMessage(string Label, string Icon, string Message, string Hint); \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/EmbeddedChat.razor b/app/MindWork AI Studio/Components/EmbeddedChat.razor index 87bbbff41..daee3a582 100644 --- a/app/MindWork AI Studio/Components/EmbeddedChat.razor +++ b/app/MindWork AI Studio/Components/EmbeddedChat.razor @@ -82,6 +82,10 @@ else { } + @if (this.QuickMessage is { } quickMessage && this.conversation.HasAnswer) + { + @quickMessage.Label + } @if (this.IsRunning) { @T("Stop") @@ -91,5 +95,9 @@ else @T("Start over") } + @if (this.QuickMessage is { Hint.Length: > 0 } hintedMessage && this.conversation.HasAnswer) + { + @hintedMessage.Hint + } } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/EmbeddedChat.razor.cs b/app/MindWork AI Studio/Components/EmbeddedChat.razor.cs index c916ab9ee..7b22ec0c9 100644 --- a/app/MindWork AI Studio/Components/EmbeddedChat.razor.cs +++ b/app/MindWork AI Studio/Components/EmbeddedChat.razor.cs @@ -171,6 +171,13 @@ public partial class EmbeddedChat : MSGComponentBase [Parameter] public string ExportFileName { get; set; } = string.Empty; + /// + /// Gets or sets a message the user sends with one click once the model has answered, such as asking for the + /// evaluation at the end of an exercise; null for none. + /// + [Parameter] + public EmbeddedChatQuickMessage? QuickMessage { get; set; } + [Inject] private EmbeddedChatService ChatService { get; init; } = null!; @@ -425,6 +432,18 @@ public partial class EmbeddedChat : MSGComponentBase await this.ChatService.SendAsync(this.ChatId, this.RequestOf(message)); } + /// + /// Sends the message of the button, see QuickMessage, as if the user had written it. What the user typed meanwhile + /// stays in the field. + /// + private async Task SendQuickMessageAsync() + { + if (this.QuickMessage is not { } quickMessage || !this.CanSend) + return; + + await this.ChatService.SendAsync(this.ChatId, this.RequestOf(new ContentText { Text = quickMessage.Message })); + } + /// /// Lets the model open the conversation, see StartRequest. /// diff --git a/app/MindWork AI Studio/Tools/AppIcons.cs b/app/MindWork AI Studio/Tools/AppIcons.cs index 3273f5580..61b3174e9 100644 --- a/app/MindWork AI Studio/Tools/AppIcons.cs +++ b/app/MindWork AI Studio/Tools/AppIcons.cs @@ -413,6 +413,9 @@ public static class AppIcons /// Enables an item, such as an installed assistant plugin, or shows that it is enabled. public const string ENABLE = LucideIcons.TOGGLE_RIGHT; + /// Ends an exercise and asks for its evaluation, such as in the test situation of the BeyondFacts Lab. + public const string END_EXERCISE = LucideIcons.FLAG; + /// Stands for encrypting a connection, such as the transport security of a mailbox. public const string ENCRYPTION = LucideIcons.LOCK_KEYHOLE; diff --git a/app/Tests/BeyondFactsLab/BflSimulationPromptTests.cs b/app/Tests/BeyondFactsLab/BflSimulationPromptTests.cs index 78d874744..84884d459 100644 --- a/app/Tests/BeyondFactsLab/BflSimulationPromptTests.cs +++ b/app/Tests/BeyondFactsLab/BflSimulationPromptTests.cs @@ -140,6 +140,19 @@ public sealed class BflSimulationPromptTests } } + [Test] + public async Task ThePromptOfTheTestSituationTellsHowTheButtonEndsAnExercise() + { + var step = (await BflPromptLibrary.ReadAsync(BflPromptLibrary.StepPath(BflStep.TEST_SITUATION))).ReplaceLineEndings("\n"); + + using (Assert.EnterMultipleScope()) + { + Assert.That(step, Does.Contain("\n### Übung beenden\n")); + Assert.That(step, Does.Contain("folge dann „Nach einer Übung“")); + Assert.That(step, Does.Contain("\n### Nach einer Übung\n")); + } + } + [Test] public async Task TheServiceLoadsWhomOnlyTheTestSituationPlays() {