Added a read-only view for managed profiles and chat templates (#813)
Some checks are pending
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions

This commit is contained in:
Thorsten Sommer 2026-06-20 17:06:43 +02:00 committed by GitHub
parent fc7197ec93
commit e04879fd7f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 454 additions and 240 deletions

View File

@ -46,6 +46,15 @@ LANG_NAME = "English (United States)"
UI_TEXT_CONTENT = {} UI_TEXT_CONTENT = {}
-- Self-hosted
UI_TEXT_CONTENT["::LLMPROVIDERSEXTENSIONS::T146444217"] = "Self-hosted"
-- No provider selected
UI_TEXT_CONTENT["::LLMPROVIDERSEXTENSIONS::T2897045472"] = "No provider selected"
-- Unknown
UI_TEXT_CONTENT["::LLMPROVIDERSEXTENSIONS::T3424652889"] = "Unknown"
-- No audit provider is configured. -- No audit provider is configured.
UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITAGENT::T2034826200"] = "No audit provider is configured." UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITAGENT::T2034826200"] = "No audit provider is configured."
@ -3514,6 +3523,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3227981830"] = "Using s
-- Add a message -- Add a message
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3372872324"] = "Add a message" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3372872324"] = "Add a message"
-- Close
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3448155331"] = "Close"
-- Unsupported content type -- Unsupported content type
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3570316759"] = "Unsupported content type" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3570316759"] = "Unsupported content type"
@ -4294,6 +4306,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3243902394"] = "The profile
-- Profile Name -- Profile Name
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3392578705"] = "Profile Name" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3392578705"] = "Profile Name"
-- Close
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3448155331"] = "Close"
-- Please enter what the LLM should know about you and/or what actions it should take. -- Please enter what the LLM should know about you and/or what actions it should take.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3708405102"] = "Please enter what the LLM should know about you and/or what actions it should take." UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3708405102"] = "Please enter what the LLM should know about you and/or what actions it should take."
@ -4813,6 +4828,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T14695
-- Add Chat Template -- Add Chat Template
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T1548314416"] = "Add Chat Template" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T1548314416"] = "Add Chat Template"
-- View
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T1582017048"] = "View"
-- Note: This advanced feature is designed for users familiar with prompt engineering concepts. Furthermore, you have to make sure yourself that your chosen provider supports the use of assistant prompts. -- Note: This advanced feature is designed for users familiar with prompt engineering concepts. Furthermore, you have to make sure yourself that your chosen provider supports the use of assistant prompts.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T1909110760"] = "Note: This advanced feature is designed for users familiar with prompt engineering concepts. Furthermore, you have to make sure yourself that your chosen provider supports the use of assistant prompts." UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T1909110760"] = "Note: This advanced feature is designed for users familiar with prompt engineering concepts. Furthermore, you have to make sure yourself that your chosen provider supports the use of assistant prompts."
@ -4852,6 +4870,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T38650
-- Delete Chat Template -- Delete Chat Template
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T4025180906"] = "Delete Chat Template" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T4025180906"] = "Delete Chat Template"
-- View Chat Template
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T4042112076"] = "View Chat Template"
-- Export Chat Template -- Export Chat Template
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T491504763"] = "Export Chat Template" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T491504763"] = "Export Chat Template"
@ -5260,6 +5281,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T143353473
-- Delete -- Delete
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T1469573738"] = "Delete" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T1469573738"] = "Delete"
-- View
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T1582017048"] = "View"
-- Your Profiles -- Your Profiles
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T2378610256"] = "Your Profiles" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T2378610256"] = "Your Profiles"
@ -5284,6 +5308,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T405841465
-- Store personal data about yourself in various profiles so that the AIs know your personal context. This saves you from having to explain your context each time, for example, in every chat. When you have different roles, you can create a profile for each role. -- Store personal data about yourself in various profiles so that the AIs know your personal context. This saves you from having to explain your context each time, for example, in every chat. When you have different roles, you can create a profile for each role.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4125557797"] = "Store personal data about yourself in various profiles so that the AIs know your personal context. This saves you from having to explain your context each time, for example, in every chat. When you have different roles, you can create a profile for each role." UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4125557797"] = "Store personal data about yourself in various profiles so that the AIs know your personal context. This saves you from having to explain your context each time, for example, in every chat. When you have different roles, you can create a profile for each role."
-- View Profile
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4219233997"] = "View Profile"
-- Add Profile -- Add Profile
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4248067241"] = "Add Profile" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4248067241"] = "Add Profile"
@ -6721,15 +6748,6 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::CONFIDENCELEVELEXTENSIONS::T3188327965"] =
-- Very Low -- Very Low
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::CONFIDENCELEVELEXTENSIONS::T786675843"] = "Very Low" UI_TEXT_CONTENT["AISTUDIO::PROVIDER::CONFIDENCELEVELEXTENSIONS::T786675843"] = "Very Low"
-- Self-hosted
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T146444217"] = "Self-hosted"
-- No provider selected
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T2897045472"] = "No provider selected"
-- Unknown
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T3424652889"] = "Unknown"
-- no model selected -- no model selected
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::MODEL::T2234274832"] = "no model selected" UI_TEXT_CONTENT["AISTUDIO::PROVIDER::MODEL::T2234274832"] = "no model selected"
@ -7870,6 +7888,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PANDOCAVAILABILITYSERVICE::T25964655
-- Failed to store the secret data due to an API issue. -- Failed to store the secret data due to an API issue.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::T1110203516"] = "Failed to store the secret data due to an API issue." UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::T1110203516"] = "Failed to store the secret data due to an API issue."
-- Failed to store the API key due to an API issue.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::T1704298921"] = "Failed to store the API key due to an API issue."
-- Failed to delete the secret data due to an API issue. -- Failed to delete the secret data due to an API issue.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::T2303057928"] = "Failed to delete the secret data due to an API issue." UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::T2303057928"] = "Failed to delete the secret data due to an API issue."

View File

@ -52,29 +52,42 @@
} }
else else
{ {
<MudStack Row="true" AlignItems="AlignItems.Center" StretchItems="StretchItems.None" Wrap="Wrap.Wrap"> @if (!this.Disabled)
<MudText Typo="Typo.body1" Inline="true"> {
@T("Drag and drop files into the marked area or click here to attach documents: ") <MudStack Row="true" AlignItems="AlignItems.Center" StretchItems="StretchItems.None" Wrap="Wrap.Wrap">
</MudText> <MudText Typo="Typo.body1" Inline="true">
<MudButton @T("Drag and drop files into the marked area or click here to attach documents: ")
Variant="Variant.Filled" </MudText>
StartIcon="@Icons.Material.Filled.Add" <MudButton
Color="Color.Primary" Variant="Variant.Filled"
OnClick="@(() => this.AddFilesManually())" StartIcon="@Icons.Material.Filled.Add"
Style="vertical-align: top; margin-top: -2px;" Color="Color.Primary"
Size="Size.Small"> OnClick="@(() => this.AddFilesManually())"
@T("Add file") Style="vertical-align: top; margin-top: -2px;"
</MudButton> Size="Size.Small">
</MudStack> @T("Add file")
</MudButton>
</MudStack>
}
<div @onmouseenter="@this.OnMouseEnter" @onmouseleave="@this.OnMouseLeave"> <div @onmouseenter="@this.OnMouseEnter" @onmouseleave="@this.OnMouseLeave">
<MudPaper Height="20em" Outlined="true" Class="@this.dragClass" Style="overflow-y: auto;"> <MudPaper Height="20em" Outlined="true" Class="@this.dragClass" Style="overflow-y: auto;">
@foreach (var fileAttachment in this.DocumentPaths) @foreach (var fileAttachment in this.DocumentPaths)
{ {
<MudChip T="string" Color="Color.Dark" Text="@fileAttachment.FileName" tabindex="-1" Icon="@Icons.Material.Filled.Search" OnClick="@(() => this.InvestigateFile(fileAttachment))" OnClose="@(() => this.RemoveDocument(fileAttachment))"/> @if (this.Disabled)
{
<MudChip T="string" Color="Color.Dark" Text="@fileAttachment.FileName" tabindex="-1" Icon="@Icons.Material.Filled.Search" OnClick="@(() => this.InvestigateFile(fileAttachment))"/>
}
else
{
<MudChip T="string" Color="Color.Dark" Text="@fileAttachment.FileName" tabindex="-1" Icon="@Icons.Material.Filled.Search" OnClick="@(() => this.InvestigateFile(fileAttachment))" OnClose="@(() => this.RemoveDocument(fileAttachment))"/>
}
} }
</MudPaper> </MudPaper>
</div> </div>
<MudButton OnClick="@(async () => await this.ClearAllFiles())" Variant="Variant.Filled" Color="Color.Info" Class="mt-2" StartIcon="@Icons.Material.Filled.Delete"> @if (!this.Disabled)
@T("Clear file list") {
</MudButton> <MudButton OnClick="@(async () => await this.ClearAllFiles())" Variant="Variant.Filled" Color="Color.Info" Class="mt-2" StartIcon="@Icons.Material.Filled.Delete">
@T("Clear file list")
</MudButton>
}
} }

View File

@ -14,16 +14,16 @@ using DialogOptions = Dialogs.DialogOptions;
public partial class AttachDocuments : MSGComponentBase public partial class AttachDocuments : MSGComponentBase
{ {
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(AttachDocuments).Namespace, nameof(AttachDocuments)); private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(AttachDocuments).Namespace, nameof(AttachDocuments));
[Parameter] [Parameter]
public string Name { get; set; } = string.Empty; public string Name { get; set; } = string.Empty;
/// <summary> /// <summary>
/// On which layer to register the drop area. Higher layers have priority over lower layers. /// On which layer to register the drop area. Higher layers have priority over lower layers.
/// </summary> /// </summary>
[Parameter] [Parameter]
public int Layer { get; set; } public int Layer { get; set; }
/// <summary> /// <summary>
/// When true, pause catching dropped files. Default is false. /// When true, pause catching dropped files. Default is false.
/// </summary> /// </summary>
@ -38,16 +38,19 @@ public partial class AttachDocuments : MSGComponentBase
[Parameter] [Parameter]
public Func<HashSet<FileAttachment>, Task> OnChange { get; set; } = _ => Task.CompletedTask; public Func<HashSet<FileAttachment>, Task> OnChange { get; set; } = _ => Task.CompletedTask;
/// <summary> /// <summary>
/// Catch all documents that are hovered over the AI Studio window and not only over the drop zone. /// Catch all documents that are hovered over the AI Studio window and not only over the drop zone.
/// </summary> /// </summary>
[Parameter] [Parameter]
public bool CatchAllDocuments { get; set; } public bool CatchAllDocuments { get; set; }
[Parameter] [Parameter]
public bool UseSmallForm { get; set; } public bool UseSmallForm { get; set; }
[Parameter]
public bool Disabled { get; set; }
/// <summary> /// <summary>
/// When true, validate media file types before attaching. Default is true. That means that /// When true, validate media file types before attaching. Default is true. That means that
/// the user cannot attach unsupported media file types when the provider or model does not /// the user cannot attach unsupported media file types when the provider or model does not
@ -56,16 +59,16 @@ public partial class AttachDocuments : MSGComponentBase
/// </summary> /// </summary>
[Parameter] [Parameter]
public bool ValidateMediaFileTypes { get; set; } = true; public bool ValidateMediaFileTypes { get; set; } = true;
[Parameter] [Parameter]
public AIStudio.Settings.Provider? Provider { get; set; } public AIStudio.Settings.Provider? Provider { get; set; }
[Inject] [Inject]
private ILogger<AttachDocuments> Logger { get; set; } = null!; private ILogger<AttachDocuments> Logger { get; set; } = null!;
[Inject] [Inject]
private RustService RustService { get; init; } = null!; private RustService RustService { get; init; } = null!;
[Inject] [Inject]
private IDialogService DialogService { get; init; } = null!; private IDialogService DialogService { get; init; } = null!;
@ -74,17 +77,17 @@ public partial class AttachDocuments : MSGComponentBase
private const Placement TOOLBAR_TOOLTIP_PLACEMENT = Placement.Top; private const Placement TOOLBAR_TOOLTIP_PLACEMENT = Placement.Top;
private static readonly string DROP_FILES_HERE_TEXT = TB("Drop files here to attach them."); private static readonly string DROP_FILES_HERE_TEXT = TB("Drop files here to attach them.");
private uint numDropAreasAboveThis; private uint numDropAreasAboveThis;
private bool isComponentHovered; private bool isComponentHovered;
private bool isDraggingOver; private bool isDraggingOver;
#region Overrides of MSGComponentBase #region Overrides of MSGComponentBase
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
this.ApplyFilters([], [ Event.TAURI_EVENT_RECEIVED, Event.REGISTER_FILE_DROP_AREA, Event.UNREGISTER_FILE_DROP_AREA ]); this.ApplyFilters([], [ Event.TAURI_EVENT_RECEIVED, Event.REGISTER_FILE_DROP_AREA, Event.UNREGISTER_FILE_DROP_AREA ]);
// Register this drop area: // Register this drop area:
await this.MessageBus.SendMessage(this, Event.REGISTER_FILE_DROP_AREA, this.Layer); await this.MessageBus.SendMessage(this, Event.REGISTER_FILE_DROP_AREA, this.Layer);
await base.OnInitializedAsync(); await base.OnInitializedAsync();
@ -92,6 +95,9 @@ public partial class AttachDocuments : MSGComponentBase
protected override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default protected override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
{ {
if (this.Disabled && triggeredEvent == Event.TAURI_EVENT_RECEIVED)
return;
switch (triggeredEvent) switch (triggeredEvent)
{ {
case Event.REGISTER_FILE_DROP_AREA when sendingComponent != this: case Event.REGISTER_FILE_DROP_AREA when sendingComponent != this:
@ -111,7 +117,7 @@ public partial class AttachDocuments : MSGComponentBase
{ {
if(this.numDropAreasAboveThis > 0) if(this.numDropAreasAboveThis > 0)
this.numDropAreasAboveThis--; this.numDropAreasAboveThis--;
if(this.numDropAreasAboveThis is 0) if(this.numDropAreasAboveThis is 0)
this.PauseCatchingDrops = false; this.PauseCatchingDrops = false;
} }
@ -122,40 +128,40 @@ public partial class AttachDocuments : MSGComponentBase
case Event.TAURI_EVENT_RECEIVED when data is TauriEvent { EventType: TauriEventType.FILE_DROP_HOVERED }: case Event.TAURI_EVENT_RECEIVED when data is TauriEvent { EventType: TauriEventType.FILE_DROP_HOVERED }:
if(this.PauseCatchingDrops) if(this.PauseCatchingDrops)
return; return;
if(!this.isComponentHovered && !this.CatchAllDocuments) if(!this.isComponentHovered && !this.CatchAllDocuments)
{ {
this.Logger.LogDebug("Attach documents component '{Name}' is not hovered, ignoring file drop hovered event.", this.Name); this.Logger.LogDebug("Attach documents component '{Name}' is not hovered, ignoring file drop hovered event.", this.Name);
return; return;
} }
this.isDraggingOver = true; this.isDraggingOver = true;
this.SetDragClass(); this.SetDragClass();
this.StateHasChanged(); this.StateHasChanged();
break; break;
case Event.TAURI_EVENT_RECEIVED when data is TauriEvent { EventType: TauriEventType.FILE_DROP_CANCELED }: case Event.TAURI_EVENT_RECEIVED when data is TauriEvent { EventType: TauriEventType.FILE_DROP_CANCELED }:
if(this.PauseCatchingDrops) if(this.PauseCatchingDrops)
return; return;
this.isDraggingOver = false; this.isDraggingOver = false;
this.StateHasChanged(); this.StateHasChanged();
break; break;
case Event.TAURI_EVENT_RECEIVED when data is TauriEvent { EventType: TauriEventType.WINDOW_NOT_FOCUSED }: case Event.TAURI_EVENT_RECEIVED when data is TauriEvent { EventType: TauriEventType.WINDOW_NOT_FOCUSED }:
if(this.PauseCatchingDrops) if(this.PauseCatchingDrops)
return; return;
this.isDraggingOver = false; this.isDraggingOver = false;
this.isComponentHovered = false; this.isComponentHovered = false;
this.ClearDragClass(); this.ClearDragClass();
this.StateHasChanged(); this.StateHasChanged();
break; break;
case Event.TAURI_EVENT_RECEIVED when data is TauriEvent { EventType: TauriEventType.FILE_DROP_DROPPED, Payload: var paths }: case Event.TAURI_EVENT_RECEIVED when data is TauriEvent { EventType: TauriEventType.FILE_DROP_DROPPED, Payload: var paths }:
if(this.PauseCatchingDrops) if(this.PauseCatchingDrops)
return; return;
if(!this.isComponentHovered && !this.CatchAllDocuments) if(!this.isComponentHovered && !this.CatchAllDocuments)
{ {
this.Logger.LogDebug("Attach documents component '{Name}' is not hovered, ignoring file drop dropped event.", this.Name); this.Logger.LogDebug("Attach documents component '{Name}' is not hovered, ignoring file drop dropped event.", this.Name);
@ -197,11 +203,14 @@ public partial class AttachDocuments : MSGComponentBase
#endregion #endregion
private const string DEFAULT_DRAG_CLASS = "relative rounded-lg border-2 border-dashed pa-4 mt-4 mud-width-full mud-height-full"; private const string DEFAULT_DRAG_CLASS = "relative rounded-lg border-2 border-dashed pa-4 mt-4 mud-width-full mud-height-full";
private string dragClass = DEFAULT_DRAG_CLASS; private string dragClass = DEFAULT_DRAG_CLASS;
private async Task AddFilesManually() private async Task AddFilesManually()
{ {
if (this.Disabled)
return;
// Ensure that Pandoc is installed and ready: // Ensure that Pandoc is installed and ready:
var pandocState = await this.PandocAvailabilityService.EnsureAvailabilityAsync( var pandocState = await this.PandocAvailabilityService.EnsureAvailabilityAsync(
showSuccessMessage: false, showSuccessMessage: false,
@ -228,43 +237,49 @@ public partial class AttachDocuments : MSGComponentBase
this.DocumentPaths.Add(FileAttachment.FromPath(selectedFilePath)); this.DocumentPaths.Add(FileAttachment.FromPath(selectedFilePath));
} }
await this.DocumentPathsChanged.InvokeAsync(this.DocumentPaths); await this.DocumentPathsChanged.InvokeAsync(this.DocumentPaths);
await this.OnChange(this.DocumentPaths); await this.OnChange(this.DocumentPaths);
} }
private async Task OpenAttachmentsDialog() private async Task OpenAttachmentsDialog()
{ {
if (this.Disabled)
return;
this.DocumentPaths = await ReviewAttachmentsDialog.OpenDialogAsync(this.DialogService, this.DocumentPaths); this.DocumentPaths = await ReviewAttachmentsDialog.OpenDialogAsync(this.DialogService, this.DocumentPaths);
} }
private async Task ClearAllFiles() private async Task ClearAllFiles()
{ {
if (this.Disabled)
return;
this.DocumentPaths.Clear(); this.DocumentPaths.Clear();
await this.DocumentPathsChanged.InvokeAsync(this.DocumentPaths); await this.DocumentPathsChanged.InvokeAsync(this.DocumentPaths);
await this.OnChange(this.DocumentPaths); await this.OnChange(this.DocumentPaths);
} }
private void SetDragClass() => this.dragClass = $"{DEFAULT_DRAG_CLASS} mud-border-primary border-4"; private void SetDragClass() => this.dragClass = $"{DEFAULT_DRAG_CLASS} mud-border-primary border-4";
private void ClearDragClass() => this.dragClass = DEFAULT_DRAG_CLASS; private void ClearDragClass() => this.dragClass = DEFAULT_DRAG_CLASS;
private void OnMouseEnter(EventArgs _) private void OnMouseEnter(EventArgs _)
{ {
if(this.PauseCatchingDrops) if(this.Disabled || this.PauseCatchingDrops)
return; return;
this.Logger.LogDebug("Attach documents component '{Name}' is hovered.", this.Name); this.Logger.LogDebug("Attach documents component '{Name}' is hovered.", this.Name);
this.isComponentHovered = true; this.isComponentHovered = true;
this.SetDragClass(); this.SetDragClass();
this.StateHasChanged(); this.StateHasChanged();
} }
private void OnMouseLeave(EventArgs _) private void OnMouseLeave(EventArgs _)
{ {
if(this.PauseCatchingDrops) if(this.Disabled || this.PauseCatchingDrops)
return; return;
this.Logger.LogDebug("Attach documents component '{Name}' is no longer hovered.", this.Name); this.Logger.LogDebug("Attach documents component '{Name}' is no longer hovered.", this.Name);
this.isComponentHovered = false; this.isComponentHovered = false;
this.ClearDragClass(); this.ClearDragClass();
@ -273,6 +288,9 @@ public partial class AttachDocuments : MSGComponentBase
private async Task RemoveDocument(FileAttachment fileAttachment) private async Task RemoveDocument(FileAttachment fileAttachment)
{ {
if (this.Disabled)
return;
this.DocumentPaths.Remove(fileAttachment); this.DocumentPaths.Remove(fileAttachment);
await this.DocumentPathsChanged.InvokeAsync(this.DocumentPaths); await this.DocumentPathsChanged.InvokeAsync(this.DocumentPaths);

View File

@ -6,7 +6,7 @@
<ActivatorContent> <ActivatorContent>
@if (this.CurrentChatTemplate != ChatTemplate.NO_CHAT_TEMPLATE) @if (this.CurrentChatTemplate != ChatTemplate.NO_CHAT_TEMPLATE)
{ {
<MudButton IconSize="Size.Large" StartIcon="@Icons.Material.Filled.RateReview" IconColor="Color.Default"> <MudButton IconSize="Size.Large" StartIcon="@this.ChatTemplateIcon(this.CurrentChatTemplate)" IconColor="Color.Default">
@this.CurrentChatTemplate.GetSafeName() @this.CurrentChatTemplate.GetSafeName()
</MudButton> </MudButton>
} }
@ -22,7 +22,7 @@
<MudDivider/> <MudDivider/>
@foreach (var chatTemplate in this.SettingsManager.ConfigurationData.ChatTemplates.GetAllChatTemplates()) @foreach (var chatTemplate in this.SettingsManager.ConfigurationData.ChatTemplates.GetAllChatTemplates())
{ {
<MudMenuItem Icon="@Icons.Material.Filled.RateReview" OnClick="@(async () => await this.SelectionChanged(chatTemplate))"> <MudMenuItem Icon="@this.ChatTemplateIcon(chatTemplate)" OnClick="@(async () => await this.SelectionChanged(chatTemplate))">
@chatTemplate.GetSafeName() @chatTemplate.GetSafeName()
</MudMenuItem> </MudMenuItem>
} }

View File

@ -11,13 +11,13 @@ public partial class ChatTemplateSelection : MSGComponentBase
{ {
[Parameter] [Parameter]
public ChatTemplate CurrentChatTemplate { get; set; } = ChatTemplate.NO_CHAT_TEMPLATE; public ChatTemplate CurrentChatTemplate { get; set; } = ChatTemplate.NO_CHAT_TEMPLATE;
[Parameter] [Parameter]
public bool CanChatThreadBeUsedForTemplate { get; set; } public bool CanChatThreadBeUsedForTemplate { get; set; }
[Parameter] [Parameter]
public ChatThread? CurrentChatThread { get; set; } public ChatThread? CurrentChatThread { get; set; }
[Parameter] [Parameter]
public EventCallback<ChatTemplate> CurrentChatTemplateChanged { get; set; } public EventCallback<ChatTemplate> CurrentChatTemplateChanged { get; set; }
@ -26,24 +26,32 @@ public partial class ChatTemplateSelection : MSGComponentBase
[Parameter] [Parameter]
public string MarginRight { get; set; } = string.Empty; public string MarginRight { get; set; } = string.Empty;
[Inject] [Inject]
private IDialogService DialogService { get; init; } = null!; private IDialogService DialogService { get; init; } = null!;
private string MarginClass => $"{this.MarginLeft} {this.MarginRight}"; private string MarginClass => $"{this.MarginLeft} {this.MarginRight}";
private string ChatTemplateIcon(ChatTemplate chatTemplate)
{
if (chatTemplate.IsEnterpriseConfiguration)
return Icons.Material.Filled.Business;
return Icons.Material.Filled.RateReview;
}
private async Task SelectionChanged(ChatTemplate chatTemplate) private async Task SelectionChanged(ChatTemplate chatTemplate)
{ {
this.CurrentChatTemplate = chatTemplate; this.CurrentChatTemplate = chatTemplate;
await this.CurrentChatTemplateChanged.InvokeAsync(chatTemplate); await this.CurrentChatTemplateChanged.InvokeAsync(chatTemplate);
} }
private async Task OpenSettingsDialog() private async Task OpenSettingsDialog()
{ {
var dialogParameters = new DialogParameters(); var dialogParameters = new DialogParameters();
await this.DialogService.ShowAsync<SettingsDialogChatTemplate>(T("Open Chat Template Options"), dialogParameters, DialogOptions.FULLSCREEN); await this.DialogService.ShowAsync<SettingsDialogChatTemplate>(T("Open Chat Template Options"), dialogParameters, DialogOptions.FULLSCREEN);
} }
private async Task CreateNewChatTemplateFromChat() private async Task CreateNewChatTemplateFromChat()
{ {
var dialogParameters = new DialogParameters<SettingsDialogChatTemplate> var dialogParameters = new DialogParameters<SettingsDialogChatTemplate>

View File

@ -10,7 +10,7 @@
<MudJustifiedText Class="mb-3" Typo="Typo.body1"> <MudJustifiedText Class="mb-3" Typo="Typo.body1">
@T("The name of the chat template is mandatory. Each chat template must have a unique name.") @T("The name of the chat template is mandatory. Each chat template must have a unique name.")
</MudJustifiedText> </MudJustifiedText>
<MudForm @ref="@this.form" @bind-IsValid="@this.dataIsValid" @bind-Errors="@this.dataIssues"> <MudForm @ref="@this.form" @bind-IsValid="@this.dataIsValid" @bind-Errors="@this.dataIssues">
@* ReSharper disable once CSharpWarnings::CS8974 *@ @* ReSharper disable once CSharpWarnings::CS8974 *@
<MudTextField <MudTextField
@ -26,9 +26,10 @@
AdornmentColor="Color.Info" AdornmentColor="Color.Info"
Validation="@this.ValidateName" Validation="@this.ValidateName"
Variant="Variant.Outlined" Variant="Variant.Outlined"
ReadOnly="@this.IsReadOnly"
UserAttributes="@SPELLCHECK_ATTRIBUTES" UserAttributes="@SPELLCHECK_ATTRIBUTES"
/> />
<MudText Typo="Typo.h6" Class="mb-3 mt-3"> <MudText Typo="Typo.h6" Class="mb-3 mt-3">
@T("System Prompt") @T("System Prompt")
</MudText> </MudText>
@ -47,16 +48,17 @@
Class="mb-3" Class="mb-3"
UserAttributes="@SPELLCHECK_ATTRIBUTES" UserAttributes="@SPELLCHECK_ATTRIBUTES"
HelperText="@T("Tell the AI your system prompt.")" HelperText="@T("Tell the AI your system prompt.")"
ReadOnly="@this.IsReadOnly"
/> />
<MudJustifiedText Class="mb-3" Typo="Typo.body1"> <MudJustifiedText Class="mb-3" Typo="Typo.body1">
@T("Are you unsure which system prompt to use? You might start with the default system prompt that AI Studio uses for all chats.") @T("Are you unsure which system prompt to use? You might start with the default system prompt that AI Studio uses for all chats.")
</MudJustifiedText> </MudJustifiedText>
<MudButton Class="mb-3" Color="Color.Default" OnClick="@this.UseDefaultSystemPrompt" StartIcon="@Icons.Material.Filled.ListAlt" Variant="Variant.Filled"> <MudButton Class="mb-3" Color="Color.Default" OnClick="@this.UseDefaultSystemPrompt" StartIcon="@Icons.Material.Filled.ListAlt" Variant="Variant.Filled" Disabled="@this.IsReadOnly">
@T("Use the default system prompt") @T("Use the default system prompt")
</MudButton> </MudButton>
<ReadFileContent Text="@T("Load system prompt from file")" @bind-FileContent="@this.DataSystemPrompt"/> <ReadFileContent Text="@T("Load system prompt from file")" @bind-FileContent="@this.DataSystemPrompt" Disabled="@this.IsReadOnly"/>
<MudText Typo="Typo.h6" Class="mb-3 mt-6"> <MudText Typo="Typo.h6" Class="mb-3 mt-6">
@T("Predefined User Input") @T("Predefined User Input")
</MudText> </MudText>
@ -77,6 +79,7 @@
Class="mb-3" Class="mb-3"
UserAttributes="@SPELLCHECK_ATTRIBUTES" UserAttributes="@SPELLCHECK_ATTRIBUTES"
HelperText="@T("Tell the AI your predefined user input.")" HelperText="@T("Tell the AI your predefined user input.")"
ReadOnly="@this.IsReadOnly"
/> />
<MudText Typo="Typo.h6" Class="mb-3 mt-6"> <MudText Typo="Typo.h6" Class="mb-3 mt-6">
@ -92,6 +95,7 @@
UseSmallForm="false" UseSmallForm="false"
CatchAllDocuments="true" CatchAllDocuments="true"
ValidateMediaFileTypes="false" ValidateMediaFileTypes="false"
Disabled="@this.IsReadOnly"
/> />
<MudText Typo="Typo.h6" Class="mb-3 mt-6"> <MudText Typo="Typo.h6" Class="mb-3 mt-6">
@ -100,8 +104,8 @@
<MudJustifiedText Class="mb-3" Typo="Typo.body1"> <MudJustifiedText Class="mb-3" Typo="Typo.body1">
@T("Using some chat templates in tandem with profiles might cause issues. Therefore, you might prohibit the usage of profiles here.") @T("Using some chat templates in tandem with profiles might cause issues. Therefore, you might prohibit the usage of profiles here.")
</MudJustifiedText> </MudJustifiedText>
<MudTextSwitch @bind-Value="@this.AllowProfileUsage" Color="Color.Primary" Label="@T("Allow the use of profiles together with this chat template?")" LabelOn="@T("Yes, allow profiles when using this template")" LabelOff="@T("No, prohibit profile use for this template")" /> <MudTextSwitch @bind-Value="@this.AllowProfileUsage" Color="Color.Primary" Label="@T("Allow the use of profiles together with this chat template?")" LabelOn="@T("Yes, allow profiles when using this template")" LabelOff="@T("No, prohibit profile use for this template")" Disabled="@this.IsReadOnly" />
<MudText Typo="Typo.h6" Class="mb-3 mt-6"> <MudText Typo="Typo.h6" Class="mb-3 mt-6">
@T("Example Conversation") @T("Example Conversation")
</MudText> </MudText>
@ -129,18 +133,18 @@
case ContentText textContent: case ContentText textContent:
<MudTextField AutoGrow="true" Value="@textContent.Text" Placeholder="@T("Enter a message")" ReadOnly="true" Variant="Variant.Text" Validation="@this.ValidateExampleTextMessage"/> <MudTextField AutoGrow="true" Value="@textContent.Text" Placeholder="@T("Enter a message")" ReadOnly="true" Variant="Variant.Text" Validation="@this.ValidateExampleTextMessage"/>
break; break;
case ContentImage { SourceType: ContentImageSource.URL or ContentImageSource.LOCAL_PATH } imageContent: case ContentImage { SourceType: ContentImageSource.URL or ContentImageSource.LOCAL_PATH } imageContent:
<MudImage Src="@imageContent.Source" Alt="@T("Image content")" Fluid="true" /> <MudImage Src="@imageContent.Source" Alt="@T("Image content")" Fluid="true" />
break; break;
default: default:
@T("Unsupported content type") @T("Unsupported content type")
break; break;
} }
</MudTd> </MudTd>
<MudTd> <MudTd>
@if (!this.isInlineEditOnGoing) @if (!this.isInlineEditOnGoing && !this.IsReadOnly)
{ {
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap"> <MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
<MudTooltip Text="@T("Add a new message below")"> <MudTooltip Text="@T("Add a new message below")">
@ -153,22 +157,29 @@
</RowTemplate> </RowTemplate>
<RowEditingTemplate> <RowEditingTemplate>
<MudTd> <MudTd>
<MudSelect Label="@T("Role")" @bind-Value="@context.Role" Required="true"> @if (this.IsReadOnly)
@foreach (var role in ChatRoles.ChatTemplateRoles()) {
{ @context.Role.ToChatTemplateName()
<MudSelectItem Value="@role"> }
@role.ToChatTemplateName() else
</MudSelectItem> {
} <MudSelect Label="@T("Role")" @bind-Value="@context.Role" Required="true">
</MudSelect> @foreach (var role in ChatRoles.ChatTemplateRoles())
{
<MudSelectItem Value="@role">
@role.ToChatTemplateName()
</MudSelectItem>
}
</MudSelect>
}
</MudTd> </MudTd>
<MudTd> <MudTd>
@switch(context.Content) @switch(context.Content)
{ {
case ContentText textContent: case ContentText textContent:
<MudTextField AutoGrow="true" @bind-Value="@textContent.Text" Label="@T("The message")" Required="true" Immediate="true" Placeholder="@T("Enter a message")"/> <MudTextField AutoGrow="true" @bind-Value="@textContent.Text" Label="@T("The message")" Required="true" Immediate="true" Placeholder="@T("Enter a message")" ReadOnly="@this.IsReadOnly"/>
break; break;
default: default:
<MudText Typo="Typo.body2"> <MudText Typo="Typo.body2">
@T("Only text content is supported in the editing mode yet.") @T("Only text content is supported in the editing mode yet.")
@ -182,8 +193,8 @@
</PagerContent> </PagerContent>
</MudTable> </MudTable>
</MudForm> </MudForm>
@if (!this.isInlineEditOnGoing) @if (!this.isInlineEditOnGoing && !this.IsReadOnly)
{ {
<MudButton Class="mb-6" Color="Color.Primary" OnClick="@this.AddMessageToEnd" StartIcon="@Icons.Material.Filled.Add" Variant="Variant.Filled"> <MudButton Class="mb-6" Color="Color.Primary" OnClick="@this.AddMessageToEnd" StartIcon="@Icons.Material.Filled.Add" Variant="Variant.Filled">
@T("Add a message") @T("Add a message")
@ -193,22 +204,31 @@
<Issues IssuesData="@this.dataIssues"/> <Issues IssuesData="@this.dataIssues"/>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled"> @if (this.IsReadOnly)
@T("Cancel")
</MudButton>
@if (!this.isInlineEditOnGoing)
{ {
<MudButton OnClick="@this.Store" Variant="Variant.Filled" Color="Color.Primary"> <MudButton OnClick="@this.Cancel" Variant="Variant.Filled">
@if (this.IsEditing) @T("Close")
{
@T("Update")
}
else
{
@T("Add")
}
</MudButton> </MudButton>
} }
else
{
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">
@T("Cancel")
</MudButton>
@if (!this.isInlineEditOnGoing)
{
<MudButton OnClick="@this.Store" Variant="Variant.Filled" Color="Color.Primary">
@if (this.IsEditing)
{
@T("Update")
}
else
{
@T("Add")
}
</MudButton>
}
}
</DialogActions> </DialogActions>
</MudDialog> </MudDialog>

View File

@ -16,37 +16,40 @@ public partial class ChatTemplateDialog : MSGComponentBase
/// </summary> /// </summary>
[Parameter] [Parameter]
public uint DataNum { get; set; } public uint DataNum { get; set; }
/// <summary> /// <summary>
/// The chat template's ID. /// The chat template's ID.
/// </summary> /// </summary>
[Parameter] [Parameter]
public string DataId { get; set; } = Guid.NewGuid().ToString(); public string DataId { get; set; } = Guid.NewGuid().ToString();
/// <summary> /// <summary>
/// The chat template name chosen by the user. /// The chat template name chosen by the user.
/// </summary> /// </summary>
[Parameter] [Parameter]
public string DataName { get; set; } = string.Empty; public string DataName { get; set; } = string.Empty;
/// <summary> /// <summary>
/// What is the system prompt? /// What is the system prompt?
/// </summary> /// </summary>
[Parameter] [Parameter]
public string DataSystemPrompt { get; set; } = string.Empty; public string DataSystemPrompt { get; set; } = string.Empty;
/// <summary> /// <summary>
/// What is the predefined user prompt? /// What is the predefined user prompt?
/// </summary> /// </summary>
[Parameter] [Parameter]
public string PredefinedUserPrompt { get; set; } = string.Empty; public string PredefinedUserPrompt { get; set; } = string.Empty;
/// <summary> /// <summary>
/// Should the dialog be in editing mode? /// Should the dialog be in editing mode?
/// </summary> /// </summary>
[Parameter] [Parameter]
public bool IsEditing { get; init; } public bool IsEditing { get; init; }
[Parameter]
public bool IsReadOnly { get; init; }
[Parameter] [Parameter]
public IReadOnlyCollection<ContentBlock> ExampleConversation { get; init; } = []; public IReadOnlyCollection<ContentBlock> ExampleConversation { get; init; } = [];
@ -55,23 +58,23 @@ public partial class ChatTemplateDialog : MSGComponentBase
[Parameter] [Parameter]
public bool AllowProfileUsage { get; set; } = true; public bool AllowProfileUsage { get; set; } = true;
[Parameter] [Parameter]
public bool CreateFromExistingChatThread { get; set; } public bool CreateFromExistingChatThread { get; set; }
[Parameter] [Parameter]
public ChatThread? ExistingChatThread { get; set; } public ChatThread? ExistingChatThread { get; set; }
[Inject] [Inject]
private ILogger<ChatTemplateDialog> Logger { get; init; } = null!; private ILogger<ChatTemplateDialog> Logger { get; init; } = null!;
private static readonly Dictionary<string, object?> SPELLCHECK_ATTRIBUTES = new(); private static readonly Dictionary<string, object?> SPELLCHECK_ATTRIBUTES = new();
/// <summary> /// <summary>
/// The list of used chat template names. We need this to check for uniqueness. /// The list of used chat template names. We need this to check for uniqueness.
/// </summary> /// </summary>
private List<string> UsedNames { get; set; } = []; private List<string> UsedNames { get; set; } = [];
private bool dataIsValid; private bool dataIsValid;
private List<ContentBlock> dataExampleConversation = []; private List<ContentBlock> dataExampleConversation = [];
private HashSet<FileAttachment> fileAttachments = []; private HashSet<FileAttachment> fileAttachments = [];
@ -80,20 +83,20 @@ public partial class ChatTemplateDialog : MSGComponentBase
private bool isInlineEditOnGoing; private bool isInlineEditOnGoing;
private ContentBlock? messageEntryBeforeEdit; private ContentBlock? messageEntryBeforeEdit;
// We get the form reference from Blazor code to validate it manually: // We get the form reference from Blazor code to validate it manually:
private MudForm form = null!; private MudForm form = null!;
#region Overrides of ComponentBase #region Overrides of ComponentBase
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
// Configure the spellchecking for the instance name input: // Configure the spellchecking for the instance name input:
this.SettingsManager.InjectSpellchecking(SPELLCHECK_ATTRIBUTES); this.SettingsManager.InjectSpellchecking(SPELLCHECK_ATTRIBUTES);
// Load the used instance names: // Load the used instance names:
this.UsedNames = this.SettingsManager.ConfigurationData.ChatTemplates.Select(x => x.Name.ToLowerInvariant()).ToList(); this.UsedNames = this.SettingsManager.ConfigurationData.ChatTemplates.Select(x => x.Name.ToLowerInvariant()).ToList();
// When editing, we need to load the data: // When editing, we need to load the data:
if(this.IsEditing) if(this.IsEditing)
{ {
@ -108,7 +111,7 @@ public partial class ChatTemplateDialog : MSGComponentBase
this.dataExampleConversation = this.ExistingChatThread.Blocks.Select(n => n.DeepClone(true)).ToList(); this.dataExampleConversation = this.ExistingChatThread.Blocks.Select(n => n.DeepClone(true)).ToList();
this.DataName = this.ExistingChatThread.Name; this.DataName = this.ExistingChatThread.Name;
} }
await base.OnInitializedAsync(); await base.OnInitializedAsync();
} }
@ -118,7 +121,7 @@ public partial class ChatTemplateDialog : MSGComponentBase
// We don't want to show validation errors when the user opens the dialog. // We don't want to show validation errors when the user opens the dialog.
if(!this.IsEditing && firstRender) if(!this.IsEditing && firstRender)
this.form.ResetValidation(); this.form.ResetValidation();
await base.OnAfterRenderAsync(firstRender); await base.OnAfterRenderAsync(firstRender);
} }
@ -128,28 +131,34 @@ public partial class ChatTemplateDialog : MSGComponentBase
{ {
Num = this.DataNum, Num = this.DataNum,
Id = this.DataId, Id = this.DataId,
Name = this.DataName, Name = this.DataName,
SystemPrompt = this.DataSystemPrompt, SystemPrompt = this.DataSystemPrompt,
PredefinedUserPrompt = this.PredefinedUserPrompt, PredefinedUserPrompt = this.PredefinedUserPrompt,
ExampleConversation = this.dataExampleConversation, ExampleConversation = this.dataExampleConversation,
FileAttachments = this.fileAttachments.Select(attachment => attachment.Normalize()).ToList(), FileAttachments = this.fileAttachments.Select(attachment => attachment.Normalize()).ToList(),
AllowProfileUsage = this.AllowProfileUsage, AllowProfileUsage = this.AllowProfileUsage,
EnterpriseConfigurationPluginId = Guid.Empty, EnterpriseConfigurationPluginId = Guid.Empty,
IsEnterpriseConfiguration = false, IsEnterpriseConfiguration = false,
}; };
private void RemoveMessage(ContentBlock item) private void RemoveMessage(ContentBlock item)
{ {
if (this.IsReadOnly)
return;
this.dataExampleConversation.Remove(item); this.dataExampleConversation.Remove(item);
} }
private void AddMessageToEnd() private void AddMessageToEnd()
{ {
if (this.IsReadOnly)
return;
var newEntry = new ContentBlock var newEntry = new ContentBlock
{ {
Role = this.dataExampleConversation.Count is 0 ? ChatRole.USER : this.dataExampleConversation.Last().Role.SelectNextRoleForTemplate(), Role = this.dataExampleConversation.Count is 0 ? ChatRole.USER : this.dataExampleConversation.Last().Role.SelectNextRoleForTemplate(),
Content = new ContentText(), Content = new ContentText(),
ContentType = ContentType.TEXT, ContentType = ContentType.TEXT,
HideFromUser = true, HideFromUser = true,
@ -161,6 +170,9 @@ public partial class ChatTemplateDialog : MSGComponentBase
private void AddMessageBelow(ContentBlock currentItem) private void AddMessageBelow(ContentBlock currentItem)
{ {
if (this.IsReadOnly)
return;
var insertedEntry = new ContentBlock var insertedEntry = new ContentBlock
{ {
Role = this.dataExampleConversation.Count is 0 ? ChatRole.USER : this.dataExampleConversation.Last().Role.SelectNextRoleForTemplate(), Role = this.dataExampleConversation.Count is 0 ? ChatRole.USER : this.dataExampleConversation.Last().Role.SelectNextRoleForTemplate(),
@ -169,7 +181,7 @@ public partial class ChatTemplateDialog : MSGComponentBase
HideFromUser = true, HideFromUser = true,
Time = DateTimeOffset.Now, Time = DateTimeOffset.Now,
}; };
// The rest of the method remains the same: // The rest of the method remains the same:
var index = this.dataExampleConversation.IndexOf(currentItem); var index = this.dataExampleConversation.IndexOf(currentItem);
if (index >= 0) if (index >= 0)
@ -177,71 +189,83 @@ public partial class ChatTemplateDialog : MSGComponentBase
else else
this.dataExampleConversation.Add(insertedEntry); this.dataExampleConversation.Add(insertedEntry);
} }
private void BackupItem(object? element) private void BackupItem(object? element)
{ {
if (this.IsReadOnly)
return;
this.isInlineEditOnGoing = true; this.isInlineEditOnGoing = true;
this.messageEntryBeforeEdit = element switch this.messageEntryBeforeEdit = element switch
{ {
ContentBlock block => block.DeepClone(), ContentBlock block => block.DeepClone(),
_ => null, _ => null,
}; };
this.StateHasChanged(); this.StateHasChanged();
} }
private void ResetItem(object? element) private void ResetItem(object? element)
{ {
if (this.IsReadOnly)
return;
this.isInlineEditOnGoing = false; this.isInlineEditOnGoing = false;
switch (element) switch (element)
{ {
case ContentBlock block: case ContentBlock block:
if (this.messageEntryBeforeEdit is null) if (this.messageEntryBeforeEdit is null)
return; // No backup to restore from return; // No backup to restore from
block.Content = this.messageEntryBeforeEdit.Content?.DeepClone(); block.Content = this.messageEntryBeforeEdit.Content?.DeepClone();
block.Role = this.messageEntryBeforeEdit.Role; block.Role = this.messageEntryBeforeEdit.Role;
break; break;
} }
this.StateHasChanged(); this.StateHasChanged();
} }
private void CommitInlineEdit(object? element) private void CommitInlineEdit(object? element)
{ {
if (this.IsReadOnly)
return;
this.isInlineEditOnGoing = false; this.isInlineEditOnGoing = false;
this.StateHasChanged(); this.StateHasChanged();
} }
private async Task Store() private async Task Store()
{ {
if (this.IsReadOnly)
return;
await this.form.Validate(); await this.form.Validate();
// When the data is not valid, we don't store it: // When the data is not valid, we don't store it:
if (!this.dataIsValid) if (!this.dataIsValid)
return; return;
// When an inline edit is ongoing, we cannot store the data: // When an inline edit is ongoing, we cannot store the data:
if (this.isInlineEditOnGoing) if (this.isInlineEditOnGoing)
return; return;
// Use the data model to store the chat template. // Use the data model to store the chat template.
// We just return this data to the parent component: // We just return this data to the parent component:
var addedChatTemplateSettings = this.CreateChatTemplateSettings(); var addedChatTemplateSettings = this.CreateChatTemplateSettings();
if(this.IsEditing) if(this.IsEditing)
this.Logger.LogInformation($"Edited chat template '{addedChatTemplateSettings.Name}'."); this.Logger.LogInformation($"Edited chat template '{addedChatTemplateSettings.Name}'.");
else else
this.Logger.LogInformation($"Created chat template '{addedChatTemplateSettings.Name}'."); this.Logger.LogInformation($"Created chat template '{addedChatTemplateSettings.Name}'.");
this.MudDialog.Close(DialogResult.Ok(addedChatTemplateSettings)); this.MudDialog.Close(DialogResult.Ok(addedChatTemplateSettings));
} }
private string? ValidateExampleTextMessage(string message) private string? ValidateExampleTextMessage(string message)
{ {
if (string.IsNullOrWhiteSpace(message)) if (string.IsNullOrWhiteSpace(message))
return T("Please enter a message for the example conversation."); return T("Please enter a message for the example conversation.");
return null; return null;
} }
@ -249,20 +273,23 @@ public partial class ChatTemplateDialog : MSGComponentBase
{ {
if (string.IsNullOrWhiteSpace(name)) if (string.IsNullOrWhiteSpace(name))
return T("Please enter a name for the chat template."); return T("Please enter a name for the chat template.");
if (name.Length > 40) if (name.Length > 40)
return T("The chat template name must not exceed 40 characters."); return T("The chat template name must not exceed 40 characters.");
// The instance name must be unique: // The instance name must be unique:
var lowerName = name.ToLowerInvariant(); var lowerName = name.ToLowerInvariant();
if (lowerName != this.dataEditingPreviousName && this.UsedNames.Contains(lowerName)) if (lowerName != this.dataEditingPreviousName && this.UsedNames.Contains(lowerName))
return T("The chat template name must be unique; the chosen name is already in use."); return T("The chat template name must be unique; the chosen name is already in use.");
return null; return null;
} }
private void UseDefaultSystemPrompt() private void UseDefaultSystemPrompt()
{ {
if (this.IsReadOnly)
return;
this.DataSystemPrompt = SystemPrompts.DEFAULT; this.DataSystemPrompt = SystemPrompts.DEFAULT;
} }

View File

@ -27,6 +27,7 @@
AdornmentColor="Color.Info" AdornmentColor="Color.Info"
Validation="@this.ValidateName" Validation="@this.ValidateName"
Variant="Variant.Outlined" Variant="Variant.Outlined"
ReadOnly="@this.IsReadOnly"
UserAttributes="@SPELLCHECK_ATTRIBUTES" UserAttributes="@SPELLCHECK_ATTRIBUTES"
/> />
@ -44,8 +45,9 @@
MaxLines="12" MaxLines="12"
UserAttributes="@SPELLCHECK_ATTRIBUTES" UserAttributes="@SPELLCHECK_ATTRIBUTES"
HelperText="@T("Tell the AI something about yourself. What is your profession? How experienced are you in this profession? Which technologies do you like?")" HelperText="@T("Tell the AI something about yourself. What is your profession? How experienced are you in this profession? Which technologies do you like?")"
ReadOnly="@this.IsReadOnly"
/> />
<ReadFileContent @bind-FileContent="@this.DataNeedToKnow"/> <ReadFileContent @bind-FileContent="@this.DataNeedToKnow" Disabled="@this.IsReadOnly"/>
<MudTextField <MudTextField
T="string" T="string"
@ -62,8 +64,9 @@
Class="mt-10" Class="mt-10"
UserAttributes="@SPELLCHECK_ATTRIBUTES" UserAttributes="@SPELLCHECK_ATTRIBUTES"
HelperText="@T("Tell the AI what you want it to do for you. What are your goals or are you trying to achieve? Like having the AI address you informally.")" HelperText="@T("Tell the AI what you want it to do for you. What are your goals or are you trying to achieve? Like having the AI address you informally.")"
ReadOnly="@this.IsReadOnly"
/> />
<ReadFileContent @bind-FileContent="@this.DataActions"/> <ReadFileContent @bind-FileContent="@this.DataActions" Disabled="@this.IsReadOnly"/>
<MudJustifiedText Typo="Typo.body2" Class="mb-3 mt-3"> <MudJustifiedText Typo="Typo.body2" Class="mb-3 mt-3">
@T("Please be aware that your profile info becomes part of the system prompt. This means it uses up context space — the “memory” the LLM uses to understand and respond to your request. If your profile is extremely long, the LLM may struggle to focus on your actual task.") @T("Please be aware that your profile info becomes part of the system prompt. This means it uses up context space — the “memory” the LLM uses to understand and respond to your request. If your profile is extremely long, the LLM may struggle to focus on your actual task.")
@ -73,18 +76,27 @@
<Issues IssuesData="@this.dataIssues"/> <Issues IssuesData="@this.dataIssues"/>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled"> @if (this.IsReadOnly)
@T("Cancel") {
</MudButton> <MudButton OnClick="@this.Cancel" Variant="Variant.Filled">
<MudButton OnClick="@this.Store" Variant="Variant.Filled" Color="Color.Primary"> @T("Close")
@if(this.IsEditing) </MudButton>
{ }
@T("Update") else
} {
else <MudButton OnClick="@this.Cancel" Variant="Variant.Filled">
{ @T("Cancel")
@T("Add") </MudButton>
} <MudButton OnClick="@this.Store" Variant="Variant.Filled" Color="Color.Primary">
</MudButton> @if(this.IsEditing)
{
@T("Update")
}
else
{
@T("Add")
}
</MudButton>
}
</DialogActions> </DialogActions>
</MudDialog> </MudDialog>

View File

@ -15,19 +15,19 @@ public partial class ProfileDialog : MSGComponentBase
/// </summary> /// </summary>
[Parameter] [Parameter]
public uint DataNum { get; set; } public uint DataNum { get; set; }
/// <summary> /// <summary>
/// The profile's ID. /// The profile's ID.
/// </summary> /// </summary>
[Parameter] [Parameter]
public string DataId { get; set; } = Guid.NewGuid().ToString(); public string DataId { get; set; } = Guid.NewGuid().ToString();
/// <summary> /// <summary>
/// The profile name chosen by the user. /// The profile name chosen by the user.
/// </summary> /// </summary>
[Parameter] [Parameter]
public string DataName { get; set; } = string.Empty; public string DataName { get; set; } = string.Empty;
/// <summary> /// <summary>
/// What should the LLM know about you? /// What should the LLM know about you?
/// </summary> /// </summary>
@ -39,27 +39,30 @@ public partial class ProfileDialog : MSGComponentBase
/// </summary> /// </summary>
[Parameter] [Parameter]
public string DataActions { get; set; } = string.Empty; public string DataActions { get; set; } = string.Empty;
/// <summary> /// <summary>
/// Should the dialog be in editing mode? /// Should the dialog be in editing mode?
/// </summary> /// </summary>
[Parameter] [Parameter]
public bool IsEditing { get; init; } public bool IsEditing { get; init; }
[Parameter]
public bool IsReadOnly { get; init; }
[Inject] [Inject]
private ILogger<ProviderDialog> Logger { get; init; } = null!; private ILogger<ProviderDialog> Logger { get; init; } = null!;
private static readonly Dictionary<string, object?> SPELLCHECK_ATTRIBUTES = new(); private static readonly Dictionary<string, object?> SPELLCHECK_ATTRIBUTES = new();
/// <summary> /// <summary>
/// The list of used profile names. We need this to check for uniqueness. /// The list of used profile names. We need this to check for uniqueness.
/// </summary> /// </summary>
private List<string> UsedNames { get; set; } = []; private List<string> UsedNames { get; set; } = [];
private bool dataIsValid; private bool dataIsValid;
private string[] dataIssues = []; private string[] dataIssues = [];
private string dataEditingPreviousName = string.Empty; private string dataEditingPreviousName = string.Empty;
// We get the form reference from Blazor code to validate it manually: // We get the form reference from Blazor code to validate it manually:
private MudForm form = null!; private MudForm form = null!;
@ -70,7 +73,7 @@ public partial class ProfileDialog : MSGComponentBase
Name = this.DataName, Name = this.DataName,
NeedToKnow = this.DataNeedToKnow, NeedToKnow = this.DataNeedToKnow,
Actions = this.DataActions, Actions = this.DataActions,
EnterpriseConfigurationPluginId = Guid.Empty, EnterpriseConfigurationPluginId = Guid.Empty,
IsEnterpriseConfiguration = false, IsEnterpriseConfiguration = false,
}; };
@ -81,16 +84,16 @@ public partial class ProfileDialog : MSGComponentBase
{ {
// Configure the spellchecking for the instance name input: // Configure the spellchecking for the instance name input:
this.SettingsManager.InjectSpellchecking(SPELLCHECK_ATTRIBUTES); this.SettingsManager.InjectSpellchecking(SPELLCHECK_ATTRIBUTES);
// Load the used instance names: // Load the used instance names:
this.UsedNames = this.SettingsManager.ConfigurationData.Profiles.Select(x => x.Name.ToLowerInvariant()).ToList(); this.UsedNames = this.SettingsManager.ConfigurationData.Profiles.Select(x => x.Name.ToLowerInvariant()).ToList();
// When editing, we need to load the data: // When editing, we need to load the data:
if(this.IsEditing) if(this.IsEditing)
{ {
this.dataEditingPreviousName = this.DataName.ToLowerInvariant(); this.dataEditingPreviousName = this.DataName.ToLowerInvariant();
} }
await base.OnInitializedAsync(); await base.OnInitializedAsync();
} }
@ -100,37 +103,40 @@ public partial class ProfileDialog : MSGComponentBase
// We don't want to show validation errors when the user opens the dialog. // We don't want to show validation errors when the user opens the dialog.
if(!this.IsEditing && firstRender) if(!this.IsEditing && firstRender)
this.form.ResetValidation(); this.form.ResetValidation();
await base.OnAfterRenderAsync(firstRender); await base.OnAfterRenderAsync(firstRender);
} }
#endregion #endregion
private async Task Store() private async Task Store()
{ {
if (this.IsReadOnly)
return;
await this.form.Validate(); await this.form.Validate();
// When the data is not valid, we don't store it: // When the data is not valid, we don't store it:
if (!this.dataIsValid) if (!this.dataIsValid)
return; return;
// Use the data model to store the profile. // Use the data model to store the profile.
// We just return this data to the parent component: // We just return this data to the parent component:
var addedProfileSettings = this.CreateProfileSettings(); var addedProfileSettings = this.CreateProfileSettings();
if(this.IsEditing) if(this.IsEditing)
this.Logger.LogInformation($"Edited profile '{addedProfileSettings.Name}'."); this.Logger.LogInformation($"Edited profile '{addedProfileSettings.Name}'.");
else else
this.Logger.LogInformation($"Created profile '{addedProfileSettings.Name}'."); this.Logger.LogInformation($"Created profile '{addedProfileSettings.Name}'.");
this.MudDialog.Close(DialogResult.Ok(addedProfileSettings)); this.MudDialog.Close(DialogResult.Ok(addedProfileSettings));
} }
private string? ValidateNeedToKnow(string text) private string? ValidateNeedToKnow(string text)
{ {
if (string.IsNullOrWhiteSpace(this.DataNeedToKnow) && string.IsNullOrWhiteSpace(this.DataActions)) if (string.IsNullOrWhiteSpace(this.DataNeedToKnow) && string.IsNullOrWhiteSpace(this.DataActions))
return T("Please enter what the LLM should know about you and/or what actions it should take."); return T("Please enter what the LLM should know about you and/or what actions it should take.");
return null; return null;
} }
@ -138,7 +144,7 @@ public partial class ProfileDialog : MSGComponentBase
{ {
if (string.IsNullOrWhiteSpace(this.DataNeedToKnow) && string.IsNullOrWhiteSpace(this.DataActions)) if (string.IsNullOrWhiteSpace(this.DataNeedToKnow) && string.IsNullOrWhiteSpace(this.DataActions))
return T("Please enter what the LLM should know about you and/or what actions it should take."); return T("Please enter what the LLM should know about you and/or what actions it should take.");
return null; return null;
} }
@ -146,15 +152,15 @@ public partial class ProfileDialog : MSGComponentBase
{ {
if (string.IsNullOrWhiteSpace(name)) if (string.IsNullOrWhiteSpace(name))
return T("Please enter a profile name."); return T("Please enter a profile name.");
if (name.Length > 40) if (name.Length > 40)
return T("The profile name must not exceed 40 characters."); return T("The profile name must not exceed 40 characters.");
// The instance name must be unique: // The instance name must be unique:
var lowerName = name.ToLowerInvariant(); var lowerName = name.ToLowerInvariant();
if (lowerName != this.dataEditingPreviousName && this.UsedNames.Contains(lowerName)) if (lowerName != this.dataEditingPreviousName && this.UsedNames.Contains(lowerName))
return T("The profile name must be unique; the chosen name is already in use."); return T("The profile name must be unique; the chosen name is already in use.");
return null; return null;
} }

View File

@ -33,9 +33,14 @@
<MudTd> <MudTd>
@if (context.IsEnterpriseConfiguration) @if (context.IsEnterpriseConfiguration)
{ {
<MudTooltip Text="@T("This template is managed by your organization.")"> <MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Business" Disabled="true"/> <MudTooltip Text="@T("This template is managed by your organization.")">
</MudTooltip> <MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Business" Disabled="true"/>
</MudTooltip>
<MudTooltip Text="@T("View")">
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Visibility" OnClick="@(() => this.ViewChatTemplate(context))"/>
</MudTooltip>
</MudStack>
} }
else else
{ {

View File

@ -6,24 +6,24 @@ namespace AIStudio.Dialogs.Settings;
public partial class SettingsDialogChatTemplate : SettingsDialogBase public partial class SettingsDialogChatTemplate : SettingsDialogBase
{ {
[Parameter] [Parameter]
public bool CreateTemplateFromExistingChatThread { get; set; } public bool CreateTemplateFromExistingChatThread { get; set; }
[Parameter] [Parameter]
public ChatThread? ExistingChatThread { get; set; } public ChatThread? ExistingChatThread { get; set; }
#region Overrides of ComponentBase #region Overrides of ComponentBase
/// <inheritdoc /> /// <inheritdoc />
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
await base.OnInitializedAsync(); await base.OnInitializedAsync();
if (this.CreateTemplateFromExistingChatThread) if (this.CreateTemplateFromExistingChatThread)
await this.AddChatTemplate(); await this.AddChatTemplate();
} }
#endregion #endregion
private async Task AddChatTemplate() private async Task AddChatTemplate()
{ {
var dialogParameters = new DialogParameters<ChatTemplateDialog> var dialogParameters = new DialogParameters<ChatTemplateDialog>
@ -41,21 +41,21 @@ public partial class SettingsDialogChatTemplate : SettingsDialogBase
var dialogResult = await dialogReference.Result; var dialogResult = await dialogReference.Result;
if (dialogResult is null || dialogResult.Canceled) if (dialogResult is null || dialogResult.Canceled)
return; return;
var addedChatTemplate = (ChatTemplate)dialogResult.Data!; var addedChatTemplate = (ChatTemplate)dialogResult.Data!;
addedChatTemplate = addedChatTemplate with { Num = this.SettingsManager.ConfigurationData.NextChatTemplateNum++ }; addedChatTemplate = addedChatTemplate with { Num = this.SettingsManager.ConfigurationData.NextChatTemplateNum++ };
this.SettingsManager.ConfigurationData.ChatTemplates.Add(addedChatTemplate); this.SettingsManager.ConfigurationData.ChatTemplates.Add(addedChatTemplate);
await this.SettingsManager.StoreSettings(); await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED); await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
} }
private async Task EditChatTemplate(ChatTemplate chatTemplate) private async Task EditChatTemplate(ChatTemplate chatTemplate)
{ {
if (chatTemplate == ChatTemplate.NO_CHAT_TEMPLATE || chatTemplate.IsEnterpriseConfiguration) if (chatTemplate == ChatTemplate.NO_CHAT_TEMPLATE || chatTemplate.IsEnterpriseConfiguration)
return; return;
var dialogParameters = new DialogParameters<ChatTemplateDialog> var dialogParameters = new DialogParameters<ChatTemplateDialog>
{ {
{ x => x.DataNum, chatTemplate.Num }, { x => x.DataNum, chatTemplate.Num },
@ -68,34 +68,53 @@ public partial class SettingsDialogChatTemplate : SettingsDialogBase
{ x => x.FileAttachments, chatTemplate.FileAttachments }, { x => x.FileAttachments, chatTemplate.FileAttachments },
{ x => x.AllowProfileUsage, chatTemplate.AllowProfileUsage }, { x => x.AllowProfileUsage, chatTemplate.AllowProfileUsage },
}; };
var dialogReference = await this.DialogService.ShowAsync<ChatTemplateDialog>(T("Edit Chat Template"), dialogParameters, DialogOptions.FULLSCREEN); var dialogReference = await this.DialogService.ShowAsync<ChatTemplateDialog>(T("Edit Chat Template"), dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result; var dialogResult = await dialogReference.Result;
if (dialogResult is null || dialogResult.Canceled) if (dialogResult is null || dialogResult.Canceled)
return; return;
var editedChatTemplate = (ChatTemplate)dialogResult.Data!; var editedChatTemplate = (ChatTemplate)dialogResult.Data!;
this.SettingsManager.ConfigurationData.ChatTemplates[this.SettingsManager.ConfigurationData.ChatTemplates.IndexOf(chatTemplate)] = editedChatTemplate; this.SettingsManager.ConfigurationData.ChatTemplates[this.SettingsManager.ConfigurationData.ChatTemplates.IndexOf(chatTemplate)] = editedChatTemplate;
await this.SettingsManager.StoreSettings(); await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED); await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
} }
private async Task ViewChatTemplate(ChatTemplate chatTemplate)
{
var dialogParameters = new DialogParameters<ChatTemplateDialog>
{
{ x => x.DataNum, chatTemplate.Num },
{ x => x.DataId, chatTemplate.Id },
{ x => x.DataName, chatTemplate.Name },
{ x => x.DataSystemPrompt, chatTemplate.SystemPrompt },
{ x => x.PredefinedUserPrompt, chatTemplate.PredefinedUserPrompt },
{ x => x.IsEditing, true },
{ x => x.IsReadOnly, true },
{ x => x.ExampleConversation, chatTemplate.ExampleConversation },
{ x => x.FileAttachments, chatTemplate.FileAttachments },
{ x => x.AllowProfileUsage, chatTemplate.AllowProfileUsage },
};
await this.DialogService.ShowAsync<ChatTemplateDialog>(T("View Chat Template"), dialogParameters, DialogOptions.FULLSCREEN);
}
private async Task DeleteChatTemplate(ChatTemplate chatTemplate) private async Task DeleteChatTemplate(ChatTemplate chatTemplate)
{ {
var dialogParameters = new DialogParameters<ConfirmDialog> var dialogParameters = new DialogParameters<ConfirmDialog>
{ {
{ x => x.Message, string.Format(T("Are you sure you want to delete the chat template '{0}'?"), chatTemplate.Name) }, { x => x.Message, string.Format(T("Are you sure you want to delete the chat template '{0}'?"), chatTemplate.Name) },
}; };
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Chat Template"), dialogParameters, DialogOptions.FULLSCREEN); var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Chat Template"), dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result; var dialogResult = await dialogReference.Result;
if (dialogResult is null || dialogResult.Canceled) if (dialogResult is null || dialogResult.Canceled)
return; return;
this.SettingsManager.ConfigurationData.ChatTemplates.Remove(chatTemplate); this.SettingsManager.ConfigurationData.ChatTemplates.Remove(chatTemplate);
await this.SettingsManager.StoreSettings(); await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED); await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
} }

View File

@ -32,9 +32,14 @@
<MudTd> <MudTd>
@if (context.IsEnterpriseConfiguration) @if (context.IsEnterpriseConfiguration)
{ {
<MudTooltip Text="@T("This profile is managed by your organization.")"> <MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Business" Disabled="true"/> <MudTooltip Text="@T("This profile is managed by your organization.")">
</MudTooltip> <MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Business" Disabled="true"/>
</MudTooltip>
<MudTooltip Text="@T("View")">
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Visibility" OnClick="() => this.ViewProfile(context)"/>
</MudTooltip>
</MudStack>
} }
else else
{ {

View File

@ -10,21 +10,21 @@ public partial class SettingsDialogProfiles : SettingsDialogBase
{ {
{ x => x.IsEditing, false }, { x => x.IsEditing, false },
}; };
var dialogReference = await this.DialogService.ShowAsync<ProfileDialog>(T("Add Profile"), dialogParameters, DialogOptions.FULLSCREEN); var dialogReference = await this.DialogService.ShowAsync<ProfileDialog>(T("Add Profile"), dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result; var dialogResult = await dialogReference.Result;
if (dialogResult is null || dialogResult.Canceled) if (dialogResult is null || dialogResult.Canceled)
return; return;
var addedProfile = (Profile)dialogResult.Data!; var addedProfile = (Profile)dialogResult.Data!;
addedProfile = addedProfile with { Num = this.SettingsManager.ConfigurationData.NextProfileNum++ }; addedProfile = addedProfile with { Num = this.SettingsManager.ConfigurationData.NextProfileNum++ };
this.SettingsManager.ConfigurationData.Profiles.Add(addedProfile); this.SettingsManager.ConfigurationData.Profiles.Add(addedProfile);
await this.SettingsManager.StoreSettings(); await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED); await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
} }
private async Task EditProfile(Profile profile) private async Task EditProfile(Profile profile)
{ {
var dialogParameters = new DialogParameters<ProfileDialog> var dialogParameters = new DialogParameters<ProfileDialog>
@ -36,19 +36,35 @@ public partial class SettingsDialogProfiles : SettingsDialogBase
{ x => x.DataActions, profile.Actions }, { x => x.DataActions, profile.Actions },
{ x => x.IsEditing, true }, { x => x.IsEditing, true },
}; };
var dialogReference = await this.DialogService.ShowAsync<ProfileDialog>(T("Edit Profile"), dialogParameters, DialogOptions.FULLSCREEN); var dialogReference = await this.DialogService.ShowAsync<ProfileDialog>(T("Edit Profile"), dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result; var dialogResult = await dialogReference.Result;
if (dialogResult is null || dialogResult.Canceled) if (dialogResult is null || dialogResult.Canceled)
return; return;
var editedProfile = (Profile)dialogResult.Data!; var editedProfile = (Profile)dialogResult.Data!;
this.SettingsManager.ConfigurationData.Profiles[this.SettingsManager.ConfigurationData.Profiles.IndexOf(profile)] = editedProfile; this.SettingsManager.ConfigurationData.Profiles[this.SettingsManager.ConfigurationData.Profiles.IndexOf(profile)] = editedProfile;
await this.SettingsManager.StoreSettings(); await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED); await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
} }
private async Task ViewProfile(Profile profile)
{
var dialogParameters = new DialogParameters<ProfileDialog>
{
{ x => x.DataNum, profile.Num },
{ x => x.DataId, profile.Id },
{ x => x.DataName, profile.Name },
{ x => x.DataNeedToKnow, profile.NeedToKnow },
{ x => x.DataActions, profile.Actions },
{ x => x.IsEditing, true },
{ x => x.IsReadOnly, true },
};
await this.DialogService.ShowAsync<ProfileDialog>(T("View Profile"), dialogParameters, DialogOptions.FULLSCREEN);
}
private async Task ExportProfile(Profile profile) private async Task ExportProfile(Profile profile)
{ {
if (!this.SettingsManager.ConfigurationData.App.ShowAdminSettings) if (!this.SettingsManager.ConfigurationData.App.ShowAdminSettings)
@ -68,15 +84,15 @@ public partial class SettingsDialogProfiles : SettingsDialogBase
{ {
{ x => x.Message, string.Format(T("Are you sure you want to delete the profile '{0}'?"), profile.Name) }, { x => x.Message, string.Format(T("Are you sure you want to delete the profile '{0}'?"), profile.Name) },
}; };
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Profile"), dialogParameters, DialogOptions.FULLSCREEN); var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Profile"), dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result; var dialogResult = await dialogReference.Result;
if (dialogResult is null || dialogResult.Canceled) if (dialogResult is null || dialogResult.Canceled)
return; return;
this.SettingsManager.ConfigurationData.Profiles.Remove(profile); this.SettingsManager.ConfigurationData.Profiles.Remove(profile);
await this.SettingsManager.StoreSettings(); await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED); await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
} }
} }

View File

@ -48,6 +48,15 @@ LANG_NAME = "Deutsch (Deutschland)"
UI_TEXT_CONTENT = {} UI_TEXT_CONTENT = {}
-- Self-hosted
UI_TEXT_CONTENT["::LLMPROVIDERSEXTENSIONS::T146444217"] = "Selbst gehostet"
-- No provider selected
UI_TEXT_CONTENT["::LLMPROVIDERSEXTENSIONS::T2897045472"] = "Kein Anbieter ausgewählt"
-- Unknown
UI_TEXT_CONTENT["::LLMPROVIDERSEXTENSIONS::T3424652889"] = "Unbekannt"
-- No audit provider is configured. -- No audit provider is configured.
UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITAGENT::T2034826200"] = "Es ist kein Audit-Anbieter konfiguriert." UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITAGENT::T2034826200"] = "Es ist kein Audit-Anbieter konfiguriert."
@ -3516,6 +3525,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3227981830"] = "Die gle
-- Add a message -- Add a message
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3372872324"] = "Nachricht hinzufügen" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3372872324"] = "Nachricht hinzufügen"
-- Close
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3448155331"] = "Schließen"
-- Unsupported content type -- Unsupported content type
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3570316759"] = "Nicht unterstützter Inhaltstyp" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3570316759"] = "Nicht unterstützter Inhaltstyp"
@ -4296,6 +4308,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3243902394"] = "Der Profilna
-- Profile Name -- Profile Name
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3392578705"] = "Profilname" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3392578705"] = "Profilname"
-- Close
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3448155331"] = "Schließen"
-- Please enter what the LLM should know about you and/or what actions it should take. -- Please enter what the LLM should know about you and/or what actions it should take.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3708405102"] = "Bitte geben Sie ein, was das LLM über Sie wissen sollte und/oder welche Aktionen es ausführen soll." UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3708405102"] = "Bitte geben Sie ein, was das LLM über Sie wissen sollte und/oder welche Aktionen es ausführen soll."
@ -4815,6 +4830,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T14695
-- Add Chat Template -- Add Chat Template
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T1548314416"] = "Chat-Vorlage hinzufügen" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T1548314416"] = "Chat-Vorlage hinzufügen"
-- View
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T1582017048"] = "Anzeigen"
-- Note: This advanced feature is designed for users familiar with prompt engineering concepts. Furthermore, you have to make sure yourself that your chosen provider supports the use of assistant prompts. -- Note: This advanced feature is designed for users familiar with prompt engineering concepts. Furthermore, you have to make sure yourself that your chosen provider supports the use of assistant prompts.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T1909110760"] = "Hinweis: Diese fortgeschrittene Funktion richtet sich an Nutzer, die mit den Grundlagen des Prompt Engineerings vertraut sind. Außerdem müssen Sie selbst sicherstellen, dass Ihr gewählter Anbieter die Verwendung von Assistenten-Prompts unterstützt." UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T1909110760"] = "Hinweis: Diese fortgeschrittene Funktion richtet sich an Nutzer, die mit den Grundlagen des Prompt Engineerings vertraut sind. Außerdem müssen Sie selbst sicherstellen, dass Ihr gewählter Anbieter die Verwendung von Assistenten-Prompts unterstützt."
@ -4854,6 +4872,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T38650
-- Delete Chat Template -- Delete Chat Template
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T4025180906"] = "Chat-Vorlage löschen" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T4025180906"] = "Chat-Vorlage löschen"
-- View Chat Template
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T4042112076"] = "Chat-Vorlage anzeigen"
-- Export Chat Template -- Export Chat Template
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T491504763"] = "Chat-Vorlage exportieren" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T491504763"] = "Chat-Vorlage exportieren"
@ -5262,6 +5283,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T143353473
-- Delete -- Delete
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T1469573738"] = "Löschen" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T1469573738"] = "Löschen"
-- View
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T1582017048"] = "Anzeigen"
-- Your Profiles -- Your Profiles
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T2378610256"] = "Ihre Profile" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T2378610256"] = "Ihre Profile"
@ -5286,6 +5310,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T405841465
-- Store personal data about yourself in various profiles so that the AIs know your personal context. This saves you from having to explain your context each time, for example, in every chat. When you have different roles, you can create a profile for each role. -- Store personal data about yourself in various profiles so that the AIs know your personal context. This saves you from having to explain your context each time, for example, in every chat. When you have different roles, you can create a profile for each role.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4125557797"] = "Speichern Sie persönliche Daten über sich in verschiedenen Profilen, damit die KIs ihren persönlichen Kontext kennen. So müssen Sie den Kontext nicht jedes Mal erneut erklären, zum Beispiel in jedem Chat. Wenn Sie verschiedene Rollen haben, können Sie für jede Rolle ein eigenes Profil anlegen." UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4125557797"] = "Speichern Sie persönliche Daten über sich in verschiedenen Profilen, damit die KIs ihren persönlichen Kontext kennen. So müssen Sie den Kontext nicht jedes Mal erneut erklären, zum Beispiel in jedem Chat. Wenn Sie verschiedene Rollen haben, können Sie für jede Rolle ein eigenes Profil anlegen."
-- View Profile
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4219233997"] = "Profil anzeigen"
-- Add Profile -- Add Profile
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4248067241"] = "Profil hinzufügen" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4248067241"] = "Profil hinzufügen"
@ -6723,15 +6750,6 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::CONFIDENCELEVELEXTENSIONS::T3188327965"] =
-- Very Low -- Very Low
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::CONFIDENCELEVELEXTENSIONS::T786675843"] = "Sehr niedrig" UI_TEXT_CONTENT["AISTUDIO::PROVIDER::CONFIDENCELEVELEXTENSIONS::T786675843"] = "Sehr niedrig"
-- Self-hosted
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T146444217"] = "Selbst gehostet"
-- No provider selected
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T2897045472"] = "Kein Anbieter ausgewählt"
-- Unknown
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T3424652889"] = "Unbekannt"
-- no model selected -- no model selected
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::MODEL::T2234274832"] = "Kein Modell ausgewählt" UI_TEXT_CONTENT["AISTUDIO::PROVIDER::MODEL::T2234274832"] = "Kein Modell ausgewählt"
@ -7872,6 +7890,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PANDOCAVAILABILITYSERVICE::T25964655
-- Failed to store the secret data due to an API issue. -- Failed to store the secret data due to an API issue.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::T1110203516"] = "Fehler beim Speichern der geheimen Daten aufgrund eines API-Problems." UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::T1110203516"] = "Fehler beim Speichern der geheimen Daten aufgrund eines API-Problems."
-- Failed to store the API key due to an API issue.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::T1704298921"] = "Fehler beim Speichern des API-Schlüssels aufgrund eines API-Problems."
-- Failed to delete the secret data due to an API issue. -- Failed to delete the secret data due to an API issue.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::T2303057928"] = "Das Löschen der geheimen Daten ist aufgrund eines API-Problems fehlgeschlagen." UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::T2303057928"] = "Das Löschen der geheimen Daten ist aufgrund eines API-Problems fehlgeschlagen."

View File

@ -48,6 +48,15 @@ LANG_NAME = "English (United States)"
UI_TEXT_CONTENT = {} UI_TEXT_CONTENT = {}
-- Self-hosted
UI_TEXT_CONTENT["::LLMPROVIDERSEXTENSIONS::T146444217"] = "Self-hosted"
-- No provider selected
UI_TEXT_CONTENT["::LLMPROVIDERSEXTENSIONS::T2897045472"] = "No provider selected"
-- Unknown
UI_TEXT_CONTENT["::LLMPROVIDERSEXTENSIONS::T3424652889"] = "Unknown"
-- No audit provider is configured. -- No audit provider is configured.
UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITAGENT::T2034826200"] = "No audit provider is configured." UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITAGENT::T2034826200"] = "No audit provider is configured."
@ -3516,6 +3525,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3227981830"] = "Using s
-- Add a message -- Add a message
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3372872324"] = "Add a message" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3372872324"] = "Add a message"
-- Close
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3448155331"] = "Close"
-- Unsupported content type -- Unsupported content type
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3570316759"] = "Unsupported content type" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3570316759"] = "Unsupported content type"
@ -4296,6 +4308,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3243902394"] = "The profile
-- Profile Name -- Profile Name
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3392578705"] = "Profile Name" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3392578705"] = "Profile Name"
-- Close
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3448155331"] = "Close"
-- Please enter what the LLM should know about you and/or what actions it should take. -- Please enter what the LLM should know about you and/or what actions it should take.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3708405102"] = "Please enter what the LLM should know about you and/or what actions it should take." UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3708405102"] = "Please enter what the LLM should know about you and/or what actions it should take."
@ -4815,6 +4830,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T14695
-- Add Chat Template -- Add Chat Template
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T1548314416"] = "Add Chat Template" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T1548314416"] = "Add Chat Template"
-- View
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T1582017048"] = "View"
-- Note: This advanced feature is designed for users familiar with prompt engineering concepts. Furthermore, you have to make sure yourself that your chosen provider supports the use of assistant prompts. -- Note: This advanced feature is designed for users familiar with prompt engineering concepts. Furthermore, you have to make sure yourself that your chosen provider supports the use of assistant prompts.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T1909110760"] = "Note: This advanced feature is designed for users familiar with prompt engineering concepts. Furthermore, you have to make sure yourself that your chosen provider supports the use of assistant prompts." UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T1909110760"] = "Note: This advanced feature is designed for users familiar with prompt engineering concepts. Furthermore, you have to make sure yourself that your chosen provider supports the use of assistant prompts."
@ -4854,6 +4872,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T38650
-- Delete Chat Template -- Delete Chat Template
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T4025180906"] = "Delete Chat Template" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T4025180906"] = "Delete Chat Template"
-- View Chat Template
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T4042112076"] = "View Chat Template"
-- Export Chat Template -- Export Chat Template
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T491504763"] = "Export Chat Template" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T491504763"] = "Export Chat Template"
@ -5262,6 +5283,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T143353473
-- Delete -- Delete
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T1469573738"] = "Delete" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T1469573738"] = "Delete"
-- View
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T1582017048"] = "View"
-- Your Profiles -- Your Profiles
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T2378610256"] = "Your Profiles" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T2378610256"] = "Your Profiles"
@ -5286,6 +5310,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T405841465
-- Store personal data about yourself in various profiles so that the AIs know your personal context. This saves you from having to explain your context each time, for example, in every chat. When you have different roles, you can create a profile for each role. -- Store personal data about yourself in various profiles so that the AIs know your personal context. This saves you from having to explain your context each time, for example, in every chat. When you have different roles, you can create a profile for each role.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4125557797"] = "Store personal data about yourself in various profiles so that the AIs know your personal context. This saves you from having to explain your context each time, for example, in every chat. When you have different roles, you can create a profile for each role." UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4125557797"] = "Store personal data about yourself in various profiles so that the AIs know your personal context. This saves you from having to explain your context each time, for example, in every chat. When you have different roles, you can create a profile for each role."
-- View Profile
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4219233997"] = "View Profile"
-- Add Profile -- Add Profile
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4248067241"] = "Add Profile" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4248067241"] = "Add Profile"
@ -6723,15 +6750,6 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::CONFIDENCELEVELEXTENSIONS::T3188327965"] =
-- Very Low -- Very Low
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::CONFIDENCELEVELEXTENSIONS::T786675843"] = "Very Low" UI_TEXT_CONTENT["AISTUDIO::PROVIDER::CONFIDENCELEVELEXTENSIONS::T786675843"] = "Very Low"
-- Self-hosted
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T146444217"] = "Self-hosted"
-- No provider selected
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T2897045472"] = "No provider selected"
-- Unknown
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T3424652889"] = "Unknown"
-- no model selected -- no model selected
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::MODEL::T2234274832"] = "no model selected" UI_TEXT_CONTENT["AISTUDIO::PROVIDER::MODEL::T2234274832"] = "no model selected"
@ -7872,6 +7890,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PANDOCAVAILABILITYSERVICE::T25964655
-- Failed to store the secret data due to an API issue. -- Failed to store the secret data due to an API issue.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::T1110203516"] = "Failed to store the secret data due to an API issue." UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::T1110203516"] = "Failed to store the secret data due to an API issue."
-- Failed to store the API key due to an API issue.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::T1704298921"] = "Failed to store the API key due to an API issue."
-- Failed to delete the secret data due to an API issue. -- Failed to delete the secret data due to an API issue.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::T2303057928"] = "Failed to delete the secret data due to an API issue." UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::T2303057928"] = "Failed to delete the secret data due to an API issue."

View File

@ -1,2 +1,4 @@
# v26.6.2, build 242 (2026-06-xx xx:xx UTC) # v26.6.2, build 242 (2026-06-xx xx:xx UTC)
- Added a read-only view for organization-managed profiles and chat templates, so users can inspect the content while the organization remains in control of changes.
- Fixed organization-managed chat templates not showing the correct icon in the chat template selection menu.
- Fixed self-hosted provider API keys sometimes being stored under a localized name. AI Studio now uses a stable key name, keeps correct entries working, and automatically migrates known localized entries for LLM, transcription, and embedding providers. Organizations using configuration plugins do not need to change their plugins; affected users who still see an invalid API key warning should open the provider, transcription, or embedding settings and update the API key once. - Fixed self-hosted provider API keys sometimes being stored under a localized name. AI Studio now uses a stable key name, keeps correct entries working, and automatically migrates known localized entries for LLM, transcription, and embedding providers. Organizations using configuration plugins do not need to change their plugins; affected users who still see an invalid API key warning should open the provider, transcription, or embedding settings and update the API key once.