mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-11 09:07:21 +00:00
Added prompt overrides for testing the BeyondFacts Lab (#1181)
This commit is contained in:
1 parent
9f2280bed7
commit
6d0052ecdf
21 files changed
+1035
-13
No files matched your search
@@ -128,6 +128,8 @@
|
|||||||
<MudGrid Spacing="3">
|
<MudGrid Spacing="3">
|
||||||
<MudItem xs="12" lg="8">
|
<MudItem xs="12" lg="8">
|
||||||
<div @key="@this.activeStep" class="px-6">
|
<div @key="@this.activeStep" class="px-6">
|
||||||
|
@* It checks the test folder anew for every step, as the key of the step recreates it: *@
|
||||||
|
<BflPromptOverrideNotice Class="mt-2 mb-2"/>
|
||||||
<MudText Typo="Typo.h5" Class="mt-2">@this.activeStep.Title</MudText>
|
<MudText Typo="Typo.h5" Class="mt-2">@this.activeStep.Title</MudText>
|
||||||
<MudText Typo="Typo.body2" Class="mb-4">@this.activeStep.Description</MudText>
|
<MudText Typo="Typo.body2" Class="mb-4">@this.activeStep.Description</MudText>
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ public sealed partial class BeyondFactsLabStore(ILogger<BeyondFactsLabStore> log
|
|||||||
throw new InvalidOperationException("The AI Studio data directory is not initialized."),
|
throw new InvalidOperationException("The AI Studio data directory is not initialized."),
|
||||||
ROOT_DIRECTORY_NAME);
|
ROOT_DIRECTORY_NAME);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the folder whose files replace the prompts of the lab while the person allows it, see BflPromptOverrides.
|
||||||
|
/// </summary>
|
||||||
|
public string PromptOverridesDirectory => Path.Combine(this.RootDirectory, BflPromptOverrides.DIRECTORY_NAME);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes a text file so that readers see either the old or the new content, never a part of it.
|
/// Writes a text file so that readers see either the old or the new content, never a part of it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -58,6 +58,12 @@ public sealed class BflConversationService(BeyondFactsLabStore store, SettingsMa
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly Lock runningLock = new();
|
private readonly Lock runningLock = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the folder whose files replace the prompts of the lab, or null while the person does not allow it. The
|
||||||
|
/// setting counts anew for every request, so that switching it on or off shows in the next answer.
|
||||||
|
/// </summary>
|
||||||
|
private string? PromptOverridesDirectory => BflPromptOverrides.ActiveDirectory(settingsManager.ConfigurationData.BeyondFactsLab, store.PromptOverridesDirectory);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Happens whenever a chat changed: a message, a chunk of an answer, or the end of a request.
|
/// Happens whenever a chat changed: a message, a chunk of an answer, or the end of a request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -144,6 +150,12 @@ public sealed class BflConversationService(BeyondFactsLabStore store, SettingsMa
|
|||||||
{
|
{
|
||||||
requestMaterial = await this.ComposeRequestAsync(location, provider);
|
requestMaterial = await this.ComposeRequestAsync(location, provider);
|
||||||
}
|
}
|
||||||
|
catch (BflPromptOverrideException exception)
|
||||||
|
{
|
||||||
|
logger.LogWarning(exception, "A prompt override of the BeyondFacts Lab has a placeholder which the lab does not fill.");
|
||||||
|
await messageBus.SendError(new(Icons.Material.Filled.ErrorOutline, string.Format(TA("The file '{0}' in the test folder of the BeyondFacts Lab contains the placeholder '{1}', which the lab does not fill in this file. Please correct the file and try again."),exception.FileName, exception.Placeholder)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
catch (Exception exception) when (exception is IOException or UnauthorizedAccessException or FormatException)
|
catch (Exception exception) when (exception is IOException or UnauthorizedAccessException or FormatException)
|
||||||
{
|
{
|
||||||
logger.LogError(exception, "The BeyondFacts Lab could not put together the system prompt of the chat '{Location}'.", location.Key);
|
logger.LogError(exception, "The BeyondFacts Lab could not put together the system prompt of the chat '{Location}'.", location.Key);
|
||||||
@@ -285,6 +297,14 @@ public sealed class BflConversationService(BeyondFactsLabStore store, SettingsMa
|
|||||||
/// <exception cref="FormatException">A prompt has a placeholder without a value.</exception>
|
/// <exception cref="FormatException">A prompt has a placeholder without a value.</exception>
|
||||||
public async Task<string> ComposeSystemPromptAsync(BflThreadLocation location, AIStudio.Settings.Provider provider) => (await this.ComposeRequestAsync(location, provider)).SystemPrompt;
|
public async Task<string> ComposeSystemPromptAsync(BflThreadLocation location, AIStudio.Settings.Provider provider) => (await this.ComposeRequestAsync(location, provider)).SystemPrompt;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether files in the test folder replace prompts of the lab right now, so that the lab can tell the
|
||||||
|
/// person.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">The cancellation token.</param>
|
||||||
|
/// <returns>True while the setting allows prompt overrides and a file replaces a prompt with another text.</returns>
|
||||||
|
public async Task<bool> UsesPromptOverridesAsync(CancellationToken token = default) => this.PromptOverridesDirectory is { } directory && (await BflPromptOverrides.FindAsync(directory, token)).Count > 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Puts together what a request of a chat carries beside the conversation: the system prompt, and the images of
|
/// Puts together what a request of a chat carries beside the conversation: the system prompt, and the images of
|
||||||
/// the material the model may see.
|
/// the material the model may see.
|
||||||
@@ -302,13 +322,13 @@ public sealed class BflConversationService(BeyondFactsLabStore store, SettingsMa
|
|||||||
{
|
{
|
||||||
case BflStepThreadLocation stepLocation:
|
case BflStepThreadLocation stepLocation:
|
||||||
var forms = await store.LoadFormsAsync(stepLocation.ProjectId);
|
var forms = await store.LoadFormsAsync(stepLocation.ProjectId);
|
||||||
var parts = await BflPromptLibrary.LoadPartsAsync(stepLocation.Step, addressForm);
|
var parts = await BflPromptLibrary.LoadPartsAsync(stepLocation.Step, addressForm, this.PromptOverridesDirectory);
|
||||||
var shares = await this.ShareMaterialsAsync(stepLocation, parts, forms, language, provider);
|
var shares = await this.ShareMaterialsAsync(stepLocation, parts, forms, language, provider);
|
||||||
var materials = await this.ReadMaterialsAsync(stepLocation.ProjectId, shares);
|
var materials = await this.ReadMaterialsAsync(stepLocation.ProjectId, shares);
|
||||||
return new(BflPromptComposer.Compose(parts, stepLocation.Step, forms, materials, language), this.ImagesOf(stepLocation.ProjectId, shares));
|
return new(BflPromptComposer.Compose(parts, stepLocation.Step, forms, materials, language), this.ImagesOf(stepLocation.ProjectId, shares));
|
||||||
|
|
||||||
case BflOnboardingThreadLocation:
|
case BflOnboardingThreadLocation:
|
||||||
return new(BflPromptComposer.ComposeOnboarding(await BflPromptLibrary.LoadOnboardingPartsAsync(addressForm), language), []);
|
return new(BflPromptComposer.ComposeOnboarding(await BflPromptLibrary.LoadOnboardingPartsAsync(addressForm, this.PromptOverridesDirectory), language), []);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException(nameof(location), location, "The lab has no prompt for this chat.");
|
throw new ArgumentOutOfRangeException(nameof(location), location, "The lab has no prompt for this chat.");
|
||||||
@@ -406,7 +426,7 @@ public sealed class BflConversationService(BeyondFactsLabStore store, SettingsMa
|
|||||||
{
|
{
|
||||||
var language = (await settingsManager.GetActiveLanguagePlugin()).LangName;
|
var language = (await settingsManager.GetActiveLanguagePlugin()).LangName;
|
||||||
var forms = await store.LoadFormsAsync(location.ProjectId);
|
var forms = await store.LoadFormsAsync(location.ProjectId);
|
||||||
var parts = await BflPromptLibrary.LoadPartsAsync(location.Step, Tools.PluginSystem.I18N.I.AddressForm);
|
var parts = await BflPromptLibrary.LoadPartsAsync(location.Step, Tools.PluginSystem.I18N.I.AddressForm, this.PromptOverridesDirectory);
|
||||||
return await this.ShareMaterialsAsync(location, parts, forms, language, provider);
|
return await this.ShareMaterialsAsync(location, parts, forms, language, provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
@inherits MSGComponentBase
|
@inherits MSGComponentBase
|
||||||
|
|
||||||
|
<BflPromptOverrideNotice Class="mb-3"/>
|
||||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidateProvider"/>
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidateProvider"/>
|
||||||
<BflChat Location="@BflOnboardingThreadLocation.INSTANCE" Provider="@this.providerSettings" ProviderIssue="@this.ValidateProvider(this.providerSettings)" ShowHeading="@false"/>
|
<BflChat Location="@BflOnboardingThreadLocation.INSTANCE" Provider="@this.providerSettings" ProviderIssue="@this.ValidateProvider(this.providerSettings)" ShowHeading="@false"/>
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using System.Collections.Frozen;
|
||||||
|
|
||||||
using AIStudio.Settings.DataModel;
|
using AIStudio.Settings.DataModel;
|
||||||
|
|
||||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||||
@@ -8,6 +10,7 @@ namespace AIStudio.Assistants.BeyondFactsLab;
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The files are German Markdown below Assistants/BeyondFactsLab, embedded into the app. A development build reads
|
/// The files are German Markdown below Assistants/BeyondFactsLab, embedded into the app. A development build reads
|
||||||
/// them from the disk, so that a changed prompt shows in the next request without building again; see AppResources.
|
/// them from the disk, so that a changed prompt shows in the next request without building again; see AppResources.
|
||||||
|
/// While the person allows it, files in the folder for prompt overrides replace them, see BflPromptOverrides.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public static class BflPromptLibrary
|
public static class BflPromptLibrary
|
||||||
{
|
{
|
||||||
@@ -42,6 +45,30 @@ public static class BflPromptLibrary
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const string ADDRESS_INFORMAL_PATH = FOLDER + "/Prompts/snippets/address-informal.md";
|
private const string ADDRESS_INFORMAL_PATH = FOLDER + "/Prompts/snippets/address-informal.md";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The placeholders BflPromptComposer fills in the core.
|
||||||
|
/// </summary>
|
||||||
|
private static readonly FrozenSet<string> CORE_PLACEHOLDERS = FrozenSet.Create(StringComparer.Ordinal,
|
||||||
|
BflPromptTemplate.RESPONSE_LANGUAGE,
|
||||||
|
BflPromptTemplate.ADDRESS_FORM,
|
||||||
|
BflPromptTemplate.KNOWLEDGE,
|
||||||
|
BflPromptTemplate.STEP,
|
||||||
|
BflPromptTemplate.MATERIALS,
|
||||||
|
BflPromptTemplate.DETAILS,
|
||||||
|
BflPromptTemplate.MISSING_DETAILS);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The placeholders BflPromptComposer fills in the prompt of the onboarding.
|
||||||
|
/// </summary>
|
||||||
|
private static readonly FrozenSet<string> ONBOARDING_PLACEHOLDERS = FrozenSet.Create(StringComparer.Ordinal,
|
||||||
|
BflPromptTemplate.RESPONSE_LANGUAGE,
|
||||||
|
BflPromptTemplate.ADDRESS_FORM);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The placeholders of every other file: none, because BflPromptComposer inserts these files as values.
|
||||||
|
/// </summary>
|
||||||
|
private static readonly FrozenSet<string> NO_PLACEHOLDERS = FrozenSet<string>.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the prompt of a step, named like the folder of the step.
|
/// Gets the prompt of a step, named like the folder of the step.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -66,39 +93,83 @@ public static class BflPromptLibrary
|
|||||||
..BflStep.All.Select(StepPath)
|
..BflStep.All.Select(StepPath)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a file of the lab's prompts relative to the folder of the lab, e.g., "Prompts/core.md".
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The file, one of the paths of this class.</param>
|
||||||
|
/// <exception cref="ArgumentException">The file lies outside the folder of the lab.</exception>
|
||||||
|
public static string RelativePath(string path) => path.StartsWith(FOLDER + "/", StringComparison.Ordinal) ? path[(FOLDER.Length + 1)..] : throw new ArgumentException("The file lies outside the folder of the BeyondFacts Lab.", nameof(path));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the placeholders a file of the lab's prompts may contain.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// BflPromptComposer fills placeholders in the core and in the prompt of the onboarding only. It inserts every
|
||||||
|
/// other file as a value, whose placeholders it leaves as they are, so that they would reach the model as text.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="path">The file, one of the paths of this class.</param>
|
||||||
|
public static IReadOnlySet<string> PlaceholdersOf(string path) => path switch
|
||||||
|
{
|
||||||
|
CORE_PATH => CORE_PLACEHOLDERS,
|
||||||
|
ONBOARDING_PATH => ONBOARDING_PLACEHOLDERS,
|
||||||
|
_ => NO_PLACEHOLDERS
|
||||||
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads the texts of the system prompt of a step.
|
/// Reads the texts of the system prompt of a step.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="step">The step.</param>
|
/// <param name="step">The step.</param>
|
||||||
/// <param name="addressForm">How the model addresses the person.</param>
|
/// <param name="addressForm">How the model addresses the person.</param>
|
||||||
|
/// <param name="overrideDirectory">The folder whose files replace the prompts, or null while the person does not allow it.</param>
|
||||||
/// <param name="token">The cancellation token.</param>
|
/// <param name="token">The cancellation token.</param>
|
||||||
/// <returns>The texts, which BflPromptComposer puts together.</returns>
|
/// <returns>The texts, which BflPromptComposer puts together.</returns>
|
||||||
/// <exception cref="FileNotFoundException">The app comes without one of the files.</exception>
|
/// <exception cref="FileNotFoundException">The app comes without one of the files.</exception>
|
||||||
public static async Task<BflPromptParts> LoadPartsAsync(BflStep step, AddressForm addressForm, CancellationToken token = default) => new(
|
/// <exception cref="BflPromptOverrideException">A file which replaces a prompt contains a placeholder which the lab does not fill in it.</exception>
|
||||||
await ReadAsync(CORE_PATH, token),
|
public static async Task<BflPromptParts> LoadPartsAsync(BflStep step, AddressForm addressForm, string? overrideDirectory = null, CancellationToken token = default) => new(
|
||||||
await ReadAsync(KNOWLEDGE_PATH, token),
|
await ReadAsync(CORE_PATH, overrideDirectory, token),
|
||||||
await ReadAsync(StepPath(step), token),
|
await ReadAsync(KNOWLEDGE_PATH, overrideDirectory, token),
|
||||||
await ReadAsync(AddressPath(addressForm), token));
|
await ReadAsync(StepPath(step), overrideDirectory, token),
|
||||||
|
await ReadAsync(AddressPath(addressForm), overrideDirectory, token));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads the texts of the system prompt of the conversation about whether the lab fits a task.
|
/// Reads the texts of the system prompt of the conversation about whether the lab fits a task.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="addressForm">How the model addresses the person.</param>
|
/// <param name="addressForm">How the model addresses the person.</param>
|
||||||
|
/// <param name="overrideDirectory">The folder whose files replace the prompts, or null while the person does not allow it.</param>
|
||||||
/// <param name="token">The cancellation token.</param>
|
/// <param name="token">The cancellation token.</param>
|
||||||
/// <returns>The texts, which BflPromptComposer puts together.</returns>
|
/// <returns>The texts, which BflPromptComposer puts together.</returns>
|
||||||
/// <exception cref="FileNotFoundException">The app comes without one of the files.</exception>
|
/// <exception cref="FileNotFoundException">The app comes without one of the files.</exception>
|
||||||
public static async Task<BflOnboardingPromptParts> LoadOnboardingPartsAsync(AddressForm addressForm, CancellationToken token = default) => new(
|
/// <exception cref="BflPromptOverrideException">A file which replaces a prompt contains a placeholder which the lab does not fill in it.</exception>
|
||||||
await ReadAsync(ONBOARDING_PATH, token),
|
public static async Task<BflOnboardingPromptParts> LoadOnboardingPartsAsync(AddressForm addressForm, string? overrideDirectory = null, CancellationToken token = default) => new(
|
||||||
await ReadAsync(AddressPath(addressForm), token));
|
await ReadAsync(ONBOARDING_PATH, overrideDirectory, token),
|
||||||
|
await ReadAsync(AddressPath(addressForm), overrideDirectory, token));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads a file of the lab's prompts.
|
/// Reads a file of the lab's prompts: the file which replaces it, if there is one, or else the file which comes
|
||||||
|
/// with the app.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The file, one of the paths of this class.</param>
|
||||||
|
/// <param name="overrideDirectory">The folder whose files replace the prompts, or null while the person does not allow it.</param>
|
||||||
|
/// <param name="token">The cancellation token.</param>
|
||||||
|
/// <returns>The text of the file.</returns>
|
||||||
|
/// <exception cref="FileNotFoundException">The app comes without the file, or the file is empty.</exception>
|
||||||
|
/// <exception cref="BflPromptOverrideException">The file which replaces the prompt contains a placeholder which the lab does not fill in it.</exception>
|
||||||
|
public static async Task<string> ReadAsync(string path, string? overrideDirectory = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
if (overrideDirectory is not null && await BflPromptOverrides.TryReadAsync(overrideDirectory, path, token) is { } replacement)
|
||||||
|
return replacement;
|
||||||
|
|
||||||
|
return await ReadBuiltInAsync(path, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a file of the lab's prompts as it comes with the app.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The file, one of the paths of this class.</param>
|
/// <param name="path">The file, one of the paths of this class.</param>
|
||||||
/// <param name="token">The cancellation token.</param>
|
/// <param name="token">The cancellation token.</param>
|
||||||
/// <returns>The text of the file.</returns>
|
/// <returns>The text of the file.</returns>
|
||||||
/// <exception cref="FileNotFoundException">The app comes without the file, or the file is empty.</exception>
|
/// <exception cref="FileNotFoundException">The app comes without the file, or the file is empty.</exception>
|
||||||
public static async Task<string> ReadAsync(string path, CancellationToken token = default)
|
public static async Task<string> ReadBuiltInAsync(string path, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
var text = await AppResources.ReadTextAsync(path, token);
|
var text = await AppResources.ReadTextAsync(path, token);
|
||||||
if (string.IsNullOrWhiteSpace(text))
|
if (string.IsNullOrWhiteSpace(text))
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tells that a file in the folder for prompt overrides contains a placeholder which the lab does not fill in it.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The lab fills placeholders in the core and in the prompt of the onboarding only, and only those it knows; in any
|
||||||
|
/// other file, a placeholder would reach the model as text. Whoever tests a changed prompt needs to learn which file
|
||||||
|
/// and which placeholder it is, so the message names both.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="fileName">The file, relative to the folder for prompt overrides, with forward slashes.</param>
|
||||||
|
/// <param name="placeholder">The name of the placeholder, without braces.</param>
|
||||||
|
public sealed class BflPromptOverrideException(string fileName, string placeholder) : FormatException($"The file '{fileName}' in the folder for prompt overrides contains the placeholder '{placeholder}', which the lab does not fill in this file.")
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the file, relative to the folder for prompt overrides, with forward slashes.
|
||||||
|
/// </summary>
|
||||||
|
public string FileName { get; } = fileName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name of the placeholder, without braces.
|
||||||
|
/// </summary>
|
||||||
|
public string Placeholder { get; } = placeholder;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
@inherits MSGComponentBase
|
||||||
|
|
||||||
|
@if (this.usesPromptOverrides)
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Warning" Variant="Variant.Outlined" Dense="true" Class="@this.Class">
|
||||||
|
@TA("The lab currently uses experimental instructions from its test folder. You can switch them off in the options of the lab.")
|
||||||
|
</MudAlert>
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
using AIStudio.Components;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tells the person that files in the test folder replace prompts of the lab, so that nobody mistakes the answers of
|
||||||
|
/// experimental prompts for those of the lab.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// It checks the folder when it appears, e.g., for every step the person opens, and whenever a setting changes. It
|
||||||
|
/// shows nothing while the setting does not allow prompt overrides, or while every file in the folder matches the
|
||||||
|
/// prompt it replaces.
|
||||||
|
/// </remarks>
|
||||||
|
public partial class BflPromptOverrideNotice : MSGComponentBase
|
||||||
|
{
|
||||||
|
[Inject]
|
||||||
|
private BflConversationService ConversationService { get; init; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private ILogger<BflPromptOverrideNotice> Logger { get; init; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the CSS classes of the notice, e.g., for its margins.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string Class { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether files in the test folder replace prompts of the lab.
|
||||||
|
/// </summary>
|
||||||
|
private bool usesPromptOverrides;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
this.ApplyFilters([], [Event.CONFIGURATION_CHANGED]);
|
||||||
|
await base.OnInitializedAsync();
|
||||||
|
await this.CheckAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||||
|
{
|
||||||
|
if (triggeredEvent is Event.CONFIGURATION_CHANGED)
|
||||||
|
{
|
||||||
|
// The person may switch the prompt overrides on or off in the options of the lab while the page is open:
|
||||||
|
await this.CheckAsync();
|
||||||
|
this.StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
await base.ProcessIncomingMessage(sendingComponent, triggeredEvent, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether files in the test folder replace prompts of the lab.
|
||||||
|
/// </summary>
|
||||||
|
private async Task CheckAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.usesPromptOverrides = await this.ConversationService.UsesPromptOverridesAsync();
|
||||||
|
}
|
||||||
|
catch (Exception exception) when (exception is IOException or UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
// Sending reports a problem with the prompts; the notice only stays hidden:
|
||||||
|
this.Logger.LogDebug(exception, "Could not check the test folder of the BeyondFacts Lab.");
|
||||||
|
this.usesPromptOverrides = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using AIStudio.Settings.DataModel;
|
||||||
|
|
||||||
|
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the files which replace the prompts of the BeyondFacts Lab, so that changed prompts can be tested without a
|
||||||
|
/// new version of AI Studio.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The folder mirrors the files of the lab below Assistants/BeyondFactsLab, e.g., Prompts/steps/02-knowledge.md, so
|
||||||
|
/// that every name stays unique. Only the files of BflPromptLibrary.AllPaths count; the lab leaves any other file in
|
||||||
|
/// the folder alone, and an empty file replaces nothing. The files are instructions of the person, who chose to use
|
||||||
|
/// them, so they bypass the prompt injection filter like the prompts which come with the app.
|
||||||
|
/// </remarks>
|
||||||
|
public static class BflPromptOverrides
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the folder below the folder of the lab.
|
||||||
|
/// </summary>
|
||||||
|
public const string DIRECTORY_NAME = "promptOverrides";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the folder whose files replace the prompts of the lab, or null while the setting does not allow them.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="settings">The settings of the lab, which an organization may lock.</param>
|
||||||
|
/// <param name="directory">The folder for prompt overrides.</param>
|
||||||
|
public static string? ActiveDirectory(DataBeyondFactsLab settings, string directory) => settings.AllowPromptOverrides ? directory : null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the file which replaces a prompt of the lab, and checks its placeholders.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="directory">The folder for prompt overrides.</param>
|
||||||
|
/// <param name="path">The prompt, one of BflPromptLibrary.AllPaths.</param>
|
||||||
|
/// <param name="token">The cancellation token.</param>
|
||||||
|
/// <returns>The text of the file, or null when no file replaces the prompt.</returns>
|
||||||
|
/// <exception cref="BflPromptOverrideException">The file contains a placeholder which the lab does not fill in it.</exception>
|
||||||
|
public static async Task<string?> TryReadAsync(string directory, string path, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var text = await TryReadFileAsync(directory, path, token);
|
||||||
|
if (text is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var allowed = BflPromptLibrary.PlaceholdersOf(path);
|
||||||
|
foreach (var placeholder in BflPromptTemplate.NamesIn(text))
|
||||||
|
{
|
||||||
|
if (!allowed.Contains(placeholder))
|
||||||
|
throw new BflPromptOverrideException(BflPromptLibrary.RelativePath(path), placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds the prompts of the lab which a file in the folder replaces with another text.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// A copy of a prompt which matches the prompt of this version, apart from line breaks and the space around the
|
||||||
|
/// text, changes nothing and does not count. A copy of an older version does.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="directory">The folder for prompt overrides.</param>
|
||||||
|
/// <param name="token">The cancellation token.</param>
|
||||||
|
/// <returns>The prompts, as paths of BflPromptLibrary.AllPaths.</returns>
|
||||||
|
public static async Task<IReadOnlyList<string>> FindAsync(string directory, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var replaced = new List<string>();
|
||||||
|
foreach (var path in BflPromptLibrary.AllPaths)
|
||||||
|
{
|
||||||
|
string? text;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
text = await TryReadFileAsync(directory, path, token);
|
||||||
|
}
|
||||||
|
catch (Exception exception) when (exception is IOException or UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
// The lab will try to read the file, too, so it replaces the prompt as far as the person can tell:
|
||||||
|
replaced.Add(path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text is not null && Normalize(text) != Normalize(await BflPromptLibrary.ReadBuiltInAsync(path, token)))
|
||||||
|
replaced.Add(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return replaced;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copies the prompts of this version into the folder, so that changes start from them. A file which exists stays
|
||||||
|
/// as it is.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="directory">The folder for prompt overrides; it is created when it is missing.</param>
|
||||||
|
/// <param name="token">The cancellation token.</param>
|
||||||
|
/// <returns>How many files the folder got.</returns>
|
||||||
|
public static async Task<int> CopyBuiltInAsync(string directory, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var copied = 0;
|
||||||
|
foreach (var path in BflPromptLibrary.AllPaths)
|
||||||
|
{
|
||||||
|
var target = FilePath(directory, path);
|
||||||
|
if (File.Exists(target))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(target)!);
|
||||||
|
var temporary = $"{target}.{Guid.NewGuid():N}.tmp";
|
||||||
|
await File.WriteAllTextAsync(temporary, await BflPromptLibrary.ReadBuiltInAsync(path, token), new UTF8Encoding(false), token);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Moving fails when the file appeared meanwhile, so the person's file is never overwritten:
|
||||||
|
File.Move(temporary, target, false);
|
||||||
|
copied++;
|
||||||
|
}
|
||||||
|
catch (IOException) when (File.Exists(target))
|
||||||
|
{
|
||||||
|
File.Delete(temporary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return copied;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the file which replaces a prompt of the lab.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="directory">The folder for prompt overrides.</param>
|
||||||
|
/// <param name="path">The prompt, one of BflPromptLibrary.AllPaths.</param>
|
||||||
|
public static string FilePath(string directory, string path) => Path.Join([directory, ..BflPromptLibrary.RelativePath(path).Split('/')]);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the file which replaces a prompt of the lab, without checking it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="directory">The folder for prompt overrides.</param>
|
||||||
|
/// <param name="path">The prompt, one of BflPromptLibrary.AllPaths.</param>
|
||||||
|
/// <param name="token">The cancellation token.</param>
|
||||||
|
/// <returns>The text of the file, or null when there is no such file or it is empty.</returns>
|
||||||
|
private static async Task<string?> TryReadFileAsync(string directory, string path, CancellationToken token)
|
||||||
|
{
|
||||||
|
string text;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
text = await File.ReadAllTextAsync(FilePath(directory, path), Encoding.UTF8, token);
|
||||||
|
}
|
||||||
|
catch (Exception exception) when (exception is FileNotFoundException or DirectoryNotFoundException)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.IsNullOrWhiteSpace(text) ? null : text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Brings a prompt into the form in which two copies compare, whatever system wrote them.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">The prompt.</param>
|
||||||
|
private static string Normalize(string text) => text.ReplaceLineEndings("\n").Trim();
|
||||||
|
}
|
||||||
@@ -60,6 +60,13 @@ public static partial class BflPromptTemplate
|
|||||||
return values.TryGetValue(name, out var value) ? value : throw new FormatException($"The prompt has the placeholder '{name}', which has no value.");
|
return values.TryGetValue(name, out var value) ? value : throw new FormatException($"The prompt has the placeholder '{name}', which has no value.");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds the placeholders of a template, as Fill reads them.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="template">The template.</param>
|
||||||
|
/// <returns>The name of each placeholder, once each, in the order they first appear.</returns>
|
||||||
|
public static IReadOnlyList<string> NamesIn(string template) => [.. PlaceholderPattern().Matches(template).Select(match => match.Groups["name"].Value).Distinct(StringComparer.Ordinal)];
|
||||||
|
|
||||||
[GeneratedRegex(@"\{\{\s*(?<name>[^{}]*?)\s*\}\}")]
|
[GeneratedRegex(@"\{\{\s*(?<name>[^{}]*?)\s*\}\}")]
|
||||||
private static partial Regex PlaceholderPattern();
|
private static partial Regex PlaceholderPattern();
|
||||||
}
|
}
|
||||||
@@ -970,6 +970,12 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCHAT::T991103295"] = "
|
|||||||
-- This conversation fills most of the context window of the model. Soon, you will have to start it over.
|
-- This conversation fills most of the context window of the model. Soon, you will have to start it over.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCHAT::T991103295::INFORMAL"] = "This conversation fills most of the context window of the model. Soon, you will have to start it over."
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCHAT::T991103295::INFORMAL"] = "This conversation fills most of the context window of the model. Soon, you will have to start it over."
|
||||||
|
|
||||||
|
-- The file '{0}' in the test folder of the BeyondFacts Lab contains the placeholder '{1}', which the lab does not fill in this file. Please correct the file and try again.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCONVERSATIONSERVICE::T1150375027"] = "The file '{0}' in the test folder of the BeyondFacts Lab contains the placeholder '{1}', which the lab does not fill in this file. Please correct the file and try again."
|
||||||
|
|
||||||
|
-- The file '{0}' in the test folder of the BeyondFacts Lab contains the placeholder '{1}', which the lab does not fill in this file. Please correct the file and try again.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCONVERSATIONSERVICE::T1150375027::INFORMAL"] = "The file '{0}' in the test folder of the BeyondFacts Lab contains the placeholder '{1}', which the lab does not fill in this file. Please correct the file and try again."
|
||||||
|
|
||||||
-- Please select a provider.
|
-- Please select a provider.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCONVERSATIONSERVICE::T1809312323"] = "Please select a provider."
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCONVERSATIONSERVICE::T1809312323"] = "Please select a provider."
|
||||||
|
|
||||||
@@ -1459,6 +1465,12 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLONBOARDING::T180931232
|
|||||||
-- Please select a provider.
|
-- Please select a provider.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLONBOARDING::T1809312323::INFORMAL"] = "Please select a provider."
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLONBOARDING::T1809312323::INFORMAL"] = "Please select a provider."
|
||||||
|
|
||||||
|
-- The lab currently uses experimental instructions from its test folder. You can switch them off in the options of the lab.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLPROMPTOVERRIDENOTICE::T1719028521"] = "The lab currently uses experimental instructions from its test folder. You can switch them off in the options of the lab."
|
||||||
|
|
||||||
|
-- The lab currently uses experimental instructions from its test folder. You can switch them off in the options of the lab.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLPROMPTOVERRIDENOTICE::T1719028521::INFORMAL"] = "The lab currently uses experimental instructions from its test folder. You can switch them off in the options of the lab."
|
||||||
|
|
||||||
-- Test situation
|
-- Test situation
|
||||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T1049923473"] = "Test situation"
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T1049923473"] = "Test situation"
|
||||||
|
|
||||||
@@ -10405,9 +10417,39 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBATCHPROCESSING::T82
|
|||||||
-- Restore default
|
-- Restore default
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBATCHPROCESSING::T913168756"] = "Restore default"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBATCHPROCESSING::T913168756"] = "Restore default"
|
||||||
|
|
||||||
|
-- Hide Expert Settings
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T1108876344"] = "Hide Expert Settings"
|
||||||
|
|
||||||
|
-- Could not open the test folder.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T1196482976"] = "Could not open the test folder."
|
||||||
|
|
||||||
|
-- The test folder holds every prompt already. AI Studio changed no file.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T1367912036"] = "The test folder holds every prompt already. AI Studio changed no file."
|
||||||
|
|
||||||
|
-- Could not open the test folder: {0}
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T1478589470"] = "Could not open the test folder: {0}"
|
||||||
|
|
||||||
|
-- These settings serve the further development of the lab, e.g., by the research team behind it. You do not need them to prepare an appearance.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T15706444"] = "These settings serve the further development of the lab, e.g., by the research team behind it. You do not need them to prepare an appearance."
|
||||||
|
|
||||||
|
-- These settings serve the further development of the lab, e.g., by the research team behind it. You do not need them to prepare an appearance.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T15706444::INFORMAL"] = "These settings serve the further development of the lab, e.g., by the research team behind it. You do not need them to prepare an appearance."
|
||||||
|
|
||||||
|
-- The lab uses its own prompts
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T1931375136"] = "The lab uses its own prompts"
|
||||||
|
|
||||||
-- Assistant: BeyondFacts Lab Options
|
-- Assistant: BeyondFacts Lab Options
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T19824556"] = "Assistant: BeyondFacts Lab Options"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T19824556"] = "Assistant: BeyondFacts Lab Options"
|
||||||
|
|
||||||
|
-- Files copied into the test folder: {0}. Files which were there already stay as they are.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2064303845"] = "Files copied into the test folder: {0}. Files which were there already stay as they are."
|
||||||
|
|
||||||
|
-- A file in the test folder replaces the prompt of the same name, e.g., 'Prompts/core.md'. The lab takes every other prompt from AI Studio. While a file replaces a prompt, the lab shows a notice above every step.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2174086011"] = "A file in the test folder replaces the prompt of the same name, e.g., 'Prompts/core.md'. The lab takes every other prompt from AI Studio. While a file replaces a prompt, the lab shows a notice above every step."
|
||||||
|
|
||||||
|
-- A file in the test folder replaces the prompt of the same name, e.g., 'Prompts/core.md'. The lab takes every other prompt from AI Studio. While a file replaces a prompt, the lab shows a notice above every step.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2174086011::INFORMAL"] = "A file in the test folder replaces the prompt of the same name, e.g., 'Prompts/core.md'. The lab takes every other prompt from AI Studio. While a file replaces a prompt, the lab shows a notice above every step."
|
||||||
|
|
||||||
-- Form of address
|
-- Form of address
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2183481736"] = "Form of address"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2183481736"] = "Form of address"
|
||||||
|
|
||||||
@@ -10417,9 +10459,27 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T257
|
|||||||
-- Select a minimum confidence level
|
-- Select a minimum confidence level
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2579793544::INFORMAL"] = "Select a minimum confidence level"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2579793544::INFORMAL"] = "Select a minimum confidence level"
|
||||||
|
|
||||||
|
-- Use prompts from the test folder?
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2718589875"] = "Use prompts from the test folder?"
|
||||||
|
|
||||||
|
-- Copy the built-in prompts into the test folder
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2787777151"] = "Copy the built-in prompts into the test folder"
|
||||||
|
|
||||||
|
-- Files in the test folder replace the prompts of the lab
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T320994561"] = "Files in the test folder replace the prompts of the lab"
|
||||||
|
|
||||||
|
-- Show Expert Settings
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3361153305"] = "Show Expert Settings"
|
||||||
|
|
||||||
-- Close
|
-- Close
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3448155331"] = "Close"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3448155331"] = "Close"
|
||||||
|
|
||||||
|
-- Unknown error
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3461425987"] = "Unknown error"
|
||||||
|
|
||||||
|
-- Could not copy the prompts into the test folder.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3478020269"] = "Could not copy the prompts into the test folder."
|
||||||
|
|
||||||
-- Whether the lab addresses you formally or informally.
|
-- Whether the lab addresses you formally or informally.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3883838251"] = "Whether the lab addresses you formally or informally."
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3883838251"] = "Whether the lab addresses you formally or informally."
|
||||||
|
|
||||||
@@ -10432,6 +10492,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T400
|
|||||||
-- 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.
|
-- 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::INFORMAL"] = "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::INFORMAL"] = "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."
|
||||||
|
|
||||||
|
-- Open test folder
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T567935877"] = "Open test folder"
|
||||||
|
|
||||||
-- Files are sent as they were first read
|
-- Files are sent as they were first read
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHAT::T1320692831"] = "Files are sent as they were first read"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHAT::T1320692831"] = "Files are sent as they were first read"
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,27 @@
|
|||||||
<ConfigurationSelect OptionDescription="@TA("Select a minimum confidence level")" SelectedValue="@this.SelectedMinimumConfidence" Data="@ConfigurationSelectDataFactory.GetBeyondFactsLabConfidenceLevelsData(this.SettingsManager)" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.BeyondFactsLab.MinimumProviderConfidence = value)" OptionHelp="@TA("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"/>
|
<ConfigurationSelect OptionDescription="@TA("Select a minimum confidence level")" SelectedValue="@this.SelectedMinimumConfidence" Data="@ConfigurationSelectDataFactory.GetBeyondFactsLabConfidenceLevelsData(this.SettingsManager)" SelectionUpdate="@(value => this.SettingsManager.ConfigurationData.BeyondFactsLab.MinimumProviderConfidence = value)" OptionHelp="@TA("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"/>
|
<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>
|
</MudPaper>
|
||||||
|
|
||||||
|
@* Only whoever develops the prompts further needs these settings, so they stay folded away. *@
|
||||||
|
<MudButton OnClick="@this.ToggleExpertSettings" Variant="Variant.Text" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Tune">
|
||||||
|
@(this.showExpertSettings ? T("Hide Expert Settings") : T("Show Expert Settings"))
|
||||||
|
</MudButton>
|
||||||
|
<MudCollapse Expanded="@this.showExpertSettings">
|
||||||
|
<MudPaper Class="pa-3 mt-3 mb-8 border-dashed border rounded-lg">
|
||||||
|
<MudJustifiedText Typo="Typo.body2" Class="mb-3">
|
||||||
|
@TA("These settings serve the further development of the lab, e.g., by the research team behind it. You do not need them to prepare an appearance.")
|
||||||
|
</MudJustifiedText>
|
||||||
|
<ConfigurationOption OptionDescription="@T("Use prompts from the test folder?")" LabelOn="@T("Files in the test folder replace the prompts of the lab")" LabelOff="@T("The lab uses its own prompts")" State="@(() => this.SettingsManager.ConfigurationData.BeyondFactsLab.AllowPromptOverrides)" StateUpdate="@(value => this.SettingsManager.ConfigurationData.BeyondFactsLab.AllowPromptOverrides = value)" OptionHelp="@TA("A file in the test folder replaces the prompt of the same name, e.g., 'Prompts/core.md'. The lab takes every other prompt from AI Studio. While a file replaces a prompt, the lab shows a notice above every step.")" IsLocked="() => IsPromptOverridesLocked"/>
|
||||||
|
<MudStack Row="true" Wrap="Wrap.Wrap" Spacing="2" Class="mt-3">
|
||||||
|
<MudButton StartIcon="@Icons.Material.Filled.FolderOpen" Variant="Variant.Outlined" OnClick="@this.OpenTestFolderAsync" Disabled="@this.ArePromptOverridesLockedOff">
|
||||||
|
@T("Open test folder")
|
||||||
|
</MudButton>
|
||||||
|
<MudButton StartIcon="@Icons.Material.Filled.ContentCopy" Variant="Variant.Outlined" OnClick="@this.CopyPromptsAsync" Disabled="@(this.ArePromptOverridesLockedOff || this.isCopyingPrompts)">
|
||||||
|
@T("Copy the built-in prompts into the test folder")
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
</MudPaper>
|
||||||
|
</MudCollapse>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">@T("Close")</MudButton>
|
<MudButton OnClick="@this.Close" Variant="Variant.Filled">@T("Close")</MudButton>
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
|
using AIStudio.Assistants.BeyondFactsLab;
|
||||||
using AIStudio.Provider;
|
using AIStudio.Provider;
|
||||||
|
using AIStudio.Settings;
|
||||||
using AIStudio.Tools.PluginSystem;
|
using AIStudio.Tools.PluginSystem;
|
||||||
|
using AIStudio.Tools.Rust;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
namespace AIStudio.Dialogs.Settings;
|
namespace AIStudio.Dialogs.Settings;
|
||||||
|
|
||||||
@@ -8,12 +13,39 @@ namespace AIStudio.Dialogs.Settings;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class SettingsDialogBeyondFactsLab : SettingsDialogBase
|
public partial class SettingsDialogBeyondFactsLab : SettingsDialogBase
|
||||||
{
|
{
|
||||||
|
[Inject]
|
||||||
|
private BeyondFactsLabStore Store { get; init; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private ILogger<SettingsDialogBeyondFactsLab> Logger { get; init; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the language of the app tells a formal address from an informal one, so that the form of address
|
/// Whether the language of the app tells a formal address from an informal one, so that the form of address
|
||||||
/// is a choice at all.
|
/// is a choice at all.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private bool offersFormOfAddress;
|
private bool offersFormOfAddress;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the settings for the further development of the lab are unfolded.
|
||||||
|
/// </summary>
|
||||||
|
private bool showExpertSettings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the prompts are being copied into the test folder right now.
|
||||||
|
/// </summary>
|
||||||
|
private bool isCopyingPrompts;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets whether an organization manages the prompt overrides.
|
||||||
|
/// </summary>
|
||||||
|
private static bool IsPromptOverridesLocked => ManagedConfiguration.TryGet(x => x.BeyondFactsLab, x => x.AllowPromptOverrides, out var meta) && meta.IsLocked;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets whether an organization switched the prompt overrides off for good, so that the test folder serves no
|
||||||
|
/// purpose.
|
||||||
|
/// </summary>
|
||||||
|
private bool ArePromptOverridesLockedOff => IsPromptOverridesLocked && !this.SettingsManager.ConfigurationData.BeyondFactsLab.AllowPromptOverrides;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
@@ -29,4 +61,57 @@ public partial class SettingsDialogBeyondFactsLab : SettingsDialogBase
|
|||||||
/// offer it either. Showing the floor instead tells what really counts.
|
/// offer it either. Showing the floor instead tells what really counts.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private ConfidenceLevel SelectedMinimumConfidence() => Tools.Components.BEYOND_FACTS_LAB_ASSISTANT.MinimumConfidence(this.SettingsManager);
|
private ConfidenceLevel SelectedMinimumConfidence() => Tools.Components.BEYOND_FACTS_LAB_ASSISTANT.MinimumConfidence(this.SettingsManager);
|
||||||
|
|
||||||
|
private void ToggleExpertSettings() => this.showExpertSettings = !this.showExpertSettings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens the test folder in the file manager, and creates it first when it is missing.
|
||||||
|
/// </summary>
|
||||||
|
private async Task OpenTestFolderAsync()
|
||||||
|
{
|
||||||
|
OpenPathResponse response;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var directory = this.Store.PromptOverridesDirectory;
|
||||||
|
Directory.CreateDirectory(directory);
|
||||||
|
response = await this.RustService.TryOpenPathInRuntimeFileManager(directory);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
this.Logger.LogWarning(exception, "Could not open the test folder of the BeyondFacts Lab.");
|
||||||
|
await this.MessageBus.SendError(new(Icons.Material.Filled.FolderOpen, T("Could not open the test folder.")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.Success)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var issue = string.IsNullOrWhiteSpace(response.Issue) ? T("Unknown error") : response.Issue;
|
||||||
|
await this.MessageBus.SendError(new(Icons.Material.Filled.FolderOpen, string.Format(T("Could not open the test folder: {0}"), issue)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copies the prompts of this version into the test folder, so that changes start from them. A file which exists
|
||||||
|
/// stays as it is.
|
||||||
|
/// </summary>
|
||||||
|
private async Task CopyPromptsAsync()
|
||||||
|
{
|
||||||
|
this.isCopyingPrompts = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var copied = await BflPromptOverrides.CopyBuiltInAsync(this.Store.PromptOverridesDirectory);
|
||||||
|
await this.MessageBus.SendSuccess(new(Icons.Material.Filled.ContentCopy, copied is 0
|
||||||
|
? T("The test folder holds every prompt already. AI Studio changed no file.")
|
||||||
|
: string.Format(T("Files copied into the test folder: {0}. Files which were there already stay as they are."), copied)));
|
||||||
|
}
|
||||||
|
catch (Exception exception) when (exception is IOException or UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
this.Logger.LogWarning(exception, "Could not copy the prompts of the BeyondFacts Lab into the test folder.");
|
||||||
|
await this.MessageBus.SendError(new(Icons.Material.Filled.ContentCopy, T("Could not copy the prompts into the test folder.")));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
this.isCopyingPrompts = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1002,6 +1002,13 @@ CONFIG["SETTINGS"] = {}
|
|||||||
-- of the lab do not show it.
|
-- of the lab do not show it.
|
||||||
-- Allowed values are: FORMAL, INFORMAL
|
-- Allowed values are: FORMAL, INFORMAL
|
||||||
-- CONFIG["SETTINGS"]["DataBeyondFactsLab.AddressForm"] = "FORMAL"
|
-- CONFIG["SETTINGS"]["DataBeyondFactsLab.AddressForm"] = "FORMAL"
|
||||||
|
--
|
||||||
|
-- Configure whether files in the folder for prompt overrides replace the prompts which come with the lab.
|
||||||
|
-- The option serves the further development of the prompts, e.g., by the research team behind the lab.
|
||||||
|
-- Preparing an appearance does not need it. Such files count as instructions of the users themselves,
|
||||||
|
-- and the lab shows a banner while one of them replaces a prompt. Set it to false and lock it to keep
|
||||||
|
-- users with the prompts which come with AI Studio.
|
||||||
|
-- CONFIG["SETTINGS"]["DataBeyondFactsLab.AllowPromptOverrides"] = false
|
||||||
|
|
||||||
-- Configure defaults for the Assistant Builder.
|
-- Configure defaults for the Assistant Builder.
|
||||||
-- Preselection must be enabled for the remaining Assistant Builder settings to take effect.
|
-- Preselection must be enabled for the remaining Assistant Builder settings to take effect.
|
||||||
|
|||||||
+63
@@ -972,6 +972,12 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCHAT::T991103295"] = "
|
|||||||
-- This conversation fills most of the context window of the model. Soon, you will have to start it over.
|
-- This conversation fills most of the context window of the model. Soon, you will have to start it over.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCHAT::T991103295::INFORMAL"] = "Dieses Gespräch füllt den größten Teil des Kontextfensters des Modells. Bald musst du es neu beginnen."
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCHAT::T991103295::INFORMAL"] = "Dieses Gespräch füllt den größten Teil des Kontextfensters des Modells. Bald musst du es neu beginnen."
|
||||||
|
|
||||||
|
-- The file '{0}' in the test folder of the BeyondFacts Lab contains the placeholder '{1}', which the lab does not fill in this file. Please correct the file and try again.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCONVERSATIONSERVICE::T1150375027"] = "Die Datei „{0}“ im Testordner des BeyondFacts Labs enthält den Platzhalter „{1}“, den das Lab in dieser Datei nicht ausfüllt. Bitte korrigieren Sie die Datei und versuchen Sie es erneut."
|
||||||
|
|
||||||
|
-- The file '{0}' in the test folder of the BeyondFacts Lab contains the placeholder '{1}', which the lab does not fill in this file. Please correct the file and try again.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCONVERSATIONSERVICE::T1150375027::INFORMAL"] = "Die Datei „{0}“ im Testordner des BeyondFacts Labs enthält den Platzhalter „{1}“, den das Lab in dieser Datei nicht ausfüllt. Bitte korrigiere die Datei und versuche es erneut."
|
||||||
|
|
||||||
-- Please select a provider.
|
-- Please select a provider.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCONVERSATIONSERVICE::T1809312323"] = "Bitte wählen Sie einen Anbieter aus."
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCONVERSATIONSERVICE::T1809312323"] = "Bitte wählen Sie einen Anbieter aus."
|
||||||
|
|
||||||
@@ -1461,6 +1467,12 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLONBOARDING::T180931232
|
|||||||
-- Please select a provider.
|
-- Please select a provider.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLONBOARDING::T1809312323::INFORMAL"] = "Bitte wähle einen Anbieter aus."
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLONBOARDING::T1809312323::INFORMAL"] = "Bitte wähle einen Anbieter aus."
|
||||||
|
|
||||||
|
-- The lab currently uses experimental instructions from its test folder. You can switch them off in the options of the lab.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLPROMPTOVERRIDENOTICE::T1719028521"] = "Das Lab verwendet derzeit experimentelle Anweisungen aus seinem Testordner. Sie können sie in den Optionen des Labs deaktivieren."
|
||||||
|
|
||||||
|
-- The lab currently uses experimental instructions from its test folder. You can switch them off in the options of the lab.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLPROMPTOVERRIDENOTICE::T1719028521::INFORMAL"] = "Das Lab verwendet derzeit experimentelle Anweisungen aus seinem Testordner. Du kannst sie in den Optionen des Labs deaktivieren."
|
||||||
|
|
||||||
-- Test situation
|
-- Test situation
|
||||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T1049923473"] = "Testsituation"
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T1049923473"] = "Testsituation"
|
||||||
|
|
||||||
@@ -10407,9 +10419,39 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBATCHPROCESSING::T82
|
|||||||
-- Restore default
|
-- Restore default
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBATCHPROCESSING::T913168756"] = "Standard wiederherstellen"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBATCHPROCESSING::T913168756"] = "Standard wiederherstellen"
|
||||||
|
|
||||||
|
-- Hide Expert Settings
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T1108876344"] = "Experten-Einstellungen ausblenden"
|
||||||
|
|
||||||
|
-- Could not open the test folder.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T1196482976"] = "Der Testordner konnte nicht geöffnet werden."
|
||||||
|
|
||||||
|
-- The test folder holds every prompt already. AI Studio changed no file.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T1367912036"] = "Der Testordner enthält bereits alle Prompts. AI Studio hat keine Datei geändert."
|
||||||
|
|
||||||
|
-- Could not open the test folder: {0}
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T1478589470"] = "Der Testordner konnte nicht geöffnet werden: {0}"
|
||||||
|
|
||||||
|
-- These settings serve the further development of the lab, e.g., by the research team behind it. You do not need them to prepare an appearance.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T15706444"] = "Diese Einstellungen dienen der Weiterentwicklung des Labs, z. B. durch das Forschungsteam hinter dem Lab. Zur Vorbereitung eines Auftritts brauchen Sie sie nicht."
|
||||||
|
|
||||||
|
-- These settings serve the further development of the lab, e.g., by the research team behind it. You do not need them to prepare an appearance.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T15706444::INFORMAL"] = "Diese Einstellungen dienen der Weiterentwicklung des Labs, z. B. durch das Forschungsteam hinter dem Lab. Zur Vorbereitung eines Auftritts brauchst du sie nicht."
|
||||||
|
|
||||||
|
-- The lab uses its own prompts
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T1931375136"] = "Das Lab verwendet seine eigenen Prompts"
|
||||||
|
|
||||||
-- Assistant: BeyondFacts Lab Options
|
-- Assistant: BeyondFacts Lab Options
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T19824556"] = "Assistent: Optionen für das BeyondFacts Lab"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T19824556"] = "Assistent: Optionen für das BeyondFacts Lab"
|
||||||
|
|
||||||
|
-- Files copied into the test folder: {0}. Files which were there already stay as they are.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2064303845"] = "In den Testordner kopierte Dateien: {0}. Dateien, die bereits dort vorhanden waren, bleiben unverändert."
|
||||||
|
|
||||||
|
-- A file in the test folder replaces the prompt of the same name, e.g., 'Prompts/core.md'. The lab takes every other prompt from AI Studio. While a file replaces a prompt, the lab shows a notice above every step.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2174086011"] = "Eine Datei im Testordner ersetzt den gleichnamigen Prompt, z. B. „Prompts/core.md“. Das Lab verwendet alle anderen Prompts aus AI Studio. Solange eine Datei einen Prompt ersetzt, zeigt das Lab über jedem Schritt einen Hinweis an."
|
||||||
|
|
||||||
|
-- A file in the test folder replaces the prompt of the same name, e.g., 'Prompts/core.md'. The lab takes every other prompt from AI Studio. While a file replaces a prompt, the lab shows a notice above every step.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2174086011::INFORMAL"] = "Eine Datei im Testordner ersetzt den gleichnamigen Prompt, z. B. „Prompts/core.md“. Das Lab verwendet alle anderen Prompts aus AI Studio. Solange eine Datei einen Prompt ersetzt, zeigt das Lab über jedem Schritt einen Hinweis an."
|
||||||
|
|
||||||
-- Form of address
|
-- Form of address
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2183481736"] = "Anrede"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2183481736"] = "Anrede"
|
||||||
|
|
||||||
@@ -10419,9 +10461,27 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T257
|
|||||||
-- Select a minimum confidence level
|
-- Select a minimum confidence level
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2579793544::INFORMAL"] = "Wähle ein minimales Vertrauensniveau aus"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2579793544::INFORMAL"] = "Wähle ein minimales Vertrauensniveau aus"
|
||||||
|
|
||||||
|
-- Use prompts from the test folder?
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2718589875"] = "Prompts aus dem Testordner verwenden?"
|
||||||
|
|
||||||
|
-- Copy the built-in prompts into the test folder
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2787777151"] = "Mitgelieferte Prompts in den Testordner kopieren"
|
||||||
|
|
||||||
|
-- Files in the test folder replace the prompts of the lab
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T320994561"] = "Dateien im Testordner ersetzen die Prompts des Labs"
|
||||||
|
|
||||||
|
-- Show Expert Settings
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3361153305"] = "Experten-Einstellungen anzeigen"
|
||||||
|
|
||||||
-- Close
|
-- Close
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3448155331"] = "Schließen"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3448155331"] = "Schließen"
|
||||||
|
|
||||||
|
-- Unknown error
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3461425987"] = "Unbekannter Fehler"
|
||||||
|
|
||||||
|
-- Could not copy the prompts into the test folder.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3478020269"] = "Die Prompts konnten nicht in den Testordner kopiert werden."
|
||||||
|
|
||||||
-- Whether the lab addresses you formally or informally.
|
-- Whether the lab addresses you formally or informally.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3883838251"] = "Ob das Lab Sie siezt oder duzt."
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3883838251"] = "Ob das Lab Sie siezt oder duzt."
|
||||||
|
|
||||||
@@ -10434,6 +10494,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T400
|
|||||||
-- 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.
|
-- 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::INFORMAL"] = "Deine Vorbereitungen enthalten meist unveröffentlichte Forschung. Daher akzeptiert das Lab nur Anbieter mit mindestens dem Vertrauensniveau „Mittel“. Du kannst diese Untergrenze anheben, etwa um nur selbst gehostete Modelle zu verwenden."
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T4002746484::INFORMAL"] = "Deine Vorbereitungen enthalten meist unveröffentlichte Forschung. Daher akzeptiert das Lab nur Anbieter mit mindestens dem Vertrauensniveau „Mittel“. Du kannst diese Untergrenze anheben, etwa um nur selbst gehostete Modelle zu verwenden."
|
||||||
|
|
||||||
|
-- Open test folder
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T567935877"] = "Testordner öffnen"
|
||||||
|
|
||||||
-- Files are sent as they were first read
|
-- 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"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHAT::T1320692831"] = "Dateien werden so gesendet, wie sie zuerst eingelesen wurden"
|
||||||
|
|
||||||
|
|||||||
+63
@@ -972,6 +972,12 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCHAT::T991103295"] = "
|
|||||||
-- This conversation fills most of the context window of the model. Soon, you will have to start it over.
|
-- This conversation fills most of the context window of the model. Soon, you will have to start it over.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCHAT::T991103295::INFORMAL"] = "This conversation fills most of the context window of the model. Soon, you will have to start it over."
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCHAT::T991103295::INFORMAL"] = "This conversation fills most of the context window of the model. Soon, you will have to start it over."
|
||||||
|
|
||||||
|
-- The file '{0}' in the test folder of the BeyondFacts Lab contains the placeholder '{1}', which the lab does not fill in this file. Please correct the file and try again.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCONVERSATIONSERVICE::T1150375027"] = "The file '{0}' in the test folder of the BeyondFacts Lab contains the placeholder '{1}', which the lab does not fill in this file. Please correct the file and try again."
|
||||||
|
|
||||||
|
-- The file '{0}' in the test folder of the BeyondFacts Lab contains the placeholder '{1}', which the lab does not fill in this file. Please correct the file and try again.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCONVERSATIONSERVICE::T1150375027::INFORMAL"] = "The file '{0}' in the test folder of the BeyondFacts Lab contains the placeholder '{1}', which the lab does not fill in this file. Please correct the file and try again."
|
||||||
|
|
||||||
-- Please select a provider.
|
-- Please select a provider.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCONVERSATIONSERVICE::T1809312323"] = "Please select a provider."
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCONVERSATIONSERVICE::T1809312323"] = "Please select a provider."
|
||||||
|
|
||||||
@@ -1461,6 +1467,12 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLONBOARDING::T180931232
|
|||||||
-- Please select a provider.
|
-- Please select a provider.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLONBOARDING::T1809312323::INFORMAL"] = "Please select a provider."
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLONBOARDING::T1809312323::INFORMAL"] = "Please select a provider."
|
||||||
|
|
||||||
|
-- The lab currently uses experimental instructions from its test folder. You can switch them off in the options of the lab.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLPROMPTOVERRIDENOTICE::T1719028521"] = "The lab currently uses experimental instructions from its test folder. You can switch them off in the options of the lab."
|
||||||
|
|
||||||
|
-- The lab currently uses experimental instructions from its test folder. You can switch them off in the options of the lab.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLPROMPTOVERRIDENOTICE::T1719028521::INFORMAL"] = "The lab currently uses experimental instructions from its test folder. You can switch them off in the options of the lab."
|
||||||
|
|
||||||
-- Test situation
|
-- Test situation
|
||||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T1049923473"] = "Test situation"
|
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T1049923473"] = "Test situation"
|
||||||
|
|
||||||
@@ -10407,9 +10419,39 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBATCHPROCESSING::T82
|
|||||||
-- Restore default
|
-- Restore default
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBATCHPROCESSING::T913168756"] = "Restore default"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBATCHPROCESSING::T913168756"] = "Restore default"
|
||||||
|
|
||||||
|
-- Hide Expert Settings
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T1108876344"] = "Hide Expert Settings"
|
||||||
|
|
||||||
|
-- Could not open the test folder.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T1196482976"] = "Could not open the test folder."
|
||||||
|
|
||||||
|
-- The test folder holds every prompt already. AI Studio changed no file.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T1367912036"] = "The test folder holds every prompt already. AI Studio changed no file."
|
||||||
|
|
||||||
|
-- Could not open the test folder: {0}
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T1478589470"] = "Could not open the test folder: {0}"
|
||||||
|
|
||||||
|
-- These settings serve the further development of the lab, e.g., by the research team behind it. You do not need them to prepare an appearance.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T15706444"] = "These settings serve the further development of the lab, e.g., by the research team behind it. You do not need them to prepare an appearance."
|
||||||
|
|
||||||
|
-- These settings serve the further development of the lab, e.g., by the research team behind it. You do not need them to prepare an appearance.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T15706444::INFORMAL"] = "These settings serve the further development of the lab, e.g., by the research team behind it. You do not need them to prepare an appearance."
|
||||||
|
|
||||||
|
-- The lab uses its own prompts
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T1931375136"] = "The lab uses its own prompts"
|
||||||
|
|
||||||
-- Assistant: BeyondFacts Lab Options
|
-- Assistant: BeyondFacts Lab Options
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T19824556"] = "Assistant: BeyondFacts Lab Options"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T19824556"] = "Assistant: BeyondFacts Lab Options"
|
||||||
|
|
||||||
|
-- Files copied into the test folder: {0}. Files which were there already stay as they are.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2064303845"] = "Files copied into the test folder: {0}. Files which were there already stay as they are."
|
||||||
|
|
||||||
|
-- A file in the test folder replaces the prompt of the same name, e.g., 'Prompts/core.md'. The lab takes every other prompt from AI Studio. While a file replaces a prompt, the lab shows a notice above every step.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2174086011"] = "A file in the test folder replaces the prompt of the same name, e.g., 'Prompts/core.md'. The lab takes every other prompt from AI Studio. While a file replaces a prompt, the lab shows a notice above every step."
|
||||||
|
|
||||||
|
-- A file in the test folder replaces the prompt of the same name, e.g., 'Prompts/core.md'. The lab takes every other prompt from AI Studio. While a file replaces a prompt, the lab shows a notice above every step.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2174086011::INFORMAL"] = "A file in the test folder replaces the prompt of the same name, e.g., 'Prompts/core.md'. The lab takes every other prompt from AI Studio. While a file replaces a prompt, the lab shows a notice above every step."
|
||||||
|
|
||||||
-- Form of address
|
-- Form of address
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2183481736"] = "Form of address"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2183481736"] = "Form of address"
|
||||||
|
|
||||||
@@ -10419,9 +10461,27 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T257
|
|||||||
-- Select a minimum confidence level
|
-- Select a minimum confidence level
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2579793544::INFORMAL"] = "Select a minimum confidence level"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2579793544::INFORMAL"] = "Select a minimum confidence level"
|
||||||
|
|
||||||
|
-- Use prompts from the test folder?
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2718589875"] = "Use prompts from the test folder?"
|
||||||
|
|
||||||
|
-- Copy the built-in prompts into the test folder
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T2787777151"] = "Copy the built-in prompts into the test folder"
|
||||||
|
|
||||||
|
-- Files in the test folder replace the prompts of the lab
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T320994561"] = "Files in the test folder replace the prompts of the lab"
|
||||||
|
|
||||||
|
-- Show Expert Settings
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3361153305"] = "Show Expert Settings"
|
||||||
|
|
||||||
-- Close
|
-- Close
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3448155331"] = "Close"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3448155331"] = "Close"
|
||||||
|
|
||||||
|
-- Unknown error
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3461425987"] = "Unknown error"
|
||||||
|
|
||||||
|
-- Could not copy the prompts into the test folder.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3478020269"] = "Could not copy the prompts into the test folder."
|
||||||
|
|
||||||
-- Whether the lab addresses you formally or informally.
|
-- Whether the lab addresses you formally or informally.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3883838251"] = "Whether the lab addresses you formally or informally."
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T3883838251"] = "Whether the lab addresses you formally or informally."
|
||||||
|
|
||||||
@@ -10434,6 +10494,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T400
|
|||||||
-- 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.
|
-- 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::INFORMAL"] = "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::INFORMAL"] = "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."
|
||||||
|
|
||||||
|
-- Open test folder
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGBEYONDFACTSLAB::T567935877"] = "Open test folder"
|
||||||
|
|
||||||
-- Files are sent as they were first read
|
-- Files are sent as they were first read
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHAT::T1320692831"] = "Files are sent as they were first read"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHAT::T1320692831"] = "Files are sent as they were first read"
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,15 @@ public sealed class DataBeyondFactsLab(Expression<Func<Data, DataBeyondFactsLab>
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public AddressForm AddressForm { get; set; } = ManagedConfiguration.Register(configSelection, value => value.AddressForm, AddressForm.FORMAL);
|
public AddressForm AddressForm { get; set; } = ManagedConfiguration.Register(configSelection, value => value.AddressForm, AddressForm.FORMAL);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether files in the folder for prompt overrides replace the prompts which come with the lab.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The option serves the further development of the prompts, e.g., by the research team behind the lab, not the
|
||||||
|
/// preparation of an appearance. An organization may switch it off and lock it.
|
||||||
|
/// </remarks>
|
||||||
|
public bool AllowPromptOverrides { get; set; } = ManagedConfiguration.Register(configSelection, value => value.AllowPromptOverrides, false);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the hash of the notes the person confirmed, see BflNotice; empty while they confirmed none.
|
/// Gets or sets the hash of the notes the person confirmed, see BflNotice; empty while they confirmed none.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -520,6 +520,7 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
|
|||||||
ManagedConfiguration.TryProcessConfiguration(x => x.BeyondFactsLab, x => x.PreselectedProvider, Guid.Empty, this.Id, settingsTable, dryRun);
|
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.MinimumProviderConfidence, this.Id, settingsTable, dryRun);
|
||||||
ManagedConfiguration.TryProcessConfiguration(x => x.BeyondFactsLab, x => x.AddressForm, this.Id, settingsTable, dryRun);
|
ManagedConfiguration.TryProcessConfiguration(x => x.BeyondFactsLab, x => x.AddressForm, this.Id, settingsTable, dryRun);
|
||||||
|
ManagedConfiguration.TryProcessConfiguration(x => x.BeyondFactsLab, x => x.AllowPromptOverrides, this.Id, settingsTable, dryRun);
|
||||||
|
|
||||||
// Config: Assistant Builder defaults?
|
// Config: Assistant Builder defaults?
|
||||||
ManagedConfiguration.TryProcessConfiguration(x => x.AssistantBuilder, x => x.PreselectOptions, this.Id, settingsTable, dryRun);
|
ManagedConfiguration.TryProcessConfiguration(x => x.AssistantBuilder, x => x.PreselectOptions, this.Id, settingsTable, dryRun);
|
||||||
|
|||||||
@@ -55,6 +55,32 @@ public sealed class BflPromptLibraryTests
|
|||||||
Assert.That(BflPromptLibrary.AddressPath(AddressForm.INFORMAL), Is.Not.EqualTo(BflPromptLibrary.AddressPath(AddressForm.FORMAL)));
|
Assert.That(BflPromptLibrary.AddressPath(AddressForm.INFORMAL), Is.Not.EqualTo(BflPromptLibrary.AddressPath(AddressForm.FORMAL)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task EveryPromptUsesOnlyThePlaceholdersTheLabFillsInIt()
|
||||||
|
{
|
||||||
|
foreach (var path in BflPromptLibrary.AllPaths)
|
||||||
|
Assert.That(BflPromptTemplate.NamesIn(await BflPromptLibrary.ReadBuiltInAsync(path)), Is.SubsetOf(BflPromptLibrary.PlaceholdersOf(path)), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TheComposerFillsEveryPlaceholderAPromptMayContain()
|
||||||
|
{
|
||||||
|
var core = string.Join("\n", BflPromptLibrary.PlaceholdersOf(BflPromptLibrary.CORE_PATH).Select(name => $"{{{{{name}}}}}"));
|
||||||
|
var onboarding = string.Join("\n", BflPromptLibrary.PlaceholdersOf(BflPromptLibrary.ONBOARDING_PATH).Select(name => $"{{{{{name}}}}}"));
|
||||||
|
|
||||||
|
using (Assert.EnterMultipleScope())
|
||||||
|
{
|
||||||
|
Assert.That(BflPromptComposer.Compose(new(core, "Leitfaden", "Schritt", "Anrede"), BflStep.FRAMEWORK, new Dictionary<BflStep, BflStepFormData>(), [], "Deutsch"), Does.Not.Contain("{{"));
|
||||||
|
Assert.That(BflPromptComposer.ComposeOnboarding(new(onboarding, "Anrede"), "Deutsch"), Does.Not.Contain("{{"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void EveryPromptLiesInTheFolderOfTheLab()
|
||||||
|
{
|
||||||
|
Assert.That(BflPromptLibrary.AllPaths.Select(BflPromptLibrary.RelativePath), Is.Unique.And.All.Not.StartWith("/"));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task AMissingPromptIsAnError()
|
public async Task AMissingPromptIsAnError()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
using AIStudio.Assistants.BeyondFactsLab;
|
||||||
|
using AIStudio.Settings;
|
||||||
|
|
||||||
|
using Lua;
|
||||||
|
using Lua.Standard;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
|
||||||
|
namespace AIStudio.Tests.BeyondFactsLab;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks that an organization can keep the prompts of the BeyondFacts Lab as they come with AI Studio.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The settings are reached through Program.SERVICE_PROVIDER, as in the app, because a managed setting writes its
|
||||||
|
/// value and its lock there.
|
||||||
|
/// </remarks>
|
||||||
|
[TestFixture]
|
||||||
|
public sealed class BflPromptOverrideSettingTests
|
||||||
|
{
|
||||||
|
private static readonly Guid PLUGIN_ID = Guid.Parse("4f0c2b7e-8d31-4a96-b5e2-7c1d9a3e6f08");
|
||||||
|
|
||||||
|
private SettingsManager settingsManager = null!;
|
||||||
|
private ServiceProvider serviceProvider = null!;
|
||||||
|
private IServiceProvider previousServiceProvider = null!;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void CreateSettings()
|
||||||
|
{
|
||||||
|
// Creating the settings data registers every managed setting:
|
||||||
|
this.settingsManager = new SettingsManager(NullLogger<SettingsManager>.Instance, null!);
|
||||||
|
|
||||||
|
this.previousServiceProvider = Program.SERVICE_PROVIDER;
|
||||||
|
this.serviceProvider = new ServiceCollection().AddSingleton(this.settingsManager).BuildServiceProvider();
|
||||||
|
Program.SERVICE_PROVIDER = this.serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
[TearDown]
|
||||||
|
public void RestoreApplicationState()
|
||||||
|
{
|
||||||
|
// The managed state of a setting outlives this fixture, so we release what the plugin took over:
|
||||||
|
ManagedConfiguration.CleanupLeftOverManagedConfigurations([], new HashSet<Guid>());
|
||||||
|
|
||||||
|
Program.SERVICE_PROVIDER = this.previousServiceProvider;
|
||||||
|
this.serviceProvider.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task AnOrganizationCanSwitchOffThePromptOverridesForGood()
|
||||||
|
{
|
||||||
|
this.settingsManager.ConfigurationData.BeyondFactsLab.AllowPromptOverrides = true;
|
||||||
|
|
||||||
|
var state = LuaState.Create();
|
||||||
|
state.OpenBasicLibrary();
|
||||||
|
await state.DoStringAsync("""
|
||||||
|
SETTINGS = {}
|
||||||
|
SETTINGS["DataBeyondFactsLab.AllowPromptOverrides"] = false
|
||||||
|
""");
|
||||||
|
|
||||||
|
if (!state.Environment["SETTINGS"].TryRead<LuaTable>(out var settingsTable))
|
||||||
|
throw new InvalidOperationException("The settings of this test are not a Lua table.");
|
||||||
|
|
||||||
|
var processed = ManagedConfiguration.TryProcessConfiguration(x => x.BeyondFactsLab, x => x.AllowPromptOverrides, PLUGIN_ID, settingsTable, false);
|
||||||
|
ManagedConfiguration.TryGet(x => x.BeyondFactsLab, x => x.AllowPromptOverrides, out var configMeta);
|
||||||
|
|
||||||
|
using (Assert.EnterMultipleScope())
|
||||||
|
{
|
||||||
|
Assert.That(processed, Is.True);
|
||||||
|
Assert.That(configMeta.IsLocked, Is.True);
|
||||||
|
Assert.That(BflPromptOverrides.ActiveDirectory(this.settingsManager.ConfigurationData.BeyondFactsLab, "prompts"), Is.Null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
using AIStudio.Assistants.BeyondFactsLab;
|
||||||
|
using AIStudio.Settings;
|
||||||
|
using AIStudio.Settings.DataModel;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
|
||||||
|
namespace AIStudio.Tests.BeyondFactsLab;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks how files in the folder for prompt overrides replace the prompts of the BeyondFacts Lab.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The folder serves whoever develops the prompts further. A file there replaces the prompt of the same name only;
|
||||||
|
/// what the folder lacks, or what it holds besides, changes nothing.
|
||||||
|
/// </remarks>
|
||||||
|
[TestFixture]
|
||||||
|
public sealed class BflPromptOverridesTests
|
||||||
|
{
|
||||||
|
private string directory = string.Empty;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void CreateDirectory()
|
||||||
|
{
|
||||||
|
this.directory = Path.Combine(Path.GetTempPath(), $"ai-studio-beyondfacts-lab-prompts-{Guid.NewGuid():N}");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TearDown]
|
||||||
|
public void DeleteDirectory()
|
||||||
|
{
|
||||||
|
if (Directory.Exists(this.directory))
|
||||||
|
Directory.Delete(this.directory, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TheFolderMirrorsTheFilesOfTheLab()
|
||||||
|
{
|
||||||
|
var path = BflPromptOverrides.FilePath(this.directory, BflPromptLibrary.StepPath(BflStep.KNOWLEDGE));
|
||||||
|
|
||||||
|
Assert.That(path, Is.EqualTo(Path.Combine(this.directory, "Prompts", "steps", $"{BflStep.KNOWLEDGE.FolderName}.md")));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void EveryPromptHasAFileOfItsOwnInTheFolder()
|
||||||
|
{
|
||||||
|
var files = BflPromptLibrary.AllPaths.Select(path => BflPromptOverrides.FilePath(this.directory, path)).ToList();
|
||||||
|
|
||||||
|
Assert.That(files, Is.Unique);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task AFileInTheFolderReplacesThePromptOfTheSameNameOnly()
|
||||||
|
{
|
||||||
|
var replaced = BflPromptLibrary.StepPath(BflStep.GOALS_AND_ROLE);
|
||||||
|
await this.WriteAsync(replaced, "Ein neuer Prompt für die Ziele.");
|
||||||
|
|
||||||
|
var parts = await BflPromptLibrary.LoadPartsAsync(BflStep.GOALS_AND_ROLE, AddressForm.FORMAL, this.directory);
|
||||||
|
var otherStep = await BflPromptLibrary.ReadAsync(BflPromptLibrary.StepPath(BflStep.VALUES_AND_PERSPECTIVES), this.directory);
|
||||||
|
|
||||||
|
using (Assert.EnterMultipleScope())
|
||||||
|
{
|
||||||
|
Assert.That(parts.Step, Is.EqualTo("Ein neuer Prompt für die Ziele."));
|
||||||
|
Assert.That(parts.Core, Is.EqualTo(await BflPromptLibrary.ReadBuiltInAsync(BflPromptLibrary.CORE_PATH)));
|
||||||
|
Assert.That(otherStep, Is.EqualTo(await BflPromptLibrary.ReadBuiltInAsync(BflPromptLibrary.StepPath(BflStep.VALUES_AND_PERSPECTIVES))));
|
||||||
|
Assert.That(await BflPromptOverrides.FindAsync(this.directory), Is.EqualTo(new[] { replaced }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task WithoutTheSettingTheFilesReplaceNothing()
|
||||||
|
{
|
||||||
|
await this.WriteAsync(BflPromptLibrary.CORE_PATH, "Ein neuer Kern.");
|
||||||
|
var settings = new DataBeyondFactsLab { AllowPromptOverrides = false };
|
||||||
|
|
||||||
|
var activeDirectory = BflPromptOverrides.ActiveDirectory(settings, this.directory);
|
||||||
|
var core = await BflPromptLibrary.ReadAsync(BflPromptLibrary.CORE_PATH, activeDirectory);
|
||||||
|
|
||||||
|
using (Assert.EnterMultipleScope())
|
||||||
|
{
|
||||||
|
Assert.That(activeDirectory, Is.Null);
|
||||||
|
Assert.That(core, Is.EqualTo(await BflPromptLibrary.ReadBuiltInAsync(BflPromptLibrary.CORE_PATH)));
|
||||||
|
Assert.That(BflPromptOverrides.ActiveDirectory(new DataBeyondFactsLab { AllowPromptOverrides = true }, this.directory), Is.EqualTo(this.directory));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task AMissingFolderReplacesNothing()
|
||||||
|
{
|
||||||
|
using (Assert.EnterMultipleScope())
|
||||||
|
{
|
||||||
|
Assert.That(await BflPromptLibrary.ReadAsync(BflPromptLibrary.ONBOARDING_PATH, this.directory), Is.EqualTo(await BflPromptLibrary.ReadBuiltInAsync(BflPromptLibrary.ONBOARDING_PATH)));
|
||||||
|
Assert.That(await BflPromptOverrides.FindAsync(this.directory), Is.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task AnEmptyFileReplacesNothing()
|
||||||
|
{
|
||||||
|
await this.WriteAsync(BflPromptLibrary.ONBOARDING_PATH, " \n");
|
||||||
|
|
||||||
|
using (Assert.EnterMultipleScope())
|
||||||
|
{
|
||||||
|
Assert.That(await BflPromptLibrary.ReadAsync(BflPromptLibrary.ONBOARDING_PATH, this.directory), Is.EqualTo(await BflPromptLibrary.ReadBuiltInAsync(BflPromptLibrary.ONBOARDING_PATH)));
|
||||||
|
Assert.That(await BflPromptOverrides.FindAsync(this.directory), Is.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task AnUnknownFileChangesNothing()
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.Combine(this.directory, "Prompts", "steps"));
|
||||||
|
await File.WriteAllTextAsync(Path.Combine(this.directory, "Prompts", "steps", "99-unknown.md"), "Ein Schritt, den es nicht gibt.");
|
||||||
|
await File.WriteAllTextAsync(Path.Combine(this.directory, "core.md"), "Ein Kern am falschen Ort.");
|
||||||
|
await File.WriteAllTextAsync(Path.Combine(this.directory, "notes.txt"), "Notizen von {{NIEMANDEM}}.");
|
||||||
|
|
||||||
|
using (Assert.EnterMultipleScope())
|
||||||
|
{
|
||||||
|
Assert.That(await BflPromptLibrary.ReadAsync(BflPromptLibrary.CORE_PATH, this.directory), Is.EqualTo(await BflPromptLibrary.ReadBuiltInAsync(BflPromptLibrary.CORE_PATH)));
|
||||||
|
Assert.That(await BflPromptOverrides.FindAsync(this.directory), Is.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task AFileWithAPlaceholderTheLabDoesNotFillThereIsAnError()
|
||||||
|
{
|
||||||
|
await this.WriteAsync(BflPromptLibrary.StepPath(BflStep.STRATEGY), "Beachte {{DETAILS}}.");
|
||||||
|
await this.WriteAsync(BflPromptLibrary.CORE_PATH, "{{STEP}} {{UNKNOWN}}");
|
||||||
|
await this.WriteAsync(BflPromptLibrary.ONBOARDING_PATH, "{{RESPONSE_LANGUAGE}} {{KNOWLEDGE}}");
|
||||||
|
|
||||||
|
var inStep = await Assert.ThrowsAsync<BflPromptOverrideException>(async () => await BflPromptLibrary.ReadAsync(BflPromptLibrary.StepPath(BflStep.STRATEGY), this.directory));
|
||||||
|
var inCore = await Assert.ThrowsAsync<BflPromptOverrideException>(async () => await BflPromptLibrary.LoadPartsAsync(BflStep.FRAMEWORK, AddressForm.FORMAL, this.directory));
|
||||||
|
var inOnboarding = await Assert.ThrowsAsync<BflPromptOverrideException>(async () => await BflPromptLibrary.LoadOnboardingPartsAsync(AddressForm.FORMAL, this.directory));
|
||||||
|
|
||||||
|
using (Assert.EnterMultipleScope())
|
||||||
|
{
|
||||||
|
Assert.That(inStep!.FileName, Is.EqualTo($"Prompts/steps/{BflStep.STRATEGY.FolderName}.md"));
|
||||||
|
Assert.That(inStep.Placeholder, Is.EqualTo(BflPromptTemplate.DETAILS));
|
||||||
|
Assert.That(inCore!.FileName, Is.EqualTo("Prompts/core.md"));
|
||||||
|
Assert.That(inCore.Placeholder, Is.EqualTo("UNKNOWN"));
|
||||||
|
Assert.That(inOnboarding!.FileName, Is.EqualTo("Prompts/onboarding.md"));
|
||||||
|
Assert.That(inOnboarding.Placeholder, Is.EqualTo(BflPromptTemplate.KNOWLEDGE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task AFileWithThePlaceholdersOfItsPromptGivesAPrompt()
|
||||||
|
{
|
||||||
|
await this.WriteAsync(BflPromptLibrary.CORE_PATH, "Ein neuer Kern.\n{{RESPONSE_LANGUAGE}}\n{{ADDRESS_FORM}}\n{{KNOWLEDGE}}\n{{STEP}}\n{{MATERIALS}}\n{{DETAILS}}\n{{MISSING_DETAILS}}");
|
||||||
|
await this.WriteAsync(BflPromptLibrary.ONBOARDING_PATH, "Ein neues Onboarding.\n{{RESPONSE_LANGUAGE}}\n{{ADDRESS_FORM}}");
|
||||||
|
|
||||||
|
var prompt = BflPromptComposer.Compose(await BflPromptLibrary.LoadPartsAsync(BflStep.TEST_SITUATION, AddressForm.FORMAL, this.directory), BflStep.TEST_SITUATION, new Dictionary<BflStep, BflStepFormData>(), [], "Deutsch");
|
||||||
|
var onboarding = BflPromptComposer.ComposeOnboarding(await BflPromptLibrary.LoadOnboardingPartsAsync(AddressForm.FORMAL, this.directory), "Deutsch");
|
||||||
|
|
||||||
|
using (Assert.EnterMultipleScope())
|
||||||
|
{
|
||||||
|
Assert.That(prompt, Does.StartWith("Ein neuer Kern."));
|
||||||
|
Assert.That(prompt, Does.Not.Contain("{{"));
|
||||||
|
Assert.That(onboarding, Does.StartWith("Ein neues Onboarding."));
|
||||||
|
Assert.That(onboarding, Does.Not.Contain("{{"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task CopyingGivesTheFolderEveryPromptOfThisVersion()
|
||||||
|
{
|
||||||
|
var copied = await BflPromptOverrides.CopyBuiltInAsync(this.directory);
|
||||||
|
|
||||||
|
using (Assert.EnterMultipleScope())
|
||||||
|
{
|
||||||
|
Assert.That(copied, Is.EqualTo(BflPromptLibrary.AllPaths.Count));
|
||||||
|
foreach (var path in BflPromptLibrary.AllPaths)
|
||||||
|
Assert.That(await File.ReadAllTextAsync(BflPromptOverrides.FilePath(this.directory, path)), Is.EqualTo(await BflPromptLibrary.ReadBuiltInAsync(path)), path);
|
||||||
|
|
||||||
|
Assert.That(await BflPromptOverrides.FindAsync(this.directory), Is.Empty, "A copy which nobody changed replaces nothing.");
|
||||||
|
Assert.That(Directory.EnumerateFiles(this.directory, "*.tmp", SearchOption.AllDirectories), Is.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task CopyingKeepsEveryFileWhichExists()
|
||||||
|
{
|
||||||
|
await this.WriteAsync(BflPromptLibrary.CORE_PATH, "Ein neuer Kern.");
|
||||||
|
await this.WriteAsync(BflPromptLibrary.StepPath(BflStep.RESULTS), string.Empty);
|
||||||
|
|
||||||
|
var copied = await BflPromptOverrides.CopyBuiltInAsync(this.directory);
|
||||||
|
var copiedAgain = await BflPromptOverrides.CopyBuiltInAsync(this.directory);
|
||||||
|
|
||||||
|
using (Assert.EnterMultipleScope())
|
||||||
|
{
|
||||||
|
Assert.That(copied, Is.EqualTo(BflPromptLibrary.AllPaths.Count - 2));
|
||||||
|
Assert.That(copiedAgain, Is.Zero);
|
||||||
|
Assert.That(await File.ReadAllTextAsync(BflPromptOverrides.FilePath(this.directory, BflPromptLibrary.CORE_PATH)), Is.EqualTo("Ein neuer Kern."));
|
||||||
|
Assert.That(await File.ReadAllTextAsync(BflPromptOverrides.FilePath(this.directory, BflPromptLibrary.StepPath(BflStep.RESULTS))), Is.Empty, "Even an empty file is the person's own.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task TheLabTellsAboutReplacedPromptsOnlyWhileTheSettingAllowsThem()
|
||||||
|
{
|
||||||
|
var settingsManager = new SettingsManager(NullLogger<SettingsManager>.Instance, null!);
|
||||||
|
var store = new BeyondFactsLabStore(NullLogger<BeyondFactsLabStore>.Instance, new BflStorageOptions { DataDirectory = this.directory });
|
||||||
|
var service = new BflConversationService(store, settingsManager, null!, null!, NullLogger<BflConversationService>.Instance);
|
||||||
|
await BflPromptOverrides.CopyBuiltInAsync(store.PromptOverridesDirectory);
|
||||||
|
|
||||||
|
settingsManager.ConfigurationData.BeyondFactsLab.AllowPromptOverrides = true;
|
||||||
|
var withCopies = await service.UsesPromptOverridesAsync();
|
||||||
|
|
||||||
|
await File.WriteAllTextAsync(BflPromptOverrides.FilePath(store.PromptOverridesDirectory, BflPromptLibrary.CORE_PATH), "Ein neuer Kern.");
|
||||||
|
var withChange = await service.UsesPromptOverridesAsync();
|
||||||
|
|
||||||
|
settingsManager.ConfigurationData.BeyondFactsLab.AllowPromptOverrides = false;
|
||||||
|
var switchedOff = await service.UsesPromptOverridesAsync();
|
||||||
|
|
||||||
|
using (Assert.EnterMultipleScope())
|
||||||
|
{
|
||||||
|
Assert.That(store.PromptOverridesDirectory, Is.EqualTo(Path.Combine(this.directory, BeyondFactsLabStore.ROOT_DIRECTORY_NAME, BflPromptOverrides.DIRECTORY_NAME)));
|
||||||
|
Assert.That(withCopies, Is.False, "Copies which nobody changed replace nothing.");
|
||||||
|
Assert.That(withChange, Is.True);
|
||||||
|
Assert.That(switchedOff, Is.False);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task ACopyWithOtherLineBreaksReplacesNothing()
|
||||||
|
{
|
||||||
|
var path = BflPromptLibrary.StepPath(BflStep.FRAMEWORK);
|
||||||
|
var builtIn = await BflPromptLibrary.ReadBuiltInAsync(path);
|
||||||
|
await this.WriteAsync(path, builtIn.ReplaceLineEndings("\r\n") + "\r\n");
|
||||||
|
|
||||||
|
Assert.That(await BflPromptOverrides.FindAsync(this.directory), Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the file which replaces a prompt.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The prompt, one of BflPromptLibrary.AllPaths.</param>
|
||||||
|
/// <param name="text">The text of the file.</param>
|
||||||
|
private async Task WriteAsync(string path, string text)
|
||||||
|
{
|
||||||
|
var file = BflPromptOverrides.FilePath(this.directory, path);
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(file)!);
|
||||||
|
await File.WriteAllTextAsync(file, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user