Improved the security of the connection between the app window and AI Studio (#1146)

This commit is contained in:
Thorsten Sommer authored and GitHub committed 2026-11-01 12:29:37 +01:00
1 parent 64dc621a7d
commit faae2951f7
26 files changed
+1056 -88

No files matched your search

@@ -116,8 +116,8 @@ else
</MudSelectItem>
}
</MudSelect>
<MudButton Variant="Variant.Outlined" Size="Size.Small" Disabled="@this.IsSpecificationDownloadDisabled" Href="@this.selectedERIVersion.SpecificationURL()" Target="_blank">
<MudIcon Icon="@Icons.Material.Filled.Link" Class="mr-2"/> @T("Download specification")
<MudButton Variant="Variant.Outlined" Size="Size.Small" Disabled="@this.IsSpecificationSavingDisabled" OnClick="@this.SaveSpecificationAsync">
<MudIcon Icon="@Icons.Material.Filled.Save" Class="mr-2"/> @T("Save specification")
</MudButton>
</MudStack>
@@ -3,6 +3,7 @@ using AIStudio.Dialogs;
using AIStudio.Dialogs.Settings;
using AIStudio.Settings.DataModel;
using AIStudio.Tools.AssistantSessions;
using AIStudio.Tools.Rust;
using Microsoft.AspNetCore.Components;
@@ -13,7 +14,7 @@ namespace AIStudio.Assistants.ERI;
public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
{
[Inject]
private HttpClient HttpClient { get; set; } = null!;
private WebRootFiles WebRootFiles { get; set; } = null!;
[Inject]
private IDialogService DialogService { get; init; } = null!;
@@ -375,9 +376,43 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
private bool IsERIInputDisabled => this.IsNoneERIServerSelected || this.IsProcessing;
/// <summary>
/// Gets whether the selected ERI specification cannot be downloaded.
/// Gets whether the selected ERI specification cannot be saved.
/// </summary>
private bool IsSpecificationDownloadDisabled => !this.selectedERIVersion.WasSpecificationSelected() || this.IsERIInputDisabled;
private bool IsSpecificationSavingDisabled => !this.selectedERIVersion.WasSpecificationSelected() || this.IsERIInputDisabled;
/// <summary>
/// Lets the user save the selected ERI specification, a copy of the one the app carries.
/// </summary>
/// <remarks>
/// The app server answers the app window only, so a link to the file would fail in the browser
/// of the system. The copy also matches the installed version of AI Studio.
/// </remarks>
private async Task SaveSpecificationAsync()
{
var specificationPath = this.selectedERIVersion.SpecificationURL();
var response = await this.RustService.SaveFile(T("Save the ERI specification"), [FileTypes.JSON], Path.GetFileName(specificationPath));
if (response.UserCancelled)
return;
await using var feedback = new FileExportFeedback();
feedback.ShowRunning(response.SaveFilePath);
try
{
if (await this.WebRootFiles.CopyToAsync(specificationPath, response.SaveFilePath))
{
await feedback.SucceededAsync(Icons.Material.Filled.CheckCircle, T("The ERI specification was saved."), response.SaveFilePath, true);
return;
}
this.Logger.LogError("The app carries no ERI specification at '{SpecificationPath}'.", specificationPath);
}
catch (Exception exception)
{
this.Logger.LogError(exception, "Failed to save the ERI specification {ERIVersion}.", this.selectedERIVersion);
}
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Cancel, T("The ERI specification could not be saved.")));
}
/// <summary>
/// Gets whether the generated-code target directory selection should be disabled.
@@ -843,7 +878,7 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
var baseDirectorySnapshot = this.baseDirectory;
var previouslyGeneratedFilesSnapshot = this.previouslyGeneratedFiles.ToArray();
this.eriSpecification = await this.selectedERIVersion.ReadSpecification(this.HttpClient);
this.eriSpecification = await this.selectedERIVersion.ReadSpecification(this.WebRootFiles);
if (string.IsNullOrWhiteSpace(this.eriSpecification))
{
this.AddInputIssue(T("The ERI specification could not be loaded. Please try again later."));
@@ -2,26 +2,14 @@ namespace AIStudio.Assistants.ERI;
public static class ERIVersionExtensions
{
public static async Task<string> ReadSpecification(this ERIVersion version, HttpClient httpClient)
{
try
{
var url = version.SpecificationURL();
using var response = await httpClient.GetAsync(url);
return await response.Content.ReadAsStringAsync();
}
catch
{
return string.Empty;
}
}
public static Task<string> ReadSpecification(this ERIVersion version, WebRootFiles webRootFiles) => webRootFiles.ReadTextAsync(version.SpecificationURL());
public static string SpecificationURL(this ERIVersion version)
{
var nameLower = version.ToString().ToLowerInvariant();
var filename = $"{nameLower}.json";
return $"specs/eri/{filename}";
}
public static bool WasSpecificationSelected(this ERIVersion version) => version != ERIVersion.NONE;
}
@@ -1537,12 +1537,21 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ERI::ASSISTANTERI::T2205711729"] = "Opera
-- You can integrate additional libraries. Perhaps you want to evaluate the prompts in advance using a machine learning method or analyze them with a text mining approach? Or maybe you want to preprocess images in the prompts? For such advanced scenarios, you can specify which libraries you want to use here. It's best to describe which library you want to integrate for which purpose. This way, the LLM that writes the ERI server for you can try to use these libraries effectively. This should result in less rework being necessary. If you don't know the necessary libraries, you can instead attempt to describe the intended use. The LLM can then attempt to choose suitable libraries. However, hallucinations can occur, and fictional libraries might be selected.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ERI::ASSISTANTERI::T2241311898"] = "You can integrate additional libraries. Perhaps you want to evaluate the prompts in advance using a machine learning method or analyze them with a text mining approach? Or maybe you want to preprocess images in the prompts? For such advanced scenarios, you can specify which libraries you want to use here. It's best to describe which library you want to integrate for which purpose. This way, the LLM that writes the ERI server for you can try to use these libraries effectively. This should result in less rework being necessary. If you don't know the necessary libraries, you can instead attempt to describe the intended use. The LLM can then attempt to choose suitable libraries. However, hallucinations can occur, and fictional libraries might be selected."
-- The ERI specification was saved.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ERI::ASSISTANTERI::T2258857117"] = "The ERI specification was saved."
-- Save the ERI specification
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ERI::ASSISTANTERI::T2260814268"] = "Save the ERI specification"
-- The ERI specification could not be loaded. Please try again later.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ERI::ASSISTANTERI::T2265640084"] = "The ERI specification could not be loaded. Please try again later."
-- You can imagine it like this: Hypothetically, when Wikipedia implemented the ERI, it would vectorize all pages using an embedding method. All of Wikipedia’s data would remain with Wikipedia, including the vector database (decentralized approach). Then, any AI Studio user could add Wikipedia as a data source to significantly reduce the hallucination of the LLM in knowledge questions.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ERI::ASSISTANTERI::T2299793803"] = "You can imagine it like this: Hypothetically, when Wikipedia implemented the ERI, it would vectorize all pages using an embedding method. All of Wikipedia’s data would remain with Wikipedia, including the vector database (decentralized approach). Then, any AI Studio user could add Wikipedia as a data source to significantly reduce the hallucination of the LLM in knowledge questions."
-- The ERI specification could not be saved.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ERI::ASSISTANTERI::T2361742875"] = "The ERI specification could not be saved."
-- Are you sure you want to delete the ERI server preset '{0}'?
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ERI::ASSISTANTERI::T2417944396"] = "Are you sure you want to delete the ERI server preset '{0}'?"
@@ -1672,9 +1681,6 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ERI::ASSISTANTERI::T3709185824"] = "Yes,
-- Common ERI server settings
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ERI::ASSISTANTERI::T371718034"] = "Common ERI server settings"
-- Download specification
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ERI::ASSISTANTERI::T3770339156"] = "Download specification"
-- Are you sure you want to delete the embedding '{0}'?
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ERI::ASSISTANTERI::T3792084967"] = "Are you sure you want to delete the embedding '{0}'?"
@@ -1741,6 +1747,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ERI::ASSISTANTERI::T746826190"] = "ERI se
-- Are you sure you want to delete the retrieval process '{0}'?
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ERI::ASSISTANTERI::T748762927"] = "Are you sure you want to delete the retrieval process '{0}'?"
-- Save specification
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ERI::ASSISTANTERI::T756799053"] = "Save specification"
-- Data source: hostname
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ERI::ASSISTANTERI::T761552889"] = "Data source: hostname"