mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-10 19:47:21 +00:00
Added the BeyondFacts Lab as a prototype (#1175)
This commit is contained in:
1 parent
db8dae1cf6
commit
b6c3fbb5db
90 files changed
+5936
-209
No files matched your search
@@ -0,0 +1,127 @@
|
||||
@attribute [Route(Routes.ASSISTANT_BEYOND_FACTS_LAB)]
|
||||
@inherits MSGComponentBase
|
||||
|
||||
<CascadingValue Value="Components.BEYOND_FACTS_LAB_ASSISTANT">
|
||||
@* The lab does not inherit AssistantBase, so it sets up the inner scrolling itself, the way the
|
||||
Visual Briefing assistant does. *@
|
||||
<div class="inner-scrolling-context">
|
||||
<PreviewPrototype ApplyInnerScrollingFix="true"/>
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Class="mb-3 mr-3" StretchItems="StretchItems.Start">
|
||||
<MudText Typo="Typo.h3">@T("BeyondFacts Lab")</MudText>
|
||||
<MudSpacer/>
|
||||
<MudIconButton Variant="Variant.Text" Icon="@Icons.Material.Filled.Settings" OnClick="@this.OpenSettingsDialogAsync"/>
|
||||
</MudStack>
|
||||
|
||||
<InnerScrolling>
|
||||
<MudText Typo="Typo.body1" Class="mb-3 mr-3">
|
||||
@T("Prepare a challenging appearance as a researcher step by step, e.g., a panel discussion, an interview, or a hearing. Each preparation belongs to one event and has its own steps, chats, and work states.")
|
||||
</MudText>
|
||||
|
||||
<MudText Typo="Typo.h5" Class="mb-1">@T("Your preparations")</MudText>
|
||||
@if (this.projects.Count == 0)
|
||||
{
|
||||
<MudText Typo="Typo.body2" Class="mb-3">@T("You have no preparations yet. Create one for the event you want to prepare for.")</MudText>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudList T="Guid"
|
||||
Color="Color.Primary"
|
||||
Class="mb-1"
|
||||
SelectedValue="@(this.selectedProject?.ProjectId ?? Guid.Empty)"
|
||||
SelectedValueChanged="@this.SelectProjectAsync">
|
||||
@foreach (var project in this.projects)
|
||||
{
|
||||
<MudListItem T="Guid" @key="project.ProjectId" Value="@project.ProjectId" Icon="@ProjectIcon(project)">
|
||||
<MudStack Spacing="0">
|
||||
<MudText Typo="Typo.body1">@this.ProjectDisplayName(project)</MudText>
|
||||
<MudText Typo="Typo.caption">@this.ProjectCaption(project)</MudText>
|
||||
@if (this.projectsWithMissingDetails.Contains(project.ProjectId))
|
||||
{
|
||||
<MudText Typo="Typo.caption" Color="Color.Warning">@T("Details missing")</MudText>
|
||||
}
|
||||
@if (!project.IsAvailable)
|
||||
{
|
||||
<MudText Typo="Typo.caption" Color="Color.Error">@this.ProjectStatusName(project.Status)</MudText>
|
||||
}
|
||||
</MudStack>
|
||||
</MudListItem>
|
||||
}
|
||||
</MudList>
|
||||
}
|
||||
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Add" OnClick="@this.CreateProjectAsync" Disabled="@this.isBusy" Class="mt-1">
|
||||
@T("New preparation")
|
||||
</MudButton>
|
||||
|
||||
<MudDivider Style="height: 0.25ch; margin: 1rem 0;" Class="mt-6"/>
|
||||
|
||||
@if (this.selectedProject is { IsAvailable: false } unavailableProject)
|
||||
{
|
||||
<MudPaper Outlined="true" Class="pa-6 mr-3">
|
||||
<MudStack Spacing="3">
|
||||
<MudText Typo="Typo.h4">@this.ProjectDisplayName(unavailableProject)</MudText>
|
||||
<MudAlert Severity="Severity.Error" Variant="Variant.Outlined">
|
||||
@this.ProjectRecoveryMessage(unavailableProject.Status)
|
||||
</MudAlert>
|
||||
<MudText Typo="Typo.body1">@T("AI Studio has left the files of this preparation unchanged. A future update may make it accessible again.")</MudText>
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1" Wrap="Wrap.Wrap">
|
||||
<MudText Typo="Typo.body2"><strong>@T("Preparation ID"):</strong> @unavailableProject.ProjectId.ToString("D")</MudText>
|
||||
<MudCopyClipboardButton TooltipMessage="@T("Copy preparation ID")" StringContent="@unavailableProject.ProjectId.ToString("D")"/>
|
||||
</MudStack>
|
||||
<MudStack Row="true" Spacing="1" Wrap="Wrap.Wrap">
|
||||
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.DeleteForever" Color="Color.Error" OnClick="@this.DeleteProjectAsync" Disabled="@this.isBusy">@T("Delete")</MudButton>
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
}
|
||||
else if (this.selectedProject?.Project is { } project)
|
||||
{
|
||||
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center" Wrap="Wrap.Wrap" Class="mb-3 mr-3">
|
||||
<MudText Typo="Typo.h4">@project.Name</MudText>
|
||||
<MudStack Row="true" Spacing="1">
|
||||
<MudButton StartIcon="@Icons.Material.Filled.DriveFileRenameOutline" OnClick="@this.RenameProjectAsync" Disabled="@this.isBusy">@T("Rename")</MudButton>
|
||||
<MudButton StartIcon="@Icons.Material.Filled.DeleteForever" Color="Color.Error" OnClick="@this.DeleteProjectAsync" Disabled="@this.isBusy">@T("Delete")</MudButton>
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
|
||||
<MudSwitch T="bool" Value="@(project.HappenedAtUtc is not null)" ValueChanged="@this.SetHappenedAsync" Disabled="@this.isBusy" Color="Color.Primary" Class="mx-1">
|
||||
@T("The event has taken place")
|
||||
</MudSwitch>
|
||||
@if (project.HappenedAtUtc is { } happenedAtUtc)
|
||||
{
|
||||
<MudText Typo="Typo.caption" Class="mx-1">@string.Format(Culture, T("Marked on {0}."), happenedAtUtc.ToLocalTime().ToString("d", Culture))</MudText>
|
||||
}
|
||||
|
||||
<MudStepperWithoutActions NonLinear="@true" ActiveIndex="@this.ActiveStepIndex" ActiveIndexChanged="@this.SelectStepAsync" Class="mt-4 mr-3">
|
||||
<ChildContent>
|
||||
@foreach (var step in BflStep.All)
|
||||
{
|
||||
<MudStep @key="step" Title="@step.Title" SecondaryText="@this.StepSecondaryText(step)">
|
||||
<MudText Typo="Typo.h5" Class="mt-2">@step.Title</MudText>
|
||||
<MudText Typo="Typo.body2" Class="mb-4">@step.Description</MudText>
|
||||
|
||||
<MudText Typo="Typo.h6" Class="mb-2">@T("Your details")</MudText>
|
||||
@if (this.forms.TryGetValue(step, out var form))
|
||||
{
|
||||
<BflStepForm Step="@step" Form="@form" FormChanged="@(() => this.SaveFormAsync(step))"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudAlert Severity="Severity.Error" Variant="Variant.Outlined">
|
||||
@T("AI Studio cannot open your details for this step. Their file may be damaged, or a newer version of AI Studio stored it. AI Studio leaves the file unchanged.")
|
||||
</MudAlert>
|
||||
}
|
||||
</MudStep>
|
||||
}
|
||||
</ChildContent>
|
||||
</MudStepperWithoutActions>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudPaper Outlined="true" Class="pa-6 mr-3">
|
||||
<MudText Typo="Typo.h5">@T("Create a preparation to begin.")</MudText>
|
||||
</MudPaper>
|
||||
}
|
||||
</InnerScrolling>
|
||||
</div>
|
||||
</CascadingValue>
|
||||
+271
@@ -0,0 +1,271 @@
|
||||
using System.Globalization;
|
||||
|
||||
using AIStudio.Dialogs;
|
||||
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
public partial class BeyondFactsLabAssistant
|
||||
{
|
||||
/// <summary>
|
||||
/// The preparations, the one changed last first.
|
||||
/// </summary>
|
||||
private IReadOnlyList<BflProjectEntry> projects = [];
|
||||
|
||||
/// <summary>
|
||||
/// The preparation shown, or null when there is none.
|
||||
/// </summary>
|
||||
private BflProjectEntry? selectedProject;
|
||||
|
||||
/// <summary>
|
||||
/// Whether a change of the preparations is running.
|
||||
/// </summary>
|
||||
private bool isBusy;
|
||||
|
||||
/// <summary>
|
||||
/// The IDs of the preparations which lack key details.
|
||||
/// </summary>
|
||||
private HashSet<Guid> projectsWithMissingDetails = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the culture of the active language plugin, for dates.
|
||||
/// </summary>
|
||||
private static CultureInfo Culture => Tools.PluginSystem.I18N.I.Culture;
|
||||
|
||||
/// <summary>
|
||||
/// Reads the preparations again and chooses the one to show.
|
||||
/// </summary>
|
||||
/// <param name="preferredId">The preparation to show, or null to keep the one shown so far.</param>
|
||||
private async Task ReloadProjectsAsync(Guid? preferredId)
|
||||
{
|
||||
this.projects = await this.Store.ListProjectsAsync();
|
||||
|
||||
var withMissingDetails = new HashSet<Guid>();
|
||||
foreach (var project in this.projects.Where(project => project.IsAvailable))
|
||||
{
|
||||
if (BflMissingDetails.StepsOf(await this.Store.LoadFormsAsync(project.ProjectId)).Count > 0)
|
||||
withMissingDetails.Add(project.ProjectId);
|
||||
}
|
||||
|
||||
this.projectsWithMissingDetails = withMissingDetails;
|
||||
await this.ShowProjectAsync(BflProjectSelection.Choose(this.projects, preferredId ?? this.selectedProject?.ProjectId, this.Store.LastSelectedProjectId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the preparation the person selected and remembers it for the next time.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
private async Task SelectProjectAsync(Guid projectId)
|
||||
{
|
||||
if (this.selectedProject?.ProjectId == projectId)
|
||||
return;
|
||||
|
||||
var project = this.projects.FirstOrDefault(candidate => candidate.ProjectId == projectId);
|
||||
if (project is null)
|
||||
return;
|
||||
|
||||
await this.RunStoreActionAsync(async () =>
|
||||
{
|
||||
await this.ShowProjectAsync(project);
|
||||
await this.Store.RememberSelectionAsync(projectId);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asks for a name and creates a preparation.
|
||||
/// </summary>
|
||||
private async Task CreateProjectAsync()
|
||||
{
|
||||
var name = await this.AskForNameAsync(T("New preparation"), T("Name the event you want to prepare for, e.g., the title of a panel discussion."), string.Empty, T("Create"));
|
||||
if (name is null)
|
||||
return;
|
||||
|
||||
await this.RunStoreActionAsync(async () =>
|
||||
{
|
||||
var project = await this.Store.CreateAsync(name);
|
||||
await this.Store.RememberSelectionAsync(project.ProjectId);
|
||||
await this.ReloadProjectsAsync(project.ProjectId);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asks for a new name and renames the preparation shown.
|
||||
/// </summary>
|
||||
private async Task RenameProjectAsync()
|
||||
{
|
||||
if (this.selectedProject?.Project is not { } project)
|
||||
return;
|
||||
|
||||
var name = await this.AskForNameAsync(T("Rename preparation"), T("Enter a new name for this preparation."), project.Name, T("Rename"));
|
||||
if (name is null)
|
||||
return;
|
||||
|
||||
await this.RunStoreActionAsync(async () =>
|
||||
{
|
||||
await this.Store.RenameAsync(project.ProjectId, name);
|
||||
await this.ReloadProjectsAsync(project.ProjectId);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the preparation shown, after the person confirmed it.
|
||||
/// </summary>
|
||||
private async Task DeleteProjectAsync()
|
||||
{
|
||||
if (this.selectedProject is not { } project)
|
||||
return;
|
||||
|
||||
var parameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{
|
||||
dialog => dialog.Message,
|
||||
project.IsAvailable
|
||||
? string.Format(T("Permanently delete the preparation '{0}' with all of its steps, chats, and work states?"), project.Name)
|
||||
: T("This preparation cannot be opened. Permanently delete it with all of its files?")
|
||||
},
|
||||
};
|
||||
|
||||
var reference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete preparation permanently"), parameters, DialogOptions.FULLSCREEN);
|
||||
var result = await reference.Result;
|
||||
if (result is null || result.Canceled)
|
||||
return;
|
||||
|
||||
await this.RunStoreActionAsync(async () =>
|
||||
{
|
||||
await this.Store.DeleteAsync(project.ProjectId);
|
||||
this.selectedProject = null;
|
||||
await this.ReloadProjectsAsync(null);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks the event of the preparation shown as having taken place, or takes that back.
|
||||
/// </summary>
|
||||
/// <param name="happened">Whether the event has taken place.</param>
|
||||
private async Task SetHappenedAsync(bool happened)
|
||||
{
|
||||
if (this.selectedProject?.Project is not { } project)
|
||||
return;
|
||||
|
||||
await this.RunStoreActionAsync(async () =>
|
||||
{
|
||||
await this.Store.SetHappenedAsync(project.ProjectId, happened ? DateTimeOffset.UtcNow : null);
|
||||
await this.ReloadProjectsAsync(project.ProjectId);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asks for the name of a preparation.
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the dialog.</param>
|
||||
/// <param name="message">What the dialog asks for.</param>
|
||||
/// <param name="currentName">The name to start with.</param>
|
||||
/// <param name="confirmText">The text of the button which confirms.</param>
|
||||
/// <returns>The name entered, or null when the person canceled.</returns>
|
||||
private async Task<string?> AskForNameAsync(string title, string message, string currentName, string confirmText)
|
||||
{
|
||||
var parameters = new DialogParameters<SingleInputDialog>
|
||||
{
|
||||
{ dialog => dialog.Message, message },
|
||||
{ dialog => dialog.InputHeaderText, T("Name of the preparation") },
|
||||
{ dialog => dialog.UserInput, currentName },
|
||||
{ dialog => dialog.ConfirmText, confirmText },
|
||||
{ dialog => dialog.ConfirmColor, Color.Primary },
|
||||
{ dialog => dialog.AllowEmptyInput, false },
|
||||
{ dialog => dialog.EmptyInputErrorMessage, T("Please enter a name.") },
|
||||
};
|
||||
|
||||
var reference = await this.DialogService.ShowAsync<SingleInputDialog>(title, parameters, DialogOptions.FULLSCREEN);
|
||||
var result = await reference.Result;
|
||||
if (result is null || result.Canceled || result.Data is not string name || string.IsNullOrWhiteSpace(name))
|
||||
return null;
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a change of the preparations, and tells the person when it fails.
|
||||
/// </summary>
|
||||
/// <param name="action">The change.</param>
|
||||
private async Task RunStoreActionAsync(Func<Task> action)
|
||||
{
|
||||
this.isBusy = true;
|
||||
try
|
||||
{
|
||||
await action();
|
||||
}
|
||||
catch (Exception exception) when (exception is IOException or UnauthorizedAccessException or InvalidDataException or ArgumentException)
|
||||
{
|
||||
this.Logger.LogError(exception, "A change of the preparations of the BeyondFacts Lab failed.");
|
||||
await this.MessageBus.SendError(new(Icons.Material.Filled.RecordVoiceOver, T("AI Studio could not save the change of your preparations. Please try again.")));
|
||||
await this.ReloadAfterFailureAsync();
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.isBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the preparations again after a change failed, so the page shows what is stored.
|
||||
/// </summary>
|
||||
private async Task ReloadAfterFailureAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await this.ReloadProjectsAsync(null);
|
||||
}
|
||||
catch (Exception exception) when (exception is IOException or UnauthorizedAccessException)
|
||||
{
|
||||
this.Logger.LogError(exception, "The preparations of the BeyondFacts Lab could not be read.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the icon of a preparation in the list.
|
||||
/// </summary>
|
||||
/// <param name="project">The preparation.</param>
|
||||
private static string ProjectIcon(BflProjectEntry project) => project switch
|
||||
{
|
||||
{ IsAvailable: false } => Icons.Material.Filled.WarningAmber,
|
||||
{ Project.HappenedAtUtc: not null } => Icons.Material.Filled.EventAvailable,
|
||||
_ => Icons.Material.Filled.Event,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of a preparation in the list, also when it cannot be read.
|
||||
/// </summary>
|
||||
/// <param name="project">The preparation.</param>
|
||||
private string ProjectDisplayName(BflProjectEntry project) => string.IsNullOrWhiteSpace(project.Name) ? T("Unavailable preparation") : project.Name;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the line below the name of a preparation in the list: when it changed, and whether its event has taken place.
|
||||
/// </summary>
|
||||
/// <param name="project">The preparation.</param>
|
||||
private string ProjectCaption(BflProjectEntry project)
|
||||
{
|
||||
var changed = string.Format(Culture, T("Changed on {0}"), project.ModifiedAtUtc.ToLocalTime().ToString("g", Culture));
|
||||
return project.Project?.HappenedAtUtc is null ? changed : $"{changed} · {T("The event has taken place")}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the short reason why a preparation cannot be opened.
|
||||
/// </summary>
|
||||
/// <param name="status">The status of the preparation.</param>
|
||||
private string ProjectStatusName(BflProjectLoadStatus status) => status switch
|
||||
{
|
||||
BflProjectLoadStatus.NEWER_VERSION => T("Requires a newer AI Studio version"),
|
||||
_ => T("Cannot be opened"),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Explains why a preparation cannot be opened.
|
||||
/// </summary>
|
||||
/// <param name="status">The status of the preparation.</param>
|
||||
private string ProjectRecoveryMessage(BflProjectLoadStatus status) => status switch
|
||||
{
|
||||
BflProjectLoadStatus.NEWER_VERSION => T("A newer version of AI Studio stored this preparation. This version cannot open it. Please update AI Studio."),
|
||||
_ => T("AI Studio cannot read this preparation. Its files may be damaged."),
|
||||
};
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
public partial class BeyondFactsLabAssistant
|
||||
{
|
||||
/// <summary>
|
||||
/// The forms of the preparation shown, by their steps. A step missing here has a form this version cannot open.
|
||||
/// </summary>
|
||||
private Dictionary<BflStep, BflStepFormData> forms = [];
|
||||
|
||||
/// <summary>
|
||||
/// The step shown.
|
||||
/// </summary>
|
||||
private BflStep activeStep = BflStep.FRAMEWORK;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position of the step shown in the stepper, which lists the steps in the order of their values.
|
||||
/// </summary>
|
||||
private int ActiveStepIndex => (int)this.activeStep;
|
||||
|
||||
/// <summary>
|
||||
/// Shows a preparation with its forms, at the step the person looked at last.
|
||||
/// </summary>
|
||||
/// <param name="project">The preparation, or null to show none.</param>
|
||||
private async Task ShowProjectAsync(BflProjectEntry? project)
|
||||
{
|
||||
this.selectedProject = project;
|
||||
if (project?.Project is { } openedProject)
|
||||
{
|
||||
this.forms = new(await this.Store.LoadFormsAsync(openedProject.ProjectId));
|
||||
this.activeStep = openedProject.CurrentStep;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.forms = [];
|
||||
this.activeStep = BflStep.FRAMEWORK;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the step the person chose in the stepper, and remembers it for the next time.
|
||||
/// </summary>
|
||||
/// <param name="index">The position of the step in the stepper.</param>
|
||||
private async Task SelectStepAsync(int index)
|
||||
{
|
||||
var steps = BflStep.All;
|
||||
if (index < 0 || index >= steps.Count || steps[index] == this.activeStep)
|
||||
return;
|
||||
|
||||
this.activeStep = steps[index];
|
||||
if (this.selectedProject?.Project is not { } project)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
this.ReplaceProject(await this.Store.SetCurrentStepAsync(project.ProjectId, this.activeStep));
|
||||
}
|
||||
catch (Exception exception) when (exception is IOException or UnauthorizedAccessException or InvalidDataException)
|
||||
{
|
||||
// Only the step to open next time is lost, which is not worth bothering the person with:
|
||||
this.Logger.LogWarning(exception, "Could not remember the step shown last in the BeyondFacts Lab.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores the form of a step after the person changed a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Unlike the other changes, saving leaves the page usable: the fields stay enabled, so that the next field
|
||||
/// keeps the focus while the previous one is stored.
|
||||
/// </remarks>
|
||||
/// <param name="step">The step.</param>
|
||||
private async Task SaveFormAsync(BflStep step)
|
||||
{
|
||||
if (this.selectedProject?.Project is not { } project || !this.forms.TryGetValue(step, out var form))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
this.ReplaceProject(await this.Store.SaveFormAsync(project.ProjectId, step, form));
|
||||
if (BflMissingDetails.StepsOf(this.forms).Count > 0)
|
||||
this.projectsWithMissingDetails.Add(project.ProjectId);
|
||||
else
|
||||
this.projectsWithMissingDetails.Remove(project.ProjectId);
|
||||
}
|
||||
catch (Exception exception) when (exception is IOException or UnauthorizedAccessException or InvalidDataException)
|
||||
{
|
||||
this.Logger.LogError(exception, "Could not save the details of a step in the BeyondFacts Lab.");
|
||||
await this.MessageBus.SendError(new(Icons.Material.Filled.RecordVoiceOver, T("AI Studio could not save your details. Please try again.")));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Puts a preparation the store just wrote into the list, without reading the list again.
|
||||
/// </summary>
|
||||
/// <param name="project">The preparation.</param>
|
||||
private void ReplaceProject(BflProject project)
|
||||
{
|
||||
var entry = BflProjectEntry.FromProject(project);
|
||||
this.projects = [.. this.projects.Select(candidate => candidate.ProjectId == project.ProjectId ? entry : candidate).OrderByDescending(candidate => candidate.ModifiedAtUtc)];
|
||||
if (this.selectedProject?.ProjectId == project.ProjectId)
|
||||
this.selectedProject = entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the line below the title of a step in the stepper.
|
||||
/// </summary>
|
||||
/// <param name="step">The step.</param>
|
||||
private string StepSecondaryText(BflStep step)
|
||||
{
|
||||
if (!this.forms.TryGetValue(step, out var form))
|
||||
return T("Cannot be opened");
|
||||
|
||||
return BflMissingDetails.Of(step, form).Count > 0 ? T("Details missing") : string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using AIStudio.Components;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// The page of the BeyondFacts Lab, which guides researchers through the preparation of a challenging appearance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The lab keeps several preparations, each with steps of their own, so it does not inherit AssistantBase,
|
||||
/// which knows one input and one result. Like the Visual Briefing assistant, it checks by itself whether it
|
||||
/// may be opened.
|
||||
/// </remarks>
|
||||
public partial class BeyondFactsLabAssistant : MSGComponentBase
|
||||
{
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private NavigationManager NavigationManager { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private BeyondFactsLabStore Store { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ILogger<BeyondFactsLabAssistant> Logger { get; init; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the person may open the lab, so that the page did not send them away.
|
||||
/// </summary>
|
||||
private bool mayOpen;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
if (!BflAccess.CanOpen(this.SettingsManager, T("BeyondFacts Lab")))
|
||||
{
|
||||
this.NavigationManager.NavigateTo(Routes.ASSISTANTS);
|
||||
return;
|
||||
}
|
||||
|
||||
this.ApplyFilters([], [Event.CONFIGURATION_CHANGED]);
|
||||
this.mayOpen = true;
|
||||
await this.RunStoreActionAsync(() => this.ReloadProjectsAsync(null));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
if (firstRender && this.mayOpen)
|
||||
await this.ShowNoticeIfNeededAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the notes of the lab, unless the person confirmed them as they are now.
|
||||
/// </summary>
|
||||
private async Task ShowNoticeIfNeededAsync()
|
||||
{
|
||||
var notice = BflNotice.Text;
|
||||
var currentHash = BflNotice.ComputeHash(notice);
|
||||
if (BflNotice.IsAccepted(this.SettingsManager.ConfigurationData.BeyondFactsLab.AcceptedNoticeHash, currentHash))
|
||||
return;
|
||||
|
||||
var parameters = new DialogParameters<BflNoticeDialog> { { dialog => dialog.Notice, notice } };
|
||||
var reference = await this.DialogService.ShowAsync<BflNoticeDialog>(T("BeyondFacts Lab"), parameters, DialogOptions.BLOCKING_FULLSCREEN);
|
||||
var result = await reference.Result;
|
||||
if (result is null || result.Canceled)
|
||||
return;
|
||||
|
||||
this.SettingsManager.ConfigurationData.BeyondFactsLab.AcceptedNoticeHash = currentHash;
|
||||
await this.SettingsManager.StoreSettings();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||
{
|
||||
// An organization may hide the lab, or the person may switch its preview feature off, while the page is open:
|
||||
if (triggeredEvent is Event.CONFIGURATION_CHANGED && !BflAccess.CanOpen(this.SettingsManager))
|
||||
{
|
||||
this.NavigationManager.NavigateTo(Routes.ASSISTANTS);
|
||||
return;
|
||||
}
|
||||
|
||||
await base.ProcessIncomingMessage(sendingComponent, triggeredEvent, data);
|
||||
}
|
||||
|
||||
private async Task OpenSettingsDialogAsync() => await this.DialogService.ShowAsync<SettingsDialogBeyondFactsLab>(null, new DialogParameters(), DialogOptions.FULLSCREEN);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// The JSON options of the files of the BeyondFacts Lab.
|
||||
/// </summary>
|
||||
internal static class BeyondFactsLabJson
|
||||
{
|
||||
/// <summary>
|
||||
/// The options with which the lab writes and reads its files.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The files stay readable for people who look into the data folder: indented, enums by name, and umlauts
|
||||
/// as they are rather than as escapes. The relaxed encoder is fine here, because nothing embeds these files
|
||||
/// into HTML. A field this version does not know makes a file unreadable rather than letting a save drop
|
||||
/// it; such a preparation is listed as one of a newer version or as unavailable, and nothing writes it.
|
||||
/// </remarks>
|
||||
internal static readonly JsonSerializerOptions PERSISTENCE = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
PropertyNameCaseInsensitive = false,
|
||||
WriteIndented = true,
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||
UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow,
|
||||
Converters = { new JsonStringEnumConverter() },
|
||||
TypeInfoResolver = BeyondFactsLabJsonContext.Default,
|
||||
}.AsReadOnly();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// The contracts with which System.Text.Json reads and writes the files of the BeyondFacts Lab, built at compile time.
|
||||
/// </summary>
|
||||
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)]
|
||||
[JsonSerializable(typeof(BflProject))]
|
||||
[JsonSerializable(typeof(BflStepFormData))]
|
||||
|
||||
// The preparation selected last:
|
||||
[JsonSerializable(typeof(Guid?))]
|
||||
internal sealed partial class BeyondFactsLabJsonContext : JsonSerializerContext;
|
||||
@@ -0,0 +1,134 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
public sealed partial class BeyondFactsLabStore
|
||||
{
|
||||
/// <summary>
|
||||
/// The folder of a preparation which holds a folder for each step.
|
||||
/// </summary>
|
||||
private const string STEPS_DIRECTORY_NAME = "steps";
|
||||
|
||||
/// <summary>
|
||||
/// The file of a step which holds its form "Your details".
|
||||
/// </summary>
|
||||
private const string FORM_FILE_NAME = "form.json";
|
||||
|
||||
/// <summary>
|
||||
/// Loads the form of a step.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
/// <param name="step">The step.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The form, or an empty one when the person entered nothing yet.</returns>
|
||||
/// <exception cref="InvalidDataException">This version cannot open the form.</exception>
|
||||
public async Task<BflStepFormData> LoadFormAsync(Guid projectId, BflStep step, CancellationToken token = default)
|
||||
{
|
||||
await this.InitializeAsync(token);
|
||||
return await this.LoadFormWithoutInitializeAsync(projectId, step, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the forms of every step of a preparation, e.g., to find missing details.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The forms this version can open, by their steps.</returns>
|
||||
public async Task<IReadOnlyDictionary<BflStep, BflStepFormData>> LoadFormsAsync(Guid projectId, CancellationToken token = default)
|
||||
{
|
||||
await this.InitializeAsync(token);
|
||||
var forms = new Dictionary<BflStep, BflStepFormData>();
|
||||
foreach (var step in BflStep.All)
|
||||
{
|
||||
try
|
||||
{
|
||||
forms[step] = await this.LoadFormWithoutInitializeAsync(projectId, step, token);
|
||||
}
|
||||
catch (InvalidDataException exception)
|
||||
{
|
||||
logger.LogWarning(exception, "Could not open the form of the step '{Step}' of the preparation '{ProjectId}' of the BeyondFacts Lab.", step, projectId);
|
||||
}
|
||||
}
|
||||
|
||||
return forms;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores the form of a step and counts it as a change of the preparation.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
/// <param name="step">The step.</param>
|
||||
/// <param name="form">The form.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The preparation with its new time of change.</returns>
|
||||
/// <exception cref="FileNotFoundException">The preparation does not exist.</exception>
|
||||
/// <exception cref="InvalidDataException">This version cannot open the preparation or the stored form, so it does not write them either.</exception>
|
||||
public async Task<BflProject> SaveFormAsync(Guid projectId, BflStep step, BflStepFormData form, CancellationToken token = default)
|
||||
{
|
||||
await this.InitializeAsync(token);
|
||||
var gate = this.GetLock(projectId);
|
||||
await gate.WaitAsync(token);
|
||||
try
|
||||
{
|
||||
var project = await this.LoadRequiredWithoutInitializeAsync(projectId, token);
|
||||
|
||||
// A form which a newer version stored, or which is damaged, stays as it is:
|
||||
await this.LoadFormWithoutInitializeAsync(projectId, step, token);
|
||||
|
||||
form.SchemaVersion = BflStepFormData.CURRENT_SCHEMA_VERSION;
|
||||
await WriteTextAtomicAsync(this.FormPath(projectId, step), JsonSerializer.Serialize(form, JSON_OPTIONS), token);
|
||||
|
||||
project.ModifiedAtUtc = DateTimeOffset.UtcNow;
|
||||
await this.StoreProjectAsync(project, token);
|
||||
return project;
|
||||
}
|
||||
finally
|
||||
{
|
||||
gate.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the form of a step without the first start of the store.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
/// <param name="step">The step.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The form, or an empty one when there is no file.</returns>
|
||||
/// <exception cref="InvalidDataException">This version cannot open the form.</exception>
|
||||
private async Task<BflStepFormData> LoadFormWithoutInitializeAsync(Guid projectId, BflStep step, CancellationToken token)
|
||||
{
|
||||
var path = this.FormPath(projectId, step);
|
||||
if (!File.Exists(path))
|
||||
return new BflStepFormData();
|
||||
|
||||
BflStepFormData? form;
|
||||
try
|
||||
{
|
||||
await using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 65_536, true);
|
||||
form = await JsonSerializer.DeserializeAsync<BflStepFormData>(stream, JSON_OPTIONS, token);
|
||||
}
|
||||
catch (Exception exception) when (exception is IOException or UnauthorizedAccessException or JsonException)
|
||||
{
|
||||
throw new InvalidDataException("This version of AI Studio cannot open the form.", exception);
|
||||
}
|
||||
|
||||
if (form is null || form.SchemaVersion is < 1 or > BflStepFormData.CURRENT_SCHEMA_VERSION || IsNull(form.Values))
|
||||
throw new InvalidDataException("This version of AI Studio cannot open the form.");
|
||||
|
||||
return form;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file of the form of a step.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
/// <param name="step">The step.</param>
|
||||
private string FormPath(Guid projectId, BflStep step) => Path.Combine(this.ProjectDirectory(projectId), STEPS_DIRECTORY_NAME, step.FolderName, FORM_FILE_NAME);
|
||||
|
||||
/// <summary>
|
||||
/// Checks for null a value which the type system says cannot be null, but which JSON can make null.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
private static bool IsNull<T>(T? value) => value is null;
|
||||
}
|
||||
@@ -0,0 +1,435 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
public sealed partial class BeyondFactsLabStore
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the preparation the person selected last, or null when there is none or it does not exist anymore.
|
||||
/// </summary>
|
||||
public Guid? LastSelectedProjectId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Lists every preparation, also those this version cannot open, the one changed last first.
|
||||
/// </summary>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The preparations.</returns>
|
||||
public async Task<IReadOnlyList<BflProjectEntry>> ListProjectsAsync(CancellationToken token = default)
|
||||
{
|
||||
await this.InitializeAsync(token);
|
||||
List<BflProjectEntry> projects = [];
|
||||
foreach (var directory in Directory.EnumerateDirectories(this.RootDirectory))
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
if (!Guid.TryParseExact(Path.GetFileName(directory), "D", out var projectId))
|
||||
continue;
|
||||
|
||||
projects.Add(await this.LoadProjectEntryAsync(projectId, directory, token));
|
||||
}
|
||||
|
||||
return [.. projects.OrderByDescending(project => project.ModifiedAtUtc)];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a preparation.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The preparation, or null when it does not exist or this version cannot open it.</returns>
|
||||
public async Task<BflProject?> LoadAsync(Guid projectId, CancellationToken token = default)
|
||||
{
|
||||
await this.InitializeAsync(token);
|
||||
try
|
||||
{
|
||||
return await this.LoadWithoutInitializeAsync(projectId, token);
|
||||
}
|
||||
catch (Exception exception) when (exception is IOException or UnauthorizedAccessException or JsonException)
|
||||
{
|
||||
this.LogUnavailableProject(projectId, exception);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a preparation.
|
||||
/// </summary>
|
||||
/// <param name="name">The name the person gave it.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The new preparation.</returns>
|
||||
/// <exception cref="ArgumentException">The name is empty.</exception>
|
||||
public async Task<BflProject> CreateAsync(string name, CancellationToken token = default)
|
||||
{
|
||||
var sanitizedName = RequireName(name);
|
||||
await this.InitializeAsync(token);
|
||||
|
||||
var projectId = Guid.NewGuid();
|
||||
var gate = this.GetLock(projectId);
|
||||
await gate.WaitAsync(token);
|
||||
try
|
||||
{
|
||||
var directory = this.ProjectDirectory(projectId);
|
||||
if (Directory.Exists(directory))
|
||||
throw new IOException($"A preparation with the ID '{projectId}' exists already.");
|
||||
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var project = new BflProject
|
||||
{
|
||||
ProjectId = projectId,
|
||||
Name = sanitizedName,
|
||||
CreatedAtUtc = now,
|
||||
ModifiedAtUtc = now,
|
||||
};
|
||||
|
||||
await this.StoreProjectAsync(project, token);
|
||||
return project;
|
||||
}
|
||||
finally
|
||||
{
|
||||
gate.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renames a preparation.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
/// <param name="name">The new name.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The renamed preparation.</returns>
|
||||
/// <exception cref="ArgumentException">The name is empty.</exception>
|
||||
public async Task<BflProject> RenameAsync(Guid projectId, string name, CancellationToken token = default)
|
||||
{
|
||||
var sanitizedName = RequireName(name);
|
||||
return await this.MutateProjectAsync(projectId, project => project.Name = sanitizedName, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks the event of a preparation as having taken place, or takes that back.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
/// <param name="happenedAtUtc">When the event took place, or null when it has not.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The changed preparation.</returns>
|
||||
public async Task<BflProject> SetHappenedAsync(Guid projectId, DateTimeOffset? happenedAtUtc, CancellationToken token = default)
|
||||
{
|
||||
return await this.MutateProjectAsync(projectId, project => project.HappenedAtUtc = happenedAtUtc?.ToUniversalTime(), token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a preparation with everything it holds, also when this version cannot open it.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
public async Task DeleteAsync(Guid projectId, CancellationToken token = default)
|
||||
{
|
||||
await this.InitializeAsync(token);
|
||||
var gate = this.GetLock(projectId);
|
||||
await gate.WaitAsync(token);
|
||||
try
|
||||
{
|
||||
var directory = this.ProjectDirectory(projectId);
|
||||
if (Directory.Exists(directory))
|
||||
Directory.Delete(directory, recursive: true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
gate.Release();
|
||||
this.ForgetLock(projectId);
|
||||
}
|
||||
|
||||
await this.ForgetSelectionAsync(projectId, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remembers the preparation the person selected, for the next time the lab opens.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
public async Task RememberSelectionAsync(Guid projectId, CancellationToken token = default)
|
||||
{
|
||||
await this.InitializeAsync(token);
|
||||
if (this.LastSelectedProjectId == projectId)
|
||||
return;
|
||||
|
||||
await this.selectionLock.WaitAsync(token);
|
||||
try
|
||||
{
|
||||
this.LastSelectedProjectId = projectId;
|
||||
await WriteTextAtomicAsync(this.SelectionPath(), JsonSerializer.Serialize<Guid?>(projectId, JSON_OPTIONS), token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.selectionLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forgets the selection when it names the given preparation.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
private async Task ForgetSelectionAsync(Guid projectId, CancellationToken token)
|
||||
{
|
||||
if (this.LastSelectedProjectId != projectId)
|
||||
return;
|
||||
|
||||
await this.selectionLock.WaitAsync(token);
|
||||
try
|
||||
{
|
||||
if (this.LastSelectedProjectId != projectId)
|
||||
return;
|
||||
|
||||
this.LastSelectedProjectId = null;
|
||||
await WriteTextAtomicAsync(this.SelectionPath(), JsonSerializer.Serialize<Guid?>(null, JSON_OPTIONS), token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.selectionLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the preparation selected last, when it still exists.
|
||||
/// </summary>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
private async Task LoadSelectionAsync(CancellationToken token)
|
||||
{
|
||||
var path = this.SelectionPath();
|
||||
if (!File.Exists(path))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var serialized = await File.ReadAllTextAsync(path, token);
|
||||
var selected = JsonSerializer.Deserialize<Guid?>(serialized, JSON_OPTIONS);
|
||||
this.LastSelectedProjectId = selected is not null && Directory.Exists(this.ProjectDirectory(selected.Value)) ? selected : null;
|
||||
}
|
||||
catch (Exception exception) when (exception is IOException or UnauthorizedAccessException or JsonException)
|
||||
{
|
||||
logger.LogWarning(exception, "Could not read the preparation of the BeyondFacts Lab selected last.");
|
||||
this.LastSelectedProjectId = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a preparation for the list, or an entry which says why it cannot be opened.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
/// <param name="directory">The folder of the preparation.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
private async Task<BflProjectEntry> LoadProjectEntryAsync(Guid projectId, string directory, CancellationToken token)
|
||||
{
|
||||
var path = this.ProjectPath(projectId);
|
||||
var fallbackModifiedAtUtc = LastWriteTimeUtc(path, directory);
|
||||
if (!File.Exists(path))
|
||||
return new(projectId, string.Empty, fallbackModifiedAtUtc, BflProjectLoadStatus.UNAVAILABLE, null);
|
||||
|
||||
string json;
|
||||
try
|
||||
{
|
||||
json = await File.ReadAllTextAsync(path, token);
|
||||
}
|
||||
catch (Exception exception) when (exception is IOException or UnauthorizedAccessException)
|
||||
{
|
||||
this.LogUnavailableProject(projectId, exception);
|
||||
return new(projectId, string.Empty, fallbackModifiedAtUtc, BflProjectLoadStatus.UNAVAILABLE, null);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var project = JsonSerializer.Deserialize<BflProject>(json, JSON_OPTIONS);
|
||||
if (project is not null && IsValidProject(project, projectId))
|
||||
return BflProjectEntry.FromProject(project);
|
||||
}
|
||||
catch (JsonException exception)
|
||||
{
|
||||
this.LogUnavailableProject(projectId, exception);
|
||||
}
|
||||
|
||||
return ReadRecoveryEntry(projectId, json, fallbackModifiedAtUtc);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads what the list can show about a preparation this version cannot open: its name, when it changed,
|
||||
/// and whether a newer version stored it.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
/// <param name="json">The content of its file.</param>
|
||||
/// <param name="fallbackModifiedAtUtc">When the file changed, for files without a readable date.</param>
|
||||
private static BflProjectEntry ReadRecoveryEntry(Guid projectId, string json, DateTimeOffset fallbackModifiedAtUtc)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var root = document.RootElement;
|
||||
if (root.ValueKind is not JsonValueKind.Object)
|
||||
return new(projectId, string.Empty, fallbackModifiedAtUtc, BflProjectLoadStatus.UNAVAILABLE, null);
|
||||
|
||||
var name = root.TryGetProperty("name", out var nameElement) && nameElement.ValueKind is JsonValueKind.String ? SanitizeName(nameElement.GetString()) : string.Empty;
|
||||
var modifiedAtUtc = root.TryGetProperty("modifiedAtUtc", out var modifiedElement) && modifiedElement.ValueKind is JsonValueKind.String && modifiedElement.TryGetDateTimeOffset(out var parsedModifiedAtUtc)
|
||||
? parsedModifiedAtUtc
|
||||
: fallbackModifiedAtUtc;
|
||||
|
||||
var isNewer = root.TryGetProperty("schemaVersion", out var versionElement) && versionElement.ValueKind is JsonValueKind.Number && versionElement.TryGetInt32(out var schemaVersion) && schemaVersion > BflProject.CURRENT_SCHEMA_VERSION;
|
||||
return new(projectId, name, modifiedAtUtc, isNewer ? BflProjectLoadStatus.NEWER_VERSION : BflProjectLoadStatus.UNAVAILABLE, null);
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return new(projectId, string.Empty, fallbackModifiedAtUtc, BflProjectLoadStatus.UNAVAILABLE, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets when the file of a preparation, or else its folder, changed last.
|
||||
/// </summary>
|
||||
/// <param name="projectPath">The file of the preparation.</param>
|
||||
/// <param name="directory">The folder of the preparation.</param>
|
||||
private static DateTimeOffset LastWriteTimeUtc(string projectPath, string directory)
|
||||
{
|
||||
try
|
||||
{
|
||||
var timestamp = File.Exists(projectPath) ? File.GetLastWriteTimeUtc(projectPath) : Directory.GetLastWriteTimeUtc(directory);
|
||||
return new DateTimeOffset(timestamp);
|
||||
}
|
||||
catch (Exception exception) when (exception is IOException or UnauthorizedAccessException)
|
||||
{
|
||||
return DateTimeOffset.UnixEpoch;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remembers the step the person looked at last, without counting it as a change of the preparation.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
/// <param name="step">The step.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The preparation.</returns>
|
||||
public async Task<BflProject> SetCurrentStepAsync(Guid projectId, BflStep step, CancellationToken token = default)
|
||||
{
|
||||
return await this.MutateProjectAsync(projectId, project => project.CurrentStep = step, token, isChange: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes a preparation under its lock and stores it.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
/// <param name="mutation">The change.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <param name="isChange">Whether the list shows the preparation as changed now.</param>
|
||||
/// <returns>The changed preparation.</returns>
|
||||
/// <exception cref="FileNotFoundException">The preparation does not exist.</exception>
|
||||
/// <exception cref="InvalidDataException">This version cannot open the preparation, so it does not write it either.</exception>
|
||||
private async Task<BflProject> MutateProjectAsync(Guid projectId, Action<BflProject> mutation, CancellationToken token, bool isChange = true)
|
||||
{
|
||||
await this.InitializeAsync(token);
|
||||
var gate = this.GetLock(projectId);
|
||||
await gate.WaitAsync(token);
|
||||
try
|
||||
{
|
||||
var project = await this.LoadRequiredWithoutInitializeAsync(projectId, token);
|
||||
mutation(project);
|
||||
if (isChange)
|
||||
project.ModifiedAtUtc = DateTimeOffset.UtcNow;
|
||||
|
||||
await this.StoreProjectAsync(project, token);
|
||||
return project;
|
||||
}
|
||||
finally
|
||||
{
|
||||
gate.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a preparation which has to exist and has to be one this version can open, without the first start of the store.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The preparation.</returns>
|
||||
/// <exception cref="FileNotFoundException">The preparation does not exist.</exception>
|
||||
/// <exception cref="InvalidDataException">This version cannot open the preparation.</exception>
|
||||
private async Task<BflProject> LoadRequiredWithoutInitializeAsync(Guid projectId, CancellationToken token)
|
||||
{
|
||||
var path = this.ProjectPath(projectId);
|
||||
if (!File.Exists(path))
|
||||
throw new FileNotFoundException("The preparation does not exist.", path);
|
||||
|
||||
BflProject? project;
|
||||
try
|
||||
{
|
||||
project = await this.LoadWithoutInitializeAsync(projectId, token);
|
||||
}
|
||||
catch (JsonException exception)
|
||||
{
|
||||
throw new InvalidDataException("This version of AI Studio cannot open the preparation.", exception);
|
||||
}
|
||||
|
||||
return project ?? throw new InvalidDataException("This version of AI Studio cannot open the preparation.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a preparation without the first start of the store.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The preparation, or null when it does not exist or is not valid.</returns>
|
||||
private async Task<BflProject?> LoadWithoutInitializeAsync(Guid projectId, CancellationToken token)
|
||||
{
|
||||
var path = this.ProjectPath(projectId);
|
||||
if (!File.Exists(path))
|
||||
return null;
|
||||
|
||||
await using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 65_536, true);
|
||||
var project = await JsonSerializer.DeserializeAsync<BflProject>(stream, JSON_OPTIONS, token);
|
||||
return project is not null && IsValidProject(project, projectId) ? project : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a preparation in the form of this version.
|
||||
/// </summary>
|
||||
/// <param name="project">The preparation.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
private async Task StoreProjectAsync(BflProject project, CancellationToken token)
|
||||
{
|
||||
project.SchemaVersion = BflProject.CURRENT_SCHEMA_VERSION;
|
||||
var json = JsonSerializer.Serialize(project, JSON_OPTIONS);
|
||||
await WriteTextAtomicAsync(this.ProjectPath(project.ProjectId), json, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks a preparation which was read from a file.
|
||||
/// </summary>
|
||||
/// <param name="project">The preparation.</param>
|
||||
/// <param name="expectedProjectId">The ID its folder names.</param>
|
||||
private static bool IsValidProject(BflProject project, Guid expectedProjectId) =>
|
||||
project.SchemaVersion is >= 1 and <= BflProject.CURRENT_SCHEMA_VERSION &&
|
||||
project.ProjectId != Guid.Empty &&
|
||||
project.ProjectId == expectedProjectId &&
|
||||
!string.IsNullOrWhiteSpace(project.Name) &&
|
||||
Enum.IsDefined(project.CurrentStep);
|
||||
|
||||
/// <summary>
|
||||
/// Cleans a name and refuses one which is empty afterward.
|
||||
/// </summary>
|
||||
/// <param name="name">The name as entered.</param>
|
||||
/// <returns>The cleaned name.</returns>
|
||||
/// <exception cref="ArgumentException">The name is empty.</exception>
|
||||
private static string RequireName(string name)
|
||||
{
|
||||
var sanitizedName = SanitizeName(name);
|
||||
if (sanitizedName.Length == 0)
|
||||
throw new ArgumentException("A preparation needs a name.", nameof(name));
|
||||
|
||||
return sanitizedName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs why a preparation cannot be opened.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
/// <param name="exception">What went wrong.</param>
|
||||
private void LogUnavailableProject(Guid projectId, Exception exception)
|
||||
{
|
||||
logger.LogWarning(exception, "Could not open the preparation '{ProjectId}' of the BeyondFacts Lab.", projectId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the preparations of the BeyondFacts Lab as files below the data directory.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Each preparation lives in a folder named by its ID. The store keeps no paths: it derives every path from
|
||||
/// IDs, so a backup works on another computer as well. It writes each file atomically, guards each preparation
|
||||
/// with a lock of its own, and never deletes anything on its own; only the person deletes a preparation.
|
||||
/// </remarks>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="storageOptions">Overrides the data directory, for tests.</param>
|
||||
public sealed partial class BeyondFactsLabStore(ILogger<BeyondFactsLabStore> logger, BflStorageOptions? storageOptions = null)
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the folder below the data directory.
|
||||
/// </summary>
|
||||
public const string ROOT_DIRECTORY_NAME = "beyondFactsLab";
|
||||
|
||||
/// <summary>
|
||||
/// The longest name of a preparation the store keeps.
|
||||
/// </summary>
|
||||
public const int MAX_NAME_LENGTH = 200;
|
||||
|
||||
/// <summary>
|
||||
/// The file of a preparation which holds its name and dates.
|
||||
/// </summary>
|
||||
private const string PROJECT_FILE_NAME = "project.json";
|
||||
|
||||
/// <summary>
|
||||
/// The file which holds the preparation selected last.
|
||||
/// </summary>
|
||||
private const string SELECTION_FILE_NAME = "selection.json";
|
||||
|
||||
/// <summary>
|
||||
/// The JSON options of all files of the lab.
|
||||
/// </summary>
|
||||
private static readonly JsonSerializerOptions JSON_OPTIONS = BeyondFactsLabJson.PERSISTENCE;
|
||||
|
||||
/// <summary>
|
||||
/// The lock of each preparation.
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<Guid, SemaphoreSlim> projectLocks = [];
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the first start of the store.
|
||||
/// </summary>
|
||||
private readonly SemaphoreSlim initializationLock = new(1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes writing the selection.
|
||||
/// </summary>
|
||||
private readonly SemaphoreSlim selectionLock = new(1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// Whether the store cleaned up and read the selection already.
|
||||
/// </summary>
|
||||
private bool initialized;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the folder of the lab below the data directory.
|
||||
/// </summary>
|
||||
private string RootDirectory => Path.Combine(
|
||||
storageOptions?.DataDirectory ??
|
||||
SettingsManager.DataDirectory ??
|
||||
throw new InvalidOperationException("The AI Studio data directory is not initialized."),
|
||||
ROOT_DIRECTORY_NAME);
|
||||
|
||||
/// <summary>
|
||||
/// Writes a text file so that readers see either the old or the new content, never a part of it.
|
||||
/// </summary>
|
||||
/// <param name="targetPath">The file to write.</param>
|
||||
/// <param name="content">The new content.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
private static async Task WriteTextAtomicAsync(string targetPath, string content, CancellationToken token)
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetPath)!);
|
||||
var temporaryPath = $"{targetPath}.tmp-{Guid.NewGuid():N}";
|
||||
try
|
||||
{
|
||||
await File.WriteAllTextAsync(temporaryPath, content, new UTF8Encoding(false), token);
|
||||
await using (var stream = new FileStream(temporaryPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None, 4_096, true))
|
||||
await stream.FlushAsync(token);
|
||||
|
||||
File.Move(temporaryPath, targetPath, overwrite: true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryDeleteFile(temporaryPath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a file when it exists, without failing.
|
||||
/// </summary>
|
||||
/// <param name="path">The file to delete.</param>
|
||||
private static void TryDeleteFile(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(path))
|
||||
File.Delete(path);
|
||||
}
|
||||
catch (Exception exception) when (exception is IOException or UnauthorizedAccessException)
|
||||
{
|
||||
// Cleaning up a temporary file is best effort; the next start tries again.
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes control characters from a name, trims it, and cuts it to the longest name the store keeps.
|
||||
/// </summary>
|
||||
/// <param name="name">The name as entered or as read from a file.</param>
|
||||
/// <returns>The cleaned name, empty when nothing remains.</returns>
|
||||
internal static string SanitizeName(string? name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
return string.Empty;
|
||||
|
||||
var sanitized = new string(name.Where(character => !char.IsControl(character)).ToArray()).Trim();
|
||||
return sanitized.Length <= MAX_NAME_LENGTH ? sanitized : sanitized[..MAX_NAME_LENGTH].TrimEnd();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the lock of a preparation.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
private SemaphoreSlim GetLock(Guid projectId) => this.projectLocks.GetOrAdd(projectId, _ => new(1, 1));
|
||||
|
||||
/// <summary>
|
||||
/// Drops the lock of a preparation which does not exist anymore.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The semaphore is not disposed: another operation might still wait on it.
|
||||
/// </remarks>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
private void ForgetLock(Guid projectId) => this.projectLocks.TryRemove(projectId, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the folder of a preparation.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
private string ProjectDirectory(Guid projectId) => Path.Combine(this.RootDirectory, projectId.ToString("D"));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file which holds the name and the dates of a preparation.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
private string ProjectPath(Guid projectId) => Path.Combine(this.ProjectDirectory(projectId), PROJECT_FILE_NAME);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file which holds the preparation selected last.
|
||||
/// </summary>
|
||||
private string SelectionPath() => Path.Combine(this.RootDirectory, SELECTION_FILE_NAME);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the folder of the lab, removes temporary files a crash left behind, and reads the selection.
|
||||
/// </summary>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
private async Task InitializeAsync(CancellationToken token)
|
||||
{
|
||||
if (this.initialized)
|
||||
return;
|
||||
|
||||
await this.initializationLock.WaitAsync(token);
|
||||
try
|
||||
{
|
||||
if (this.initialized)
|
||||
return;
|
||||
|
||||
Directory.CreateDirectory(this.RootDirectory);
|
||||
foreach (var temporaryPath in Directory.EnumerateFiles(this.RootDirectory, "*.tmp-*", SearchOption.AllDirectories))
|
||||
TryDeleteFile(temporaryPath);
|
||||
|
||||
await this.LoadSelectionAsync(token);
|
||||
this.initialized = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.initializationLock.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using AIStudio.Settings;
|
||||
|
||||
using ComponentKind = AIStudio.Tools.Components;
|
||||
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// Decides whether the BeyondFacts Lab may be opened.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The lab is a prototype behind its own preview feature, and an organization can hide it like any other
|
||||
/// assistant. The tile asks the same question through its required preview feature; the page asks here,
|
||||
/// because somebody might reach its address without the tile, e.g., from a link or the browser history.
|
||||
/// </remarks>
|
||||
public static class BflAccess
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks whether the BeyondFacts Lab may be opened with the given settings.
|
||||
/// </summary>
|
||||
/// <param name="settingsManager">The settings to check.</param>
|
||||
/// <param name="assistantName">The name of the lab for the log; without one, nothing gets logged.</param>
|
||||
/// <returns>True, when the preview feature is switched on and no configuration hides the lab.</returns>
|
||||
public static bool CanOpen(SettingsManager settingsManager, string assistantName = "") => settingsManager.IsAssistantVisible(
|
||||
ComponentKind.BEYOND_FACTS_LAB_ASSISTANT,
|
||||
assistantName: assistantName,
|
||||
requiredPreviewFeature: ComponentKind.BEYOND_FACTS_LAB_ASSISTANT.RequiredPreviewFeature);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// How the BeyondFacts Lab addresses the person in languages which tell a formal address from an informal one, such as German.
|
||||
/// </summary>
|
||||
public enum BflAddressForm
|
||||
{
|
||||
/// <summary>
|
||||
/// The formal address, such as "Sie" in German. The texts of AI Studio use it as well.
|
||||
/// </summary>
|
||||
FORMAL,
|
||||
|
||||
/// <summary>
|
||||
/// The informal address, such as "du" in German.
|
||||
/// </summary>
|
||||
INFORMAL,
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
public static class BflAddressFormExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(BflAddressFormExtensions).Namespace, nameof(BflAddressFormExtensions));
|
||||
|
||||
/// <param name="addressForm">The form of address.</param>
|
||||
extension(BflAddressForm addressForm)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the form of address, as the settings show it.
|
||||
/// </summary>
|
||||
public string Name => addressForm switch
|
||||
{
|
||||
BflAddressForm.FORMAL => TB("Formal address (Sie in German)"),
|
||||
BflAddressForm.INFORMAL => TB("Informal address (du in German)"),
|
||||
|
||||
_ => TB("Formal address (Sie in German)"),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// Every field of the forms "Your details" in the BeyondFacts Lab.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The forms store their values under the IDs of the fields and options, so these IDs never change. A field which
|
||||
/// is no longer needed keeps its ID reserved. The texts of the fields come from BflFieldTexts.
|
||||
/// </remarks>
|
||||
public static class BflFieldCatalog
|
||||
{
|
||||
// Event details:
|
||||
public const string FORMAT = "format";
|
||||
public const string EVENT_TOPIC = "event_topic";
|
||||
public const string AUDIENCE = "audience";
|
||||
public const string CONTRIBUTION_TOPIC = "contribution_topic";
|
||||
public const string EVENT_DATE = "event_date";
|
||||
public const string EVENT_LOCATION = "event_location";
|
||||
public const string EVENT_DURATION = "event_duration";
|
||||
public const string CONTRIBUTION_DURATION = "contribution_duration";
|
||||
public const string OTHER_PARTICIPANTS = "other_participants";
|
||||
public const string EVENT_LINK = "event_link";
|
||||
|
||||
// Knowledge and uncertainty:
|
||||
public const string CONTRIBUTION_SUMMARY = "contribution_summary";
|
||||
public const string CORE_THESES = "core_theses";
|
||||
public const string UNCERTAINTIES = "uncertainties";
|
||||
public const string MATERIAL_NOTES = "material_notes";
|
||||
|
||||
// Goals and role:
|
||||
public const string GOALS = "goals";
|
||||
public const string GOAL_DIRECTIONS = "goal_directions";
|
||||
|
||||
// Values and perspectives:
|
||||
public const string VALUES_AND_CONCERNS = "values_and_concerns";
|
||||
public const string PERSONAL_CONCERN = "personal_concern";
|
||||
public const string OTHER_PERSPECTIVES = "other_perspectives";
|
||||
|
||||
// Strategy:
|
||||
public const string COMMUNICATION_WORRIES = "communication_worries";
|
||||
|
||||
// Test situation:
|
||||
public const string QUESTION_TYPE = "question_type";
|
||||
public const string DIFFICULTY = "difficulty";
|
||||
public const string TEST_NOTES = "test_notes";
|
||||
|
||||
// Results and materials:
|
||||
public const string POSITION_CHANGES = "position_changes";
|
||||
|
||||
// The options of the directions of a goal:
|
||||
public const string GOAL_INFORM = "goal_inform";
|
||||
public const string GOAL_MODERATE = "goal_moderate";
|
||||
public const string GOAL_TAKE_POSITION = "goal_take_position";
|
||||
public const string GOAL_RAISE_AWARENESS = "goal_raise_awareness";
|
||||
public const string GOAL_SHOW_OPTIONS = "goal_show_options";
|
||||
public const string GOAL_INFLUENCE_POLICY = "goal_influence_policy";
|
||||
|
||||
// The options of the type of questions:
|
||||
public const string QUESTIONS_MIXED = "questions_mixed";
|
||||
public const string QUESTIONS_SCIENTIFIC_CRITICISM = "questions_scientific_criticism";
|
||||
public const string QUESTIONS_EMOTIONAL_CRITICISM = "questions_emotional_criticism";
|
||||
public const string QUESTIONS_MANIPULATIVE = "questions_manipulative";
|
||||
public const string QUESTIONS_POLITICAL_INSTRUMENTALIZATION = "questions_political_instrumentalization";
|
||||
|
||||
// The options of the difficulty:
|
||||
public const string DIFFICULTY_MODERATE = "difficulty_moderate";
|
||||
public const string DIFFICULTY_DEMANDING = "difficulty_demanding";
|
||||
public const string DIFFICULTY_VERY_DEMANDING = "difficulty_very_demanding";
|
||||
|
||||
/// <summary>
|
||||
/// Every field, step by step, in the order the forms show them.
|
||||
/// </summary>
|
||||
public static readonly IReadOnlyList<BflFieldDefinition> FIELDS =
|
||||
[
|
||||
new(FORMAT, BflStep.FRAMEWORK, BflFieldKind.SINGLE_LINE) { IsKeyDetail = true, IsSharedWithResearch = true },
|
||||
new(EVENT_TOPIC, BflStep.FRAMEWORK, BflFieldKind.SINGLE_LINE) { IsKeyDetail = true, IsSharedWithResearch = true },
|
||||
new(AUDIENCE, BflStep.FRAMEWORK, BflFieldKind.SINGLE_LINE) { IsKeyDetail = true },
|
||||
|
||||
// The topic of the contribution may be unpublished, so it never reaches the research:
|
||||
new(CONTRIBUTION_TOPIC, BflStep.FRAMEWORK, BflFieldKind.MULTI_LINE) { IsKeyDetail = true },
|
||||
|
||||
new(EVENT_DATE, BflStep.FRAMEWORK, BflFieldKind.SINGLE_LINE) { IsSharedWithResearch = true },
|
||||
new(EVENT_LOCATION, BflStep.FRAMEWORK, BflFieldKind.SINGLE_LINE) { IsSharedWithResearch = true },
|
||||
new(EVENT_DURATION, BflStep.FRAMEWORK, BflFieldKind.SINGLE_LINE),
|
||||
new(CONTRIBUTION_DURATION, BflStep.FRAMEWORK, BflFieldKind.SINGLE_LINE),
|
||||
new(OTHER_PARTICIPANTS, BflStep.FRAMEWORK, BflFieldKind.MULTI_LINE) { IsSharedWithResearch = true, NameRole = BflNameRole.PARTICIPANTS },
|
||||
new(EVENT_LINK, BflStep.FRAMEWORK, BflFieldKind.LINK) { IsSharedWithResearch = true },
|
||||
|
||||
new(CONTRIBUTION_SUMMARY, BflStep.KNOWLEDGE, BflFieldKind.MULTI_LINE) { IsKeyDetail = true },
|
||||
new(CORE_THESES, BflStep.KNOWLEDGE, BflFieldKind.MULTI_LINE),
|
||||
new(UNCERTAINTIES, BflStep.KNOWLEDGE, BflFieldKind.MULTI_LINE),
|
||||
new(MATERIAL_NOTES, BflStep.KNOWLEDGE, BflFieldKind.MULTI_LINE),
|
||||
|
||||
new(GOALS, BflStep.GOALS_AND_ROLE, BflFieldKind.MULTI_LINE),
|
||||
new(GOAL_DIRECTIONS, BflStep.GOALS_AND_ROLE, BflFieldKind.MULTI_CHOICE)
|
||||
{
|
||||
Options = [new(GOAL_INFORM), new(GOAL_MODERATE), new(GOAL_TAKE_POSITION), new(GOAL_RAISE_AWARENESS), new(GOAL_SHOW_OPTIONS), new(GOAL_INFLUENCE_POLICY)],
|
||||
},
|
||||
|
||||
new(VALUES_AND_CONCERNS, BflStep.VALUES_AND_PERSPECTIVES, BflFieldKind.MULTI_LINE),
|
||||
new(PERSONAL_CONCERN, BflStep.VALUES_AND_PERSPECTIVES, BflFieldKind.MULTI_LINE) { IsPersonalDisclosure = true },
|
||||
new(OTHER_PERSPECTIVES, BflStep.VALUES_AND_PERSPECTIVES, BflFieldKind.MULTI_LINE),
|
||||
|
||||
new(COMMUNICATION_WORRIES, BflStep.STRATEGY, BflFieldKind.MULTI_LINE),
|
||||
|
||||
new(QUESTION_TYPE, BflStep.TEST_SITUATION, BflFieldKind.CHOICE)
|
||||
{
|
||||
Options = [new(QUESTIONS_MIXED), new(QUESTIONS_SCIENTIFIC_CRITICISM), new(QUESTIONS_EMOTIONAL_CRITICISM), new(QUESTIONS_MANIPULATIVE), new(QUESTIONS_POLITICAL_INSTRUMENTALIZATION)],
|
||||
},
|
||||
new(DIFFICULTY, BflStep.TEST_SITUATION, BflFieldKind.CHOICE)
|
||||
{
|
||||
Options = [new(DIFFICULTY_MODERATE), new(DIFFICULTY_DEMANDING), new(DIFFICULTY_VERY_DEMANDING)],
|
||||
},
|
||||
new(TEST_NOTES, BflStep.TEST_SITUATION, BflFieldKind.MULTI_LINE),
|
||||
|
||||
new(POSITION_CHANGES, BflStep.RESULTS, BflFieldKind.MULTI_LINE),
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the fields of a step, in the order its form shows them.
|
||||
/// </summary>
|
||||
/// <param name="step">The step.</param>
|
||||
public static IReadOnlyList<BflFieldDefinition> ForStep(BflStep step) => [.. FIELDS.Where(field => field.Step == step)];
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// A field of the form "Your details" of a step in the BeyondFacts Lab.
|
||||
/// </summary>
|
||||
/// <param name="Id">The ID of the field, which a form stores. It never changes.</param>
|
||||
/// <param name="Step">The step whose form shows the field.</param>
|
||||
/// <param name="Kind">How the field takes its value.</param>
|
||||
public sealed record BflFieldDefinition(string Id, BflStep Step, BflFieldKind Kind)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets whether the step works poorly without the field, so the lab marks it as missing while it is empty.
|
||||
/// The person may continue anyway; the chat asks for it.
|
||||
/// </summary>
|
||||
public bool IsKeyDetail { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the field holds something personal, which exports and sharing leave out unless the person
|
||||
/// chooses otherwise.
|
||||
/// </summary>
|
||||
public bool IsPersonalDisclosure { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the field holds public details about the event, which the research on the web may use.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Nothing else of a preparation reaches the research: neither the other fields, nor materials, nor work states.
|
||||
/// </remarks>
|
||||
public bool IsSharedWithResearch { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the field holds names of people.
|
||||
/// </summary>
|
||||
public BflNameRole NameRole { get; init; } = BflNameRole.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the options of a field which offers a choice, in the order to show them.
|
||||
/// </summary>
|
||||
public IReadOnlyList<BflFieldOption> Options { get; init; } = [];
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// How a field of the BeyondFacts Lab takes its value.
|
||||
/// </summary>
|
||||
public enum BflFieldKind
|
||||
{
|
||||
/// <summary>
|
||||
/// A short text in one line.
|
||||
/// </summary>
|
||||
SINGLE_LINE,
|
||||
|
||||
/// <summary>
|
||||
/// A longer text over several lines.
|
||||
/// </summary>
|
||||
MULTI_LINE,
|
||||
|
||||
/// <summary>
|
||||
/// The address of a web page.
|
||||
/// </summary>
|
||||
LINK,
|
||||
|
||||
/// <summary>
|
||||
/// One of the options of the field.
|
||||
/// </summary>
|
||||
CHOICE,
|
||||
|
||||
/// <summary>
|
||||
/// Any number of the options of the field.
|
||||
/// </summary>
|
||||
MULTI_CHOICE,
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
public static class BflFieldKindExtensions
|
||||
{
|
||||
/// <param name="kind">The kind of field.</param>
|
||||
extension(BflFieldKind kind)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the longest value a field of this kind takes, in characters.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The values go into the prompts of the chats, so each kind gets a limit that leaves room for the
|
||||
/// rest of a preparation.
|
||||
/// </remarks>
|
||||
public int MaxLength => kind switch
|
||||
{
|
||||
BflFieldKind.SINGLE_LINE => 300,
|
||||
BflFieldKind.MULTI_LINE => 4_000,
|
||||
BflFieldKind.LINK => 2_000,
|
||||
BflFieldKind.CHOICE => 100,
|
||||
BflFieldKind.MULTI_CHOICE => 1_000,
|
||||
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether a field of this kind offers options to choose from.
|
||||
/// </summary>
|
||||
public bool HasOptions => kind is BflFieldKind.CHOICE or BflFieldKind.MULTI_CHOICE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// An option of a field of the BeyondFacts Lab.
|
||||
/// </summary>
|
||||
/// <param name="Id">The ID of the option, which a form stores. It never changes and is unique across all fields.</param>
|
||||
public sealed record BflFieldOption(string Id);
|
||||
@@ -0,0 +1,112 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// The texts of the fields and options of the BeyondFacts Lab, in the language of the app.
|
||||
/// </summary>
|
||||
public static class BflFieldTexts
|
||||
{
|
||||
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(BflFieldTexts).Namespace, nameof(BflFieldTexts));
|
||||
|
||||
/// <param name="definition">The field.</param>
|
||||
extension(BflFieldDefinition definition)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the label of the field.
|
||||
/// </summary>
|
||||
public string Label => definition.Id switch
|
||||
{
|
||||
BflFieldCatalog.FORMAT => TB("Format of the event"),
|
||||
BflFieldCatalog.EVENT_TOPIC => TB("Topic of the event"),
|
||||
BflFieldCatalog.AUDIENCE => TB("Audience"),
|
||||
BflFieldCatalog.CONTRIBUTION_TOPIC => TB("Topic of your contribution or your specific assignment"),
|
||||
BflFieldCatalog.EVENT_DATE => TB("Date and time"),
|
||||
BflFieldCatalog.EVENT_LOCATION => TB("Place"),
|
||||
BflFieldCatalog.EVENT_DURATION => TB("Total duration of the event"),
|
||||
BflFieldCatalog.CONTRIBUTION_DURATION => TB("Duration of your contribution"),
|
||||
BflFieldCatalog.OTHER_PARTICIPANTS => TB("Other participants"),
|
||||
BflFieldCatalog.EVENT_LINK => TB("Link to the event"),
|
||||
|
||||
BflFieldCatalog.CONTRIBUTION_SUMMARY => TB("Short description of your contribution"),
|
||||
BflFieldCatalog.CORE_THESES => TB("Core theses and arguments (if any)"),
|
||||
BflFieldCatalog.UNCERTAINTIES => TB("Uncertainties and controversies in content and science (if known)"),
|
||||
BflFieldCatalog.MATERIAL_NOTES => TB("Notes on how to use your materials"),
|
||||
|
||||
BflFieldCatalog.GOALS => TB("What do you want to achieve with your contribution?"),
|
||||
BflFieldCatalog.GOAL_DIRECTIONS => TB("Suggestions"),
|
||||
|
||||
BflFieldCatalog.VALUES_AND_CONCERNS => TB("Values and concerns: What matters most to you about this topic?"),
|
||||
BflFieldCatalog.PERSONAL_CONCERN => TB("Personal involvement: Where does the topic touch you personally? Which question or criticism could trigger a strong reaction?"),
|
||||
BflFieldCatalog.OTHER_PERSPECTIVES => TB("Other perspectives: Whose point of view would you like to understand better, or have you hardly considered so far?"),
|
||||
|
||||
BflFieldCatalog.COMMUNICATION_WORRIES => TB("Uncertainties and worries about communicating"),
|
||||
|
||||
BflFieldCatalog.QUESTION_TYPE => TB("Type of questions"),
|
||||
BflFieldCatalog.DIFFICULTY => TB("Difficulty at the start"),
|
||||
BflFieldCatalog.TEST_NOTES => TB("Special notes for the test"),
|
||||
|
||||
BflFieldCatalog.POSITION_CHANGES => TB("What have you changed, kept, or left open compared to your initial position, and why?"),
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the help text below the field, or an empty string when it needs none.
|
||||
/// </summary>
|
||||
public string Hint => definition.Id switch
|
||||
{
|
||||
BflFieldCatalog.FORMAT => TB("E.g., panel, keynote, or interview; online, on site, or hybrid"),
|
||||
BflFieldCatalog.EVENT_TOPIC => TB("The central question or the title"),
|
||||
BflFieldCatalog.AUDIENCE => TB("E.g., students, experts, or the general public"),
|
||||
BflFieldCatalog.CONTRIBUTION_TOPIC => TB("Stays in your preparation and never goes into the research on the web"),
|
||||
BflFieldCatalog.EVENT_DATE => TB("E.g., December 10, 2 pm"),
|
||||
BflFieldCatalog.EVENT_LOCATION => TB("E.g., the venue and the city, or online"),
|
||||
BflFieldCatalog.EVENT_DURATION => TB("E.g., 90 minutes"),
|
||||
BflFieldCatalog.CONTRIBUTION_DURATION => TB("E.g., 8 minutes"),
|
||||
BflFieldCatalog.OTHER_PARTICIPANTS => TB("Names and roles, as far as you know them"),
|
||||
BflFieldCatalog.EVENT_LINK => TB("The address of the event page, beginning with https://"),
|
||||
|
||||
BflFieldCatalog.CONTRIBUTION_SUMMARY => TB("A few sentences are enough"),
|
||||
BflFieldCatalog.MATERIAL_NOTES => TB("E.g., use only paper X as the basis for the content"),
|
||||
|
||||
BflFieldCatalog.GOALS => TB("In your own words, with the suggestions below, or still open: the chat sharpens your goal and role with you."),
|
||||
|
||||
BflFieldCatalog.PERSONAL_CONCERN => TB("Personal detail: it goes into exports and shared sessions only when you explicitly choose so."),
|
||||
|
||||
BflFieldCatalog.COMMUNICATION_WORRIES => TB("E.g., aggressive moderation, time pressure, or a particular counterpart"),
|
||||
|
||||
BflFieldCatalog.DIFFICULTY => TB("You can raise the difficulty after each question."),
|
||||
BflFieldCatalog.TEST_NOTES => TB("E.g., take up a confirmed conflict of values or a perspective from the step about values and perspectives"),
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
/// <param name="option">The option.</param>
|
||||
extension(BflFieldOption option)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the label of the option.
|
||||
/// </summary>
|
||||
public string Label => option.Id switch
|
||||
{
|
||||
BflFieldCatalog.GOAL_INFORM => TB("Inform"),
|
||||
BflFieldCatalog.GOAL_MODERATE => TB("Moderate the discussion"),
|
||||
BflFieldCatalog.GOAL_TAKE_POSITION => TB("Take a position in the debate"),
|
||||
BflFieldCatalog.GOAL_RAISE_AWARENESS => TB("Raise awareness"),
|
||||
BflFieldCatalog.GOAL_SHOW_OPTIONS => TB("Show options for action"),
|
||||
BflFieldCatalog.GOAL_INFLUENCE_POLICY => TB("Influence policy"),
|
||||
|
||||
BflFieldCatalog.QUESTIONS_MIXED => TB("Mixed"),
|
||||
BflFieldCatalog.QUESTIONS_SCIENTIFIC_CRITICISM => TB("Scientific criticism"),
|
||||
BflFieldCatalog.QUESTIONS_EMOTIONAL_CRITICISM => TB("Emotionally charged criticism"),
|
||||
BflFieldCatalog.QUESTIONS_MANIPULATIVE => TB("Manipulative questions and straw men"),
|
||||
BflFieldCatalog.QUESTIONS_POLITICAL_INSTRUMENTALIZATION => TB("Political instrumentalization"),
|
||||
|
||||
BflFieldCatalog.DIFFICULTY_MODERATE => TB("Moderate"),
|
||||
BflFieldCatalog.DIFFICULTY_DEMANDING => TB("Demanding"),
|
||||
BflFieldCatalog.DIFFICULTY_VERY_DEMANDING => TB("Very demanding"),
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// Finds the key details a preparation of the BeyondFacts Lab still lacks.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Missing details never block anything: the person may continue, and the chat asks for them.
|
||||
/// </remarks>
|
||||
public static class BflMissingDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the key details of a step which its form leaves empty.
|
||||
/// </summary>
|
||||
/// <param name="step">The step.</param>
|
||||
/// <param name="form">The form of the step.</param>
|
||||
/// <returns>The fields which are missing, in the order the form shows them.</returns>
|
||||
public static IReadOnlyList<BflFieldDefinition> Of(BflStep step, BflStepFormData form) =>
|
||||
[.. BflFieldCatalog.ForStep(step).Where(field => field.IsKeyDetail && string.IsNullOrWhiteSpace(form.Get(field.Id)))];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the steps whose forms leave key details empty.
|
||||
/// </summary>
|
||||
/// <param name="forms">The forms of a preparation, by their steps; a step without a form counts as empty.</param>
|
||||
/// <returns>The steps with missing details, in their order.</returns>
|
||||
public static IReadOnlyList<BflStep> StepsOf(IReadOnlyDictionary<BflStep, BflStepFormData> forms) =>
|
||||
[.. BflStep.All.Where(step => Of(step, forms.GetValueOrDefault(step) ?? new BflStepFormData()).Count > 0)];
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// Whether a field of the BeyondFacts Lab holds names of people, which sharing a session replaces.
|
||||
/// </summary>
|
||||
public enum BflNameRole
|
||||
{
|
||||
/// <summary>
|
||||
/// The field holds no names on purpose.
|
||||
/// </summary>
|
||||
NONE,
|
||||
|
||||
/// <summary>
|
||||
/// The field holds the names of other participants of the event.
|
||||
/// </summary>
|
||||
PARTICIPANTS,
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// The notes which the BeyondFacts Lab shows before the first use, and again whenever their text changes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The notes are a text of the localization, so they come in every language of the app. The settings keep a hash of
|
||||
/// the notes as the app showed them when the person confirmed them. A new wording, in English or in a translation,
|
||||
/// therefore shows them again, and so does a switch to another language, whose notes the person has not read yet.
|
||||
/// </remarks>
|
||||
public static class BflNotice
|
||||
{
|
||||
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(BflNotice).Namespace, nameof(BflNotice));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the notes in the language of the app, as Markdown.
|
||||
/// </summary>
|
||||
public static string Text => TB("""
|
||||
## Welcome!
|
||||
|
||||
The BeyondFacts Lab guides you through an AI-supported process of reflection, which helps you take a position in challenging communication situations and stay able to communicate.
|
||||
|
||||
Data protection depends on the AI provider you choose, its rules for processing and storing data, and your configuration. Enter only data that are suitable and approved for this.
|
||||
|
||||
Please check all results critically. Depending on the AI model, the answers may differ. Hallucinations cannot be ruled out.
|
||||
|
||||
This is a prototype, so we look forward to your feedback.
|
||||
""");
|
||||
|
||||
/// <summary>
|
||||
/// Computes the hash of the notes as the app shows them.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Line endings and the space around the text do not count, so that a build from a checkout with other line endings
|
||||
/// does not show the notes again.
|
||||
/// </remarks>
|
||||
/// <param name="notes">The notes, as the app shows them.</param>
|
||||
/// <returns>The hash, as lowercase hexadecimal digits.</returns>
|
||||
public static string ComputeHash(string notes) => Convert.ToHexStringLower(SHA256.HashData(Encoding.UTF8.GetBytes(notes.ReplaceLineEndings("\n").Trim())));
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the person confirmed the notes as the app shows them now.
|
||||
/// </summary>
|
||||
/// <param name="acceptedHash">The hash the settings keep.</param>
|
||||
/// <param name="currentHash">The hash of the notes as the app shows them now.</param>
|
||||
public static bool IsAccepted(string acceptedHash, string currentHash) => string.Equals(acceptedHash, currentHash, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
@inherits MSGComponentBase
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
<MudJustifiedMarkdown Value="@this.Notice"/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="@this.Confirm" Variant="Variant.Filled" Color="Color.Primary">
|
||||
@T("I have read the notes")
|
||||
</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
@@ -0,0 +1,22 @@
|
||||
using AIStudio.Components;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// Shows the notes of the BeyondFacts Lab, which the person confirms having read.
|
||||
/// </summary>
|
||||
public partial class BflNoticeDialog : MSGComponentBase
|
||||
{
|
||||
[CascadingParameter]
|
||||
private IMudDialogInstance MudDialog { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the notes, as Markdown.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string Notice { get; set; } = string.Empty;
|
||||
|
||||
private void Confirm() => this.MudDialog.Close(DialogResult.Ok(true));
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// A preparation in the BeyondFacts Lab: one event, for which a person works through the steps of the lab.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The lab stores it as project.json in the folder of the preparation. Everything else the preparation
|
||||
/// holds lies in files beside it, which the lab finds by the ID of the preparation, never by a stored path.
|
||||
/// </remarks>
|
||||
public sealed class BflProject
|
||||
{
|
||||
/// <summary>
|
||||
/// The version of the stored form this version of AI Studio writes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A preparation of a newer form is listed but neither opened nor written, so that an older version of
|
||||
/// AI Studio cannot lose what a newer one stored. Raise it with every field a released version does not
|
||||
/// know yet: that version refuses fields it does not know, and only the higher number tells the person
|
||||
/// that a newer version stored the preparation.
|
||||
/// </remarks>
|
||||
public const int CURRENT_SCHEMA_VERSION = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the version of the stored form.
|
||||
/// </summary>
|
||||
public int SchemaVersion { get; set; } = CURRENT_SCHEMA_VERSION;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ID of the preparation, which is also the name of its folder.
|
||||
/// </summary>
|
||||
public Guid ProjectId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name the person gave the preparation.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets when the preparation was created.
|
||||
/// </summary>
|
||||
public DateTimeOffset CreatedAtUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets when the preparation was changed last.
|
||||
/// </summary>
|
||||
public DateTimeOffset ModifiedAtUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets when the person marked the event as having taken place, or null while it has not.
|
||||
/// </summary>
|
||||
public DateTimeOffset? HappenedAtUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the step the person looked at last, where the lab opens the preparation again.
|
||||
/// </summary>
|
||||
public BflStep CurrentStep { get; set; } = BflStep.FRAMEWORK;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// A preparation in the list of the BeyondFacts Lab, also when it cannot be opened.
|
||||
/// </summary>
|
||||
/// <param name="ProjectId">The ID of the preparation.</param>
|
||||
/// <param name="Name">The name to show; empty when even the name cannot be read.</param>
|
||||
/// <param name="ModifiedAtUtc">When the preparation was changed last.</param>
|
||||
/// <param name="Status">Whether the preparation can be opened.</param>
|
||||
/// <param name="Project">The preparation, when it can be opened.</param>
|
||||
public sealed record BflProjectEntry(Guid ProjectId, string Name, DateTimeOffset ModifiedAtUtc, BflProjectLoadStatus Status, BflProject? Project)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets whether the preparation can be opened.
|
||||
/// </summary>
|
||||
public bool IsAvailable => this.Status is BflProjectLoadStatus.AVAILABLE && this.Project is not null;
|
||||
|
||||
/// <summary>
|
||||
/// Creates the entry of a preparation which can be opened.
|
||||
/// </summary>
|
||||
/// <param name="project">The preparation.</param>
|
||||
public static BflProjectEntry FromProject(BflProject project) => new(project.ProjectId, project.Name, project.ModifiedAtUtc, BflProjectLoadStatus.AVAILABLE, project);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this version of AI Studio can open a stored preparation of the BeyondFacts Lab.
|
||||
/// </summary>
|
||||
public enum BflProjectLoadStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// The preparation can be opened.
|
||||
/// </summary>
|
||||
AVAILABLE,
|
||||
|
||||
/// <summary>
|
||||
/// A newer version of AI Studio stored the preparation. It stays as it is until that version opens it again.
|
||||
/// </summary>
|
||||
NEWER_VERSION,
|
||||
|
||||
/// <summary>
|
||||
/// The preparation cannot be read, e.g., because its file is damaged. It stays as it is; nothing deletes it on its own.
|
||||
/// </summary>
|
||||
UNAVAILABLE,
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// Decides which preparation the BeyondFacts Lab shows after its list changed.
|
||||
/// </summary>
|
||||
public static class BflProjectSelection
|
||||
{
|
||||
/// <summary>
|
||||
/// Chooses the preparation to show: the one asked for, else the one selected last, else the one changed last.
|
||||
/// </summary>
|
||||
/// <param name="projects">The preparations, the one changed last first.</param>
|
||||
/// <param name="preferredId">The preparation asked for, e.g., one just created or the one shown so far.</param>
|
||||
/// <param name="lastSelectedId">The preparation the person selected last, from an earlier session.</param>
|
||||
/// <returns>The preparation to show, or null when there is none.</returns>
|
||||
public static BflProjectEntry? Choose(IReadOnlyList<BflProjectEntry> projects, Guid? preferredId, Guid? lastSelectedId)
|
||||
{
|
||||
foreach (var candidateId in new[] { preferredId, lastSelectedId })
|
||||
{
|
||||
if (candidateId is null)
|
||||
continue;
|
||||
|
||||
var candidate = projects.FirstOrDefault(project => project.ProjectId == candidateId);
|
||||
if (candidate is not null)
|
||||
return candidate;
|
||||
}
|
||||
|
||||
return projects.Count > 0 ? projects[0] : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// The steps of a preparation in the BeyondFacts Lab, in the order the lab suggests.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The names are stored, e.g., as the step shown last, so they never change. People may jump to any step.
|
||||
/// </remarks>
|
||||
public enum BflStep
|
||||
{
|
||||
/// <summary>
|
||||
/// What the event is about, for whom, and what the person was asked to contribute.
|
||||
/// </summary>
|
||||
FRAMEWORK,
|
||||
|
||||
/// <summary>
|
||||
/// What the person knows for sure, what is uncertain, and where their expertise ends.
|
||||
/// </summary>
|
||||
KNOWLEDGE,
|
||||
|
||||
/// <summary>
|
||||
/// What the person wants to achieve, and in which role they speak.
|
||||
/// </summary>
|
||||
GOALS_AND_ROLE,
|
||||
|
||||
/// <summary>
|
||||
/// What matters to the person, what touches them, and what moves others.
|
||||
/// </summary>
|
||||
VALUES_AND_PERSPECTIVES,
|
||||
|
||||
/// <summary>
|
||||
/// How the person conveys evidence, trade-offs between values, and open questions.
|
||||
/// </summary>
|
||||
STRATEGY,
|
||||
|
||||
/// <summary>
|
||||
/// Critical questions and opposing positions, played through.
|
||||
/// </summary>
|
||||
TEST_SITUATION,
|
||||
|
||||
/// <summary>
|
||||
/// What the person takes away from the preparation, and the materials for the event.
|
||||
/// </summary>
|
||||
RESULTS,
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
public static class BflStepExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(BflStepExtensions).Namespace, nameof(BflStepExtensions));
|
||||
|
||||
/// <param name="step">The step.</param>
|
||||
extension(BflStep step)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the title of the step. It names the topic, never a number.
|
||||
/// </summary>
|
||||
public string Title => step switch
|
||||
{
|
||||
BflStep.FRAMEWORK => TB("Event details"),
|
||||
BflStep.KNOWLEDGE => TB("Knowledge and uncertainty"),
|
||||
BflStep.GOALS_AND_ROLE => TB("Goals and role"),
|
||||
BflStep.VALUES_AND_PERSPECTIVES => TB("Values and perspectives"),
|
||||
BflStep.STRATEGY => TB("Strategy"),
|
||||
BflStep.TEST_SITUATION => TB("Test situation"),
|
||||
BflStep.RESULTS => TB("Results and materials"),
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets one sentence about what the step is for.
|
||||
/// </summary>
|
||||
public string Description => step switch
|
||||
{
|
||||
BflStep.FRAMEWORK => TB("What the event is about, for whom, and what you were asked to contribute."),
|
||||
BflStep.KNOWLEDGE => TB("What you know for sure, what is uncertain, and where your expertise ends."),
|
||||
BflStep.GOALS_AND_ROLE => TB("What you want to achieve, and in which role you speak."),
|
||||
BflStep.VALUES_AND_PERSPECTIVES => TB("What matters to you, what touches you, and what moves others."),
|
||||
BflStep.STRATEGY => TB("How you convey evidence, trade-offs between values, and open questions."),
|
||||
BflStep.TEST_SITUATION => TB("Play through critical questions and opposing positions."),
|
||||
BflStep.RESULTS => TB("What you take away from your preparation."),
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets a hint for the form of the step, or an empty string when the step needs none.
|
||||
/// </summary>
|
||||
public string FormHint => step switch
|
||||
{
|
||||
BflStep.VALUES_AND_PERSPECTIVES => TB("You can fill in single fields or start right away in the chat."),
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the folder in which a preparation keeps the files of the step.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The number keeps the folders in the order of the steps for people who look into the data folder.
|
||||
/// These names are stored on disk, so they never change.
|
||||
/// </remarks>
|
||||
public string FolderName => step switch
|
||||
{
|
||||
BflStep.FRAMEWORK => "01-framework",
|
||||
BflStep.KNOWLEDGE => "02-knowledge",
|
||||
BflStep.GOALS_AND_ROLE => "03-goals",
|
||||
BflStep.VALUES_AND_PERSPECTIVES => "04-values",
|
||||
BflStep.STRATEGY => "05-strategy",
|
||||
BflStep.TEST_SITUATION => "06-test",
|
||||
BflStep.RESULTS => "07-results",
|
||||
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(step), step, "The step has no folder."),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets every step, in the order the lab suggests.
|
||||
/// </summary>
|
||||
public static IReadOnlyList<BflStep> All => Enum.GetValues<BflStep>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
@inherits MSGComponentBase
|
||||
|
||||
@if (this.MissingFields.Count > 0)
|
||||
{
|
||||
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined" Dense="true" Class="mb-3">
|
||||
@T("Some details are still missing. The chat will ask for them, and you can continue anyway.")
|
||||
<br/>
|
||||
@string.Join(", ", this.MissingFields.Select(field => field.Label))
|
||||
</MudAlert>
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(this.Step.FormHint))
|
||||
{
|
||||
<MudText Typo="Typo.body2" Class="mb-3">@this.Step.FormHint</MudText>
|
||||
}
|
||||
|
||||
<MudStack Spacing="3">
|
||||
@foreach (var field in this.Fields)
|
||||
{
|
||||
switch (field.Kind)
|
||||
{
|
||||
case BflFieldKind.SINGLE_LINE:
|
||||
case BflFieldKind.LINK:
|
||||
<MudTextField @key="field.Id"
|
||||
T="string"
|
||||
Value="@this.Form.Get(field.Id)"
|
||||
ValueChanged="@(value => this.SetValueAsync(field, value))"
|
||||
Label="@LabelOf(field)"
|
||||
HelperText="@field.Hint"
|
||||
MaxLength="@field.Kind.MaxLength"
|
||||
Validation="@this.ValidationOf(field)"
|
||||
Disabled="@this.Disabled"
|
||||
Variant="Variant.Outlined"/>
|
||||
break;
|
||||
|
||||
case BflFieldKind.MULTI_LINE:
|
||||
<MudTextField @key="field.Id"
|
||||
T="string"
|
||||
Value="@this.Form.Get(field.Id)"
|
||||
ValueChanged="@(value => this.SetValueAsync(field, value))"
|
||||
Label="@LabelOf(field)"
|
||||
HelperText="@field.Hint"
|
||||
MaxLength="@field.Kind.MaxLength"
|
||||
Disabled="@this.Disabled"
|
||||
Variant="Variant.Outlined"
|
||||
Lines="3"
|
||||
Sizing="InputSizing.Auto"
|
||||
MaxLines="12"/>
|
||||
break;
|
||||
|
||||
case BflFieldKind.CHOICE:
|
||||
<MudSelect @key="field.Id"
|
||||
T="string"
|
||||
Value="@this.Form.Get(field.Id)"
|
||||
ValueChanged="@(value => this.SetValueAsync(field, value))"
|
||||
Label="@LabelOf(field)"
|
||||
HelperText="@field.Hint"
|
||||
Clearable="true"
|
||||
Disabled="@this.Disabled"
|
||||
Variant="Variant.Outlined">
|
||||
@foreach (var option in field.Options)
|
||||
{
|
||||
<MudSelectItem T="string" Value="@option.Id">@option.Label</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
break;
|
||||
|
||||
case BflFieldKind.MULTI_CHOICE:
|
||||
<div @key="field.Id">
|
||||
<MudText Typo="Typo.body2">@LabelOf(field)</MudText>
|
||||
<MudChipSet T="string"
|
||||
SelectionMode="SelectionMode.MultiSelection"
|
||||
SelectedValues="@this.Form.GetChoices(field.Id)"
|
||||
SelectedValuesChanged="@(values => this.SetChoicesAsync(field, values))"
|
||||
CheckMark="true"
|
||||
Color="Color.Primary"
|
||||
Variant="Variant.Outlined"
|
||||
Disabled="@this.Disabled">
|
||||
@foreach (var option in field.Options)
|
||||
{
|
||||
<MudChip T="string" Value="@option.Id" Text="@option.Label"/>
|
||||
}
|
||||
</MudChipSet>
|
||||
@if (!string.IsNullOrWhiteSpace(field.Hint))
|
||||
{
|
||||
<MudText Typo="Typo.caption">@field.Hint</MudText>
|
||||
}
|
||||
</div>
|
||||
break;
|
||||
}
|
||||
}
|
||||
</MudStack>
|
||||
|
||||
@if (this.HasKeyDetails)
|
||||
{
|
||||
<MudText Typo="Typo.caption" Class="mt-2">@T("* The chat works best when you fill in these details.")</MudText>
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using AIStudio.Components;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// The form "Your details" of one step of the BeyondFacts Lab.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The form changes the values it gets and tells its parent, which stores them. Empty key details are marked but
|
||||
/// never block anything.
|
||||
/// </remarks>
|
||||
public partial class BflStepForm : MSGComponentBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the step whose fields the form shows.
|
||||
/// </summary>
|
||||
[Parameter, EditorRequired]
|
||||
public BflStep Step { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the values of the form, which the form changes.
|
||||
/// </summary>
|
||||
[Parameter, EditorRequired]
|
||||
public BflStepFormData Form { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the fields cannot be changed.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the callback raised after a value changed.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public EventCallback FormChanged { get; set; }
|
||||
|
||||
private IReadOnlyList<BflFieldDefinition> Fields => BflFieldCatalog.ForStep(this.Step);
|
||||
|
||||
private IReadOnlyList<BflFieldDefinition> MissingFields => BflMissingDetails.Of(this.Step, this.Form);
|
||||
|
||||
private bool HasKeyDetails => this.Fields.Any(definition => definition.IsKeyDetail);
|
||||
|
||||
private static string LabelOf(BflFieldDefinition field) => field.IsKeyDetail ? $"{field.Label} *" : field.Label;
|
||||
|
||||
private async Task SetValueAsync(BflFieldDefinition field, string? value)
|
||||
{
|
||||
var limited = value is not null && value.Length > field.Kind.MaxLength ? value[..field.Kind.MaxLength] : value;
|
||||
if (string.Equals(this.Form.Get(field.Id), limited ?? string.Empty, StringComparison.Ordinal))
|
||||
return;
|
||||
|
||||
this.Form.Set(field.Id, limited);
|
||||
await this.FormChanged.InvokeAsync();
|
||||
}
|
||||
|
||||
private async Task SetChoicesAsync(BflFieldDefinition field, IReadOnlyCollection<string>? optionIds)
|
||||
{
|
||||
this.Form.SetChoices(field, optionIds ?? []);
|
||||
await this.FormChanged.InvokeAsync();
|
||||
}
|
||||
|
||||
private Func<string, string?>? ValidationOf(BflFieldDefinition field) => field.Kind is BflFieldKind.LINK ? this.ValidateLink : null;
|
||||
|
||||
private string? ValidateLink(string value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
return null;
|
||||
|
||||
return Uri.TryCreate(value.Trim(), UriKind.Absolute, out var uri) && (uri.Scheme == Uri.UriSchemeHttps || uri.Scheme == Uri.UriSchemeHttp)
|
||||
? null
|
||||
: T("Please enter an address that begins with https:// or http://.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// What the person entered into the form "Your details" of one step, stored as form.json in the folder of the step.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The values are stored under the IDs of the fields of BflFieldCatalog. A value of a field this version does not
|
||||
/// know, e.g., one a newer version added, stays as it is and is written back unchanged.
|
||||
/// </remarks>
|
||||
public sealed class BflStepFormData
|
||||
{
|
||||
/// <summary>
|
||||
/// The version of the stored form this version of AI Studio writes. Raise it with every new property.
|
||||
/// </summary>
|
||||
public const int CURRENT_SCHEMA_VERSION = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The character which separates the options of a field with several choices.
|
||||
/// </summary>
|
||||
private const char OPTION_SEPARATOR = ',';
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the version of the stored form.
|
||||
/// </summary>
|
||||
public int SchemaVersion { get; set; } = CURRENT_SCHEMA_VERSION;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the values, by the IDs of their fields.
|
||||
/// </summary>
|
||||
public Dictionary<string, string> Values { get; set; } = new(StringComparer.Ordinal);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of a field, or an empty string when the field has none.
|
||||
/// </summary>
|
||||
/// <param name="fieldId">The ID of the field.</param>
|
||||
public string Get(string fieldId) => this.Values.GetValueOrDefault(fieldId, string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of a field; an empty value removes it.
|
||||
/// </summary>
|
||||
/// <param name="fieldId">The ID of the field.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
public void Set(string fieldId, string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
this.Values.Remove(fieldId);
|
||||
else
|
||||
this.Values[fieldId] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the options chosen in a field with several choices.
|
||||
/// </summary>
|
||||
/// <param name="fieldId">The ID of the field.</param>
|
||||
public IReadOnlySet<string> GetChoices(string fieldId) => this.Get(fieldId).Split(OPTION_SEPARATOR, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToHashSet(StringComparer.Ordinal);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the options chosen in a field with several choices.
|
||||
/// </summary>
|
||||
/// <param name="field">The field.</param>
|
||||
/// <param name="optionIds">The IDs of the chosen options.</param>
|
||||
public void SetChoices(BflFieldDefinition field, IEnumerable<string> optionIds)
|
||||
{
|
||||
var chosen = optionIds.ToHashSet(StringComparer.Ordinal);
|
||||
|
||||
// Stored in the order of the options, so that the same choice always gives the same value:
|
||||
this.Set(field.Id, string.Join(OPTION_SEPARATOR, field.Options.Select(option => option.Id).Where(chosen.Contains)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// Overrides where the BeyondFacts Lab stores its preparations, for tests.
|
||||
/// </summary>
|
||||
public sealed class BflStorageOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or initializes the folder in which the folder beyondFactsLab is created.
|
||||
/// </summary>
|
||||
public string? DataDirectory { get; init; }
|
||||
}
|
||||
@@ -38,9 +38,7 @@ else if (!this.isLoading && string.IsNullOrWhiteSpace(this.loadingIssue))
|
||||
@context.Key
|
||||
</pre>
|
||||
</MudTd>
|
||||
<MudTd>
|
||||
@context.Value
|
||||
</MudTd>
|
||||
<MudTd Style="white-space: pre-line;">@context.Value</MudTd>
|
||||
</RowTemplate>
|
||||
<PagerContent>
|
||||
<MudTablePager />
|
||||
@@ -68,9 +66,7 @@ else if (!this.isLoading && string.IsNullOrWhiteSpace(this.loadingIssue))
|
||||
@context.Key
|
||||
</pre>
|
||||
</MudTd>
|
||||
<MudTd>
|
||||
@context.Value
|
||||
</MudTd>
|
||||
<MudTd Style="white-space: pre-line;">@context.Value</MudTd>
|
||||
</RowTemplate>
|
||||
<PagerContent>
|
||||
<MudTablePager />
|
||||
@@ -112,9 +108,7 @@ else if (!this.isLoading && string.IsNullOrWhiteSpace(this.loadingIssue))
|
||||
@context.Key
|
||||
</pre>
|
||||
</MudTd>
|
||||
<MudTd>
|
||||
@context.Value
|
||||
</MudTd>
|
||||
<MudTd Style="white-space: pre-line;">@context.Value</MudTd>
|
||||
</RowTemplate>
|
||||
<PagerContent>
|
||||
<MudTablePager />
|
||||
|
||||
@@ -5,14 +5,8 @@ using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
|
||||
using SharedTools;
|
||||
|
||||
#if RELEASE
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
||||
namespace AIStudio.Assistants.I18N;
|
||||
|
||||
public partial class AssistantI18N : AssistantBaseCore<SettingsDialogI18N>
|
||||
@@ -42,7 +36,10 @@ public partial class AssistantI18N : AssistantBaseCore<SettingsDialogI18N>
|
||||
daily lives with it. The app offers the regular chat mode for which LLMs have become
|
||||
known. However, AI Studio also offers so-called assistants, where users no longer
|
||||
have to prompt.
|
||||
|
||||
|
||||
Some texts are longer notes in Markdown, with headings, paragraphs, or lists. Keep their
|
||||
Markdown and their line breaks exactly as they are, and translate only the words.
|
||||
|
||||
# Target Audience
|
||||
The app is intended for everyone, not just IT specialists or scientists. When translating,
|
||||
make sure the texts are easy for everyone to understand.
|
||||
@@ -250,17 +247,7 @@ public partial class AssistantI18N : AssistantBaseCore<SettingsDialogI18N>
|
||||
//
|
||||
// Read the file `Assistants\I18N\allTexts.lua`:
|
||||
//
|
||||
#if DEBUG
|
||||
var filePath = Path.Join(Environment.CurrentDirectory, "Assistants", "I18N");
|
||||
var resourceFileProvider = new PhysicalFileProvider(filePath);
|
||||
#else
|
||||
var resourceFileProvider = new ManifestEmbeddedFileProvider(Assembly.GetAssembly(type: typeof(Program))!, "Assistants/I18N");
|
||||
#endif
|
||||
|
||||
var file = resourceFileProvider.GetFileInfo("allTexts.lua");
|
||||
await using var fileStream = file.CreateReadStream();
|
||||
using var reader = new StreamReader(fileStream);
|
||||
var newI18NDataLuaCode = await reader.ReadToEndAsync();
|
||||
var newI18NDataLuaCode = await AppResources.ReadTextAsync("Assistants/I18N/allTexts.lua");
|
||||
|
||||
//
|
||||
// Next, we try to load the text as a language plugin -- without
|
||||
|
||||
@@ -733,6 +733,336 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BATCHPROCESSING::BATCHPROCESSINGPROMPTSOU
|
||||
-- Use a document analysis policy
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BATCHPROCESSING::BATCHPROCESSINGPROMPTSOURCEEXTENSIONS::T3309547196"] = "Use a document analysis policy"
|
||||
|
||||
-- Prepare a challenging appearance as a researcher step by step, e.g., a panel discussion, an interview, or a hearing. Each preparation belongs to one event and has its own steps, chats, and work states.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1218290190"] = "Prepare a challenging appearance as a researcher step by step, e.g., a panel discussion, an interview, or a hearing. Each preparation belongs to one event and has its own steps, chats, and work states."
|
||||
|
||||
-- Changed on {0}
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1279137966"] = "Changed on {0}"
|
||||
|
||||
-- Delete
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1469573738"] = "Delete"
|
||||
|
||||
-- AI Studio cannot read this preparation. Its files may be damaged.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1496519091"] = "AI Studio cannot read this preparation. Its files may be damaged."
|
||||
|
||||
-- AI Studio cannot open your details for this step. Their file may be damaged, or a newer version of AI Studio stored it. AI Studio leaves the file unchanged.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1564342983"] = "AI Studio cannot open your details for this step. Their file may be damaged, or a newer version of AI Studio stored it. AI Studio leaves the file unchanged."
|
||||
|
||||
-- You have no preparations yet. Create one for the event you want to prepare for.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1599306665"] = "You have no preparations yet. Create one for the event you want to prepare for."
|
||||
|
||||
-- Enter a new name for this preparation.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T193031277"] = "Enter a new name for this preparation."
|
||||
|
||||
-- Cannot be opened
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1981873292"] = "Cannot be opened"
|
||||
|
||||
-- Copy preparation ID
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2344012850"] = "Copy preparation ID"
|
||||
|
||||
-- Your details
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T25208484"] = "Your details"
|
||||
|
||||
-- Create
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2567824509"] = "Create"
|
||||
|
||||
-- Preparation ID
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2671360307"] = "Preparation ID"
|
||||
|
||||
-- Create a preparation to begin.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2751865753"] = "Create a preparation to begin."
|
||||
|
||||
-- AI Studio has left the files of this preparation unchanged. A future update may make it accessible again.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2824385338"] = "AI Studio has left the files of this preparation unchanged. A future update may make it accessible again."
|
||||
|
||||
-- The event has taken place
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2842348392"] = "The event has taken place"
|
||||
|
||||
-- Marked on {0}.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3254361846"] = "Marked on {0}."
|
||||
|
||||
-- Rename preparation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3342203974"] = "Rename preparation"
|
||||
|
||||
-- Rename
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3355849203"] = "Rename"
|
||||
|
||||
-- Delete preparation permanently
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T354252674"] = "Delete preparation permanently"
|
||||
|
||||
-- Unavailable preparation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3646236084"] = "Unavailable preparation"
|
||||
|
||||
-- This preparation cannot be opened. Permanently delete it with all of its files?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3647862078"] = "This preparation cannot be opened. Permanently delete it with all of its files?"
|
||||
|
||||
-- Name the event you want to prepare for, e.g., the title of a panel discussion.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3679923414"] = "Name the event you want to prepare for, e.g., the title of a panel discussion."
|
||||
|
||||
-- Name of the preparation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3746973353"] = "Name of the preparation"
|
||||
|
||||
-- Please enter a name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3818326829"] = "Please enter a name."
|
||||
|
||||
-- AI Studio could not save your details. Please try again.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3921500806"] = "AI Studio could not save your details. Please try again."
|
||||
|
||||
-- BeyondFacts Lab
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3956097526"] = "BeyondFacts Lab"
|
||||
|
||||
-- A newer version of AI Studio stored this preparation. This version cannot open it. Please update AI Studio.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T395932079"] = "A newer version of AI Studio stored this preparation. This version cannot open it. Please update AI Studio."
|
||||
|
||||
-- AI Studio could not save the change of your preparations. Please try again.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T4073131406"] = "AI Studio could not save the change of your preparations. Please try again."
|
||||
|
||||
-- Requires a newer AI Studio version
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T4087140083"] = "Requires a newer AI Studio version"
|
||||
|
||||
-- Details missing
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T506453229"] = "Details missing"
|
||||
|
||||
-- Permanently delete the preparation '{0}' with all of its steps, chats, and work states?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T772787118"] = "Permanently delete the preparation '{0}' with all of its steps, chats, and work states?"
|
||||
|
||||
-- New preparation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T851005164"] = "New preparation"
|
||||
|
||||
-- Your preparations
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T972511272"] = "Your preparations"
|
||||
|
||||
-- Informal address (du in German)
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLADDRESSFORMEXTENSIONS::T554958468"] = "Informal address (du in German)"
|
||||
|
||||
-- Formal address (Sie in German)
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLADDRESSFORMEXTENSIONS::T859201973"] = "Formal address (Sie in German)"
|
||||
|
||||
-- Moderate the discussion
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1131866893"] = "Moderate the discussion"
|
||||
|
||||
-- E.g., take up a confirmed conflict of values or a perspective from the step about values and perspectives
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1134014442"] = "E.g., take up a confirmed conflict of values or a perspective from the step about values and perspectives"
|
||||
|
||||
-- What have you changed, kept, or left open compared to your initial position, and why?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1455147822"] = "What have you changed, kept, or left open compared to your initial position, and why?"
|
||||
|
||||
-- Show options for action
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1601690165"] = "Show options for action"
|
||||
|
||||
-- Manipulative questions and straw men
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1656068515"] = "Manipulative questions and straw men"
|
||||
|
||||
-- Topic of the event
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1766174370"] = "Topic of the event"
|
||||
|
||||
-- Moderate
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T177463328"] = "Moderate"
|
||||
|
||||
-- Other perspectives: Whose point of view would you like to understand better, or have you hardly considered so far?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1885081930"] = "Other perspectives: Whose point of view would you like to understand better, or have you hardly considered so far?"
|
||||
|
||||
-- Stays in your preparation and never goes into the research on the web
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1949096445"] = "Stays in your preparation and never goes into the research on the web"
|
||||
|
||||
-- Very demanding
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1967151608"] = "Very demanding"
|
||||
|
||||
-- Date and time
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2054731205"] = "Date and time"
|
||||
|
||||
-- Notes on how to use your materials
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2123646094"] = "Notes on how to use your materials"
|
||||
|
||||
-- Duration of your contribution
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T223853289"] = "Duration of your contribution"
|
||||
|
||||
-- Special notes for the test
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2242825987"] = "Special notes for the test"
|
||||
|
||||
-- Uncertainties and worries about communicating
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2428368968"] = "Uncertainties and worries about communicating"
|
||||
|
||||
-- Difficulty at the start
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T243923502"] = "Difficulty at the start"
|
||||
|
||||
-- E.g., 90 minutes
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2537467295"] = "E.g., 90 minutes"
|
||||
|
||||
-- Place
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2597472412"] = "Place"
|
||||
|
||||
-- E.g., 8 minutes
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2697611204"] = "E.g., 8 minutes"
|
||||
|
||||
-- A few sentences are enough
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2765665158"] = "A few sentences are enough"
|
||||
|
||||
-- Other participants
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T277557865"] = "Other participants"
|
||||
|
||||
-- What do you want to achieve with your contribution?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2818219287"] = "What do you want to achieve with your contribution?"
|
||||
|
||||
-- Scientific criticism
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2865565843"] = "Scientific criticism"
|
||||
|
||||
-- The address of the event page, beginning with https://
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2900883219"] = "The address of the event page, beginning with https://"
|
||||
|
||||
-- Names and roles, as far as you know them
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3020176946"] = "Names and roles, as far as you know them"
|
||||
|
||||
-- Personal detail: it goes into exports and shared sessions only when you explicitly choose so.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3063635296"] = "Personal detail: it goes into exports and shared sessions only when you explicitly choose so."
|
||||
|
||||
-- Political instrumentalization
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3078371152"] = "Political instrumentalization"
|
||||
|
||||
-- Mixed
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3114856344"] = "Mixed"
|
||||
|
||||
-- Personal involvement: Where does the topic touch you personally? Which question or criticism could trigger a strong reaction?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3199962614"] = "Personal involvement: Where does the topic touch you personally? Which question or criticism could trigger a strong reaction?"
|
||||
|
||||
-- Format of the event
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3217479436"] = "Format of the event"
|
||||
|
||||
-- E.g., panel, keynote, or interview; online, on site, or hybrid
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3297023821"] = "E.g., panel, keynote, or interview; online, on site, or hybrid"
|
||||
|
||||
-- Values and concerns: What matters most to you about this topic?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3444467007"] = "Values and concerns: What matters most to you about this topic?"
|
||||
|
||||
-- Audience
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3506118019"] = "Audience"
|
||||
|
||||
-- Suggestions
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3534677498"] = "Suggestions"
|
||||
|
||||
-- Emotionally charged criticism
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3552262211"] = "Emotionally charged criticism"
|
||||
|
||||
-- Link to the event
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3658981841"] = "Link to the event"
|
||||
|
||||
-- Raise awareness
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3790372874"] = "Raise awareness"
|
||||
|
||||
-- E.g., use only paper X as the basis for the content
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3804983466"] = "E.g., use only paper X as the basis for the content"
|
||||
|
||||
-- Core theses and arguments (if any)
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T39824489"] = "Core theses and arguments (if any)"
|
||||
|
||||
-- Total duration of the event
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T4051894943"] = "Total duration of the event"
|
||||
|
||||
-- Influence policy
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T4052553234"] = "Influence policy"
|
||||
|
||||
-- You can raise the difficulty after each question.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T519332031"] = "You can raise the difficulty after each question."
|
||||
|
||||
-- E.g., students, experts, or the general public
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T533097975"] = "E.g., students, experts, or the general public"
|
||||
|
||||
-- Short description of your contribution
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T667686615"] = "Short description of your contribution"
|
||||
|
||||
-- Type of questions
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T805059831"] = "Type of questions"
|
||||
|
||||
-- The central question or the title
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T828389221"] = "The central question or the title"
|
||||
|
||||
-- Uncertainties and controversies in content and science (if known)
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T82922542"] = "Uncertainties and controversies in content and science (if known)"
|
||||
|
||||
-- Inform
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T85613896"] = "Inform"
|
||||
|
||||
-- E.g., December 10, 2 pm
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T881586300"] = "E.g., December 10, 2 pm"
|
||||
|
||||
-- E.g., the venue and the city, or online
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T916990010"] = "E.g., the venue and the city, or online"
|
||||
|
||||
-- In your own words, with the suggestions below, or still open: the chat sharpens your goal and role with you.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T917276711"] = "In your own words, with the suggestions below, or still open: the chat sharpens your goal and role with you."
|
||||
|
||||
-- Topic of your contribution or your specific assignment
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T932670647"] = "Topic of your contribution or your specific assignment"
|
||||
|
||||
-- E.g., aggressive moderation, time pressure, or a particular counterpart
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T965551209"] = "E.g., aggressive moderation, time pressure, or a particular counterpart"
|
||||
|
||||
-- Take a position in the debate
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T974131333"] = "Take a position in the debate"
|
||||
|
||||
-- Demanding
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T985014450"] = "Demanding"
|
||||
|
||||
-- ## Welcome! The BeyondFacts Lab guides you through an AI-supported process of reflection, which helps you take a position in challenging communication situations and stay able to communicate. Data protection depends on the AI provider you choose, its rules for processing and storing data, and your configuration. Enter only data that are suitable and approved for this. Please check all results critically. Depending on the AI model, the answers may differ. Hallucinations cannot be ruled out. This is a prototype, so we look forward to your feedback.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLNOTICE::T940905480"] = "## Welcome!\n\nThe BeyondFacts Lab guides you through an AI-supported process of reflection, which helps you take a position in challenging communication situations and stay able to communicate.\n\nData protection depends on the AI provider you choose, its rules for processing and storing data, and your configuration. Enter only data that are suitable and approved for this.\n\nPlease check all results critically. Depending on the AI model, the answers may differ. Hallucinations cannot be ruled out.\n\nThis is a prototype, so we look forward to your feedback."
|
||||
|
||||
-- I have read the notes
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLNOTICEDIALOG::T4071737336"] = "I have read the notes"
|
||||
|
||||
-- Test situation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T1049923473"] = "Test situation"
|
||||
|
||||
-- Knowledge and uncertainty
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T1147870258"] = "Knowledge and uncertainty"
|
||||
|
||||
-- How you convey evidence, trade-offs between values, and open questions.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T1290723890"] = "How you convey evidence, trade-offs between values, and open questions."
|
||||
|
||||
-- Event details
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T135387869"] = "Event details"
|
||||
|
||||
-- What the event is about, for whom, and what you were asked to contribute.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T158292580"] = "What the event is about, for whom, and what you were asked to contribute."
|
||||
|
||||
-- You can fill in single fields or start right away in the chat.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T2524378548"] = "You can fill in single fields or start right away in the chat."
|
||||
|
||||
-- Goals and role
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T2988481290"] = "Goals and role"
|
||||
|
||||
-- What matters to you, what touches you, and what moves others.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T3022557767"] = "What matters to you, what touches you, and what moves others."
|
||||
|
||||
-- What you want to achieve, and in which role you speak.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T3052319680"] = "What you want to achieve, and in which role you speak."
|
||||
|
||||
-- Strategy
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T3205650756"] = "Strategy"
|
||||
|
||||
-- Results and materials
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T3602322182"] = "Results and materials"
|
||||
|
||||
-- Values and perspectives
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T494574525"] = "Values and perspectives"
|
||||
|
||||
-- What you take away from your preparation.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T517692701"] = "What you take away from your preparation."
|
||||
|
||||
-- Play through critical questions and opposing positions.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T573043138"] = "Play through critical questions and opposing positions."
|
||||
|
||||
-- What you know for sure, what is uncertain, and where your expertise ends.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T762525208"] = "What you know for sure, what is uncertain, and where your expertise ends."
|
||||
|
||||
-- Some details are still missing. The chat will ask for them, and you can continue anyway.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPFORM::T1474406854"] = "Some details are still missing. The chat will ask for them, and you can continue anyway."
|
||||
|
||||
-- Please enter an address that begins with https:// or http://.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPFORM::T2837809367"] = "Please enter an address that begins with https:// or http://."
|
||||
|
||||
-- * The chat works best when you fill in these details.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPFORM::T3011046288"] = "* The chat works best when you fill in these details."
|
||||
|
||||
-- Extended bias poster
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BIASDAY::BIASOFTHEDAYASSISTANT::T1241605514"] = "Extended bias poster"
|
||||
|
||||
@@ -4414,6 +4744,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATAFOLDERS::T1259707710"] = "Your projec
|
||||
-- Your temporary chats, along with their transcripts.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATAFOLDERS::T1293614512"] = "Your temporary chats, along with their transcripts."
|
||||
|
||||
-- Your preparations in the BeyondFacts Lab.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATAFOLDERS::T1415474409"] = "Your preparations in the BeyondFacts Lab."
|
||||
|
||||
-- What belongs into a backup
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATAFOLDERS::T1682175486"] = "What belongs into a backup"
|
||||
|
||||
@@ -9589,6 +9922,24 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBATCHPROCESSING::T82
|
||||
-- Restore default
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBATCHPROCESSING::T913168756"] = "Restore default"
|
||||
|
||||
-- How the lab addresses you in languages with a formal and an informal address, such as German.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T1832497993"] = "How the lab addresses you in languages with a formal and an informal address, such as German."
|
||||
|
||||
-- Assistant: BeyondFacts Lab Options
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T19824556"] = "Assistant: BeyondFacts Lab Options"
|
||||
|
||||
-- Form of address
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2183481736"] = "Form of address"
|
||||
|
||||
-- Select a minimum confidence level
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2579793544"] = "Select a minimum confidence level"
|
||||
|
||||
-- Close
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3448155331"] = "Close"
|
||||
|
||||
-- Your preparations usually contain unpublished research. Therefore, the lab accepts only providers with at least medium confidence. You can raise this minimum, e.g., to use only self-hosted models.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T4002746484"] = "Your preparations usually contain unpublished research. Therefore, the lab accepts only providers with at least medium confidence. You can raise this minimum, e.g., to use only self-hosted models."
|
||||
|
||||
-- Files are sent as they were first read
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHAT::T1320692831"] = "Files are sent as they were first read"
|
||||
|
||||
@@ -11197,12 +11548,18 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T3733831260"] = "Generate your own
|
||||
-- Generate an ERI server to integrate business systems.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T3756213118"] = "Generate an ERI server to integrate business systems."
|
||||
|
||||
-- Prepare step by step for a challenging appearance as a researcher, e.g., a panel discussion or an interview.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T386812830"] = "Prepare step by step for a challenging appearance as a researcher, e.g., a panel discussion or an interview."
|
||||
|
||||
-- Use an LLM to find an icon for a given context.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T3881504200"] = "Use an LLM to find an icon for a given context."
|
||||
|
||||
-- Job Posting
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T3930052338"] = "Job Posting"
|
||||
|
||||
-- BeyondFacts Lab
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T3956097526"] = "BeyondFacts Lab"
|
||||
|
||||
-- Ask a question about a legal document.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T3970214537"] = "Ask a question about a legal document."
|
||||
|
||||
@@ -12919,6 +13276,9 @@ UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::OUTBOUNDDATARESTRICTIONEXTENSION
|
||||
-- Only services configured in AI Studio
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::OUTBOUNDDATARESTRICTIONEXTENSIONS::T750634166"] = "Only services configured in AI Studio"
|
||||
|
||||
-- BeyondFacts Lab: Prepare for challenging conversations about your research, step by step
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::PREVIEWFEATURESEXTENSIONS::T1110065980"] = "BeyondFacts Lab: Prepare for challenging conversations about your research, step by step"
|
||||
|
||||
-- Visual Briefing Assistant: Turn source material into an interactive briefing
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::PREVIEWFEATURESEXTENSIONS::T1217946647"] = "Visual Briefing Assistant: Turn source material into an interactive briefing"
|
||||
|
||||
@@ -13168,6 +13528,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T348883878"] = "Document
|
||||
-- Translation Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T3887962308"] = "Translation Assistant"
|
||||
|
||||
-- BeyondFacts Lab
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T3956097526"] = "BeyondFacts Lab"
|
||||
|
||||
-- Agenda Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T4034795997"] = "Agenda Assistant"
|
||||
|
||||
|
||||
+1
-23
@@ -10,11 +10,6 @@ using AIStudio.Tools.Rust;
|
||||
using AIStudio.Tools.Services;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
#if !DEBUG
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
#endif
|
||||
|
||||
namespace AIStudio.Assistants.PromptOptimizer;
|
||||
|
||||
public partial class AssistantPromptOptimizer : AssistantBaseCore<SettingsDialogPromptOptimizer>
|
||||
@@ -578,24 +573,7 @@ public partial class AssistantPromptOptimizer : AssistantBaseCore<SettingsDialog
|
||||
});
|
||||
}
|
||||
|
||||
private static async Task<string> ReadPromptingGuidelineAsync()
|
||||
{
|
||||
#if DEBUG
|
||||
var guidelinePath = Path.Join(Environment.CurrentDirectory, "Assistants", "PromptOptimizer", "prompting_guideline.md");
|
||||
return File.Exists(guidelinePath)
|
||||
? await File.ReadAllTextAsync(guidelinePath)
|
||||
: string.Empty;
|
||||
#else
|
||||
var resourceFileProvider = new ManifestEmbeddedFileProvider(Assembly.GetAssembly(type: typeof(Program))!, "Assistants/PromptOptimizer");
|
||||
var file = resourceFileProvider.GetFileInfo("prompting_guideline.md");
|
||||
if (!file.Exists)
|
||||
return string.Empty;
|
||||
|
||||
await using var fileStream = file.CreateReadStream();
|
||||
using var reader = new StreamReader(fileStream);
|
||||
return await reader.ReadToEndAsync();
|
||||
#endif
|
||||
}
|
||||
private static async Task<string> ReadPromptingGuidelineAsync() => await AppResources.ReadTextAsync("Assistants/PromptOptimizer/prompting_guideline.md");
|
||||
|
||||
private async Task<string> GetPromptingGuidelineForOptimizationAsync()
|
||||
{
|
||||
|
||||
@@ -82,6 +82,7 @@ public partial class DataFolders : MSGComponentBase
|
||||
DataBackupEntryKind.PLUGINS => T("Your plugins, such as the assistants you built, apart from the folders listed below."),
|
||||
DataBackupEntryKind.TOKENIZERS => T("The tokenizers you chose for your providers."),
|
||||
DataBackupEntryKind.VISUAL_BRIEFINGS => T("Your projects in the Visual Briefing Assistant."),
|
||||
DataBackupEntryKind.BEYOND_FACTS_LAB => T("Your preparations in the BeyondFacts Lab."),
|
||||
DataBackupEntryKind.DATA_SOURCE_INDEX => T("The index of your local data sources: which files AI Studio embedded, and their text."),
|
||||
DataBackupEntryKind.VECTOR_STORES => T("The embeddings of your local data sources."),
|
||||
DataBackupEntryKind.MEDIA_TRANSCRIPTS => T("The transcripts of the audio and video files in your local data sources."),
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
CompletedStepColor="Color.Primary"
|
||||
CurrentStepColor="Color.Primary"
|
||||
ErrorStepColor="Color.Error"
|
||||
NonLinear="@false"
|
||||
NonLinear="@this.NonLinear"
|
||||
ShowResetButton="@false"
|
||||
OnPreviewInteraction="@this.PreviewInteractionAsync"
|
||||
Class="@this.Classname">
|
||||
|
||||
@@ -36,6 +36,17 @@ public partial class MudStepperWithoutActions : ComponentBase
|
||||
[Parameter]
|
||||
public bool ReadOnly { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the user may jump to any step, not only to the next one.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Off by default, which keeps the order of a process that runs step by step, such as a build.
|
||||
/// Switch it on where people work through the steps themselves and may return to an earlier one
|
||||
/// or look ahead, as in the BeyondFacts Lab.
|
||||
/// </remarks>
|
||||
[Parameter]
|
||||
public bool NonLinear { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets additional CSS classes for the stepper.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
@using AIStudio.Settings
|
||||
@inherits SettingsDialogBase
|
||||
|
||||
<MudDialog>
|
||||
<TitleContent>
|
||||
<MudText Typo="Typo.h6" Class="d-flex align-center">
|
||||
<MudIcon Icon="@Icons.Material.Filled.RecordVoiceOver" Class="mr-2"/>
|
||||
@T("Assistant: BeyondFacts Lab Options")
|
||||
</MudText>
|
||||
</TitleContent>
|
||||
<DialogContent>
|
||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||
<ConfigurationSelect OptionDescription="@T("Form of address")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BeyondFactsLab.AddressForm)" Data="@ConfigurationSelectDataFactory.GetBeyondFactsLabAddressFormData()" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.BeyondFactsLab.AddressForm = value)" OptionHelp="@T("How the lab addresses you in languages with a formal and an informal address, such as German.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.BeyondFactsLab, x => x.AddressForm, out var meta) && meta.IsLocked"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Select a minimum confidence level")" SelectedValue="@this.SelectedMinimumConfidence" Data="@ConfigurationSelectDataFactory.GetBeyondFactsLabConfidenceLevelsData(this.SettingsManager)" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.BeyondFactsLab.MinimumProviderConfidence = value)" OptionHelp="@T("Your preparations usually contain unpublished research. Therefore, the lab accepts only providers with at least medium confidence. You can raise this minimum, e.g., to use only self-hosted models.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.BeyondFactsLab, x => x.MinimumProviderConfidence, out var meta) && meta.IsLocked"/>
|
||||
<ConfigurationProviderSelection Component="Components.BEYOND_FACTS_LAB_ASSISTANT" Data="@this.AvailableLLMProviders" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BeyondFactsLab.PreselectedProvider)" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.BeyondFactsLab.PreselectedProvider = value)" IsLocked="() => ManagedConfiguration.TryGet(x => x.BeyondFactsLab, x => x.PreselectedProvider, out var meta) && meta.IsLocked"/>
|
||||
</MudPaper>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">@T("Close")</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
@@ -0,0 +1,18 @@
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Dialogs.Settings;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the code-behind type for the settings of the BeyondFacts Lab.
|
||||
/// </summary>
|
||||
public partial class SettingsDialogBeyondFactsLab : SettingsDialogBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Shows the minimum confidence which applies, never one below the floor of the lab.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A value below the floor, e.g., from a configuration plugin, has no effect, and the list does not
|
||||
/// offer it either. Showing the floor instead tells what really counts.
|
||||
/// </remarks>
|
||||
private ConfidenceLevel SelectedMinimumConfidence() => Tools.Components.BEYOND_FACTS_LAB_ASSISTANT.MinimumConfidence(this.SettingsManager);
|
||||
}
|
||||
@@ -70,6 +70,7 @@
|
||||
|
||||
<AssistantCategoryBlock Title="@T("Learning")">
|
||||
<AssistantBlock TSettings="SettingsDialogAssistantBias" Component="Components.BIAS_DAY_ASSISTANT" Name="@T("Bias of the Day")" Description="@T("Learn about one cognitive bias every day.")" Icon="@Icons.Material.Filled.Psychology" Link="@Routes.ASSISTANT_BIAS"/>
|
||||
<AssistantBlock TSettings="SettingsDialogBeyondFactsLab" Component="Components.BEYOND_FACTS_LAB_ASSISTANT" RequiredPreviewFeature="Components.BEYOND_FACTS_LAB_ASSISTANT.RequiredPreviewFeature" Name="@T("BeyondFacts Lab")" Description="@T("Prepare step by step for a challenging appearance as a researcher, e.g., a panel discussion or an interview.")" Icon="@Icons.Material.Filled.RecordVoiceOver" Link="@Routes.ASSISTANT_BEYOND_FACTS_LAB"/>
|
||||
</AssistantCategoryBlock>
|
||||
|
||||
<AssistantCategoryBlock Title="@T("Software Engineering")">
|
||||
|
||||
@@ -914,7 +914,7 @@ CONFIG["SETTINGS"] = {}
|
||||
-- LEGAL_CHECK_ASSISTANT, SYNONYMS_ASSISTANT, MY_TASKS_ASSISTANT,
|
||||
-- JOB_POSTING_ASSISTANT, BIAS_DAY_ASSISTANT, ERI_ASSISTANT,
|
||||
-- DOCUMENT_ANALYSIS_ASSISTANT, BATCH_PROCESSING_ASSISTANT, SLIDE_BUILDER_ASSISTANT,
|
||||
-- VISUAL_BRIEFING_ASSISTANT, I18N_ASSISTANT,
|
||||
-- VISUAL_BRIEFING_ASSISTANT, BEYOND_FACTS_LAB_ASSISTANT, I18N_ASSISTANT,
|
||||
-- LOG_VIEWER_ASSISTANT
|
||||
--
|
||||
-- Replaces, does not merge: a configuration with a higher priority replaces this list
|
||||
@@ -982,6 +982,25 @@ CONFIG["SETTINGS"] = {}
|
||||
-- documents. Providers below this level cannot be selected in the assistant.
|
||||
-- CONFIG["SETTINGS"]["DataVisualBriefing.MinimumProviderConfidence"] = "NONE"
|
||||
|
||||
-- Configure organization defaults for the BeyondFacts Lab.
|
||||
-- The lab guides researchers step by step through the preparation of a challenging appearance,
|
||||
-- such as a panel discussion or an interview. It is a prototype: users see it only when they allow
|
||||
-- prototypes and enable the preview feature PRE_BEYOND_FACTS_LAB_2026.
|
||||
--
|
||||
-- Configure the provider which new preparations start with.
|
||||
-- It must be one of the provider IDs defined in CONFIG["LLM_PROVIDERS"].
|
||||
-- CONFIG["SETTINGS"]["DataBeyondFactsLab.PreselectedProvider"] = "00000000-0000-0000-0000-000000000000"
|
||||
--
|
||||
-- Configure the minimum provider confidence required by the lab.
|
||||
-- Allowed values are: NONE, UNTRUSTED, UNKNOWN, VERY_LOW, LOW, MODERATE, MEDIUM, HIGH
|
||||
-- Unpublished research is the usual material of the lab, so the lab never accepts less than MEDIUM.
|
||||
-- A lower value has no effect; HIGH allows only self-hosted models in the built-in confidence schemes.
|
||||
-- CONFIG["SETTINGS"]["DataBeyondFactsLab.MinimumProviderConfidence"] = "MEDIUM"
|
||||
--
|
||||
-- Configure how the lab addresses users in languages with a formal and an informal address, such as German.
|
||||
-- Allowed values are: FORMAL, INFORMAL
|
||||
-- CONFIG["SETTINGS"]["DataBeyondFactsLab.AddressForm"] = "FORMAL"
|
||||
|
||||
-- Configure defaults for the Assistant Builder.
|
||||
-- Preselection must be enabled for the remaining Assistant Builder settings to take effect.
|
||||
-- CONFIG["SETTINGS"]["DataAssistantBuilder.PreselectOptions"] = true
|
||||
|
||||
+363
@@ -735,6 +735,336 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BATCHPROCESSING::BATCHPROCESSINGPROMPTSOU
|
||||
-- Use a document analysis policy
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BATCHPROCESSING::BATCHPROCESSINGPROMPTSOURCEEXTENSIONS::T3309547196"] = "Regelwerk für die Dokumentenanalyse verwenden"
|
||||
|
||||
-- Prepare a challenging appearance as a researcher step by step, e.g., a panel discussion, an interview, or a hearing. Each preparation belongs to one event and has its own steps, chats, and work states.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1218290190"] = "Bereiten Sie einen herausfordernden Auftritt mit Ihrer Forschung Schritt für Schritt vor, etwa eine Podiumsdiskussion, ein Interview oder eine Anhörung. Jede Vorbereitung gehört zu einer Veranstaltung und hat eigene Schritte, Chats und Arbeitsstände."
|
||||
|
||||
-- Changed on {0}
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1279137966"] = "Geändert am {0}"
|
||||
|
||||
-- Delete
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1469573738"] = "Löschen"
|
||||
|
||||
-- AI Studio cannot read this preparation. Its files may be damaged.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1496519091"] = "AI Studio kann diese Vorbereitung nicht lesen. Die Dateien dieser Vorbereitung sind möglicherweise beschädigt."
|
||||
|
||||
-- AI Studio cannot open your details for this step. Their file may be damaged, or a newer version of AI Studio stored it. AI Studio leaves the file unchanged.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1564342983"] = "AI Studio kann Ihre Angaben zu diesem Schritt nicht öffnen. Die Datei ist möglicherweise beschädigt oder wurde mit einer neueren Version von AI Studio gespeichert. AI Studio lässt die Datei unverändert."
|
||||
|
||||
-- You have no preparations yet. Create one for the event you want to prepare for.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1599306665"] = "Sie haben noch keine Vorbereitung. Erstellen Sie eine für die Veranstaltung, auf die Sie sich vorbereiten möchten."
|
||||
|
||||
-- Enter a new name for this preparation.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T193031277"] = "Geben Sie einen neuen Namen für diese Vorbereitung ein."
|
||||
|
||||
-- Cannot be opened
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1981873292"] = "Kann nicht geöffnet werden"
|
||||
|
||||
-- Copy preparation ID
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2344012850"] = "Vorbereitungs-ID kopieren"
|
||||
|
||||
-- Your details
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T25208484"] = "Ihre Angaben"
|
||||
|
||||
-- Create
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2567824509"] = "Erstellen"
|
||||
|
||||
-- Preparation ID
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2671360307"] = "Vorbereitungs-ID"
|
||||
|
||||
-- Create a preparation to begin.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2751865753"] = "Erstellen Sie eine Vorbereitung, um zu beginnen."
|
||||
|
||||
-- AI Studio has left the files of this preparation unchanged. A future update may make it accessible again.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2824385338"] = "AI Studio hat die Dateien dieser Vorbereitung unverändert gelassen. Ein zukünftiges Update könnte sie wieder zugänglich machen."
|
||||
|
||||
-- The event has taken place
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2842348392"] = "Die Veranstaltung hat stattgefunden"
|
||||
|
||||
-- Marked on {0}.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3254361846"] = "Markiert am {0}."
|
||||
|
||||
-- Rename preparation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3342203974"] = "Vorbereitung umbenennen"
|
||||
|
||||
-- Rename
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3355849203"] = "Umbenennen"
|
||||
|
||||
-- Delete preparation permanently
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T354252674"] = "Vorbereitung endgültig löschen"
|
||||
|
||||
-- Unavailable preparation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3646236084"] = "Vorbereitung nicht verfügbar"
|
||||
|
||||
-- This preparation cannot be opened. Permanently delete it with all of its files?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3647862078"] = "Diese Vorbereitung kann nicht geöffnet werden. Möchten Sie sie mit allen Dateien endgültig löschen?"
|
||||
|
||||
-- Name the event you want to prepare for, e.g., the title of a panel discussion.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3679923414"] = "Geben Sie die Veranstaltung an, auf die Sie sich vorbereiten möchten, z. B. den Titel einer Podiumsdiskussion."
|
||||
|
||||
-- Name of the preparation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3746973353"] = "Name der Vorbereitung"
|
||||
|
||||
-- Please enter a name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3818326829"] = "Bitte geben Sie einen Namen ein."
|
||||
|
||||
-- AI Studio could not save your details. Please try again.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3921500806"] = "AI Studio konnte Ihre Angaben nicht speichern. Bitte versuchen Sie es erneut."
|
||||
|
||||
-- BeyondFacts Lab
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3956097526"] = "BeyondFacts Lab"
|
||||
|
||||
-- A newer version of AI Studio stored this preparation. This version cannot open it. Please update AI Studio.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T395932079"] = "Eine neuere Version von AI Studio hat diese Vorbereitung gespeichert. Diese Version kann sie nicht öffnen. Bitte aktualisieren Sie AI Studio."
|
||||
|
||||
-- AI Studio could not save the change of your preparations. Please try again.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T4073131406"] = "AI Studio konnte die Änderung an Ihren Vorbereitungen nicht speichern. Bitte versuchen Sie es erneut."
|
||||
|
||||
-- Requires a newer AI Studio version
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T4087140083"] = "Erfordert eine neuere Version von AI Studio"
|
||||
|
||||
-- Details missing
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T506453229"] = "Angaben fehlen"
|
||||
|
||||
-- Permanently delete the preparation '{0}' with all of its steps, chats, and work states?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T772787118"] = "Die Vorbereitung „{0}“ mit allen Schritten, Chats und Arbeitsständen endgültig löschen?"
|
||||
|
||||
-- New preparation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T851005164"] = "Neue Vorbereitung"
|
||||
|
||||
-- Your preparations
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T972511272"] = "Ihre Vorbereitungen"
|
||||
|
||||
-- Informal address (du in German)
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLADDRESSFORMEXTENSIONS::T554958468"] = "Informelle Anrede (du)"
|
||||
|
||||
-- Formal address (Sie in German)
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLADDRESSFORMEXTENSIONS::T859201973"] = "Formelle Anrede (Sie)"
|
||||
|
||||
-- Moderate the discussion
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1131866893"] = "Diskussion moderieren"
|
||||
|
||||
-- E.g., take up a confirmed conflict of values or a perspective from the step about values and perspectives
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1134014442"] = "Z. B. einen bestätigten Wertekonflikt oder eine Perspektive aus dem Schritt „Werte und Perspektiven“ aufgreifen"
|
||||
|
||||
-- What have you changed, kept, or left open compared to your initial position, and why?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1455147822"] = "Was haben Sie gegenüber Ihrer Ausgangsposition verändert, beibehalten oder offengelassen, und weshalb?"
|
||||
|
||||
-- Show options for action
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1601690165"] = "Handlungsoptionen aufzeigen"
|
||||
|
||||
-- Manipulative questions and straw men
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1656068515"] = "Manipulative Fragen und Strohmänner"
|
||||
|
||||
-- Topic of the event
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1766174370"] = "Thema der Veranstaltung"
|
||||
|
||||
-- Moderate
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T177463328"] = "Moderat"
|
||||
|
||||
-- Other perspectives: Whose point of view would you like to understand better, or have you hardly considered so far?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1885081930"] = "Andere Perspektiven: Wessen Sichtweise möchten Sie besser verstehen oder haben Sie bisher wenig berücksichtigt?"
|
||||
|
||||
-- Stays in your preparation and never goes into the research on the web
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1949096445"] = "Bleibt in Ihrer Vorbereitung und geht nie in die Recherche im Web ein"
|
||||
|
||||
-- Very demanding
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1967151608"] = "Sehr anspruchsvoll"
|
||||
|
||||
-- Date and time
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2054731205"] = "Datum und Uhrzeit"
|
||||
|
||||
-- Notes on how to use your materials
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2123646094"] = "Hinweise zur Nutzung Ihrer Materialien"
|
||||
|
||||
-- Duration of your contribution
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T223853289"] = "Dauer Ihres Beitrags"
|
||||
|
||||
-- Special notes for the test
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2242825987"] = "Besondere Hinweise für den Test"
|
||||
|
||||
-- Uncertainties and worries about communicating
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2428368968"] = "Kommunikative Unsicherheiten und Sorgen"
|
||||
|
||||
-- Difficulty at the start
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T243923502"] = "Schwierigkeit zu Beginn"
|
||||
|
||||
-- E.g., 90 minutes
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2537467295"] = "Z. B. 90 Minuten"
|
||||
|
||||
-- Place
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2597472412"] = "Ort"
|
||||
|
||||
-- E.g., 8 minutes
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2697611204"] = "Z. B. 8 Minuten"
|
||||
|
||||
-- A few sentences are enough
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2765665158"] = "Ein paar Sätze genügen"
|
||||
|
||||
-- Other participants
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T277557865"] = "Weitere Teilnehmende"
|
||||
|
||||
-- What do you want to achieve with your contribution?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2818219287"] = "Was möchten Sie mit Ihrem Beitrag erreichen?"
|
||||
|
||||
-- Scientific criticism
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2865565843"] = "Wissenschaftliche Kritik"
|
||||
|
||||
-- The address of the event page, beginning with https://
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2900883219"] = "Die Adresse der Veranstaltungsseite, beginnend mit https://"
|
||||
|
||||
-- Names and roles, as far as you know them
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3020176946"] = "Namen und Rollen, soweit bekannt"
|
||||
|
||||
-- Personal detail: it goes into exports and shared sessions only when you explicitly choose so.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3063635296"] = "Persönliche Angabe: Exporte und geteilte Sitzungen enthalten sie nur, wenn Sie das ausdrücklich wählen."
|
||||
|
||||
-- Political instrumentalization
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3078371152"] = "Politische Instrumentalisierung"
|
||||
|
||||
-- Mixed
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3114856344"] = "Gemischt"
|
||||
|
||||
-- Personal involvement: Where does the topic touch you personally? Which question or criticism could trigger a strong reaction?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3199962614"] = "Persönliche Betroffenheit: Wo berührt Sie das Thema persönlich? Welche Frage oder Kritik könnte bei Ihnen eine starke Reaktion auslösen?"
|
||||
|
||||
-- Format of the event
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3217479436"] = "Format der Veranstaltung"
|
||||
|
||||
-- E.g., panel, keynote, or interview; online, on site, or hybrid
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3297023821"] = "Z. B. Podiumsdiskussion, Keynote oder Interview; online, vor Ort oder hybrid"
|
||||
|
||||
-- Values and concerns: What matters most to you about this topic?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3444467007"] = "Werte und Anliegen: Was ist Ihnen bei diesem Thema besonders wichtig?"
|
||||
|
||||
-- Audience
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3506118019"] = "Zielgruppe"
|
||||
|
||||
-- Suggestions
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3534677498"] = "Vorschläge"
|
||||
|
||||
-- Emotionally charged criticism
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3552262211"] = "Emotionalisierte Kritik"
|
||||
|
||||
-- Link to the event
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3658981841"] = "Link zur Veranstaltung"
|
||||
|
||||
-- Raise awareness
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3790372874"] = "Bewusstsein schaffen"
|
||||
|
||||
-- E.g., use only paper X as the basis for the content
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3804983466"] = "Z. B. nur Paper X als inhaltliche Grundlage verwenden"
|
||||
|
||||
-- Core theses and arguments (if any)
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T39824489"] = "Kernthesen und Argumente (falls vorhanden)"
|
||||
|
||||
-- Total duration of the event
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T4051894943"] = "Dauer der Veranstaltung insgesamt"
|
||||
|
||||
-- Influence policy
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T4052553234"] = "Politischen Einfluss nehmen"
|
||||
|
||||
-- You can raise the difficulty after each question.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T519332031"] = "Nach jeder Frage können Sie die Schwierigkeit erhöhen."
|
||||
|
||||
-- E.g., students, experts, or the general public
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T533097975"] = "Z. B. Studierende, Fachpublikum oder die breite Öffentlichkeit"
|
||||
|
||||
-- Short description of your contribution
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T667686615"] = "Kurzbeschreibung Ihres Beitrags"
|
||||
|
||||
-- Type of questions
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T805059831"] = "Art der Fragen"
|
||||
|
||||
-- The central question or the title
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T828389221"] = "Die zentrale Fragestellung oder der Titel"
|
||||
|
||||
-- Uncertainties and controversies in content and science (if known)
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T82922542"] = "Inhaltliche und wissenschaftliche Unsicherheiten und Kontroversen (falls bekannt)"
|
||||
|
||||
-- Inform
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T85613896"] = "Informieren"
|
||||
|
||||
-- E.g., December 10, 2 pm
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T881586300"] = "Z. B. 10. Dezember, 14 Uhr"
|
||||
|
||||
-- E.g., the venue and the city, or online
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T916990010"] = "Z. B. der Veranstaltungsort und die Stadt oder online"
|
||||
|
||||
-- In your own words, with the suggestions below, or still open: the chat sharpens your goal and role with you.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T917276711"] = "Ob in eigenen Worten, mit den Vorschlägen unten oder noch ganz offen: Im Chat schärfen Sie gemeinsam mit der KI Ihr Ziel und Ihre Rolle."
|
||||
|
||||
-- Topic of your contribution or your specific assignment
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T932670647"] = "Thema Ihres Beitrags oder Ihr konkreter Auftrag"
|
||||
|
||||
-- E.g., aggressive moderation, time pressure, or a particular counterpart
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T965551209"] = "Z. B. aggressive Moderation, Zeitdruck oder ein bestimmtes Gegenüber"
|
||||
|
||||
-- Take a position in the debate
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T974131333"] = "Sich in der Debatte positionieren"
|
||||
|
||||
-- Demanding
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T985014450"] = "Anspruchsvoll"
|
||||
|
||||
-- ## Welcome! The BeyondFacts Lab guides you through an AI-supported process of reflection, which helps you take a position in challenging communication situations and stay able to communicate. Data protection depends on the AI provider you choose, its rules for processing and storing data, and your configuration. Enter only data that are suitable and approved for this. Please check all results critically. Depending on the AI model, the answers may differ. Hallucinations cannot be ruled out. This is a prototype, so we look forward to your feedback.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLNOTICE::T940905480"] = "## Herzlich willkommen!\n\nIm BeyondFacts Lab werden Sie durch einen geführten, KI-unterstützten Reflexionsprozess geleitet, der Ihnen dabei hilft, sich in herausfordernden Kommunikationssituationen zu positionieren und kommunikationsfähig zu bleiben.\n\nDer Datenschutz hängt vom gewählten KI-Anbieter, dessen Verarbeitungs- und Speicherregeln sowie Ihrer Konfiguration ab. Geben Sie nur Daten ein, die dafür geeignet und freigegeben sind.\n\nBitte prüfen Sie alle Ergebnisse kritisch. Die Antworten können, je nach KI-Modell, unterschiedlich ausfallen. Halluzinationen sind nicht ausgeschlossen.\n\nEs handelt sich um einen Prototyp, daher freuen wir uns über Ihr Feedback."
|
||||
|
||||
-- I have read the notes
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLNOTICEDIALOG::T4071737336"] = "Ich habe die Hinweise gelesen"
|
||||
|
||||
-- Test situation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T1049923473"] = "Testsituation"
|
||||
|
||||
-- Knowledge and uncertainty
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T1147870258"] = "Wissen und Unsicherheit"
|
||||
|
||||
-- How you convey evidence, trade-offs between values, and open questions.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T1290723890"] = "Wie Sie Evidenz, Wertabwägungen und offene Fragen vermitteln."
|
||||
|
||||
-- Event details
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T135387869"] = "Rahmendaten"
|
||||
|
||||
-- What the event is about, for whom, and what you were asked to contribute.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T158292580"] = "Worum es bei der Veranstaltung geht, für wen sie gedacht ist und was Sie beitragen sollen."
|
||||
|
||||
-- You can fill in single fields or start right away in the chat.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T2524378548"] = "Sie können einzelne Felder ausfüllen oder direkt im Chat beginnen."
|
||||
|
||||
-- Goals and role
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T2988481290"] = "Ziele und Rolle"
|
||||
|
||||
-- What matters to you, what touches you, and what moves others.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T3022557767"] = "Was Ihnen wichtig ist, was Sie berührt und was andere bewegt."
|
||||
|
||||
-- What you want to achieve, and in which role you speak.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T3052319680"] = "Was Sie erreichen möchten und in welcher Rolle Sie sprechen."
|
||||
|
||||
-- Strategy
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T3205650756"] = "Strategie"
|
||||
|
||||
-- Results and materials
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T3602322182"] = "Ergebnisse und Materialien"
|
||||
|
||||
-- Values and perspectives
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T494574525"] = "Werte und Perspektiven"
|
||||
|
||||
-- What you take away from your preparation.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T517692701"] = "Was Sie aus Ihrer Vorbereitung mitnehmen."
|
||||
|
||||
-- Play through critical questions and opposing positions.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T573043138"] = "Kritische Nachfragen und Gegenpositionen durchspielen."
|
||||
|
||||
-- What you know for sure, what is uncertain, and where your expertise ends.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T762525208"] = "Was Sie sicher wissen, was unsicher ist und wo Ihre Expertise endet."
|
||||
|
||||
-- Some details are still missing. The chat will ask for them, and you can continue anyway.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPFORM::T1474406854"] = "Einige Angaben fehlen noch. Der Chat fragt danach, und Sie können trotzdem weitermachen."
|
||||
|
||||
-- Please enter an address that begins with https:// or http://.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPFORM::T2837809367"] = "Bitte geben Sie eine Adresse ein, die mit https:// oder http:// beginnt."
|
||||
|
||||
-- * The chat works best when you fill in these details.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPFORM::T3011046288"] = "* Der Chat funktioniert am besten, wenn Sie diese Angaben ausfüllen."
|
||||
|
||||
-- Extended bias poster
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BIASDAY::BIASOFTHEDAYASSISTANT::T1241605514"] = "Erweitertes Poster der Verzerrungen"
|
||||
|
||||
@@ -4416,6 +4746,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATAFOLDERS::T1259707710"] = "Ihre Projek
|
||||
-- Your temporary chats, along with their transcripts.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATAFOLDERS::T1293614512"] = "Ihre temporären Chats mit ihren Transkripten."
|
||||
|
||||
-- Your preparations in the BeyondFacts Lab.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATAFOLDERS::T1415474409"] = "Ihre Vorbereitungen im BeyondFacts Lab."
|
||||
|
||||
-- What belongs into a backup
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATAFOLDERS::T1682175486"] = "Was in eine Sicherung gehört"
|
||||
|
||||
@@ -9591,6 +9924,24 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBATCHPROCESSING::T82
|
||||
-- Restore default
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBATCHPROCESSING::T913168756"] = "Standard wiederherstellen"
|
||||
|
||||
-- How the lab addresses you in languages with a formal and an informal address, such as German.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T1832497993"] = "Wie das Lab Sie in Sprachen anspricht, die eine formelle und eine informelle Anrede kennen, etwa im Deutschen."
|
||||
|
||||
-- Assistant: BeyondFacts Lab Options
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T19824556"] = "Assistent: Optionen für das BeyondFacts Lab"
|
||||
|
||||
-- Form of address
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2183481736"] = "Anrede"
|
||||
|
||||
-- Select a minimum confidence level
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2579793544"] = "Wählen Sie ein minimales Vertrauensniveau aus"
|
||||
|
||||
-- Close
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3448155331"] = "Schließen"
|
||||
|
||||
-- Your preparations usually contain unpublished research. Therefore, the lab accepts only providers with at least medium confidence. You can raise this minimum, e.g., to use only self-hosted models.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T4002746484"] = "Ihre Vorbereitungen enthalten meist unveröffentlichte Forschung. Daher akzeptiert das Lab nur Anbieter mit mindestens dem Vertrauensniveau „Mittel“. Sie können diese Untergrenze anheben, etwa um nur selbst gehostete Modelle zu verwenden."
|
||||
|
||||
-- Files are sent as they were first read
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHAT::T1320692831"] = "Dateien werden so gesendet, wie sie zuerst eingelesen wurden"
|
||||
|
||||
@@ -11199,12 +11550,18 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T3733831260"] = "Erstellen Sie Ihr
|
||||
-- Generate an ERI server to integrate business systems.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T3756213118"] = "Erstellen Sie einen ERI-Server zur Integration von Geschäftssystemen."
|
||||
|
||||
-- Prepare step by step for a challenging appearance as a researcher, e.g., a panel discussion or an interview.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T386812830"] = "Bereiten Sie sich Schritt für Schritt auf einen herausfordernden Auftritt mit Ihrer Forschung vor, etwa eine Podiumsdiskussion oder ein Interview."
|
||||
|
||||
-- Use an LLM to find an icon for a given context.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T3881504200"] = "Verwenden Sie ein LLM, um ein Icon für einen bestimmten Kontext zu finden."
|
||||
|
||||
-- Job Posting
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T3930052338"] = "Stellenanzeige"
|
||||
|
||||
-- BeyondFacts Lab
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T3956097526"] = "BeyondFacts Lab"
|
||||
|
||||
-- Ask a question about a legal document.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T3970214537"] = "Stellen Sie Fragen zu einem juristischen Dokument."
|
||||
|
||||
@@ -12921,6 +13278,9 @@ UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::OUTBOUNDDATARESTRICTIONEXTENSION
|
||||
-- Only services configured in AI Studio
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::OUTBOUNDDATARESTRICTIONEXTENSIONS::T750634166"] = "Nur in AI Studio konfigurierte Dienste"
|
||||
|
||||
-- BeyondFacts Lab: Prepare for challenging conversations about your research, step by step
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::PREVIEWFEATURESEXTENSIONS::T1110065980"] = "BeyondFacts Lab: Sich Schritt für Schritt auf herausfordernde Gespräche über die eigene Forschung vorbereiten"
|
||||
|
||||
-- Visual Briefing Assistant: Turn source material into an interactive briefing
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::PREVIEWFEATURESEXTENSIONS::T1217946647"] = "Assistent für visuelle Briefings: Quellmaterial in ein interaktives Briefing verwandeln"
|
||||
|
||||
@@ -13170,6 +13530,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T348883878"] = "Assisten
|
||||
-- Translation Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T3887962308"] = "Übersetzungs-Assistent"
|
||||
|
||||
-- BeyondFacts Lab
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T3956097526"] = "BeyondFacts Lab"
|
||||
|
||||
-- Agenda Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T4034795997"] = "Agenda-Assistent"
|
||||
|
||||
|
||||
+363
@@ -735,6 +735,336 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BATCHPROCESSING::BATCHPROCESSINGPROMPTSOU
|
||||
-- Use a document analysis policy
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BATCHPROCESSING::BATCHPROCESSINGPROMPTSOURCEEXTENSIONS::T3309547196"] = "Use a document analysis policy"
|
||||
|
||||
-- Prepare a challenging appearance as a researcher step by step, e.g., a panel discussion, an interview, or a hearing. Each preparation belongs to one event and has its own steps, chats, and work states.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1218290190"] = "Prepare a challenging appearance as a researcher step by step, e.g., a panel discussion, an interview, or a hearing. Each preparation belongs to one event and has its own steps, chats, and work states."
|
||||
|
||||
-- Changed on {0}
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1279137966"] = "Changed on {0}"
|
||||
|
||||
-- Delete
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1469573738"] = "Delete"
|
||||
|
||||
-- AI Studio cannot read this preparation. Its files may be damaged.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1496519091"] = "AI Studio cannot read this preparation. Its files may be damaged."
|
||||
|
||||
-- AI Studio cannot open your details for this step. Their file may be damaged, or a newer version of AI Studio stored it. AI Studio leaves the file unchanged.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1564342983"] = "AI Studio cannot open your details for this step. Their file may be damaged, or a newer version of AI Studio stored it. AI Studio leaves the file unchanged."
|
||||
|
||||
-- You have no preparations yet. Create one for the event you want to prepare for.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1599306665"] = "You have no preparations yet. Create one for the event you want to prepare for."
|
||||
|
||||
-- Enter a new name for this preparation.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T193031277"] = "Enter a new name for this preparation."
|
||||
|
||||
-- Cannot be opened
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1981873292"] = "Cannot be opened"
|
||||
|
||||
-- Copy preparation ID
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2344012850"] = "Copy preparation ID"
|
||||
|
||||
-- Your details
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T25208484"] = "Your details"
|
||||
|
||||
-- Create
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2567824509"] = "Create"
|
||||
|
||||
-- Preparation ID
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2671360307"] = "Preparation ID"
|
||||
|
||||
-- Create a preparation to begin.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2751865753"] = "Create a preparation to begin."
|
||||
|
||||
-- AI Studio has left the files of this preparation unchanged. A future update may make it accessible again.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2824385338"] = "AI Studio has left the files of this preparation unchanged. A future update may make it accessible again."
|
||||
|
||||
-- The event has taken place
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2842348392"] = "The event has taken place"
|
||||
|
||||
-- Marked on {0}.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3254361846"] = "Marked on {0}."
|
||||
|
||||
-- Rename preparation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3342203974"] = "Rename preparation"
|
||||
|
||||
-- Rename
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3355849203"] = "Rename"
|
||||
|
||||
-- Delete preparation permanently
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T354252674"] = "Delete preparation permanently"
|
||||
|
||||
-- Unavailable preparation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3646236084"] = "Unavailable preparation"
|
||||
|
||||
-- This preparation cannot be opened. Permanently delete it with all of its files?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3647862078"] = "This preparation cannot be opened. Permanently delete it with all of its files?"
|
||||
|
||||
-- Name the event you want to prepare for, e.g., the title of a panel discussion.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3679923414"] = "Name the event you want to prepare for, e.g., the title of a panel discussion."
|
||||
|
||||
-- Name of the preparation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3746973353"] = "Name of the preparation"
|
||||
|
||||
-- Please enter a name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3818326829"] = "Please enter a name."
|
||||
|
||||
-- AI Studio could not save your details. Please try again.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3921500806"] = "AI Studio could not save your details. Please try again."
|
||||
|
||||
-- BeyondFacts Lab
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T3956097526"] = "BeyondFacts Lab"
|
||||
|
||||
-- A newer version of AI Studio stored this preparation. This version cannot open it. Please update AI Studio.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T395932079"] = "A newer version of AI Studio stored this preparation. This version cannot open it. Please update AI Studio."
|
||||
|
||||
-- AI Studio could not save the change of your preparations. Please try again.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T4073131406"] = "AI Studio could not save the change of your preparations. Please try again."
|
||||
|
||||
-- Requires a newer AI Studio version
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T4087140083"] = "Requires a newer AI Studio version"
|
||||
|
||||
-- Details missing
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T506453229"] = "Details missing"
|
||||
|
||||
-- Permanently delete the preparation '{0}' with all of its steps, chats, and work states?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T772787118"] = "Permanently delete the preparation '{0}' with all of its steps, chats, and work states?"
|
||||
|
||||
-- New preparation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T851005164"] = "New preparation"
|
||||
|
||||
-- Your preparations
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T972511272"] = "Your preparations"
|
||||
|
||||
-- Informal address (du in German)
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLADDRESSFORMEXTENSIONS::T554958468"] = "Informal address (du in German)"
|
||||
|
||||
-- Formal address (Sie in German)
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLADDRESSFORMEXTENSIONS::T859201973"] = "Formal address (Sie in German)"
|
||||
|
||||
-- Moderate the discussion
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1131866893"] = "Moderate the discussion"
|
||||
|
||||
-- E.g., take up a confirmed conflict of values or a perspective from the step about values and perspectives
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1134014442"] = "E.g., take up a confirmed conflict of values or a perspective from the step about values and perspectives"
|
||||
|
||||
-- What have you changed, kept, or left open compared to your initial position, and why?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1455147822"] = "What have you changed, kept, or left open compared to your initial position, and why?"
|
||||
|
||||
-- Show options for action
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1601690165"] = "Show options for action"
|
||||
|
||||
-- Manipulative questions and straw men
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1656068515"] = "Manipulative questions and straw men"
|
||||
|
||||
-- Topic of the event
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1766174370"] = "Topic of the event"
|
||||
|
||||
-- Moderate
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T177463328"] = "Moderate"
|
||||
|
||||
-- Other perspectives: Whose point of view would you like to understand better, or have you hardly considered so far?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1885081930"] = "Other perspectives: Whose point of view would you like to understand better, or have you hardly considered so far?"
|
||||
|
||||
-- Stays in your preparation and never goes into the research on the web
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1949096445"] = "Stays in your preparation and never goes into the research on the web"
|
||||
|
||||
-- Very demanding
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T1967151608"] = "Very demanding"
|
||||
|
||||
-- Date and time
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2054731205"] = "Date and time"
|
||||
|
||||
-- Notes on how to use your materials
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2123646094"] = "Notes on how to use your materials"
|
||||
|
||||
-- Duration of your contribution
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T223853289"] = "Duration of your contribution"
|
||||
|
||||
-- Special notes for the test
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2242825987"] = "Special notes for the test"
|
||||
|
||||
-- Uncertainties and worries about communicating
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2428368968"] = "Uncertainties and worries about communicating"
|
||||
|
||||
-- Difficulty at the start
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T243923502"] = "Difficulty at the start"
|
||||
|
||||
-- E.g., 90 minutes
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2537467295"] = "E.g., 90 minutes"
|
||||
|
||||
-- Place
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2597472412"] = "Place"
|
||||
|
||||
-- E.g., 8 minutes
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2697611204"] = "E.g., 8 minutes"
|
||||
|
||||
-- A few sentences are enough
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2765665158"] = "A few sentences are enough"
|
||||
|
||||
-- Other participants
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T277557865"] = "Other participants"
|
||||
|
||||
-- What do you want to achieve with your contribution?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2818219287"] = "What do you want to achieve with your contribution?"
|
||||
|
||||
-- Scientific criticism
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2865565843"] = "Scientific criticism"
|
||||
|
||||
-- The address of the event page, beginning with https://
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T2900883219"] = "The address of the event page, beginning with https://"
|
||||
|
||||
-- Names and roles, as far as you know them
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3020176946"] = "Names and roles, as far as you know them"
|
||||
|
||||
-- Personal detail: it goes into exports and shared sessions only when you explicitly choose so.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3063635296"] = "Personal detail: it goes into exports and shared sessions only when you explicitly choose so."
|
||||
|
||||
-- Political instrumentalization
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3078371152"] = "Political instrumentalization"
|
||||
|
||||
-- Mixed
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3114856344"] = "Mixed"
|
||||
|
||||
-- Personal involvement: Where does the topic touch you personally? Which question or criticism could trigger a strong reaction?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3199962614"] = "Personal involvement: Where does the topic touch you personally? Which question or criticism could trigger a strong reaction?"
|
||||
|
||||
-- Format of the event
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3217479436"] = "Format of the event"
|
||||
|
||||
-- E.g., panel, keynote, or interview; online, on site, or hybrid
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3297023821"] = "E.g., panel, keynote, or interview; online, on site, or hybrid"
|
||||
|
||||
-- Values and concerns: What matters most to you about this topic?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3444467007"] = "Values and concerns: What matters most to you about this topic?"
|
||||
|
||||
-- Audience
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3506118019"] = "Audience"
|
||||
|
||||
-- Suggestions
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3534677498"] = "Suggestions"
|
||||
|
||||
-- Emotionally charged criticism
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3552262211"] = "Emotionally charged criticism"
|
||||
|
||||
-- Link to the event
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3658981841"] = "Link to the event"
|
||||
|
||||
-- Raise awareness
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3790372874"] = "Raise awareness"
|
||||
|
||||
-- E.g., use only paper X as the basis for the content
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T3804983466"] = "E.g., use only paper X as the basis for the content"
|
||||
|
||||
-- Core theses and arguments (if any)
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T39824489"] = "Core theses and arguments (if any)"
|
||||
|
||||
-- Total duration of the event
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T4051894943"] = "Total duration of the event"
|
||||
|
||||
-- Influence policy
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T4052553234"] = "Influence policy"
|
||||
|
||||
-- You can raise the difficulty after each question.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T519332031"] = "You can raise the difficulty after each question."
|
||||
|
||||
-- E.g., students, experts, or the general public
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T533097975"] = "E.g., students, experts, or the general public"
|
||||
|
||||
-- Short description of your contribution
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T667686615"] = "Short description of your contribution"
|
||||
|
||||
-- Type of questions
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T805059831"] = "Type of questions"
|
||||
|
||||
-- The central question or the title
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T828389221"] = "The central question or the title"
|
||||
|
||||
-- Uncertainties and controversies in content and science (if known)
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T82922542"] = "Uncertainties and controversies in content and science (if known)"
|
||||
|
||||
-- Inform
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T85613896"] = "Inform"
|
||||
|
||||
-- E.g., December 10, 2 pm
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T881586300"] = "E.g., December 10, 2 pm"
|
||||
|
||||
-- E.g., the venue and the city, or online
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T916990010"] = "E.g., the venue and the city, or online"
|
||||
|
||||
-- In your own words, with the suggestions below, or still open: the chat sharpens your goal and role with you.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T917276711"] = "In your own words, with the suggestions below, or still open: the chat sharpens your goal and role with you."
|
||||
|
||||
-- Topic of your contribution or your specific assignment
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T932670647"] = "Topic of your contribution or your specific assignment"
|
||||
|
||||
-- E.g., aggressive moderation, time pressure, or a particular counterpart
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T965551209"] = "E.g., aggressive moderation, time pressure, or a particular counterpart"
|
||||
|
||||
-- Take a position in the debate
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T974131333"] = "Take a position in the debate"
|
||||
|
||||
-- Demanding
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLFIELDTEXTS::T985014450"] = "Demanding"
|
||||
|
||||
-- ## Welcome! The BeyondFacts Lab guides you through an AI-supported process of reflection, which helps you take a position in challenging communication situations and stay able to communicate. Data protection depends on the AI provider you choose, its rules for processing and storing data, and your configuration. Enter only data that are suitable and approved for this. Please check all results critically. Depending on the AI model, the answers may differ. Hallucinations cannot be ruled out. This is a prototype, so we look forward to your feedback.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLNOTICE::T940905480"] = "## Welcome!\n\nThe BeyondFacts Lab guides you through an AI-supported process of reflection, which helps you take a position in challenging communication situations and stay able to communicate.\n\nData protection depends on the AI provider you choose, its rules for processing and storing data, and your configuration. Enter only data that are suitable and approved for this.\n\nPlease check all results critically. Depending on the AI model, the answers may differ. Hallucinations cannot be ruled out.\n\nThis is a prototype, so we look forward to your feedback."
|
||||
|
||||
-- I have read the notes
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLNOTICEDIALOG::T4071737336"] = "I have read the notes"
|
||||
|
||||
-- Test situation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T1049923473"] = "Test situation"
|
||||
|
||||
-- Knowledge and uncertainty
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T1147870258"] = "Knowledge and uncertainty"
|
||||
|
||||
-- How you convey evidence, trade-offs between values, and open questions.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T1290723890"] = "How you convey evidence, trade-offs between values, and open questions."
|
||||
|
||||
-- Event details
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T135387869"] = "Event details"
|
||||
|
||||
-- What the event is about, for whom, and what you were asked to contribute.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T158292580"] = "What the event is about, for whom, and what you were asked to contribute."
|
||||
|
||||
-- You can fill in single fields or start right away in the chat.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T2524378548"] = "You can fill in single fields or start right away in the chat."
|
||||
|
||||
-- Goals and role
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T2988481290"] = "Goals and role"
|
||||
|
||||
-- What matters to you, what touches you, and what moves others.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T3022557767"] = "What matters to you, what touches you, and what moves others."
|
||||
|
||||
-- What you want to achieve, and in which role you speak.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T3052319680"] = "What you want to achieve, and in which role you speak."
|
||||
|
||||
-- Strategy
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T3205650756"] = "Strategy"
|
||||
|
||||
-- Results and materials
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T3602322182"] = "Results and materials"
|
||||
|
||||
-- Values and perspectives
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T494574525"] = "Values and perspectives"
|
||||
|
||||
-- What you take away from your preparation.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T517692701"] = "What you take away from your preparation."
|
||||
|
||||
-- Play through critical questions and opposing positions.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T573043138"] = "Play through critical questions and opposing positions."
|
||||
|
||||
-- What you know for sure, what is uncertain, and where your expertise ends.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T762525208"] = "What you know for sure, what is uncertain, and where your expertise ends."
|
||||
|
||||
-- Some details are still missing. The chat will ask for them, and you can continue anyway.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPFORM::T1474406854"] = "Some details are still missing. The chat will ask for them, and you can continue anyway."
|
||||
|
||||
-- Please enter an address that begins with https:// or http://.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPFORM::T2837809367"] = "Please enter an address that begins with https:// or http://."
|
||||
|
||||
-- * The chat works best when you fill in these details.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPFORM::T3011046288"] = "* The chat works best when you fill in these details."
|
||||
|
||||
-- Extended bias poster
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BIASDAY::BIASOFTHEDAYASSISTANT::T1241605514"] = "Extended bias poster"
|
||||
|
||||
@@ -4416,6 +4746,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATAFOLDERS::T1259707710"] = "Your projec
|
||||
-- Your temporary chats, along with their transcripts.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATAFOLDERS::T1293614512"] = "Your temporary chats, along with their transcripts."
|
||||
|
||||
-- Your preparations in the BeyondFacts Lab.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATAFOLDERS::T1415474409"] = "Your preparations in the BeyondFacts Lab."
|
||||
|
||||
-- What belongs into a backup
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATAFOLDERS::T1682175486"] = "What belongs into a backup"
|
||||
|
||||
@@ -9591,6 +9924,24 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBATCHPROCESSING::T82
|
||||
-- Restore default
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBATCHPROCESSING::T913168756"] = "Restore default"
|
||||
|
||||
-- How the lab addresses you in languages with a formal and an informal address, such as German.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T1832497993"] = "How the lab addresses you in languages with a formal and an informal address, such as German."
|
||||
|
||||
-- Assistant: BeyondFacts Lab Options
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T19824556"] = "Assistant: BeyondFacts Lab Options"
|
||||
|
||||
-- Form of address
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2183481736"] = "Form of address"
|
||||
|
||||
-- Select a minimum confidence level
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2579793544"] = "Select a minimum confidence level"
|
||||
|
||||
-- Close
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3448155331"] = "Close"
|
||||
|
||||
-- Your preparations usually contain unpublished research. Therefore, the lab accepts only providers with at least medium confidence. You can raise this minimum, e.g., to use only self-hosted models.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T4002746484"] = "Your preparations usually contain unpublished research. Therefore, the lab accepts only providers with at least medium confidence. You can raise this minimum, e.g., to use only self-hosted models."
|
||||
|
||||
-- Files are sent as they were first read
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHAT::T1320692831"] = "Files are sent as they were first read"
|
||||
|
||||
@@ -11199,12 +11550,18 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T3733831260"] = "Generate your own
|
||||
-- Generate an ERI server to integrate business systems.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T3756213118"] = "Generate an ERI server to integrate business systems."
|
||||
|
||||
-- Prepare step by step for a challenging appearance as a researcher, e.g., a panel discussion or an interview.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T386812830"] = "Prepare step by step for a challenging appearance as a researcher, e.g., a panel discussion or an interview."
|
||||
|
||||
-- Use an LLM to find an icon for a given context.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T3881504200"] = "Use an LLM to find an icon for a given context."
|
||||
|
||||
-- Job Posting
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T3930052338"] = "Job Posting"
|
||||
|
||||
-- BeyondFacts Lab
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T3956097526"] = "BeyondFacts Lab"
|
||||
|
||||
-- Ask a question about a legal document.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T3970214537"] = "Ask a question about a legal document."
|
||||
|
||||
@@ -12921,6 +13278,9 @@ UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::OUTBOUNDDATARESTRICTIONEXTENSION
|
||||
-- Only services configured in AI Studio
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::OUTBOUNDDATARESTRICTIONEXTENSIONS::T750634166"] = "Only services configured in AI Studio"
|
||||
|
||||
-- BeyondFacts Lab: Prepare for challenging conversations about your research, step by step
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::PREVIEWFEATURESEXTENSIONS::T1110065980"] = "BeyondFacts Lab: Prepare for challenging conversations about your research, step by step"
|
||||
|
||||
-- Visual Briefing Assistant: Turn source material into an interactive briefing
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::PREVIEWFEATURESEXTENSIONS::T1217946647"] = "Visual Briefing Assistant: Turn source material into an interactive briefing"
|
||||
|
||||
@@ -13170,6 +13530,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T348883878"] = "Document
|
||||
-- Translation Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T3887962308"] = "Translation Assistant"
|
||||
|
||||
-- BeyondFacts Lab
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T3956097526"] = "BeyondFacts Lab"
|
||||
|
||||
-- Agenda Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T4034795997"] = "Agenda Assistant"
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using AIStudio.Agents;
|
||||
using AIStudio.Agents.AssistantAudit;
|
||||
using AIStudio.Assistants.BeyondFactsLab;
|
||||
using AIStudio.Assistants.VisualBriefing;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.ToolCallingSystem;
|
||||
@@ -229,6 +230,7 @@ internal sealed class Program
|
||||
builder.Services.AddSingleton<VisualBriefingBuildOrchestrator>();
|
||||
builder.Services.AddSingleton<VisualBriefingPreviewTokenService>();
|
||||
builder.Services.AddSingleton<IMediaTranscriptStorage, VisualBriefingTranscriptStorage>();
|
||||
builder.Services.AddSingleton<BeyondFactsLabStore>();
|
||||
builder.Services.AddSingleton<PluginInstallService>();
|
||||
builder.Services.AddSingleton<UpdatePolicy>();
|
||||
builder.Services.AddSingleton<AssistantPluginGenerationService>();
|
||||
|
||||
@@ -27,6 +27,7 @@ public sealed partial class Routes
|
||||
public const string ASSISTANT_SYNONYMS = "/assistant/synonyms";
|
||||
public const string ASSISTANT_SLIDE_BUILDER = "/assistant/slide-builder";
|
||||
public const string ASSISTANT_VISUAL_BRIEFING = "/assistant/visual-briefing";
|
||||
public const string ASSISTANT_BEYOND_FACTS_LAB = "/assistant/beyondfacts-lab";
|
||||
public const string ASSISTANT_MY_TASKS = "/assistant/my-tasks";
|
||||
public const string ASSISTANT_JOB_POSTING = "/assistant/job-posting";
|
||||
public const string ASSISTANT_BIAS = "/assistant/bias-of-the-day";
|
||||
|
||||
@@ -29,6 +29,7 @@ public enum ConfigurableAssistant
|
||||
LOG_VIEWER_ASSISTANT,
|
||||
VISUAL_BRIEFING_ASSISTANT,
|
||||
BATCH_PROCESSING_ASSISTANT,
|
||||
BEYOND_FACTS_LAB_ASSISTANT,
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
I18N_ASSISTANT,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using AIStudio.Assistants.Agenda;
|
||||
using AIStudio.Assistants.BeyondFactsLab;
|
||||
using AIStudio.Assistants.Coding;
|
||||
using AIStudio.Assistants.IconFinder;
|
||||
using AIStudio.Assistants.RewriteImprove;
|
||||
@@ -315,6 +316,27 @@ public static class ConfigurationSelectDataFactory
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the minimum confidence levels the BeyondFacts Lab offers: none below its floor.
|
||||
/// </summary>
|
||||
/// <param name="settingsManager">The settings, whose enforced global minimum may lift the floor.</param>
|
||||
public static IEnumerable<ConfigurationSelectData<ConfidenceLevel>> GetBeyondFactsLabConfidenceLevelsData(SettingsManager settingsManager)
|
||||
{
|
||||
var minimumLevel = DataBeyondFactsLab.MINIMUM_CONFIDENCE_FLOOR;
|
||||
if (settingsManager.ConfigurationData.Confidence is { EnforceGlobalMinimumConfidence: true } && settingsManager.ConfigurationData.Confidence.GlobalMinimumConfidence > minimumLevel)
|
||||
minimumLevel = settingsManager.ConfigurationData.Confidence.GlobalMinimumConfidence;
|
||||
|
||||
foreach (var level in Enum.GetValues<ConfidenceLevel>())
|
||||
if (level >= minimumLevel)
|
||||
yield return new(level.Name, level);
|
||||
}
|
||||
|
||||
public static IEnumerable<ConfigurationSelectData<BflAddressForm>> GetBeyondFactsLabAddressFormData()
|
||||
{
|
||||
foreach (var addressForm in Enum.GetValues<BflAddressForm>())
|
||||
yield return new(addressForm.Name, addressForm);
|
||||
}
|
||||
|
||||
public static IEnumerable<ConfigurationSelectData<ConfidenceLevel>> GetDataSourceConfidenceLevelsData()
|
||||
{
|
||||
foreach (var level in Enum.GetValues<ConfidenceLevel>())
|
||||
|
||||
@@ -217,6 +217,11 @@ public sealed class Data
|
||||
/// Gets the managed Visual Briefing Assistant defaults.
|
||||
/// </summary>
|
||||
public DataVisualBriefing VisualBriefing { get; set; } = new(x => x.VisualBriefing);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the managed settings of the BeyondFacts Lab.
|
||||
/// </summary>
|
||||
public DataBeyondFactsLab BeyondFactsLab { get; set; } = new(x => x.BeyondFactsLab);
|
||||
|
||||
public DataLegalCheck LegalCheck { get; set; } = new();
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
using AIStudio.Assistants.BeyondFactsLab;
|
||||
using AIStudio.Provider;
|
||||
|
||||
namespace AIStudio.Settings.DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the settings of the BeyondFacts Lab, which an organization may manage.
|
||||
/// </summary>
|
||||
/// <param name="configSelection">The managed-configuration selector.</param>
|
||||
public sealed class DataBeyondFactsLab(Expression<Func<Data, DataBeyondFactsLab>>? configSelection = null)
|
||||
{
|
||||
/// <summary>
|
||||
/// The lowest minimum confidence the BeyondFacts Lab accepts, whatever the person or their organization configures.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Unpublished research is the usual material of the lab. The built-in confidence schemes give this level
|
||||
/// to the cloud providers of the region one trusts and to self-hosted models; HIGH alone would leave only
|
||||
/// self-hosted models. A higher minimum of the person or the organization wins.
|
||||
/// </remarks>
|
||||
public const ConfidenceLevel MINIMUM_CONFIDENCE_FLOOR = ConfidenceLevel.MEDIUM;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes unmanaged settings of the BeyondFacts Lab.
|
||||
/// </summary>
|
||||
public DataBeyondFactsLab() : this(null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the provider which new preparations start with.
|
||||
/// </summary>
|
||||
public string PreselectedProvider { get; set; } = ManagedConfiguration.Register(configSelection, value => value.PreselectedProvider, string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum confidence a provider needs for the lab.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A value below MINIMUM_CONFIDENCE_FLOOR has no effect, see ComponentsExtensions.MinimumConfidence.
|
||||
/// </remarks>
|
||||
public ConfidenceLevel MinimumProviderConfidence { get; set; } = ManagedConfiguration.Register(configSelection, value => value.MinimumProviderConfidence, MINIMUM_CONFIDENCE_FLOOR);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how the lab addresses the person in languages with a formal and an informal address.
|
||||
/// </summary>
|
||||
public BflAddressForm AddressForm { get; set; } = ManagedConfiguration.Register(configSelection, value => value.AddressForm, BflAddressForm.FORMAL);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the hash of the notes the person confirmed, see BflNotice; empty while they confirmed none.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only the person can confirm the notes, so an organization cannot manage this setting.
|
||||
/// </remarks>
|
||||
public string AcceptedNoticeHash { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -19,4 +19,5 @@ public enum PreviewFeatures
|
||||
PRE_VISUAL_BRIEFING_ASSISTANT_2026,
|
||||
PRE_MAILBOXES_2026,
|
||||
PRE_MATHEMATICS_2026,
|
||||
PRE_BEYOND_FACTS_LAB_2026,
|
||||
}
|
||||
@@ -22,6 +22,7 @@ public static class PreviewFeaturesExtensions
|
||||
PreviewFeatures.PRE_VISUAL_BRIEFING_ASSISTANT_2026 => TB("Visual Briefing Assistant: Turn source material into an interactive briefing"),
|
||||
PreviewFeatures.PRE_MAILBOXES_2026 => TB("Mailboxes: Let the AI search and read your e-mails, kept in a local index on your computer"),
|
||||
PreviewFeatures.PRE_MATHEMATICS_2026 => TB("Mathematics: Let the AI calculate exactly and work with formulas, right on your computer"),
|
||||
PreviewFeatures.PRE_BEYOND_FACTS_LAB_2026 => TB("BeyondFacts Lab: Prepare for challenging conversations about your research, step by step"),
|
||||
|
||||
_ => TB("Unknown preview feature")
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@ public static class PreviewVisibilityExtensions
|
||||
{
|
||||
features.Add(PreviewFeatures.PRE_MAILBOXES_2026);
|
||||
features.Add(PreviewFeatures.PRE_MATHEMATICS_2026);
|
||||
features.Add(PreviewFeatures.PRE_BEYOND_FACTS_LAB_2026);
|
||||
}
|
||||
|
||||
if (visibility >= PreviewVisibility.EXPERIMENTAL)
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
using System.Text;
|
||||
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
|
||||
#if RELEASE
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
/// <summary>
|
||||
/// Reads files which come with AI Studio, such as prompts, guides, or the built-in plugins.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The files lie below the project folder and are listed as EmbeddedResource in the project file. A development
|
||||
/// build reads them from the disk, so that a change shows without building again; a release reads them from the
|
||||
/// resources embedded into the app. Paths are relative to the project folder and use forward slashes.
|
||||
/// </remarks>
|
||||
public static class AppResources
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a file provider for a folder of the app.
|
||||
/// </summary>
|
||||
/// <param name="relativeFolder">The folder, relative to the project folder, e.g., "Plugins".</param>
|
||||
/// <returns>The file provider, whose paths are relative to that folder.</returns>
|
||||
public static IFileProvider FolderProvider(string relativeFolder)
|
||||
{
|
||||
var folder = Normalize(relativeFolder);
|
||||
#if DEBUG
|
||||
return new PhysicalFileProvider(Path.Join(Environment.CurrentDirectory, folder));
|
||||
#else
|
||||
return new ManifestEmbeddedFileProvider(Assembly.GetAssembly(typeof(Program))!, folder);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a text file of the app.
|
||||
/// </summary>
|
||||
/// <param name="relativePath">The file, relative to the project folder, e.g., "Assistants/PromptOptimizer/prompting_guideline.md".</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The text, or an empty string when the app has no such file.</returns>
|
||||
public static async Task<string> ReadTextAsync(string relativePath, CancellationToken token = default)
|
||||
{
|
||||
var path = Normalize(relativePath);
|
||||
#if DEBUG
|
||||
var filePath = Path.Join(Environment.CurrentDirectory, path);
|
||||
return File.Exists(filePath) ? await File.ReadAllTextAsync(filePath, Encoding.UTF8, token) : string.Empty;
|
||||
#else
|
||||
var file = new ManifestEmbeddedFileProvider(Assembly.GetAssembly(typeof(Program))!).GetFileInfo(path);
|
||||
if (!file.Exists || file.IsDirectory)
|
||||
return string.Empty;
|
||||
|
||||
await using var stream = file.CreateReadStream();
|
||||
using var reader = new StreamReader(stream, Encoding.UTF8);
|
||||
return await reader.ReadToEndAsync(token);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Turns a path into the form both kinds of builds understand: forward slashes, without a slash at either end.
|
||||
/// </summary>
|
||||
/// <param name="relativePath">The path.</param>
|
||||
private static string Normalize(string relativePath) => relativePath.Replace('\\', '/').Trim('/');
|
||||
}
|
||||
@@ -72,6 +72,7 @@ public static class AssistantVisibilityExtensions
|
||||
Components.BATCH_PROCESSING_ASSISTANT => ConfigurableAssistant.BATCH_PROCESSING_ASSISTANT,
|
||||
Components.SLIDE_BUILDER_ASSISTANT => ConfigurableAssistant.SLIDE_BUILDER_ASSISTANT,
|
||||
Components.VISUAL_BRIEFING_ASSISTANT => ConfigurableAssistant.VISUAL_BRIEFING_ASSISTANT,
|
||||
Components.BEYOND_FACTS_LAB_ASSISTANT => ConfigurableAssistant.BEYOND_FACTS_LAB_ASSISTANT,
|
||||
Components.I18N_ASSISTANT => ConfigurableAssistant.I18N_ASSISTANT,
|
||||
Components.LOG_VIEWER_ASSISTANT => ConfigurableAssistant.LOG_VIEWER_ASSISTANT,
|
||||
|
||||
|
||||
@@ -43,4 +43,5 @@ public enum Components
|
||||
LOG_VIEWER_ASSISTANT,
|
||||
VISUAL_BRIEFING_ASSISTANT,
|
||||
BATCH_PROCESSING_ASSISTANT,
|
||||
BEYOND_FACTS_LAB_ASSISTANT,
|
||||
}
|
||||
@@ -22,6 +22,7 @@ public static class ComponentsExtensions
|
||||
public PreviewFeatures RequiredPreviewFeature => component switch
|
||||
{
|
||||
Components.VISUAL_BRIEFING_ASSISTANT => PreviewFeatures.PRE_VISUAL_BRIEFING_ASSISTANT_2026,
|
||||
Components.BEYOND_FACTS_LAB_ASSISTANT => PreviewFeatures.PRE_BEYOND_FACTS_LAB_2026,
|
||||
|
||||
_ => PreviewFeatures.NONE,
|
||||
};
|
||||
@@ -33,15 +34,16 @@ public static class ComponentsExtensions
|
||||
/// <remarks>
|
||||
/// Components return <c>false</c> for three different reasons. The chat has no assistant sessions
|
||||
/// at all. Dynamic assistants key their sessions per plugin. The visual briefing assistant keys
|
||||
/// its sessions per briefing, so it owns one slot per stored briefing rather than one per
|
||||
/// component. All must be excluded from the single-slot checks, which is why this is a
|
||||
/// capability and not a component comparison.
|
||||
/// its sessions per briefing, and the BeyondFacts Lab per step of a preparation, so they own one
|
||||
/// slot per stored item rather than one per component. All must be excluded from the single-slot
|
||||
/// checks, which is why this is a capability and not a component comparison.
|
||||
/// </remarks>
|
||||
/// <value><c>true</c> when the component owns exactly one session slot.</value>
|
||||
public bool HasSingleSessionSlot => component switch
|
||||
{
|
||||
Components.CHAT or Components.DYNAMIC_ASSISTANT => false,
|
||||
Components.VISUAL_BRIEFING_ASSISTANT => false,
|
||||
Components.BEYOND_FACTS_LAB_ASSISTANT => false,
|
||||
|
||||
_ => true,
|
||||
};
|
||||
@@ -68,7 +70,10 @@ public static class ComponentsExtensions
|
||||
Components.DOCUMENT_ANALYSIS_ASSISTANT => false,
|
||||
Components.BATCH_PROCESSING_ASSISTANT => false,
|
||||
Components.LOG_VIEWER_ASSISTANT => false,
|
||||
|
||||
|
||||
// The lab takes in nothing from other places, and its answers stay in its own steps:
|
||||
Components.BEYOND_FACTS_LAB_ASSISTANT => false,
|
||||
|
||||
Components.APP_SETTINGS => false,
|
||||
Components.WRITER => false,
|
||||
|
||||
@@ -105,7 +110,8 @@ public static class ComponentsExtensions
|
||||
Components.VISUAL_BRIEFING_ASSISTANT => TB("Visual Briefing Assistant"),
|
||||
Components.META_ASSISTANT => TB("Assistant Builder"),
|
||||
Components.LOG_VIEWER_ASSISTANT => TB("Log Viewer Assistant"),
|
||||
|
||||
Components.BEYOND_FACTS_LAB_ASSISTANT => TB("BeyondFacts Lab"),
|
||||
|
||||
Components.CHAT => TB("New Chat"),
|
||||
|
||||
_ => Enum.GetName(component)!,
|
||||
@@ -157,7 +163,10 @@ public static class ComponentsExtensions
|
||||
Components.SLIDE_BUILDER_ASSISTANT => settingsManager.ConfigurationData.SlideBuilder.PreselectOptions ? settingsManager.ConfigurationData.SlideBuilder.MinimumProviderConfidence : default,
|
||||
Components.META_ASSISTANT => settingsManager.ConfigurationData.AssistantBuilder.PreselectOptions ? settingsManager.ConfigurationData.AssistantBuilder.MinimumProviderConfidence : default,
|
||||
Components.VISUAL_BRIEFING_ASSISTANT => settingsManager.ConfigurationData.VisualBriefing.MinimumProviderConfidence,
|
||||
|
||||
|
||||
// Unpublished research is the usual material of the lab, so its minimum never drops below the floor:
|
||||
Components.BEYOND_FACTS_LAB_ASSISTANT => settingsManager.ConfigurationData.BeyondFactsLab.MinimumProviderConfidence > DataBeyondFactsLab.MINIMUM_CONFIDENCE_FLOOR ? settingsManager.ConfigurationData.BeyondFactsLab.MinimumProviderConfidence : DataBeyondFactsLab.MINIMUM_CONFIDENCE_FLOOR,
|
||||
|
||||
// The minimum confidence for the Document Analysis Assistant is set per policy.
|
||||
// We do this inside the Document Analysis Assistant component:
|
||||
Components.DOCUMENT_ANALYSIS_ASSISTANT => ConfidenceLevel.NONE,
|
||||
@@ -191,6 +200,7 @@ public static class ComponentsExtensions
|
||||
Components.SLIDE_BUILDER_ASSISTANT => settingsManager.ConfigurationData.SlideBuilder.PreselectOptions ? settingsManager.GetProviderById(settingsManager.ConfigurationData.SlideBuilder.PreselectedProvider) : Settings.Provider.NONE,
|
||||
Components.META_ASSISTANT => settingsManager.ConfigurationData.AssistantBuilder.PreselectOptions ? settingsManager.GetProviderById(settingsManager.ConfigurationData.AssistantBuilder.PreselectedProvider) : Settings.Provider.NONE,
|
||||
Components.VISUAL_BRIEFING_ASSISTANT => settingsManager.GetProviderById(settingsManager.ConfigurationData.VisualBriefing.PreselectedProvider),
|
||||
Components.BEYOND_FACTS_LAB_ASSISTANT => settingsManager.GetProviderById(settingsManager.ConfigurationData.BeyondFactsLab.PreselectedProvider),
|
||||
|
||||
// The Document Analysis Assistant does not have a preselected provider at the component level.
|
||||
// The provider is selected per policy instead. We do this inside the Document Analysis Assistant component.
|
||||
@@ -229,6 +239,9 @@ public static class ComponentsExtensions
|
||||
// The profile is selected per policy instead. We do this inside the Document Analysis Assistant component:
|
||||
Components.DOCUMENT_ANALYSIS_ASSISTANT => Profile.NO_PROFILE.Id,
|
||||
|
||||
// The lab works with what the person enters in each preparation and never with a profile:
|
||||
Components.BEYOND_FACTS_LAB_ASSISTANT => Profile.NO_PROFILE.Id,
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ public static class DataBackupContents
|
||||
new(DataBackupLocation.DATA_DIRECTORY, PLUGINS_FOLDER, DataBackupEntryKind.PLUGINS),
|
||||
new(DataBackupLocation.DATA_DIRECTORY, "tokenizers/", DataBackupEntryKind.TOKENIZERS),
|
||||
new(DataBackupLocation.DATA_DIRECTORY, "visualBriefings/", DataBackupEntryKind.VISUAL_BRIEFINGS),
|
||||
new(DataBackupLocation.DATA_DIRECTORY, "beyondFactsLab/", DataBackupEntryKind.BEYOND_FACTS_LAB),
|
||||
new(DataBackupLocation.DATA_DIRECTORY, "databases/sqlite/", DataBackupEntryKind.DATA_SOURCE_INDEX),
|
||||
new(DataBackupLocation.DATA_DIRECTORY, "databases/vector_database/stores/", DataBackupEntryKind.VECTOR_STORES),
|
||||
new(DataBackupLocation.DATA_DIRECTORY, "mediaTranscripts/", DataBackupEntryKind.MEDIA_TRANSCRIPTS),
|
||||
|
||||
@@ -18,6 +18,7 @@ public sealed record DataBackupEntry(DataBackupLocation Location, string Relativ
|
||||
or DataBackupEntryKind.PLUGINS
|
||||
or DataBackupEntryKind.TOKENIZERS
|
||||
or DataBackupEntryKind.VISUAL_BRIEFINGS
|
||||
or DataBackupEntryKind.BEYOND_FACTS_LAB
|
||||
or DataBackupEntryKind.DATA_SOURCE_INDEX
|
||||
or DataBackupEntryKind.VECTOR_STORES
|
||||
or DataBackupEntryKind.MEDIA_TRANSCRIPTS;
|
||||
|
||||
@@ -41,6 +41,11 @@ public enum DataBackupEntryKind
|
||||
/// </summary>
|
||||
VISUAL_BRIEFINGS,
|
||||
|
||||
/// <summary>
|
||||
/// The preparations of the BeyondFacts Lab.
|
||||
/// </summary>
|
||||
BEYOND_FACTS_LAB,
|
||||
|
||||
/// <summary>
|
||||
/// The SQLite index of the data sources: which files are embedded, and their text chunks.
|
||||
/// </summary>
|
||||
|
||||
@@ -19,7 +19,7 @@ public static class ILangExtensions
|
||||
typeName = typeName[..typeName.IndexOf('`')];
|
||||
|
||||
var ns = $"{typeNamespace}::{typeName}".ToUpperInvariant().Replace(".", "::");
|
||||
var key = $"root::{ns}::T{fallbackEN.ToFNV32()}";
|
||||
var key = $"root::{ns}::T{I18NKeyHash.Of(fallbackEN)}";
|
||||
|
||||
if(plugin is NoPluginLanguage)
|
||||
return fallbackEN;
|
||||
|
||||
@@ -516,6 +516,11 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.VisualBriefing, x => x.OptimizeImages, this.Id, settingsTable, dryRun);
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.VisualBriefing, x => x.MinimumProviderConfidence, this.Id, settingsTable, dryRun);
|
||||
|
||||
// Config: BeyondFacts Lab defaults?
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.BeyondFactsLab, x => x.PreselectedProvider, Guid.Empty, this.Id, settingsTable, dryRun);
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.BeyondFactsLab, x => x.MinimumProviderConfidence, this.Id, settingsTable, dryRun);
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.BeyondFactsLab, x => x.AddressForm, this.Id, settingsTable, dryRun);
|
||||
|
||||
// Config: Assistant Builder defaults?
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.AssistantBuilder, x => x.PreselectOptions, this.Id, settingsTable, dryRun);
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.AssistantBuilder, x => x.PreselectedProvider, Guid.Empty, this.Id, settingsTable, dryRun);
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
|
||||
#if RELEASE
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
public static partial class PluginFactory
|
||||
@@ -48,13 +44,7 @@ public static partial class PluginFactory
|
||||
{
|
||||
try
|
||||
{
|
||||
#if DEBUG
|
||||
var basePath = Path.Join(Environment.CurrentDirectory, "Plugins");
|
||||
var resourceFileProvider = new PhysicalFileProvider(basePath);
|
||||
#else
|
||||
var resourceFileProvider = new ManifestEmbeddedFileProvider(Assembly.GetAssembly(type: typeof(Program))!, "Plugins");
|
||||
#endif
|
||||
|
||||
var resourceFileProvider = AppResources.FolderProvider("Plugins");
|
||||
var metaData = plugin.MetaData();
|
||||
var mainResourcePath = $"{metaData.ResourcePath}/plugin.lua";
|
||||
var resourceInfo = resourceFileProvider.GetFileInfo(mainResourcePath);
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// ReSharper disable RedundantUsingDirective
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
// ReSharper restore RedundantUsingDirective
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using AIStudio.Assistants.Builder;
|
||||
@@ -255,7 +251,7 @@ public sealed class AssistantPluginGenerationService(ToolRegistry toolRegistry,
|
||||
|
||||
foreach (var contextFile in ASSISTANT_CONTEXT_FILES)
|
||||
{
|
||||
var content = await ReadAppResourceTextAsync(contextFile.RelativePath);
|
||||
var content = await AppResources.ReadTextAsync(contextFile.RelativePath);
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
{
|
||||
logger.LogError($"The context for \"{contextFile.Title}\" could not be read from the assembly. Path: {contextFile.RelativePath}");
|
||||
@@ -713,7 +709,7 @@ public sealed class AssistantPluginGenerationService(ToolRegistry toolRegistry,
|
||||
|
||||
private async Task<string> LoadLuaResponseSchemaAsync()
|
||||
{
|
||||
var responseSchema = await ReadAppResourceTextAsync(LUA_RESPONSE_SCHEMA_PATH);
|
||||
var responseSchema = await AppResources.ReadTextAsync(LUA_RESPONSE_SCHEMA_PATH);
|
||||
if (!string.IsNullOrWhiteSpace(responseSchema))
|
||||
return responseSchema.Trim();
|
||||
|
||||
@@ -743,26 +739,6 @@ public sealed class AssistantPluginGenerationService(ToolRegistry toolRegistry,
|
||||
return builder.ToString().Trim();
|
||||
}
|
||||
|
||||
private static async Task<string> ReadAppResourceTextAsync(string relativePath)
|
||||
{
|
||||
relativePath = relativePath.Replace('\\', '/');
|
||||
#if DEBUG
|
||||
var filePath = Path.Join(Environment.CurrentDirectory, relativePath);
|
||||
return File.Exists(filePath)
|
||||
? await File.ReadAllTextAsync(filePath)
|
||||
: string.Empty;
|
||||
#else
|
||||
var provider = new ManifestEmbeddedFileProvider(Assembly.GetAssembly(type: typeof(Program))!);
|
||||
var file = provider.GetFileInfo(relativePath);
|
||||
if (!file.Exists)
|
||||
return string.Empty;
|
||||
|
||||
await using var stream = file.CreateReadStream();
|
||||
using var reader = new StreamReader(stream, Encoding.UTF8);
|
||||
return await reader.ReadToEndAsync();
|
||||
#endif
|
||||
}
|
||||
|
||||
private static bool ProviderIsUsable(ProviderSettings provider) => provider != ProviderSettings.NONE && provider.UsedLLMProvider is not LLMProviders.NONE;
|
||||
|
||||
private static bool IsValidChatLaunchRequest(AssistantBuilderChatLaunchRequest? launch)
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
# v26.11.2, build 261 (2026-11-xx xx:xx UTC)
|
||||
- Added the BeyondFacts Lab as a prototype. It helps researchers prepare step by step for a challenging appearance, such as a panel discussion, an interview, or a hearing. Each event gets a preparation of its own, which leads you through seven steps, from the details of the event to what you take away, and shows you which key details are still missing. Your preparations are part of your data backups. To try it, open the app settings, allow preview features down to prototype, and enable the BeyondFacts Lab. Thanks to Lara Wüster from the Technical University of Munich (TUM) for the concept and the content of the lab.
|
||||
Reference in new issue
Block a user