mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-15 21:08:28 +00:00
Added choosing whom the test situation of the BeyondFacts Lab simulates
This commit is contained in:
6 files changed
+296
-3
No files matched your search
@@ -147,6 +147,15 @@
|
||||
{
|
||||
var step = this.activeStep;
|
||||
<BflStepForm Step="@step" Form="@form" FormChanged="@(() => this.SaveFormAsync(step))"/>
|
||||
@if (step is BflStep.TEST_SITUATION)
|
||||
{
|
||||
<BflSimulationChoice ProjectId="@project.ProjectId"
|
||||
Forms="@this.forms"
|
||||
Form="@form"
|
||||
FormChanged="@(() => this.SaveFormAsync(step))"
|
||||
Provider="@this.providerSettings"
|
||||
Class="mt-3"/>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -164,7 +173,7 @@
|
||||
ProviderIssue="@this.ValidateProvider(this.providerSettings)"/>
|
||||
}
|
||||
|
||||
<BflChat Location="@(new BflStepThreadLocation(project.ProjectId, this.activeStep))" Provider="@this.providerSettings" ProviderIssue="@this.ValidateProvider(this.providerSettings)"/>
|
||||
<BflChat Location="@(new BflStepThreadLocation(project.ProjectId, this.activeStep))" Provider="@this.providerSettings" ProviderIssue="@this.ValidateProvider(this.providerSettings)" DetailsVersion="@this.ChatDetailsVersion"/>
|
||||
|
||||
<BflWorkStatePanel Location="@(new BflStepThreadLocation(project.ProjectId, this.activeStep))"
|
||||
WorkStates="@this.workStates"
|
||||
|
||||
+8
@@ -23,6 +23,14 @@ public partial class BeyondFactsLabAssistant
|
||||
/// </summary>
|
||||
private int ActiveStepIndex => (int)this.activeStep;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a number which changes whenever the person chooses others to simulate in the step "Test situation", whose
|
||||
/// research the prompt of its conversation carries, so that the conversation counts its tokens anew.
|
||||
/// </summary>
|
||||
private int ChatDetailsVersion => this.activeStep is BflStep.TEST_SITUATION && this.forms.GetValueOrDefault(BflStep.TEST_SITUATION)?.Simulation is { } simulation
|
||||
? HashCode.Combine(simulation.Mode, string.Join('\n', simulation.People))
|
||||
: 0;
|
||||
|
||||
/// <summary>
|
||||
/// Shows a preparation with its forms, at the step the person looked at last.
|
||||
/// </summary>
|
||||
|
||||
@@ -43,6 +43,13 @@ public partial class BflChat : MSGComponentBase
|
||||
[Parameter]
|
||||
public string? ProviderIssue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a number which changes whenever a detail changes which takes much room in the next request, such as
|
||||
/// whom the step "Test situation" simulates, whose research the prompt carries.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public int DetailsVersion { get; set; }
|
||||
|
||||
[Inject]
|
||||
private BflConversationService ConversationService { get; init; } = null!;
|
||||
|
||||
@@ -70,9 +77,10 @@ public partial class BflChat : MSGComponentBase
|
||||
|
||||
/// <summary>
|
||||
/// Gets a number which changes whenever what the next request carries beside the conversation changed: the
|
||||
/// material, or the guidelines of the organization after a configuration plugin was reloaded.
|
||||
/// material, the guidelines of the organization after a configuration plugin was reloaded, or a detail which takes
|
||||
/// much room, see DetailsVersion.
|
||||
/// </summary>
|
||||
private int ContextVersion => HashCode.Combine(this.materialsVersion, this.SettingsManager.ConfigurationData.BeyondFactsLab.OrganizationRules);
|
||||
private int ContextVersion => HashCode.Combine(this.materialsVersion, this.SettingsManager.ConfigurationData.BeyondFactsLab.OrganizationRules, this.DetailsVersion);
|
||||
|
||||
/// <summary>
|
||||
/// Gets what the chat says before its conversation begins.
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
@inherits MSGComponentBase
|
||||
|
||||
@* Whom the conversation of the step plays belongs to the details of the step, so it stands in a dashed frame like the
|
||||
sections of the other forms. It stays right above the conversation, so that the person sees whom the lab simulates. *@
|
||||
<MudPaper Elevation="1" Class="@($"pa-3 border-dashed border rounded-lg {this.Class}")">
|
||||
<MudText Typo="Typo.h6" Class="mb-2">@T("Simulation")</MudText>
|
||||
|
||||
<MudSelect T="BflSimulationMode"
|
||||
Value="@this.Mode"
|
||||
ValueChanged="@this.SetModeAsync"
|
||||
Label="@T("Whom the lab plays")"
|
||||
HelperText="@this.Mode.Description"
|
||||
Disabled="@this.Disabled"
|
||||
Variant="Variant.Outlined">
|
||||
@foreach (var mode in BflSimulationMode.All)
|
||||
{
|
||||
<MudSelectItem T="BflSimulationMode" Value="@mode">@mode.Title</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
|
||||
@if (this.Mode.HasPeople)
|
||||
{
|
||||
@if (BflEventVisibility.IsNotPublic(this.Forms))
|
||||
{
|
||||
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined" Dense="true" Icon="@AppIcons.NOT_PUBLIC" Class="mt-3">
|
||||
<MudJustifiedText Typo="Typo.body2">@TA("The event is not public. Therefore, the lab does not research its participants, and it can simulate only generic counterparts.")</MudJustifiedText>
|
||||
</MudAlert>
|
||||
}
|
||||
else if (this.Candidates.Count is 0)
|
||||
{
|
||||
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined" Dense="true" Icon="@AppIcons.PERSON_RESEARCH" Class="mt-3">
|
||||
<MudJustifiedText Typo="Typo.body2">@string.Format(Culture, TA("Nobody can be simulated yet. In the step '{0}', let the lab research single participants; everyone whose public positions the research documents appears here. Until then, the lab plays generic counterparts."), BflStep.STRATEGY.Title)</MudJustifiedText>
|
||||
</MudAlert>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudText Typo="Typo.body2" Class="mt-3">@this.ChooseText</MudText>
|
||||
<MudChipSet T="string"
|
||||
SelectionMode="SelectionMode.MultiSelection"
|
||||
SelectedValues="@this.ChosenNames"
|
||||
SelectedValuesChanged="@this.ChooseAsync"
|
||||
CheckMark="true"
|
||||
Color="Color.Primary"
|
||||
Variant="Variant.Outlined"
|
||||
Disabled="@this.Disabled">
|
||||
@foreach (var candidate in this.Candidates)
|
||||
{
|
||||
<MudChip @key="candidate.Name" T="string" Value="@candidate.Name" Text="@candidate.Name" Disabled="@this.IsOverTheLimit(candidate)"/>
|
||||
}
|
||||
</MudChipSet>
|
||||
<MudText Typo="Typo.caption" Class="d-block mb-1">@string.Format(Culture, TA("Only people whose public positions the research documents can be chosen. Let the lab research more of them in the step '{0}'."), BflStep.STRATEGY.Title)</MudText>
|
||||
|
||||
@if (this.Effective.People.Count is 0)
|
||||
{
|
||||
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined" Dense="true" Class="mt-2">
|
||||
<MudJustifiedText Typo="Typo.body2">@TA("Choose at least one person. Until then, the lab plays generic counterparts.")</MudJustifiedText>
|
||||
</MudAlert>
|
||||
}
|
||||
else
|
||||
{
|
||||
@switch (this.Fit)
|
||||
{
|
||||
case BflSimulationFit.TOO_SMALL:
|
||||
<MudAlert Severity="Severity.Error" Variant="Variant.Outlined" Dense="true" Class="mt-2">
|
||||
<MudJustifiedText Typo="Typo.body2">@string.Format(Culture, TA("The research on the people you chose takes about {0} tokens. The model of the selected provider reads {1} tokens at once, which is likely too little for the lab, your material, and the conversation besides. Choose fewer people or another provider; until then, the conversation sends nothing."), this.DossierTokens.CompactCount(), this.ContextTokens.CompactCount())</MudJustifiedText>
|
||||
</MudAlert>
|
||||
break;
|
||||
|
||||
case BflSimulationFit.UNKNOWN_CONTEXT:
|
||||
<MudAlert Severity="Severity.Warning" Variant="Variant.Outlined" Dense="true" Class="mt-2">
|
||||
<MudJustifiedText Typo="Typo.body2">@string.Format(Culture, TA("The research on the people you chose takes about {0} tokens. Nobody knows how much the model of the selected provider reads at once, so the lab cannot tell whether that fits. The conversation shows how much room is left."), this.DossierTokens.CompactCount())</MudJustifiedText>
|
||||
</MudAlert>
|
||||
break;
|
||||
|
||||
default:
|
||||
<MudText Typo="Typo.caption" Class="d-block mt-1">@string.Format(Culture, T("The research on these people takes about {0} of the {1} tokens the model reads at once."), this.DossierTokens.CompactCount(), this.ContextTokens.CompactCount())</MudText>
|
||||
break;
|
||||
}
|
||||
|
||||
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined" Dense="true" Icon="@AppIcons.PERSON_RESEARCH" Class="mt-3">
|
||||
<MudJustifiedText Typo="Typo.body2">@string.Format(Culture, TA("In this exercise, the lab simulates {0}, based on summaries of public sources. The simulated people never said what the lab lets them say, and the lab marks each of their contributions as a simulation."), string.Join(", ", this.Effective.People.Select(person => person.Name)))</MudJustifiedText>
|
||||
</MudAlert>
|
||||
}
|
||||
}
|
||||
}
|
||||
</MudPaper>
|
||||
@@ -0,0 +1,168 @@
|
||||
using System.Globalization;
|
||||
|
||||
using AIStudio.Components;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// Lets the person choose whom the conversation of the step "Test situation" plays: the mode, and the people from the
|
||||
/// research on single participants.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The choice is part of the details of the step, see BflStepFormData.Simulation; the page stores it like every other
|
||||
/// detail. Only the people who qualify can be chosen, see BflSimulationCandidates, and only as many as the mode allows.
|
||||
/// The area tells the person how much of the context window of the model the research on the chosen people takes,
|
||||
/// and refuses, like the conversation, when a known window is likely too small.
|
||||
/// </remarks>
|
||||
public partial class BflSimulationChoice : MSGComponentBase
|
||||
{
|
||||
[Inject]
|
||||
private BeyondFactsLabStore Store { get; init; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ID of the preparation.
|
||||
/// </summary>
|
||||
[Parameter, EditorRequired]
|
||||
public Guid ProjectId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the forms of the preparation, by their steps, which tell whether the event is public.
|
||||
/// </summary>
|
||||
[Parameter, EditorRequired]
|
||||
public IReadOnlyDictionary<BflStep, BflStepFormData> Forms { get; set; } = new Dictionary<BflStep, BflStepFormData>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the form of the step "Test situation", which keeps the choice.
|
||||
/// </summary>
|
||||
[Parameter, EditorRequired]
|
||||
public BflStepFormData Form { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets what happens after the person changed the choice, e.g., storing the form.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public EventCallback FormChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the provider of the preparation, whose model decides how much research fits.
|
||||
/// </summary>
|
||||
[Parameter, EditorRequired]
|
||||
public AIStudio.Settings.Provider Provider { get; set; } = AIStudio.Settings.Provider.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the person cannot change the choice right now.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets further CSS classes of the area.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string Class { get; set; } = string.Empty;
|
||||
|
||||
private static CultureInfo Culture => Tools.PluginSystem.I18N.I.Culture;
|
||||
|
||||
/// <summary>
|
||||
/// The research on the topics this version can open, with the list of participants.
|
||||
/// </summary>
|
||||
private IReadOnlyDictionary<BflResearchTopic, BflResearch> topics = new Dictionary<BflResearchTopic, BflResearch>();
|
||||
|
||||
/// <summary>
|
||||
/// The research on every person stored for the preparation.
|
||||
/// </summary>
|
||||
private IReadOnlyList<BflPersonResearch> people = [];
|
||||
|
||||
/// <summary>
|
||||
/// The preparation whose research was loaded, to load again when the page shows another one.
|
||||
/// </summary>
|
||||
private Guid loadedProjectId;
|
||||
|
||||
private BflSimulationMode Mode => this.Form.Simulation?.Mode ?? BflSimulationMode.GENERIC;
|
||||
|
||||
private IReadOnlyList<BflSimulatedPerson> Candidates => BflSimulationCandidates.Of(this.Forms, this.topics, this.people);
|
||||
|
||||
private BflEffectiveSimulation Effective => BflEffectiveSimulation.Of(this.Form.Simulation, this.Candidates);
|
||||
|
||||
private IReadOnlyCollection<string> ChosenNames => [.. this.Effective.People.Select(person => person.Name)];
|
||||
|
||||
private string ChooseText => this.Mode.MaxPeople is 1 ? TA("Choose the person:") : string.Format(Culture, TA("Choose up to {0} people:"), this.Mode.MaxPeople);
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the model of the provider is known: without a provider, the area says nothing about the window.
|
||||
/// </summary>
|
||||
private bool HasProvider => this.Provider.UsedLLMProvider is not LLMProviders.NONE;
|
||||
|
||||
private BflSimulationFit Fit => this.HasProvider ? BflSimulationBudget.Check(this.Effective, this.Provider.GetModelProfile().Context) : BflSimulationFit.FITS;
|
||||
|
||||
private int DossierTokens => BflSimulationBudget.DossierTokens(this.Effective.People);
|
||||
|
||||
private int ContextTokens => this.HasProvider ? this.Provider.GetModelProfile().Context.DefaultTokens : 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await base.OnParametersSetAsync();
|
||||
if (this.loadedProjectId == this.ProjectId)
|
||||
return;
|
||||
|
||||
this.loadedProjectId = this.ProjectId;
|
||||
this.topics = new Dictionary<BflResearchTopic, BflResearch>();
|
||||
this.people = [];
|
||||
await this.LoadAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tells whether a person cannot be chosen, because the mode allows no more people. With room for one person only,
|
||||
/// choosing another one replaces the first, see ChooseAsync.
|
||||
/// </summary>
|
||||
/// <param name="candidate">The person.</param>
|
||||
private bool IsOverTheLimit(BflSimulatedPerson candidate)
|
||||
{
|
||||
var chosen = this.ChosenNames;
|
||||
return this.Mode.MaxPeople > 1 && chosen.Count >= this.Mode.MaxPeople && !chosen.Contains(candidate.Name);
|
||||
}
|
||||
|
||||
private async Task SetModeAsync(BflSimulationMode mode)
|
||||
{
|
||||
if (this.Form.Simulation is null && mode is BflSimulationMode.GENERIC)
|
||||
return;
|
||||
|
||||
this.Form.Simulation ??= new();
|
||||
this.Form.Simulation.Mode = mode;
|
||||
await this.FormChanged.InvokeAsync();
|
||||
}
|
||||
|
||||
private async Task ChooseAsync(IReadOnlyCollection<string>? names)
|
||||
{
|
||||
var chosen = names ?? [];
|
||||
var current = this.ChosenNames;
|
||||
|
||||
// The person chose these just now; with room for one person only, the newest one replaces the others:
|
||||
List<string> kept = [.. current.Where(chosen.Contains), .. chosen.Where(name => !current.Contains(name))];
|
||||
if (kept.Count > this.Mode.MaxPeople)
|
||||
kept = [.. kept.TakeLast(this.Mode.MaxPeople)];
|
||||
|
||||
this.Form.Simulation ??= new() { Mode = this.Mode };
|
||||
this.Form.Simulation.People = kept;
|
||||
await this.FormChanged.InvokeAsync();
|
||||
}
|
||||
|
||||
private async Task LoadAsync()
|
||||
{
|
||||
var projectId = this.ProjectId;
|
||||
var loadedTopics = await this.Store.LoadAllResearchAsync(projectId);
|
||||
var loadedPeople = await this.Store.LoadAllPersonResearchAsync(projectId);
|
||||
|
||||
// The page may have shown another preparation meanwhile:
|
||||
if (projectId != this.ProjectId)
|
||||
return;
|
||||
|
||||
this.topics = loadedTopics;
|
||||
this.people = loadedPeople;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@ public static class BflSimulationModeExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(BflSimulationModeExtensions).Namespace, nameof(BflSimulationModeExtensions));
|
||||
|
||||
private static string TA(string fallbackEN) => Tools.PluginSystem.I18N.I.TA(fallbackEN, typeof(BflSimulationModeExtensions).Namespace, nameof(BflSimulationModeExtensions));
|
||||
|
||||
/// <param name="mode">The mode.</param>
|
||||
extension(BflSimulationMode mode)
|
||||
{
|
||||
@@ -22,6 +24,18 @@ public static class BflSimulationModeExtensions
|
||||
_ => TB("Generic counterparts, no specific person"),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets what the chat does in this mode, and when the person gets feedback, as the selection explains it.
|
||||
/// </summary>
|
||||
public string Description => mode switch
|
||||
{
|
||||
BflSimulationMode.SINGLE_PERSON => TA("The lab plays one participant in their role at the event, based on the research on them. The simulation runs until you end it; then you get feedback on the whole exercise."),
|
||||
BflSimulationMode.PANEL_DISCUSSION => TA("The lab plays a panel discussion with up to three participants, each in their role at the event, and adds a moderation if none of them moderates. The simulation runs until you end it; then you get feedback on the whole exercise."),
|
||||
BflSimulationMode.PRESS_CONFERENCE => TA("The lab plays a press conference with up to five participants, each in their role at the event, and adds questions from the press if none of them asks any. The simulation runs until you end it; then you get feedback on the whole exercise."),
|
||||
|
||||
_ => TA("The lab asks critical questions as they could come up at the event, without playing a specific person. After every answer, you get feedback."),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets how many people from the research the chat plays at most in this mode; none for generic counterparts.
|
||||
/// </summary>
|
||||
|
||||
Reference in new issue
Block a user