mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-22 19:35:00 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e973b51f75 | ||
|
|
c9474c55b7 | ||
|
|
c0b7016c3a | ||
|
|
78315195cb | ||
|
|
6040e88dad | ||
|
|
ad3d8cbb3a | ||
|
|
c836bd7f33 |
No files matched your search
@@ -12,7 +12,7 @@ Things we are currently working on:
|
||||
- [x] ~~App: Add an option to show preview features (PR [#222](https://github.com/MindWorkAI/AI-Studio/pull/222))~~
|
||||
- [x] ~~App: Configure embedding providers (PR [#224](https://github.com/MindWorkAI/AI-Studio/pull/224))~~
|
||||
- [x] ~~App: Implement an [ERI](https://github.com/MindWorkAI/ERI) server coding assistant (PR [#231](https://github.com/MindWorkAI/AI-Studio/pull/231))~~
|
||||
- [x] ~~App: Management of data sources (local & external data via [ERI](https://github.com/MindWorkAI/ERI)) (PR [#259](https://github.com/MindWorkAI/AI-Studio/pull/259))~~
|
||||
- [x] ~~App: Management of data sources (local & external data via [ERI](https://github.com/MindWorkAI/ERI)) (PR [#259](https://github.com/MindWorkAI/AI-Studio/pull/259), [#273](https://github.com/MindWorkAI/AI-Studio/pull/273))~~
|
||||
- [ ] Runtime: Extract data from txt / md / pdf / docx / xlsx files
|
||||
- [ ] (*Optional*) Runtime: Implement internal embedding provider through [fastembed-rs](https://github.com/Anush008/fastembed-rs)
|
||||
- [ ] App: Implement external embedding providers
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,10 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -2,10 +2,6 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MindWork AI Studio", "MindWork AI Studio\MindWork AI Studio.csproj", "{059FDFCC-7D0B-474E-9F20-B9C437DF1CDD}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ERIClients", "ERIClients", "{5C2AF789-287B-4FCB-B675-7273D8CD4579}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ERIClientV1", "ERIClientV1\ERIClientV1.csproj", "{9E35A273-0FA6-4BD5-8880-A1DDAC106926}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -16,12 +12,7 @@ Global
|
||||
{059FDFCC-7D0B-474E-9F20-B9C437DF1CDD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{059FDFCC-7D0B-474E-9F20-B9C437DF1CDD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{059FDFCC-7D0B-474E-9F20-B9C437DF1CDD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9E35A273-0FA6-4BD5-8880-A1DDAC106926}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9E35A273-0FA6-4BD5-8880-A1DDAC106926}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9E35A273-0FA6-4BD5-8880-A1DDAC106926}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9E35A273-0FA6-4BD5-8880-A1DDAC106926}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{9E35A273-0FA6-4BD5-8880-A1DDAC106926} = {5C2AF789-287B-4FCB-B675-7273D8CD4579}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -7,7 +7,7 @@ public static class ERIVersionExtensions
|
||||
try
|
||||
{
|
||||
var url = version.SpecificationURL();
|
||||
var response = await httpClient.GetAsync(url);
|
||||
using var response = await httpClient.GetAsync(url);
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
catch
|
||||
|
||||
@@ -13,6 +13,7 @@ public partial class Changelog
|
||||
|
||||
public static readonly Log[] LOGS =
|
||||
[
|
||||
new (203, "v0.9.28, build 203 (2025-02-09 16:33 UTC)", "v0.9.28.md"),
|
||||
new (202, "v0.9.27, build 202 (2025-01-21 18:24 UTC)", "v0.9.27.md"),
|
||||
new (201, "v0.9.26, build 201 (2025-01-13 19:11 UTC)", "v0.9.26.md"),
|
||||
new (200, "v0.9.25, build 200 (2025-01-04 18:33 UTC)", "v0.9.25.md"),
|
||||
|
||||
@@ -23,7 +23,7 @@ public partial class Changelog : ComponentBase
|
||||
|
||||
private async Task ReadLogAsync()
|
||||
{
|
||||
var response = await this.HttpClient.GetAsync($"changelog/{this.SelectedLog.Filename}");
|
||||
using var response = await this.HttpClient.GetAsync($"changelog/{this.SelectedLog.Filename}");
|
||||
this.LogContent = await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,9 @@ public partial class ConfigurationProviderSelection : ComponentBase, IMessageBus
|
||||
|
||||
private IEnumerable<ConfigurationSelectData<string>> FilteredData()
|
||||
{
|
||||
if(this.Component is not Tools.Components.NONE and not Tools.Components.APP_SETTINGS)
|
||||
yield return new("Use app default", string.Empty);
|
||||
|
||||
var minimumLevel = this.SettingsManager.GetMinimumConfidenceLevel(this.Component);
|
||||
foreach (var providerId in this.Data)
|
||||
{
|
||||
|
||||
@@ -19,6 +19,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
<ConfigurationProviderSelection Data="@this.AvailableLLMProvidersFunc()" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProvider = selectedValue)" HelpText="@(() => "Would you like to set one provider as the default for the entire app? When you configure a different provider for an assistant, it will always take precedence.")"/>
|
||||
<ConfigurationProviderSelection Component="Components.APP_SETTINGS" Data="@this.AvailableLLMProvidersFunc()" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProvider = selectedValue)" HelpText="@(() => "Would you like to set one provider as the default for the entire app? When you configure a different provider for an assistant, it will always take precedence.")"/>
|
||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProfile = selectedValue)" OptionHelp="Would you like to set one of your profiles as the default for the entire app? When you configure a different profile for an assistant, it will always take precedence."/>
|
||||
</ExpansionPanel>
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||
<ConfigurationOption OptionDescription="Preselect chat options?" LabelOn="Chat options are preselected" LabelOff="No chat options are preselected" State="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Chat.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect chat options. This is might be useful when you prefer a specific provider."/>
|
||||
<ConfigurationProviderSelection Data="@this.AvailableLLMProvidersFunc()" Disabled="@(() => !this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.PreselectedProvider = selectedValue)"/>
|
||||
<ConfigurationProviderSelection Component="Components.CHAT" Data="@this.AvailableLLMProvidersFunc()" Disabled="@(() => !this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.PreselectedProvider = selectedValue)"/>
|
||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.PreselectedProfile = selectedValue)" OptionHelp="Would you like to set one of your profiles as the default for chats?"/>
|
||||
</MudPaper>
|
||||
</ExpansionPanel>
|
||||
@@ -1,4 +1,3 @@
|
||||
@using AIStudio.Settings
|
||||
@using AIStudio.Settings.DataModel
|
||||
@inherits SettingsPanelBase
|
||||
|
||||
@@ -37,12 +36,7 @@
|
||||
<MudTd>@this.GetEmbeddingName(context)</MudTd>
|
||||
|
||||
<MudTd Style="text-align: left;">
|
||||
@if (context is IERIDataSource)
|
||||
{
|
||||
@* <MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Info" Class="ma-2" OnClick="() => this.ShowInformation(context)"> *@
|
||||
@* Show Information *@
|
||||
@* </MudButton> *@
|
||||
}
|
||||
<MudIconButton Variant="Variant.Filled" Color="Color.Info" Icon="@Icons.Material.Filled.Info" Class="ma-2" OnClick="() => this.ShowInformation(context)"/>
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" Class="ma-2" OnClick="() => this.EditDataSource(context)">
|
||||
Edit
|
||||
</MudButton>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using AIStudio.Dialogs;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
|
||||
using ERI_Client.V1;
|
||||
using AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
@@ -216,10 +215,37 @@ public partial class SettingsPanelDataSources : SettingsPanelBase
|
||||
}
|
||||
}
|
||||
|
||||
private Task ShowInformation(IDataSource dataSource)
|
||||
private async Task ShowInformation(IDataSource dataSource)
|
||||
{
|
||||
#warning Implement the information dialog for ERI data sources.
|
||||
return Task.CompletedTask;
|
||||
switch (dataSource)
|
||||
{
|
||||
case DataSourceLocalFile localFile:
|
||||
var localFileDialogParameters = new DialogParameters<DataSourceLocalFileInfoDialog>
|
||||
{
|
||||
{ x => x.DataSource, localFile },
|
||||
};
|
||||
|
||||
await this.DialogService.ShowAsync<DataSourceLocalFileInfoDialog>("Local File Data Source Information", localFileDialogParameters, DialogOptions.FULLSCREEN);
|
||||
break;
|
||||
|
||||
case DataSourceLocalDirectory localDirectory:
|
||||
var localDirectoryDialogParameters = new DialogParameters<DataSourceLocalDirectoryInfoDialog>
|
||||
{
|
||||
{ x => x.DataSource, localDirectory },
|
||||
};
|
||||
|
||||
await this.DialogService.ShowAsync<DataSourceLocalDirectoryInfoDialog>("Local Directory Data Source Information", localDirectoryDialogParameters, DialogOptions.FULLSCREEN);
|
||||
break;
|
||||
|
||||
case DataSourceERI_V1 eriV1DataSource:
|
||||
var eriV1DialogParameters = new DialogParameters<DataSourceERI_V1InfoDialog>
|
||||
{
|
||||
{ x => x.DataSource, eriV1DataSource },
|
||||
};
|
||||
|
||||
await this.DialogService.ShowAsync<DataSourceERI_V1InfoDialog>("ERI v1 Data Source Information", eriV1DialogParameters, DialogOptions.FULLSCREEN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateDataSources()
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<MudStack Row="@true" AlignItems="AlignItems.Center">
|
||||
<MudTextField
|
||||
T="string"
|
||||
ReadOnly="@true"
|
||||
Label="@this.Label"
|
||||
Text="@this.Value"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Adornment="Adornment.Start"
|
||||
AdornmentIcon="@this.Icon"
|
||||
UserAttributes="@USER_INPUT_ATTRIBUTES" />
|
||||
|
||||
@if (this.ShowingCopyButton)
|
||||
{
|
||||
<MudTooltip Text="@this.ClipboardTooltip">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.ContentCopy" Size="Size.Medium" OnClick="@(() => this.CopyToClipboard(this.Value))"/>
|
||||
</MudTooltip>
|
||||
}
|
||||
</MudStack>
|
||||
@@ -0,0 +1,50 @@
|
||||
using AIStudio.Settings;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class TextInfoLine : ComponentBase
|
||||
{
|
||||
[Parameter]
|
||||
public string Label { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public string Icon { get; set; } = Icons.Material.Filled.Info;
|
||||
|
||||
[Parameter]
|
||||
public string Value { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public string ClipboardTooltipSubject { get; set; } = "the text";
|
||||
|
||||
[Parameter]
|
||||
public bool ShowingCopyButton { get; set; } = true;
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ISnackbar Snackbar { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private SettingsManager SettingsManager { get; init; } = null!;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
// Configure the spellchecking for the user input:
|
||||
this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES);
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();
|
||||
|
||||
private string ClipboardTooltip => $"Copy {this.ClipboardTooltipSubject} to the clipboard";
|
||||
|
||||
private async Task CopyToClipboard(string content) => await this.RustService.CopyText2Clipboard(this.Snackbar, content);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<MudStack Row="@true" AlignItems="AlignItems.Start">
|
||||
<MudTextField
|
||||
T="string"
|
||||
ReadOnly="@true"
|
||||
Label="@this.Label"
|
||||
Text="@this.Value"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Lines="3"
|
||||
MaxLines="@this.MaxLines"
|
||||
AutoGrow="@true"
|
||||
UserAttributes="@USER_INPUT_ATTRIBUTES" />
|
||||
|
||||
@if (this.ShowingCopyButton)
|
||||
{
|
||||
<MudTooltip Text="@this.ClipboardTooltip">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.ContentCopy" Size="Size.Medium" OnClick="@(() => this.CopyToClipboard(this.Value))"/>
|
||||
</MudTooltip>
|
||||
}
|
||||
</MudStack>
|
||||
@@ -0,0 +1,50 @@
|
||||
using AIStudio.Settings;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class TextInfoLines : ComponentBase
|
||||
{
|
||||
[Parameter]
|
||||
public string Label { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public string Value { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public string ClipboardTooltipSubject { get; set; } = "the text";
|
||||
|
||||
[Parameter]
|
||||
public int MaxLines { get; set; } = 30;
|
||||
|
||||
[Parameter]
|
||||
public bool ShowingCopyButton { get; set; } = true;
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ISnackbar Snackbar { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private SettingsManager SettingsManager { get; init; } = null!;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
// Configure the spellchecking for the user input:
|
||||
this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES);
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();
|
||||
|
||||
private string ClipboardTooltip => $"Copy {this.ClipboardTooltipSubject} to the clipboard";
|
||||
|
||||
private async Task CopyToClipboard(string content) => await this.RustService.CopyText2Clipboard(this.Snackbar, content);
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
@using AIStudio.Tools.ERIClient.DataModel
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
|
||||
<MudText Typo="Typo.h5">
|
||||
Common data source information
|
||||
</MudText>
|
||||
|
||||
<TextInfoLine Icon="@Icons.Material.Filled.Tag" Label="Data source name" Value="@this.DataSource.Name" ClipboardTooltipSubject="the data source name"/>
|
||||
<TextInfoLine Icon="@Icons.Material.Filled.NetworkCheck" Label="ERI server hostname" Value="@this.DataSource.Hostname" ClipboardTooltipSubject="the ERI server hostname"/>
|
||||
<TextInfoLine Icon="@Icons.Material.Filled.Tag" Label="ERI server port" Value="@this.Port" ClipboardTooltipSubject="the ERI server port"/>
|
||||
|
||||
@if (!this.IsConnectionEncrypted())
|
||||
{
|
||||
<MudJustifiedText Typo="Typo.body1" Color="Color.Error" Class="mb-3">
|
||||
Please note: the connection to the ERI v1 server is not encrypted. This means that all
|
||||
data sent to the server is transmitted in plain text. Please ask the ERI server administrator
|
||||
to enable encryption.
|
||||
</MudJustifiedText>
|
||||
}
|
||||
|
||||
@if (this.DataSource.AuthMethod is AuthMethod.USERNAME_PASSWORD)
|
||||
{
|
||||
<TextInfoLine Icon="@Icons.Material.Filled.Person2" Label="Username" Value="@this.DataSource.Username" ClipboardTooltipSubject="the username"/>
|
||||
}
|
||||
|
||||
<TextInfoLines Label="Server description" MaxLines="14" Value="@this.serverDescription" ClipboardTooltipSubject="the server description"/>
|
||||
<TextInfoLines Label="Security requirements" MaxLines="3" Value="@this.securityRequirements.Explain()" ClipboardTooltipSubject="the security requirements"/>
|
||||
|
||||
<MudText Typo="Typo.h5" Class="mt-6">
|
||||
Retrieval information
|
||||
</MudText>
|
||||
@if (!this.retrievalInfoformation.Any())
|
||||
{
|
||||
<MudJustifiedText Typo="Typo.body1" Color="Color.Info" Class="mb-3">
|
||||
The data source does not provide any retrieval information.
|
||||
</MudJustifiedText>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudExpansionPanels Class="mb-3">
|
||||
@for (var index = 0; index < this.retrievalInfoformation.Count; index++)
|
||||
{
|
||||
var info = this.retrievalInfoformation[index];
|
||||
<ExpansionPanel HeaderText="@this.RetrievalName(info)" HeaderIcon="@Icons.Material.Filled.Info" IsExpanded="index == 0">
|
||||
<TextInfoLines Label="Description" MaxLines="14" Value="@info.Description" ClipboardTooltipSubject="the retrieval description"/>
|
||||
<TextInfoLines Label="Parameters" MaxLines="14" Value="@this.RetrievalParameters(info)" ClipboardTooltipSubject="the retrieval parameters"/>
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(info.Link))
|
||||
{
|
||||
<MudButton Href="@info.Link" Target="_blank" Class="mt-3" Color="Color.Primary" StartIcon="@Icons.Material.Filled.OpenInNew">
|
||||
Open web link, show more information
|
||||
</MudButton>
|
||||
}
|
||||
|
||||
<MudText Typo="Typo.h6" Class="mt-3">
|
||||
Embeddings
|
||||
</MudText>
|
||||
@if (!info.Embeddings.Any())
|
||||
{
|
||||
<MudJustifiedText Typo="Typo.body1" Color="Color.Info" Class="mb-3">
|
||||
The data source does not provide any embedding information.
|
||||
</MudJustifiedText>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudExpansionPanels>
|
||||
@for (var embeddingIndex = 0; embeddingIndex < info.Embeddings.Count; embeddingIndex++)
|
||||
{
|
||||
var embedding = info.Embeddings[embeddingIndex];
|
||||
<ExpansionPanel HeaderText="@embedding.EmbeddingName" HeaderIcon="@Icons.Material.Filled.Info" IsExpanded="embeddingIndex == 0">
|
||||
<TextInfoLine Icon="@Icons.Material.Filled.FormatShapes" Label="Type" Value="@embedding.EmbeddingType" ClipboardTooltipSubject="the embedding type"/>
|
||||
<TextInfoLines Label="Description" MaxLines="14" Value="@embedding.Description" ClipboardTooltipSubject="the embedding description"/>
|
||||
<TextInfoLines Label="When to use" MaxLines="3" Value="@embedding.UsedWhen" ClipboardTooltipSubject="when is the embedding used"/>
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(embedding.Link))
|
||||
{
|
||||
<MudButton Href="@embedding.Link" Target="_blank" Class="mt-3" Color="Color.Primary" StartIcon="@Icons.Material.Filled.OpenInNew">
|
||||
Open web link, show more information
|
||||
</MudButton>
|
||||
}
|
||||
</ExpansionPanel>
|
||||
}
|
||||
</MudExpansionPanels>
|
||||
}
|
||||
</ExpansionPanel>
|
||||
}
|
||||
</MudExpansionPanels>
|
||||
}
|
||||
|
||||
<Issues IssuesData="@this.dataIssues"/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
@if (this.IsOperationInProgress)
|
||||
{
|
||||
<MudProgressLinear Color="Color.Primary" Indeterminate="true" Class="ml-5 mr-5"/>
|
||||
}
|
||||
<MudButton OnClick="@this.GetERIMetadata" Variant="Variant.Filled" Color="Color.Info">Reload</MudButton>
|
||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">Close</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
@@ -0,0 +1,178 @@
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
using System.Text;
|
||||
|
||||
using AIStudio.Assistants.ERI;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.ERIClient;
|
||||
using AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using RetrievalInfo = AIStudio.Tools.ERIClient.DataModel.RetrievalInfo;
|
||||
|
||||
namespace AIStudio.Dialogs;
|
||||
|
||||
public partial class DataSourceERI_V1InfoDialog : ComponentBase, IAsyncDisposable, ISecretId
|
||||
{
|
||||
[CascadingParameter]
|
||||
private MudDialogInstance MudDialog { get; set; } = null!;
|
||||
|
||||
[Parameter]
|
||||
public DataSourceERI_V1 DataSource { get; set; }
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
this.eriServerTasks.Add(this.GetERIMetadata());
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private readonly CancellationTokenSource cts = new();
|
||||
private readonly List<Task> eriServerTasks = new();
|
||||
private readonly List<string> dataIssues = [];
|
||||
|
||||
private string serverDescription = string.Empty;
|
||||
private ProviderType securityRequirements = ProviderType.NONE;
|
||||
private IReadOnlyList<RetrievalInfo> retrievalInfoformation = [];
|
||||
|
||||
private bool IsOperationInProgress { get; set; } = true;
|
||||
|
||||
private bool IsConnectionEncrypted() => this.DataSource.Hostname.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
private string Port => this.DataSource.Port == 0 ? string.Empty : $"{this.DataSource.Port}";
|
||||
|
||||
private string RetrievalName(RetrievalInfo retrievalInfo)
|
||||
{
|
||||
var hasId = !string.IsNullOrWhiteSpace(retrievalInfo.Id);
|
||||
var hasName = !string.IsNullOrWhiteSpace(retrievalInfo.Name);
|
||||
|
||||
if (hasId && hasName)
|
||||
return $"[{retrievalInfo.Id}] {retrievalInfo.Name}";
|
||||
|
||||
if (hasId)
|
||||
return $"[{retrievalInfo.Id}] Unnamed retrieval process";
|
||||
|
||||
return hasName ? retrievalInfo.Name : "Unnamed retrieval process";
|
||||
}
|
||||
|
||||
private string RetrievalParameters(RetrievalInfo retrievalInfo)
|
||||
{
|
||||
var parameters = retrievalInfo.ParametersDescription;
|
||||
if (parameters is null || parameters.Count == 0)
|
||||
return "This retrieval process has no parameters.";
|
||||
|
||||
var sb = new StringBuilder();
|
||||
foreach (var (paramName, description) in parameters)
|
||||
{
|
||||
sb.Append("Parameter: ");
|
||||
sb.AppendLine(paramName);
|
||||
sb.AppendLine(description);
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private async Task GetERIMetadata()
|
||||
{
|
||||
this.dataIssues.Clear();
|
||||
|
||||
try
|
||||
{
|
||||
this.IsOperationInProgress = true;
|
||||
this.StateHasChanged();
|
||||
|
||||
using var client = ERIClientFactory.Get(ERIVersion.V1, this.DataSource);
|
||||
if(client is null)
|
||||
{
|
||||
this.dataIssues.Add("Failed to connect to the ERI v1 server. The server is not supported.");
|
||||
return;
|
||||
}
|
||||
|
||||
var loginResult = await client.AuthenticateAsync(this.DataSource, this.RustService);
|
||||
if (!loginResult.Successful)
|
||||
{
|
||||
this.dataIssues.Add(loginResult.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
var dataSourceInfo = await client.GetDataSourceInfoAsync(this.cts.Token);
|
||||
if (!dataSourceInfo.Successful)
|
||||
{
|
||||
this.dataIssues.Add(dataSourceInfo.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
this.serverDescription = dataSourceInfo.Data.Description;
|
||||
|
||||
var securityRequirementsResult = await client.GetSecurityRequirementsAsync(this.cts.Token);
|
||||
if (!securityRequirementsResult.Successful)
|
||||
{
|
||||
this.dataIssues.Add(securityRequirementsResult.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
this.securityRequirements = securityRequirementsResult.Data.AllowedProviderType;
|
||||
|
||||
var retrievalInfoResult = await client.GetRetrievalInfoAsync(this.cts.Token);
|
||||
if (!retrievalInfoResult.Successful)
|
||||
{
|
||||
this.dataIssues.Add(retrievalInfoResult.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
this.retrievalInfoformation = retrievalInfoResult.Data ?? [];
|
||||
|
||||
this.StateHasChanged();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.dataIssues.Add($"Failed to connect to the ERI v1 server. The message was: {e.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.IsOperationInProgress = false;
|
||||
this.StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void Close()
|
||||
{
|
||||
this.cts.Cancel();
|
||||
this.MudDialog.Close();
|
||||
}
|
||||
|
||||
#region Implementation of ISecretId
|
||||
|
||||
public string SecretId => this.DataSource.Id;
|
||||
|
||||
public string SecretName => this.DataSource.Name;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IDisposable
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await this.cts.CancelAsync();
|
||||
await Task.WhenAll(this.eriServerTasks);
|
||||
|
||||
this.cts.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
@using ERI_Client.V1
|
||||
@using AIStudio.Tools.ERIClient.DataModel
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
<MudForm @ref="@this.form" @bind-IsValid="@this.dataIsValid" @bind-Errors="@this.dataIssues">
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using AIStudio.Assistants.ERI;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.ERIClient;
|
||||
using AIStudio.Tools.ERIClient.DataModel;
|
||||
using AIStudio.Tools.Validation;
|
||||
|
||||
using ERI_Client.V1;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
@@ -43,7 +44,6 @@ public partial class DataSourceERI_V1Dialog : ComponentBase, ISecretId
|
||||
private string[] dataIssues = [];
|
||||
private string dataSecretStorageIssue = string.Empty;
|
||||
private string dataEditingPreviousInstanceName = string.Empty;
|
||||
private HttpClient? httpClient;
|
||||
private List<AuthMethod> availableAuthMethods = [];
|
||||
private bool connectionTested;
|
||||
private bool connectionSuccessfulTested;
|
||||
@@ -167,31 +167,38 @@ public partial class DataSourceERI_V1Dialog : ComponentBase, ISecretId
|
||||
{
|
||||
try
|
||||
{
|
||||
this.httpClient = new HttpClient
|
||||
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(14));
|
||||
var dataSource = new DataSourceERI_V1
|
||||
{
|
||||
BaseAddress = new Uri($"{this.dataHostname}:{this.dataPort}"),
|
||||
Timeout = TimeSpan.FromSeconds(5),
|
||||
Hostname = this.dataHostname,
|
||||
Port = this.dataPort
|
||||
};
|
||||
|
||||
using (this.httpClient)
|
||||
|
||||
using var client = ERIClientFactory.Get(ERIVersion.V1, dataSource);
|
||||
if(client is null)
|
||||
{
|
||||
var client = new Client(this.httpClient);
|
||||
var authSchemes = await client.GetAuthMethodsAsync();
|
||||
if (authSchemes is null)
|
||||
{
|
||||
await this.form.Validate();
|
||||
|
||||
Array.Resize(ref this.dataIssues, this.dataIssues.Length + 1);
|
||||
this.dataIssues[^1] = "Failed to connect to the ERI v1 server. The server did not respond.";
|
||||
return;
|
||||
}
|
||||
|
||||
this.availableAuthMethods = authSchemes.Select(n => n.AuthMethod).ToList();
|
||||
|
||||
this.connectionTested = true;
|
||||
this.connectionSuccessfulTested = true;
|
||||
this.Logger.LogInformation("Connection to the ERI v1 server was successful tested.");
|
||||
await this.form.Validate();
|
||||
|
||||
Array.Resize(ref this.dataIssues, this.dataIssues.Length + 1);
|
||||
this.dataIssues[^1] = "Failed to connect to the ERI v1 server. The server is not supported.";
|
||||
return;
|
||||
}
|
||||
|
||||
var authSchemes = await client.GetAuthMethodsAsync(cts.Token);
|
||||
if (!authSchemes.Successful)
|
||||
{
|
||||
await this.form.Validate();
|
||||
|
||||
Array.Resize(ref this.dataIssues, this.dataIssues.Length + 1);
|
||||
this.dataIssues[^1] = authSchemes.Message;
|
||||
return;
|
||||
}
|
||||
|
||||
this.availableAuthMethods = authSchemes.Data!.Select(n => n.AuthMethod).ToList();
|
||||
|
||||
this.connectionTested = true;
|
||||
this.connectionSuccessfulTested = true;
|
||||
this.Logger.LogInformation("Connection to the ERI v1 server was successful tested.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
<TextInfoLine Icon="@Icons.Material.Filled.Tag" Label="Data source name" Value="@this.DataSource.Name" ClipboardTooltipSubject="the data source name"/>
|
||||
|
||||
<TextInfoLine Icon="@Icons.Material.Filled.FolderOpen" Label="Path" Value="@this.DataSource.Path" ClipboardTooltipSubject="this path"/>
|
||||
@if (!this.IsDirectoryAvailable)
|
||||
{
|
||||
<MudJustifiedText Typo="Typo.body1" Color="Color.Error" Class="mb-3">
|
||||
The directory chosen for the data source does not exist anymore. Please edit the data source and correct the path.
|
||||
</MudJustifiedText>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudJustifiedText Typo="Typo.body1" Color="Color.Tertiary" Class="mb-3">
|
||||
The directory chosen for the data source exists.
|
||||
</MudJustifiedText>
|
||||
}
|
||||
|
||||
<TextInfoLine Icon="@Icons.Material.Filled.Layers" Label="Embedding name" Value="@this.embeddingProvider.Name" ClipboardTooltipSubject="the embedding name"/>
|
||||
@if (this.IsCloudEmbedding)
|
||||
{
|
||||
<MudJustifiedText Typo="Typo.body1" Color="Color.Error" Class="mb-3">
|
||||
The embedding runs in the cloud. All your data from the folder '@this.DataSource.Path' and all its subdirectories
|
||||
will be sent to the cloud.
|
||||
</MudJustifiedText>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudJustifiedText Typo="Typo.body1" Color="Color.Tertiary" Class="mb-3">
|
||||
The embedding runs locally or in your organization. Your data is not sent to the cloud.
|
||||
</MudJustifiedText>
|
||||
}
|
||||
|
||||
<TextInfoLine Icon="@Icons.Material.Filled.SquareFoot" Label="Number of files" Value="@this.NumberFilesInDirectory" ClipboardTooltipSubject="the number of files in the directory"/>
|
||||
<TextInfoLines Label="Files list" MaxLines="14" Value="@this.directoryFiles.ToString()" ClipboardTooltipSubject="the files list"/>
|
||||
@if (this.directorySizeNumFiles > 100)
|
||||
{
|
||||
<MudJustifiedText Typo="Typo.body1" Color="Color.Warning" Class="mb-3">
|
||||
For performance reasons, only the first 100 files are shown. The directory contains @this.NumberFilesInDirectory files in total.
|
||||
</MudJustifiedText>
|
||||
}
|
||||
|
||||
<TextInfoLine Icon="@Icons.Material.Filled.SquareFoot" Label="Total directory size" Value="@this.directorySizeBytes.FileSize()" ClipboardTooltipSubject="the total directory size"/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
@if (this.IsOperationInProgress)
|
||||
{
|
||||
<MudProgressLinear Color="Color.Primary" Indeterminate="true" Class="ml-5 mr-5"/>
|
||||
}
|
||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">Close</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
@@ -0,0 +1,112 @@
|
||||
using System.Text;
|
||||
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace AIStudio.Dialogs;
|
||||
|
||||
public partial class DataSourceLocalDirectoryInfoDialog : ComponentBase, IAsyncDisposable
|
||||
{
|
||||
[CascadingParameter]
|
||||
private MudDialogInstance MudDialog { get; set; } = null!;
|
||||
|
||||
[Parameter]
|
||||
public DataSourceLocalDirectory DataSource { get; set; }
|
||||
|
||||
[Inject]
|
||||
private SettingsManager SettingsManager { get; init; } = null!;
|
||||
|
||||
private readonly Timer refreshTimer = new(TimeSpan.FromSeconds(1.6))
|
||||
{
|
||||
AutoReset = true,
|
||||
};
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
this.embeddingProvider = this.SettingsManager.ConfigurationData.EmbeddingProviders.FirstOrDefault(x => x.Id == this.DataSource.EmbeddingId);
|
||||
this.directoryInfo = new DirectoryInfo(this.DataSource.Path);
|
||||
|
||||
if (this.directoryInfo.Exists)
|
||||
{
|
||||
this.directorySizeTask = this.directoryInfo.DetermineContentSize(this.UpdateDirectorySize, this.UpdateDirectoryFiles, this.UpdateFileList, MAX_FILES_TO_SHOW, this.DirectoryOperationDone, this.cts.Token);
|
||||
this.refreshTimer.Elapsed += (_, _) => this.InvokeAsync(this.StateHasChanged);
|
||||
this.refreshTimer.Start();
|
||||
}
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private const int MAX_FILES_TO_SHOW = 100;
|
||||
|
||||
private readonly CancellationTokenSource cts = new();
|
||||
|
||||
private EmbeddingProvider embeddingProvider;
|
||||
private DirectoryInfo directoryInfo = null!;
|
||||
private long directorySizeBytes;
|
||||
private long directorySizeNumFiles;
|
||||
private readonly StringBuilder directoryFiles = new();
|
||||
private Task directorySizeTask = Task.CompletedTask;
|
||||
|
||||
private bool IsOperationInProgress { get; set; } = true;
|
||||
|
||||
private bool IsCloudEmbedding => !this.embeddingProvider.IsSelfHosted;
|
||||
|
||||
private bool IsDirectoryAvailable => this.directoryInfo.Exists;
|
||||
|
||||
private void UpdateFileList(string file)
|
||||
{
|
||||
this.directoryFiles.Append("- ");
|
||||
this.directoryFiles.AppendLine(file);
|
||||
}
|
||||
|
||||
private void UpdateDirectorySize(long size)
|
||||
{
|
||||
this.directorySizeBytes = size;
|
||||
}
|
||||
|
||||
private void UpdateDirectoryFiles(long numFiles) => this.directorySizeNumFiles = numFiles;
|
||||
|
||||
private void DirectoryOperationDone()
|
||||
{
|
||||
this.refreshTimer.Stop();
|
||||
this.IsOperationInProgress = false;
|
||||
this.InvokeAsync(this.StateHasChanged);
|
||||
}
|
||||
|
||||
private string NumberFilesInDirectory => $"{this.directorySizeNumFiles:###,###,###,###}";
|
||||
|
||||
private void Close()
|
||||
{
|
||||
this.cts.Cancel();
|
||||
this.MudDialog.Close();
|
||||
}
|
||||
|
||||
#region Implementation of IDisposable
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await this.cts.CancelAsync();
|
||||
await this.directorySizeTask;
|
||||
|
||||
this.cts.Dispose();
|
||||
this.refreshTimer.Stop();
|
||||
this.refreshTimer.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -23,7 +23,6 @@ public partial class DataSourceLocalFileDialog : ComponentBase
|
||||
[Inject]
|
||||
private SettingsManager SettingsManager { get; init; } = null!;
|
||||
|
||||
|
||||
private static readonly Dictionary<string, object?> SPELLCHECK_ATTRIBUTES = new();
|
||||
|
||||
private readonly DataSourceValidation dataSourceValidation;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
<TextInfoLine Icon="@Icons.Material.Filled.Tag" Label="Data source name" Value="@this.DataSource.Name" ClipboardTooltipSubject="the data source name"/>
|
||||
|
||||
<TextInfoLine Icon="@Icons.Material.Filled.FolderOpen" Label="File path" Value="@this.DataSource.FilePath" ClipboardTooltipSubject="this path"/>
|
||||
@if (!this.IsFileAvailable)
|
||||
{
|
||||
<MudJustifiedText Typo="Typo.body1" Color="Color.Error" Class="mb-3">
|
||||
The file chosen for the data source does not exist anymore. Please edit the data source and choose another file or correct the path.
|
||||
</MudJustifiedText>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudJustifiedText Typo="Typo.body1" Color="Color.Tertiary" Class="mb-3">
|
||||
The file chosen for the data source exists.
|
||||
</MudJustifiedText>
|
||||
}
|
||||
|
||||
<TextInfoLine Icon="@Icons.Material.Filled.Layers" Label="Embedding name" Value="@this.embeddingProvider.Name" ClipboardTooltipSubject="the embedding name"/>
|
||||
@if (this.IsCloudEmbedding)
|
||||
{
|
||||
<MudJustifiedText Typo="Typo.body1" Color="Color.Error" Class="mb-3">
|
||||
The embedding runs in the cloud. All your data within the
|
||||
file '@this.DataSource.FilePath' will be sent to the cloud.
|
||||
</MudJustifiedText>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudJustifiedText Typo="Typo.body1" Color="Color.Tertiary" Class="mb-3">
|
||||
The embedding runs locally or in your organization. Your data is not sent to the cloud.
|
||||
</MudJustifiedText>
|
||||
}
|
||||
|
||||
<TextInfoLine Icon="@Icons.Material.Filled.SquareFoot" Label="File size" Value="@this.FileSize" ClipboardTooltipSubject="the file size"/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">Close</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
@@ -0,0 +1,40 @@
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Dialogs;
|
||||
|
||||
public partial class DataSourceLocalFileInfoDialog : ComponentBase
|
||||
{
|
||||
[CascadingParameter]
|
||||
private MudDialogInstance MudDialog { get; set; } = null!;
|
||||
|
||||
[Parameter]
|
||||
public DataSourceLocalFile DataSource { get; set; }
|
||||
|
||||
[Inject]
|
||||
private SettingsManager SettingsManager { get; init; } = null!;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
this.embeddingProvider = this.SettingsManager.ConfigurationData.EmbeddingProviders.FirstOrDefault(x => x.Id == this.DataSource.EmbeddingId);
|
||||
this.fileInfo = new FileInfo(this.DataSource.FilePath);
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private EmbeddingProvider embeddingProvider;
|
||||
private FileInfo fileInfo = null!;
|
||||
|
||||
private bool IsCloudEmbedding => !this.embeddingProvider.IsSelfHosted;
|
||||
|
||||
private bool IsFileAvailable => this.fileInfo.Exists;
|
||||
|
||||
private string FileSize => this.fileInfo.FileSize();
|
||||
|
||||
private void Close() => this.MudDialog.Close();
|
||||
}
|
||||
@@ -47,16 +47,12 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CodeBeam.MudBlazor.Extensions" Version="7.1.0" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.72" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.0" />
|
||||
<PackageReference Include="MudBlazor" Version="7.15.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.1" />
|
||||
<PackageReference Include="MudBlazor" Version="7.16.0" />
|
||||
<PackageReference Include="MudBlazor.Markdown" Version="7.14.0" />
|
||||
<PackageReference Include="ReverseMarkdown" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ERIClientV1\ERIClientV1.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Read the meta data file -->
|
||||
<Target Name="ReadMetaData" BeforeTargets="BeforeBuild">
|
||||
<Error Text="The ../../metadata.txt file was not found!" Condition="!Exists('../../metadata.txt')" />
|
||||
|
||||
@@ -26,7 +26,7 @@ public partial class Home : ComponentBase
|
||||
private async Task ReadLastChangeAsync()
|
||||
{
|
||||
var latest = Changelog.LOGS.MaxBy(n => n.Build);
|
||||
var response = await this.HttpClient.GetAsync($"changelog/{latest.Filename}");
|
||||
using var response = await this.HttpClient.GetAsync($"changelog/{latest.Filename}");
|
||||
this.LastChangeContent = await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,64 +2,101 @@
|
||||
|
||||
<div class="inner-scrolling-context">
|
||||
<MudText Typo="Typo.h3" Class="mb-2">Supporters</MudText>
|
||||
|
||||
<InnerScrolling>
|
||||
|
||||
<MudExpansionPanels>
|
||||
|
||||
<div class="border-solid border-2 rounded-lg pa-3 mb-6">
|
||||
<MudText Typo="Typo.h4" Class="mb-2">Our Titans</MudText>
|
||||
<MudText Typo="Typo.body1" Style="text-align: justify; hyphens: auto;" Class="mb-3">
|
||||
In this section, we highlight the titan supporters of MindWork AI Studio. Titans are prestigious companies that provide significant support to our mission.
|
||||
</MudText>
|
||||
<MudText Typo="Typo.body1" Style="text-align: justify; hyphens: auto;" Class="mb-3">
|
||||
For companies, sponsoring MindWork AI Studio is not only a way to support innovation but also a valuable opportunity for public relations and marketing. Your company's name and logo will be featured prominently, showcasing your commitment to using cutting-edge AI tools and enhancing your reputation as an innovative enterprise.
|
||||
</MudText>
|
||||
<MudButton Href="https://github.com/sponsors/MindWorkAI" StartIcon="@Icons.Material.Filled.Business" Variant="Variant.Filled" Target="_blank">
|
||||
Become our first Titan
|
||||
</MudButton>
|
||||
</div>
|
||||
|
||||
<MudGrid Justify="Justify.Center" Spacing="2" Style="width: 100%;" Class="ma-0">
|
||||
<MudItem Class="pa-0 pr-2" xs="6">
|
||||
<MudPaper Elevation="3" Class="border-solid border rounded-lg pa-3">
|
||||
<MudText Typo="Typo.h4" Class="mb-3 d-flex align-center">
|
||||
<MudIcon Icon="@Icons.Material.Filled.People" Size="Size.Large" class="mr-2"/>
|
||||
Individual Contributors
|
||||
<ExpansionPanel HeaderText="Financial Support" IsExpanded="@true" HeaderIcon="@Icons.Material.Filled.AttachMoney">
|
||||
<div class="border-solid border-2 rounded-lg pa-3 mb-6">
|
||||
<MudText Typo="Typo.h4" Class="mb-2">Our Titans</MudText>
|
||||
<MudText Typo="Typo.body1" Style="text-align: justify; hyphens: auto;" Class="mb-3">
|
||||
In this section, we highlight the titan supporters of MindWork AI Studio. Titans are prestigious companies that provide significant support to our mission.
|
||||
</MudText>
|
||||
|
||||
<MudButton Href="https://github.com/sponsors/MindWorkAI" StartIcon="@Icons.Material.Filled.People" Variant="Variant.Filled" Target="_blank">
|
||||
Become a contributor
|
||||
</MudButton>
|
||||
|
||||
<MudText Typo="Typo.h6" Class="mt-8">
|
||||
The first 10 supporters who make a monthly contribution:
|
||||
<MudText Typo="Typo.body1" Style="text-align: justify; hyphens: auto;" Class="mb-3">
|
||||
For companies, sponsoring MindWork AI Studio is not only a way to support innovation but also a valuable opportunity for public relations and marketing. Your company's name and logo will be featured prominently, showcasing your commitment to using cutting-edge AI tools and enhancing your reputation as an innovative enterprise.
|
||||
</MudText>
|
||||
<MudList T="string">
|
||||
<Supporter Name="richard-stanton" Type="SupporterType.INDIVIDUAL" URL="https://github.com/richard-stanton" Acknowledgment="Thank you, Richard, for being the first."/>
|
||||
<Supporter Name="peerschuett" Type="SupporterType.INDIVIDUAL" URL="https://github.com/peerschuett" Acknowledgment="Thank you, Peer, for your courage in being the second person to support the project financially."/>
|
||||
</MudList>
|
||||
|
||||
<MudText Typo="Typo.h6" Class="mt-8">
|
||||
The first 10 supporters who make a one-time contribution:
|
||||
</MudText>
|
||||
<MudList T="string">
|
||||
<Supporter Name="Bessieres" Type="SupporterType.INDIVIDUAL" URL="https://github.com/Bessieres" Acknowledgment="Thank you for being the first to contribute a one-time donation."/>
|
||||
</MudList>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="6" Class="pa-0 pl-2">
|
||||
<MudPaper Elevation="3" Class="border-solid border rounded-lg pa-3">
|
||||
<MudText Typo="Typo.h4" Class="mb-3 d-flex align-center">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Business" Size="Size.Large" class="mr-2"/>
|
||||
Business Contributors
|
||||
</MudText>
|
||||
|
||||
<MudButton Href="https://github.com/sponsors/MindWorkAI" StartIcon="@Icons.Material.Filled.Business" Variant="Variant.Filled" Target="_blank">
|
||||
Become a contributor
|
||||
Become our first Titan
|
||||
</MudButton>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
</div>
|
||||
|
||||
<MudGrid Justify="Justify.Center" Spacing="2" Style="width: 100%;" Class="ma-0">
|
||||
<MudItem Class="pa-0 pr-2" xs="6">
|
||||
<MudPaper Elevation="3" Class="border-solid border rounded-lg pa-3">
|
||||
<MudText Typo="Typo.h4" Class="mb-3 d-flex align-center">
|
||||
<MudIcon Icon="@Icons.Material.Filled.People" Size="Size.Large" class="mr-2"/>
|
||||
Individual Contributors
|
||||
</MudText>
|
||||
|
||||
<MudButton Href="https://github.com/sponsors/MindWorkAI" StartIcon="@Icons.Material.Filled.People" Variant="Variant.Filled" Target="_blank">
|
||||
Become a contributor
|
||||
</MudButton>
|
||||
|
||||
<MudText Typo="Typo.h6" Class="mt-8">
|
||||
The first 10 supporters who make a monthly contribution:
|
||||
</MudText>
|
||||
<MudList T="string">
|
||||
<Supporter Name="richard-stanton" Type="SupporterType.INDIVIDUAL" URL="https://github.com/richard-stanton" Acknowledgment="Thank you, Richard, for being the first."/>
|
||||
<Supporter Name="peerschuett" Type="SupporterType.INDIVIDUAL" URL="https://github.com/peerschuett" Acknowledgment="Thank you, Peer, for your courage in being the second person to support the project financially."/>
|
||||
<Supporter Name="donework" Type="SupporterType.INDIVIDUAL" URL="https://github.com/donework" Acknowledgment="Thanks Dominic for being the third supporter."/>
|
||||
</MudList>
|
||||
|
||||
<MudText Typo="Typo.h6" Class="mt-8">
|
||||
The first 10 supporters who make a one-time contribution:
|
||||
</MudText>
|
||||
<MudList T="string">
|
||||
<Supporter Name="Bessieres" Type="SupporterType.INDIVIDUAL" URL="https://github.com/Bessieres" Acknowledgment="Thank you for being the first to contribute a one-time donation."/>
|
||||
</MudList>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="6" Class="pa-0 pl-2">
|
||||
<MudPaper Elevation="3" Class="border-solid border rounded-lg pa-3">
|
||||
<MudText Typo="Typo.h4" Class="mb-3 d-flex align-center">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Business" Size="Size.Large" class="mr-2"/>
|
||||
Business Contributors
|
||||
</MudText>
|
||||
|
||||
<MudButton Href="https://github.com/sponsors/MindWorkAI" StartIcon="@Icons.Material.Filled.Business" Variant="Variant.Filled" Target="_blank">
|
||||
Become a contributor
|
||||
</MudButton>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
</ExpansionPanel>
|
||||
|
||||
<ExpansionPanel HeaderText="Content Contributors" HeaderIcon="@Icons.Material.Filled.Code">
|
||||
|
||||
<MudGrid Justify="Justify.Center" Spacing="2" Style="width: 100%;" Class="ma-0">
|
||||
<MudItem Class="pa-0 pr-2" xs="6">
|
||||
<MudPaper Elevation="3" Class="border-solid border rounded-lg pa-3">
|
||||
<MudText Typo="Typo.h4" Class="mb-3 d-flex align-center">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Code" Size="Size.Large" class="mr-2"/>
|
||||
Code Contributions
|
||||
</MudText>
|
||||
|
||||
<MudList T="string">
|
||||
<Supporter Name="DevNullx64" Type="SupporterType.INDIVIDUAL" URL="https://github.com/DevNullx64" Acknowledgment="Thanks Luc for your build script contribution."/>
|
||||
<Supporter Name="SolsticeSpectrum" Type="SupporterType.INDIVIDUAL" URL="https://github.com/SolsticeSpectrum" Acknowledgment="Thanks for your build script contribution."/>
|
||||
</MudList>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
|
||||
<MudItem Class="pa-0 pr-2" xs="6">
|
||||
<MudPaper Elevation="3" Class="border-solid border rounded-lg pa-3">
|
||||
<MudText Typo="Typo.h4" Class="mb-3 d-flex align-start">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Image" Size="Size.Large" class="mr-2"/>
|
||||
Moderation, Design, Wiki, and Documentation
|
||||
</MudText>
|
||||
|
||||
<MudList T="string">
|
||||
<Supporter Name="KeSah001" Type="SupporterType.INDIVIDUAL" URL="https://github.com/KeSah001" Acknowledgment="Thank you very much, Kerstin, for taking care of creating the Wiki."/>
|
||||
</MudList>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
</ExpansionPanel>
|
||||
|
||||
</MudExpansionPanels>
|
||||
</InnerScrolling>
|
||||
</div>
|
||||
@@ -103,9 +103,14 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
{
|
||||
using var request = await requestBuilder();
|
||||
|
||||
//
|
||||
// Send the request with the ResponseHeadersRead option.
|
||||
// This allows us to read the stream as soon as the headers are received.
|
||||
// This is important because we want to stream the responses.
|
||||
//
|
||||
// Please notice: We do not dispose the response here. The caller is responsible
|
||||
// for disposing the response object. This is important because the response
|
||||
// object is used to read the stream.
|
||||
var nextResponse = await this.httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token);
|
||||
if (nextResponse.IsSuccessStatusCode)
|
||||
{
|
||||
|
||||
@@ -136,8 +136,8 @@ public class ProviderGoogle(ILogger logger) : BaseProvider("https://generativela
|
||||
if (secretKey is null)
|
||||
return default;
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, $"models?key={secretKey}");
|
||||
var response = await this.httpClient.SendAsync(request, token);
|
||||
using var request = new HttpRequestMessage(HttpMethod.Get, $"models?key={secretKey}");
|
||||
using var response = await this.httpClient.SendAsync(request, token);
|
||||
|
||||
if(!response.IsSuccessStatusCode)
|
||||
return default;
|
||||
|
||||
@@ -127,10 +127,10 @@ public class ProviderGroq(ILogger logger) : BaseProvider("https://api.groq.com/o
|
||||
if (secretKey is null)
|
||||
return [];
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "models");
|
||||
using var request = new HttpRequestMessage(HttpMethod.Get, "models");
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secretKey);
|
||||
|
||||
var response = await this.httpClient.SendAsync(request, token);
|
||||
using var response = await this.httpClient.SendAsync(request, token);
|
||||
if(!response.IsSuccessStatusCode)
|
||||
return [];
|
||||
|
||||
|
||||
@@ -138,10 +138,10 @@ public sealed class ProviderMistral(ILogger logger) : BaseProvider("https://api.
|
||||
if (secretKey is null)
|
||||
return default;
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "models");
|
||||
using var request = new HttpRequestMessage(HttpMethod.Get, "models");
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secretKey);
|
||||
|
||||
var response = await this.httpClient.SendAsync(request, token);
|
||||
using var response = await this.httpClient.SendAsync(request, token);
|
||||
if(!response.IsSuccessStatusCode)
|
||||
return default;
|
||||
|
||||
|
||||
@@ -154,10 +154,10 @@ public sealed class ProviderOpenAI(ILogger logger) : BaseProvider("https://api.o
|
||||
if (secretKey is null)
|
||||
return [];
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "models");
|
||||
using var request = new HttpRequestMessage(HttpMethod.Get, "models");
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secretKey);
|
||||
|
||||
var response = await this.httpClient.SendAsync(request, token);
|
||||
using var response = await this.httpClient.SendAsync(request, token);
|
||||
if(!response.IsSuccessStatusCode)
|
||||
return [];
|
||||
|
||||
|
||||
@@ -154,11 +154,11 @@ public sealed class ProviderSelfHosted(ILogger logger, Host host, string hostnam
|
||||
}
|
||||
};
|
||||
|
||||
var lmStudioRequest = new HttpRequestMessage(HttpMethod.Get, "models");
|
||||
using var lmStudioRequest = new HttpRequestMessage(HttpMethod.Get, "models");
|
||||
if(secretKey is not null)
|
||||
lmStudioRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", apiKeyProvisional);
|
||||
|
||||
var lmStudioResponse = await this.httpClient.SendAsync(lmStudioRequest, token);
|
||||
using var lmStudioResponse = await this.httpClient.SendAsync(lmStudioRequest, token);
|
||||
if(!lmStudioResponse.IsSuccessStatusCode)
|
||||
return [];
|
||||
|
||||
|
||||
@@ -127,10 +127,10 @@ public sealed class ProviderX(ILogger logger) : BaseProvider("https://api.x.ai/v
|
||||
if (secretKey is null)
|
||||
return [];
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "models");
|
||||
using var request = new HttpRequestMessage(HttpMethod.Get, "models");
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secretKey);
|
||||
|
||||
var response = await this.httpClient.SendAsync(request, token);
|
||||
using var response = await this.httpClient.SendAsync(request, token);
|
||||
if(!response.IsSuccessStatusCode)
|
||||
return [];
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using ERI_Client.V1;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
using AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
namespace AIStudio.Settings.DataModel;
|
||||
|
||||
/// <summary>
|
||||
@@ -24,23 +25,15 @@ public readonly record struct DataSourceERI_V1 : IERIDataSource
|
||||
/// <inheritdoc />
|
||||
public DataSourceType Type { get; init; } = DataSourceType.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// The hostname of the ERI server.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public string Hostname { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The port of the ERI server.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public int Port { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The authentication method to use.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public AuthMethod AuthMethod { get; init; } = AuthMethod.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// The username to use for authentication, when the auth. method is USERNAME_PASSWORD.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public string Username { get; init; } = string.Empty;
|
||||
}
|
||||
@@ -1,12 +1,26 @@
|
||||
using ERI_Client.V1;
|
||||
using AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
namespace AIStudio.Settings;
|
||||
|
||||
public interface IERIDataSource : IExternalDataSource
|
||||
{
|
||||
/// <summary>
|
||||
/// The hostname of the ERI server.
|
||||
/// </summary>
|
||||
public string Hostname { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The port of the ERI server.
|
||||
/// </summary>
|
||||
public int Port { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The authentication method to use.
|
||||
/// </summary>
|
||||
public AuthMethod AuthMethod { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The username to use for authentication, when the auth. method is USERNAME_PASSWORD.
|
||||
/// </summary>
|
||||
public string Username { get; init; }
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using ERI_Client.V1;
|
||||
using AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
|
||||
@@ -20,4 +20,5 @@ public enum Components
|
||||
ERI_ASSISTANT,
|
||||
|
||||
CHAT,
|
||||
APP_SETTINGS,
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
public static class DirectoryInfoExtensions
|
||||
{
|
||||
private static readonly EnumerationOptions ENUMERATION_OPTIONS = new()
|
||||
{
|
||||
IgnoreInaccessible = true,
|
||||
RecurseSubdirectories = true,
|
||||
ReturnSpecialDirectories = false,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Determines the size of the directory and all its subdirectories, as well as the number of files. When desired,
|
||||
/// it can report the found files up to a certain limit.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You might set reportMaxFiles to a negative value to report all files. Any positive value will limit the number
|
||||
/// of reported files. The cancellation token can be used to stop the operation. The cancellation operation is also able
|
||||
/// to cancel slow operations, e.g., when the directory is on a slow network drive.
|
||||
///
|
||||
/// After stopping the operation, the total size and number of files are reported as they were at the time of cancellation.
|
||||
///
|
||||
/// Please note that the entire operation is done on a background thread. Thus, when reporting the found files or the
|
||||
/// current total size, you need to use the appropriate dispatcher to update the UI. Usually, you can use the InvokeAsync
|
||||
/// method to update the UI from a background thread.
|
||||
/// </remarks>
|
||||
/// <param name="directoryInfo">The root directory to determine the size of.</param>
|
||||
/// <param name="reportCurrentTotalSize">The callback to report the current total size of the directory.</param>
|
||||
/// <param name="reportCurrentNumFiles">The callback to report the current number of files found.</param>
|
||||
/// <param name="reportNextFile">The callback to report the next file found. The file name is relative to the root directory.</param>
|
||||
/// <param name="reportMaxFiles">The maximum number of files to report. A negative value reports all files.</param>
|
||||
/// <param name="done">The callback to report that the operation is done.</param>
|
||||
/// <param name="cancellationToken">The cancellation token to stop the operation.</param>
|
||||
public static async Task DetermineContentSize(this DirectoryInfo directoryInfo, Action<long> reportCurrentTotalSize, Action<long> reportCurrentNumFiles, Action<string> reportNextFile, int reportMaxFiles = -1, Action? done = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var rootDirectoryLen = directoryInfo.FullName.Length;
|
||||
long totalSize = 0;
|
||||
long numFiles = 0;
|
||||
|
||||
await Task.Factory.StartNew(() => {
|
||||
foreach (var file in directoryInfo.EnumerateFiles("*", ENUMERATION_OPTIONS))
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
totalSize += file.Length;
|
||||
numFiles++;
|
||||
|
||||
if (numFiles % 100 == 0)
|
||||
{
|
||||
reportCurrentTotalSize(totalSize);
|
||||
reportCurrentNumFiles(numFiles);
|
||||
}
|
||||
|
||||
if (reportMaxFiles < 0 || numFiles <= reportMaxFiles)
|
||||
reportNextFile(file.FullName[rootDirectoryLen..]);
|
||||
}
|
||||
}, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
||||
|
||||
reportCurrentTotalSize(totalSize);
|
||||
reportCurrentNumFiles(numFiles);
|
||||
|
||||
if(done is not null)
|
||||
done();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace AIStudio.Tools.ERIClient;
|
||||
|
||||
public sealed class APIResponse<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Was the API call successful?
|
||||
/// </summary>
|
||||
public bool Successful { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When the API call was not successful, this will contain the error message.
|
||||
/// </summary>
|
||||
public string Message { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The data returned by the API call.
|
||||
/// </summary>
|
||||
public T? Data { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// An authentication field.
|
||||
/// </summary>
|
||||
public enum AuthField
|
||||
{
|
||||
NONE,
|
||||
USERNAME,
|
||||
PASSWORD,
|
||||
TOKEN,
|
||||
KERBEROS_TICKET,
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// The mapping between an AuthField and the field name in the authentication request.
|
||||
/// </summary>
|
||||
/// <param name="AuthField">The AuthField that is mapped to the field name.</param>
|
||||
/// <param name="FieldName">The field name in the authentication request.</param>
|
||||
public record AuthFieldMapping(AuthField AuthField, string FieldName);
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
public enum AuthMethod
|
||||
{
|
||||
NONE,
|
||||
KERBEROS,
|
||||
USERNAME_PASSWORD,
|
||||
TOKEN,
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// The response to an authentication request.
|
||||
/// </summary>
|
||||
/// <param name="Success">True, when the authentication was successful.</param>
|
||||
/// <param name="Token">The token to use for further requests.</param>
|
||||
/// <param name="Message">When the authentication was not successful, this contains the reason.</param>
|
||||
public readonly record struct AuthResponse(bool Success, string? Token, string? Message);
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// Describes one authentication scheme for this data source.
|
||||
/// </summary>
|
||||
/// <param name="AuthMethod">The method used for authentication, e.g., "API Key," "Username/Password," etc.</param>
|
||||
/// <param name="AuthFieldMappings">A list of field mappings for the authentication method. The client must know,
|
||||
/// e.g., how the password field is named in the request.</param>
|
||||
public readonly record struct AuthScheme(AuthMethod AuthMethod, List<AuthFieldMapping> AuthFieldMappings);
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// A chat thread, which is a list of content blocks.
|
||||
/// </summary>
|
||||
/// <param name="ContentBlocks">The content blocks in this chat thread.</param>
|
||||
public readonly record struct ChatThread(List<ContentBlock> ContentBlocks);
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// A block of content of a chat thread.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Images and other media are base64 encoded.
|
||||
/// </remarks>
|
||||
/// <param name="Content">The content of the block. Remember that images and other media are base64 encoded.</param>
|
||||
/// <param name="Role">The role of the content in the chat thread.</param>
|
||||
/// <param name="Type">The type of the content, e.g., text, image, video, etc.</param>
|
||||
public readonly record struct ContentBlock(string Content, Role Role, ContentType Type);
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// The type of content.
|
||||
/// </summary>
|
||||
public enum ContentType
|
||||
{
|
||||
NONE,
|
||||
UNKNOWN,
|
||||
|
||||
TEXT,
|
||||
IMAGE,
|
||||
VIDEO,
|
||||
AUDIO,
|
||||
SPEECH,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// Matching context returned by the data source as a result of a retrieval request.
|
||||
/// </summary>
|
||||
/// <param name="Name">The name of the source, e.g., a document name, database name,
|
||||
/// collection name, etc.</param>
|
||||
/// <param name="Category">What are the contents of the source? For example, is it a
|
||||
/// dictionary, a book chapter, business concept, a paper, etc.</param>
|
||||
/// <param name="Path">The path to the content, e.g., a URL, a file path, a path in a
|
||||
/// graph database, etc.</param>
|
||||
/// <param name="Type">The type of the content, e.g., text, image, video, audio, speech, etc.</param>
|
||||
/// <param name="MatchedContent">The content that matched the user prompt. For text, you
|
||||
/// return the matched text and, e.g., three words before and after it.</param>
|
||||
/// <param name="SurroundingContent">The surrounding content of the matched content.
|
||||
/// For text, you may return, e.g., one sentence or paragraph before and after
|
||||
/// the matched content.</param>
|
||||
/// <param name="Links">Links to related content, e.g., links to Wikipedia articles,
|
||||
/// links to sources, etc.</param>
|
||||
public readonly record struct Context(
|
||||
string Name,
|
||||
string Category,
|
||||
string? Path,
|
||||
ContentType Type,
|
||||
string MatchedContent,
|
||||
string[] SurroundingContent,
|
||||
string[] Links);
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// Information about the data source.
|
||||
/// </summary>
|
||||
/// <param name="Name">The name of the data source, e.g., "Internal Organization Documents."</param>
|
||||
/// <param name="Description">A short description of the data source. What kind of data does it contain?
|
||||
/// What is the data source used for?</param>
|
||||
public readonly record struct DataSourceInfo(string Name, string Description);
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// Represents information about the used embedding for this data source. The purpose of this information is to give the
|
||||
/// interested user an idea of what kind of embedding is used and what it does.
|
||||
/// </summary>
|
||||
/// <param name="EmbeddingType">What kind of embedding is used. For example, "Transformer Embedding," "Contextual Word
|
||||
/// Embedding," "Graph Embedding," etc.</param>
|
||||
/// <param name="EmbeddingName">Name the embedding used. This can be a library, a framework, or the name of the used
|
||||
/// algorithm.</param>
|
||||
/// <param name="Description">A short description of the embedding. Describe what the embedding is doing.</param>
|
||||
/// <param name="UsedWhen">Describe when the embedding is used. For example, when the user prompt contains certain
|
||||
/// keywords, or anytime?</param>
|
||||
/// <param name="Link">A link to the embedding's documentation or the source code. Might be null.</param>
|
||||
public readonly record struct EmbeddingInfo(
|
||||
string EmbeddingType,
|
||||
string EmbeddingName,
|
||||
string Description,
|
||||
string UsedWhen,
|
||||
string? Link);
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// Known types of providers that can process data.
|
||||
/// </summary>
|
||||
public enum ProviderType
|
||||
{
|
||||
/// <summary>
|
||||
/// The related data is not allowed to be sent to any provider.
|
||||
/// </summary>
|
||||
NONE,
|
||||
|
||||
/// <summary>
|
||||
/// The related data can be sent to any provider.
|
||||
/// </summary>
|
||||
ANY,
|
||||
|
||||
/// <summary>
|
||||
/// The related data can be sent to a provider that is hosted by the same organization, either on-premises or locally.
|
||||
/// </summary>
|
||||
SELF_HOSTED,
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
public static class ProviderTypeExtensions
|
||||
{
|
||||
public static string Explain(this ProviderType providerType) => providerType switch
|
||||
{
|
||||
ProviderType.NONE => "The related data is not allowed to be sent to any LLM provider. This means that this data source cannot be used at the moment.",
|
||||
ProviderType.ANY => "The related data can be sent to any provider, regardless of where it is hosted (cloud or self-hosted).",
|
||||
ProviderType.SELF_HOSTED => "The related data can be sent to a provider that is hosted by the same organization, either on-premises or locally. Cloud-based providers are not allowed.",
|
||||
|
||||
_ => "Unknown configuration. This data source cannot be used at the moment.",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// Information about a retrieval process, which this data source implements.
|
||||
/// </summary>
|
||||
/// <param name="Id">A unique identifier for the retrieval process. This can be a GUID, a unique name, or an increasing integer.</param>
|
||||
/// <param name="Name">The name of the retrieval process, e.g., "Keyword-Based Wikipedia Article Retrieval".</param>
|
||||
/// <param name="Description">A short description of the retrieval process. What kind of retrieval process is it?</param>
|
||||
/// <param name="Link">A link to the retrieval process's documentation, paper, Wikipedia article, or the source code. Might be null.</param>
|
||||
/// <param name="ParametersDescription">A dictionary that describes the parameters of the retrieval process. The key is the parameter name,
|
||||
/// and the value is a description of the parameter. Although each parameter will be sent as a string, the description should indicate the
|
||||
/// expected type and range, e.g., 0.0 to 1.0 for a float parameter.</param>
|
||||
/// <param name="Embeddings">A list of embeddings used in this retrieval process. It might be empty in case no embedding is used.</param>
|
||||
public readonly record struct RetrievalInfo(
|
||||
string Id,
|
||||
string Name,
|
||||
string Description,
|
||||
string? Link,
|
||||
Dictionary<string, string>? ParametersDescription,
|
||||
List<EmbeddingInfo> Embeddings);
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// The retrieval request sent by AI Studio.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Images and other media are base64 encoded.
|
||||
/// </remarks>
|
||||
/// <param name="LatestUserPrompt">The latest user prompt that AI Studio received.</param>
|
||||
/// <param name="LatestUserPromptType">The type of the latest user prompt, e.g., text, image, etc.</param>
|
||||
/// <param name="Thread">The chat thread that the user is currently in.</param>
|
||||
/// <param name="RetrievalProcessId">Optional. The ID of the retrieval process that the data source should use.
|
||||
/// When null, the data source chooses an appropriate retrieval process. Selecting a retrieval process is optional
|
||||
/// for AI Studio users. Most users do not specify a retrieval process.</param>
|
||||
/// <param name="Parameters">A dictionary of parameters that the data source should use for the retrieval process.
|
||||
/// Although each parameter will be sent as a string, the retrieval process specifies the expected type and range.</param>
|
||||
/// <param name="MaxMatches">The maximum number of matches that the data source should return. AI Studio uses
|
||||
/// any value below 1 to indicate that the data source should return as many matches as appropriate.</param>
|
||||
public readonly record struct RetrievalRequest(
|
||||
string LatestUserPrompt,
|
||||
ContentType LatestUserPromptType,
|
||||
ChatThread Thread,
|
||||
string? RetrievalProcessId,
|
||||
Dictionary<string, string>? Parameters,
|
||||
int MaxMatches);
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// Possible roles of any chat thread.
|
||||
/// </summary>
|
||||
public enum Role
|
||||
{
|
||||
NONE,
|
||||
UNKNOW,
|
||||
|
||||
SYSTEM,
|
||||
USER,
|
||||
AI,
|
||||
AGENT,
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the security requirements for this data source.
|
||||
/// </summary>
|
||||
/// <param name="AllowedProviderType">Which provider types are allowed to process the data?</param>
|
||||
public readonly record struct SecurityRequirements(ProviderType AllowedProviderType);
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AIStudio.Tools.ERIClient;
|
||||
|
||||
public abstract class ERIClientBase(string baseAddress) : IDisposable
|
||||
{
|
||||
protected static readonly JsonSerializerOptions JSON_OPTIONS = new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
AllowTrailingCommas = true,
|
||||
PropertyNamingPolicy = null,
|
||||
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
|
||||
PropertyNameCaseInsensitive = true,
|
||||
Converters =
|
||||
{
|
||||
new JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseUpper),
|
||||
}
|
||||
};
|
||||
|
||||
protected readonly HttpClient httpClient = new()
|
||||
{
|
||||
BaseAddress = new Uri(baseAddress),
|
||||
};
|
||||
|
||||
protected string securityToken = string.Empty;
|
||||
|
||||
#region Implementation of IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.httpClient.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using AIStudio.Assistants.ERI;
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Tools.ERIClient;
|
||||
|
||||
public static class ERIClientFactory
|
||||
{
|
||||
public static IERIClient? Get(ERIVersion version, IERIDataSource dataSource) => version switch
|
||||
{
|
||||
ERIVersion.V1 => new ERIClientV1($"{dataSource.Hostname}:{dataSource.Port}"),
|
||||
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
namespace AIStudio.Tools.ERIClient;
|
||||
|
||||
public class ERIClientV1(string baseAddress) : ERIClientBase(baseAddress), IERIClient
|
||||
{
|
||||
#region Implementation of IERIClient
|
||||
|
||||
public async Task<APIResponse<List<AuthScheme>>> GetAuthMethodsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var response = await this.httpClient.GetAsync("/auth/methods", cancellationToken);
|
||||
if(!response.IsSuccessStatusCode)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = $"Failed to retrieve the authentication methods: there was an issue communicating with the ERI server. Code: {response.StatusCode}, Reason: {response.ReasonPhrase}"
|
||||
};
|
||||
}
|
||||
|
||||
var authMethods = await response.Content.ReadFromJsonAsync<List<AuthScheme>>(JSON_OPTIONS, cancellationToken);
|
||||
if(authMethods is null)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to retrieve the authentication methods: the ERI server did not return a valid response."
|
||||
};
|
||||
}
|
||||
|
||||
return new()
|
||||
{
|
||||
Successful = true,
|
||||
Data = authMethods
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<APIResponse<AuthResponse>> AuthenticateAsync(IERIDataSource dataSource, RustService rustService, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var authMethod = dataSource.AuthMethod;
|
||||
var username = dataSource.Username;
|
||||
switch (dataSource.AuthMethod)
|
||||
{
|
||||
case AuthMethod.NONE:
|
||||
using (var request = new HttpRequestMessage(HttpMethod.Post, $"auth?authMethod={authMethod}"))
|
||||
{
|
||||
using var noneAuthResponse = await this.httpClient.SendAsync(request, cancellationToken);
|
||||
if(!noneAuthResponse.IsSuccessStatusCode)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = $"Failed to authenticate with the ERI server. Code: {noneAuthResponse.StatusCode}, Reason: {noneAuthResponse.ReasonPhrase}"
|
||||
};
|
||||
}
|
||||
|
||||
var noneAuthResult = await noneAuthResponse.Content.ReadFromJsonAsync<AuthResponse>(JSON_OPTIONS, cancellationToken);
|
||||
if(noneAuthResult == default)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to authenticate with the ERI server: the response was invalid."
|
||||
};
|
||||
}
|
||||
|
||||
this.securityToken = noneAuthResult.Token ?? string.Empty;
|
||||
return new()
|
||||
{
|
||||
Successful = true,
|
||||
Data = noneAuthResult
|
||||
};
|
||||
}
|
||||
|
||||
case AuthMethod.USERNAME_PASSWORD:
|
||||
var passwordResponse = await rustService.GetSecret(dataSource);
|
||||
if (!passwordResponse.Success)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to retrieve the password."
|
||||
};
|
||||
}
|
||||
|
||||
var password = await passwordResponse.Secret.Decrypt(Program.ENCRYPTION);
|
||||
using (var request = new HttpRequestMessage(HttpMethod.Post, $"auth?authMethod={authMethod}"))
|
||||
{
|
||||
// We must send both values inside the header. The username field is named 'user'.
|
||||
// The password field is named 'password'.
|
||||
request.Headers.Add("user", username);
|
||||
request.Headers.Add("password", password);
|
||||
|
||||
using var usernamePasswordAuthResponse = await this.httpClient.SendAsync(request, cancellationToken);
|
||||
if(!usernamePasswordAuthResponse.IsSuccessStatusCode)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = $"Failed to authenticate with the ERI server. Code: {usernamePasswordAuthResponse.StatusCode}, Reason: {usernamePasswordAuthResponse.ReasonPhrase}"
|
||||
};
|
||||
}
|
||||
|
||||
var usernamePasswordAuthResult = await usernamePasswordAuthResponse.Content.ReadFromJsonAsync<AuthResponse>(JSON_OPTIONS, cancellationToken);
|
||||
if(usernamePasswordAuthResult == default)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to authenticate with the server: the response was invalid."
|
||||
};
|
||||
}
|
||||
|
||||
this.securityToken = usernamePasswordAuthResult.Token ?? string.Empty;
|
||||
return new()
|
||||
{
|
||||
Successful = true,
|
||||
Data = usernamePasswordAuthResult
|
||||
};
|
||||
}
|
||||
|
||||
case AuthMethod.TOKEN:
|
||||
var tokenResponse = await rustService.GetSecret(dataSource);
|
||||
if (!tokenResponse.Success)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to retrieve the access token."
|
||||
};
|
||||
}
|
||||
|
||||
var token = await tokenResponse.Secret.Decrypt(Program.ENCRYPTION);
|
||||
using (var request = new HttpRequestMessage(HttpMethod.Post, $"auth?authMethod={authMethod}"))
|
||||
{
|
||||
request.Headers.Add("Authorization", $"Bearer {token}");
|
||||
|
||||
using var tokenAuthResponse = await this.httpClient.SendAsync(request, cancellationToken);
|
||||
if(!tokenAuthResponse.IsSuccessStatusCode)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = $"Failed to authenticate with the ERI server. Code: {tokenAuthResponse.StatusCode}, Reason: {tokenAuthResponse.ReasonPhrase}"
|
||||
};
|
||||
}
|
||||
|
||||
var tokenAuthResult = await tokenAuthResponse.Content.ReadFromJsonAsync<AuthResponse>(JSON_OPTIONS, cancellationToken);
|
||||
if(tokenAuthResult == default)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to authenticate with the ERI server: the response was invalid."
|
||||
};
|
||||
}
|
||||
|
||||
this.securityToken = tokenAuthResult.Token ?? string.Empty;
|
||||
return new()
|
||||
{
|
||||
Successful = true,
|
||||
Data = tokenAuthResult
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
this.securityToken = string.Empty;
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "The authentication method is not supported yet."
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<APIResponse<DataSourceInfo>> GetDataSourceInfoAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var request = new HttpRequestMessage(HttpMethod.Get, "/dataSource");
|
||||
request.Headers.Add("token", this.securityToken);
|
||||
|
||||
using var response = await this.httpClient.SendAsync(request, cancellationToken);
|
||||
if(!response.IsSuccessStatusCode)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = $"Failed to retrieve the data source information: there was an issue communicating with the ERI server. Code: {response.StatusCode}, Reason: {response.ReasonPhrase}"
|
||||
};
|
||||
}
|
||||
|
||||
var dataSourceInfo = await response.Content.ReadFromJsonAsync<DataSourceInfo>(JSON_OPTIONS, cancellationToken);
|
||||
if(dataSourceInfo == default)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to retrieve the data source information: the ERI server did not return a valid response."
|
||||
};
|
||||
}
|
||||
|
||||
return new()
|
||||
{
|
||||
Successful = true,
|
||||
Data = dataSourceInfo
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<APIResponse<List<EmbeddingInfo>>> GetEmbeddingInfoAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var request = new HttpRequestMessage(HttpMethod.Get, "/embedding/info");
|
||||
request.Headers.Add("token", this.securityToken);
|
||||
|
||||
using var response = await this.httpClient.SendAsync(request, cancellationToken);
|
||||
if(!response.IsSuccessStatusCode)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = $"Failed to retrieve the embedding information: there was an issue communicating with the ERI server. Code: {response.StatusCode}, Reason: {response.ReasonPhrase}"
|
||||
};
|
||||
}
|
||||
|
||||
var embeddingInfo = await response.Content.ReadFromJsonAsync<List<EmbeddingInfo>>(JSON_OPTIONS, cancellationToken);
|
||||
if(embeddingInfo is null)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to retrieve the embedding information: the ERI server did not return a valid response."
|
||||
};
|
||||
}
|
||||
|
||||
return new()
|
||||
{
|
||||
Successful = true,
|
||||
Data = embeddingInfo
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<APIResponse<List<RetrievalInfo>>> GetRetrievalInfoAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var request = new HttpRequestMessage(HttpMethod.Get, "/retrieval/info");
|
||||
request.Headers.Add("token", this.securityToken);
|
||||
|
||||
using var response = await this.httpClient.SendAsync(request, cancellationToken);
|
||||
if(!response.IsSuccessStatusCode)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = $"Failed to retrieve the retrieval information: there was an issue communicating with the ERI server. Code: {response.StatusCode}, Reason: {response.ReasonPhrase}"
|
||||
};
|
||||
}
|
||||
|
||||
var retrievalInfo = await response.Content.ReadFromJsonAsync<List<RetrievalInfo>>(JSON_OPTIONS, cancellationToken);
|
||||
if(retrievalInfo is null)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to retrieve the retrieval information: the ERI server did not return a valid response."
|
||||
};
|
||||
}
|
||||
|
||||
return new()
|
||||
{
|
||||
Successful = true,
|
||||
Data = retrievalInfo
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<APIResponse<List<Context>>> ExecuteRetrievalAsync(RetrievalRequest request, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/retrieval");
|
||||
requestMessage.Headers.Add("token", this.securityToken);
|
||||
|
||||
using var content = new StringContent(JsonSerializer.Serialize(request, JSON_OPTIONS), Encoding.UTF8, "application/json");
|
||||
requestMessage.Content = content;
|
||||
|
||||
using var response = await this.httpClient.SendAsync(requestMessage, cancellationToken);
|
||||
if(!response.IsSuccessStatusCode)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = $"Failed to execute the retrieval request: there was an issue communicating with the ERI server. Code: {response.StatusCode}, Reason: {response.ReasonPhrase}"
|
||||
};
|
||||
}
|
||||
|
||||
var contexts = await response.Content.ReadFromJsonAsync<List<Context>>(JSON_OPTIONS, cancellationToken);
|
||||
if(contexts is null)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to execute the retrieval request: the ERI server did not return a valid response."
|
||||
};
|
||||
}
|
||||
|
||||
return new()
|
||||
{
|
||||
Successful = true,
|
||||
Data = contexts
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<APIResponse<SecurityRequirements>> GetSecurityRequirementsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var request = new HttpRequestMessage(HttpMethod.Get, "/security/requirements");
|
||||
request.Headers.Add("token", this.securityToken);
|
||||
|
||||
using var response = await this.httpClient.SendAsync(request, cancellationToken);
|
||||
if(!response.IsSuccessStatusCode)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = $"Failed to retrieve the security requirements: there was an issue communicating with the ERI server. Code: {response.StatusCode}, Reason: {response.ReasonPhrase}"
|
||||
};
|
||||
}
|
||||
|
||||
var securityRequirements = await response.Content.ReadFromJsonAsync<SecurityRequirements>(JSON_OPTIONS, cancellationToken);
|
||||
if(securityRequirements == default)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to retrieve the security requirements: the ERI server did not return a valid response."
|
||||
};
|
||||
}
|
||||
|
||||
return new()
|
||||
{
|
||||
Successful = true,
|
||||
Data = securityRequirements
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
namespace AIStudio.Tools.ERIClient;
|
||||
|
||||
public interface IERIClient : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the available authentication methods from the ERI server.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// No authentication is required to retrieve the available authentication methods.
|
||||
/// </remarks>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The available authentication methods.</returns>
|
||||
public Task<APIResponse<List<AuthScheme>>> GetAuthMethodsAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Authenticate the user to the ERI server.
|
||||
/// </summary>
|
||||
/// <param name="dataSource">The data source to use.</param>
|
||||
/// <param name="rustService">The Rust service.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The authentication response.</returns>
|
||||
public Task<APIResponse<AuthResponse>> AuthenticateAsync(IERIDataSource dataSource, RustService rustService, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the data source information from the ERI server.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The data source information.</returns>
|
||||
public Task<APIResponse<DataSourceInfo>> GetDataSourceInfoAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the embedding information from the ERI server.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A list of embedding information.</returns>
|
||||
public Task<APIResponse<List<EmbeddingInfo>>> GetEmbeddingInfoAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the retrieval information from the ERI server.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A list of retrieval information.</returns>
|
||||
public Task<APIResponse<List<RetrievalInfo>>> GetRetrievalInfoAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Executes a retrieval request on the ERI server.
|
||||
/// </summary>
|
||||
/// <param name="request">The retrieval request.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The retrieved contexts to use for augmentation and generation.</returns>
|
||||
public Task<APIResponse<List<Context>>> ExecuteRetrievalAsync(RetrievalRequest request, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the security requirements from the ERI server.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The security requirements.</returns>
|
||||
public Task<APIResponse<SecurityRequirements>> GetSecurityRequirementsAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
public static class FileInfoExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the file size in human-readable format.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo">The file info object.</param>
|
||||
/// <returns>The file size in human-readable format.</returns>
|
||||
public static string FileSize(this FileInfo fileInfo)
|
||||
{
|
||||
if (!fileInfo.Exists)
|
||||
return "N/A";
|
||||
|
||||
return fileInfo.Length.FileSize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
public static class LongExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Formats the file size in a human-readable format.
|
||||
/// </summary>
|
||||
/// <param name="sizeBytes">The size in bytes.</param>
|
||||
/// <returns>The formatted file size.</returns>
|
||||
public static string FileSize(this long sizeBytes)
|
||||
{
|
||||
string[] sizes = { "B", "kB", "MB", "GB", "TB" };
|
||||
var order = 0;
|
||||
while (sizeBytes >= 1024 && order < sizes.Length - 1)
|
||||
{
|
||||
order++;
|
||||
sizeBytes /= 1024;
|
||||
}
|
||||
|
||||
return $"{sizeBytes:0.##} {sizes[order]}";
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using ERI_Client.V1;
|
||||
using AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
namespace AIStudio.Tools.Validation;
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
def main [] {}
|
||||
|
||||
def are_assets_exist [rid: string]: string -> bool {
|
||||
def are_assets_exist [rid: string] {
|
||||
$"bin/release/net8.0/($rid)/publish/wwwroot/_content/MudBlazor/MudBlazor.min.css" | path exists
|
||||
}
|
||||
|
||||
def "main help" []: nothing -> nothing {
|
||||
def "main help" [] {
|
||||
print "Usage: nu build.nu [action]"
|
||||
print ""
|
||||
print "Optional Actions:"
|
||||
@@ -26,7 +26,7 @@ def "main help" []: nothing -> nothing {
|
||||
print ""
|
||||
}
|
||||
|
||||
def "main prepare" [action: string]: string -> nothing {
|
||||
def "main prepare" [action: string] {
|
||||
if (update_app_version $action) {
|
||||
main fix_web_assets
|
||||
inc_build_number
|
||||
@@ -36,7 +36,7 @@ def "main prepare" [action: string]: string -> nothing {
|
||||
}
|
||||
}
|
||||
|
||||
def "main metadata" []: nothing -> nothing {
|
||||
def "main metadata" [] {
|
||||
update_dotnet_version
|
||||
update_rust_version
|
||||
update_mudblazor_version
|
||||
@@ -46,7 +46,7 @@ def "main metadata" []: nothing -> nothing {
|
||||
update_license_year "Pages/About.razor.cs"
|
||||
}
|
||||
|
||||
def "main fix_web_assets" []: nothing -> nothing {
|
||||
def "main fix_web_assets" [] {
|
||||
|
||||
# Get the matching RIDs for the current OS:
|
||||
let rids = get_rids
|
||||
@@ -70,7 +70,7 @@ def "main fix_web_assets" []: nothing -> nothing {
|
||||
}
|
||||
}
|
||||
|
||||
def "main publish" []: nothing -> nothing {
|
||||
def "main publish" [] {
|
||||
|
||||
main metadata
|
||||
|
||||
@@ -139,7 +139,7 @@ def "main publish" []: nothing -> nothing {
|
||||
print "Building done."
|
||||
}
|
||||
|
||||
def get_rids []: nothing -> list {
|
||||
def get_rids [] {
|
||||
# Define the list of RIDs to build for, cf. https://learn.microsoft.com/en-us/dotnet/core/rid-catalog:
|
||||
let rids = ["win-x64", "win-arm64", "linux-x64", "linux-arm64", "osx-arm64", "osx-x64"]
|
||||
|
||||
@@ -163,11 +163,15 @@ def get_rids []: nothing -> list {
|
||||
$rids
|
||||
}
|
||||
|
||||
def get_os []: nothing -> string {
|
||||
(sys host).name | str downcase
|
||||
def get_os [] {
|
||||
let os = (sys host).name | str downcase
|
||||
if $os =~ "linux" {
|
||||
return "linux"
|
||||
}
|
||||
$os
|
||||
}
|
||||
|
||||
def update_build_time []: nothing -> nothing {
|
||||
def update_build_time [] {
|
||||
mut meta_lines = open --raw ../../metadata.txt | lines
|
||||
mut build_time = $meta_lines.1
|
||||
|
||||
@@ -179,7 +183,7 @@ def update_build_time []: nothing -> nothing {
|
||||
$meta_lines | save --raw --force ../../metadata.txt
|
||||
}
|
||||
|
||||
def inc_build_number []: nothing -> nothing {
|
||||
def inc_build_number [] {
|
||||
mut meta_lines = open --raw ../../metadata.txt | lines
|
||||
mut build_number = $meta_lines.2 | into int
|
||||
|
||||
@@ -191,7 +195,7 @@ def inc_build_number []: nothing -> nothing {
|
||||
$meta_lines | save --raw --force ../../metadata.txt
|
||||
}
|
||||
|
||||
def update_dotnet_version []: nothing -> nothing {
|
||||
def update_dotnet_version [] {
|
||||
mut meta_lines = open --raw ../../metadata.txt | lines
|
||||
mut dotnet_sdk_version = $meta_lines.3
|
||||
mut dotnet_version = $meta_lines.4
|
||||
@@ -211,11 +215,11 @@ def update_dotnet_version []: nothing -> nothing {
|
||||
$meta_lines | save --raw --force ../../metadata.txt
|
||||
}
|
||||
|
||||
def update_rust_version []: nothing -> nothing {
|
||||
def update_rust_version [] {
|
||||
mut meta_lines = open --raw ../../metadata.txt | lines
|
||||
mut rust_version = $meta_lines.5
|
||||
|
||||
let rust_data = (^rustc -Vv) | parse --regex 'rustc (?<version>[0-9.]+) \((?<commit>[a-zA-Z0-9]+)'
|
||||
let rust_data = (^rustc -Vv) | parse --regex 'rustc (?<version>[0-9.]+)(?:-nightly)? \((?<commit>[a-zA-Z0-9]+)'
|
||||
let version = $rust_data.version.0
|
||||
let commit = $rust_data.commit.0
|
||||
|
||||
@@ -225,7 +229,7 @@ def update_rust_version []: nothing -> nothing {
|
||||
$meta_lines | save --raw --force ../../metadata.txt
|
||||
}
|
||||
|
||||
def update_mudblazor_version []: nothing -> nothing {
|
||||
def update_mudblazor_version [] {
|
||||
mut meta_lines = open --raw ../../metadata.txt | lines
|
||||
mut mudblazor_version = $meta_lines.6
|
||||
|
||||
@@ -238,7 +242,7 @@ def update_mudblazor_version []: nothing -> nothing {
|
||||
$meta_lines | save --raw --force ../../metadata.txt
|
||||
}
|
||||
|
||||
def update_tauri_version []: nothing -> nothing {
|
||||
def update_tauri_version [] {
|
||||
mut meta_lines = open --raw ../../metadata.txt | lines
|
||||
mut tauri_version = $meta_lines.7
|
||||
|
||||
@@ -272,7 +276,7 @@ def update_license_year [licence_file: string] {
|
||||
$updated_license_text | save --raw --force $licence_file
|
||||
}
|
||||
|
||||
def update_app_version [action: string]: string -> bool {
|
||||
def update_app_version [action: string] {
|
||||
mut meta_lines = open --raw ../../metadata.txt | lines
|
||||
mut app_version = $meta_lines.0
|
||||
|
||||
@@ -317,7 +321,7 @@ def update_app_version [action: string]: string -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
def update_project_commit_hash []: nothing -> nothing {
|
||||
def update_project_commit_hash [] {
|
||||
mut meta_lines = open --raw ../../metadata.txt | lines
|
||||
mut commit_hash = $meta_lines.8
|
||||
|
||||
@@ -352,7 +356,7 @@ def update_project_commit_hash []: nothing -> nothing {
|
||||
$meta_lines | save --raw --force ../../metadata.txt
|
||||
}
|
||||
|
||||
def update_changelog []: nothing -> nothing {
|
||||
def update_changelog [] {
|
||||
# Get all changelog files:
|
||||
let all_changelog_files = glob wwwroot/changelog/*.md
|
||||
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
},
|
||||
"Microsoft.Extensions.FileProviders.Embedded": {
|
||||
"type": "Direct",
|
||||
"requested": "[9.0.0, )",
|
||||
"resolved": "9.0.0",
|
||||
"contentHash": "6Ev1goLIvggLF6uCs6oZvdr9JM+2b1Zj+4FLdBWNW5iw3tm2BymVIb0yMsjnQTBWL7YUmqVWH3u45hSqOfvuqg==",
|
||||
"requested": "[9.0.1, )",
|
||||
"resolved": "9.0.1",
|
||||
"contentHash": "Pnw9plviwRguhVxHjthooumWH8yUxfF54tuu/+VoVBOuqBmzu6VUnn4NGf3eVkEO1k5q2nihOIAQL/7UMemSmQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.FileProviders.Abstractions": "9.0.0"
|
||||
"Microsoft.Extensions.FileProviders.Abstractions": "9.0.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.NET.ILLink.Tasks": {
|
||||
@@ -39,9 +39,9 @@
|
||||
},
|
||||
"MudBlazor": {
|
||||
"type": "Direct",
|
||||
"requested": "[7.15.0, )",
|
||||
"resolved": "7.15.0",
|
||||
"contentHash": "b2+hO7ZTGwsGhu9SoowuNe2ETeF1GzExpg/hIYR++rSh+vy4v/QD0iOsrATWv3LdFJXSgk3d1+XIr5srW3jBiA==",
|
||||
"requested": "[7.16.0, )",
|
||||
"resolved": "7.16.0",
|
||||
"contentHash": "68bEFn7MLCyOB4yYxtvZG+AA+bZbxBDcIOhnSVFrHrAg8LLXVw1LxHCp9EyRktlREpnoAO1JxINp+5WeHc9Z4w==",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.Components": "8.0.8",
|
||||
"Microsoft.AspNetCore.Components.Web": "8.0.8",
|
||||
@@ -147,10 +147,10 @@
|
||||
},
|
||||
"Microsoft.Extensions.FileProviders.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.0",
|
||||
"contentHash": "uK439QzYR0q2emLVtYzwyK3x+T5bTY4yWsd/k/ZUS9LR6Sflp8MIdhGXW8kQCd86dQD4tLqvcbLkku8qHY263Q==",
|
||||
"resolved": "9.0.1",
|
||||
"contentHash": "DguZYt1DWL05+8QKWL3b6bW7A2pC5kYFMY5iXM6W2M23jhvcNa8v6AU8PvVJBcysxHwr9/jax0agnwoBumsSwg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Primitives": "9.0.0"
|
||||
"Microsoft.Extensions.Primitives": "9.0.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Localization": {
|
||||
@@ -188,8 +188,8 @@
|
||||
},
|
||||
"Microsoft.Extensions.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.0",
|
||||
"contentHash": "N3qEBzmLMYiASUlKxxFIISP4AiwuPTHF5uCh+2CWSwwzAJiIYx0kBJsS30cp1nvhSySFAVi30jecD307jV+8Kg=="
|
||||
"resolved": "9.0.1",
|
||||
"contentHash": "bHtTesA4lrSGD1ZUaMIx6frU3wyy0vYtTa/hM6gGQu5QNrydObv8T5COiGUWsisflAfmsaFOe9Xvw5NSO99z0g=="
|
||||
},
|
||||
"Microsoft.JSInterop": {
|
||||
"type": "Transitive",
|
||||
@@ -205,11 +205,8 @@
|
||||
"type": "Transitive",
|
||||
"resolved": "0.16.9",
|
||||
"contentHash": "7WaVMHklpT3Ye2ragqRIwlFRsb6kOk63BOGADV0fan3ulVfGLUYkDi5yNUsZS/7FVNkWbtHAlDLmu4WnHGfqvQ=="
|
||||
},
|
||||
"ericlientv1": {
|
||||
"type": "Project"
|
||||
}
|
||||
},
|
||||
"net8.0/osx-x64": {}
|
||||
"net8.0/osx-arm64": {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# v0.9.28, build 203 (2025-02-09 16:33 UTC)
|
||||
- Added an information view to all data sources to the data source configuration page. The data source configuration is a preview feature behind the RAG feature flag.
|
||||
- Added a ERI ((E)xternal (R)etrieval (I)nterface) client for communication with any ERI server.
|
||||
- Added the option to select the default app provider in the assistant settings under provider selection. Thank you, Peer (`peerschuett`), for reporting the issue.
|
||||
- Added `donework` as financial supporter. Thank you, Dominic!
|
||||
- Added content contributors to the supporter page. We thank Luc (`DevNullx64`) for his build script contribution as well as Kerstin (`KeSah001`) for creating the Wiki pages.
|
||||
- Improved the resource handling when loading models.
|
||||
- Improved the build script to support Arch Linux and other Linux distributions, along with Rust nightly environments. Thanks `SolsticeSpectrum` for the contribution.
|
||||
- Upgraded the dependencies Rust, .NET, OpenSSL, MudBlazor, `rand`, `rand_chacha`, and others to the latest usable versions.
|
||||
+8
-8
@@ -1,9 +1,9 @@
|
||||
0.9.27
|
||||
2025-01-21 18:24:04 UTC
|
||||
202
|
||||
8.0.111 (commit f3835a7fb4)
|
||||
8.0.11 (commit 9cb3b725e3)
|
||||
1.84.0 (commit 9fc6b4312)
|
||||
7.15.0
|
||||
0.9.28
|
||||
2025-02-09 16:33:40 UTC
|
||||
203
|
||||
8.0.112 (commit f7cd007b9d)
|
||||
8.0.12 (commit 89ef51c5d8)
|
||||
1.84.1 (commit e71f9a9a9)
|
||||
7.16.0
|
||||
1.8.1
|
||||
940459ca467, release
|
||||
6da4cf1425f, release
|
||||
Generated
+236
-56
@@ -325,9 +325,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.6.1"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
|
||||
checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -1310,6 +1310,18 @@ dependencies = [
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.13.3+wasi-0.2.2",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.29.0"
|
||||
@@ -1706,6 +1718,23 @@ dependencies = [
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.27.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"hyper 1.4.1",
|
||||
"hyper-util",
|
||||
"rustls 0.23.22",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.1",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.5.0"
|
||||
@@ -1737,9 +1766,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.6"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956"
|
||||
checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
@@ -1750,7 +1779,6 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
@@ -2319,7 +2347,7 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "mindwork-ai-studio"
|
||||
version = "0.9.27"
|
||||
version = "0.9.28"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"arboard",
|
||||
@@ -2331,11 +2359,12 @@ dependencies = [
|
||||
"keyring",
|
||||
"log",
|
||||
"once_cell",
|
||||
"openssl",
|
||||
"pbkdf2",
|
||||
"rand 0.8.5",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand 0.9.0",
|
||||
"rand_chacha 0.9.0",
|
||||
"rcgen",
|
||||
"reqwest 0.12.4",
|
||||
"reqwest 0.12.12",
|
||||
"rocket",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2745,9 +2774,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.66"
|
||||
version = "0.10.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
|
||||
checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cfg-if",
|
||||
@@ -2786,9 +2815,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.103"
|
||||
version = "0.9.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
|
||||
checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -3050,26 +3079,6 @@ dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.14"
|
||||
@@ -3237,6 +3246,17 @@ dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.0",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
@@ -3257,6 +3277,16 @@ dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
@@ -3275,6 +3305,16 @@ dependencies = [
|
||||
"getrandom 0.2.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff"
|
||||
dependencies = [
|
||||
"getrandom 0.3.1",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
@@ -3433,8 +3473,8 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"system-configuration",
|
||||
"sync_wrapper 0.1.2",
|
||||
"system-configuration 0.5.1",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-util",
|
||||
@@ -3449,9 +3489,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.4"
|
||||
version = "0.12.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10"
|
||||
checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
@@ -3463,6 +3503,7 @@ dependencies = [
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"hyper 1.4.1",
|
||||
"hyper-rustls",
|
||||
"hyper-tls 0.6.0",
|
||||
"hyper-util",
|
||||
"ipnet",
|
||||
@@ -3477,16 +3518,17 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"system-configuration",
|
||||
"sync_wrapper 1.0.2",
|
||||
"system-configuration 0.6.1",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tower",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"winreg 0.52.0",
|
||||
"windows-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3601,7 +3643,7 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"ref-cast",
|
||||
"rustls",
|
||||
"rustls 0.21.12",
|
||||
"rustls-pemfile 1.0.4",
|
||||
"serde",
|
||||
"smallvec",
|
||||
@@ -3609,7 +3651,7 @@ dependencies = [
|
||||
"state 0.6.0",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-rustls 0.24.1",
|
||||
"uncased",
|
||||
]
|
||||
|
||||
@@ -3649,10 +3691,23 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"rustls-webpki",
|
||||
"rustls-webpki 0.101.7",
|
||||
"sct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fb9263ab4eb695e42321db096e3b8fbd715a59b154d5c88d82db2175b681ba7"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki 0.102.8",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.4"
|
||||
@@ -3674,9 +3729,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.7.0"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
|
||||
checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
@@ -3688,6 +3743,17 @@ dependencies = [
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.102.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.17"
|
||||
@@ -4109,9 +4175,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.4.1"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
@@ -4141,6 +4207,15 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.1"
|
||||
@@ -4160,7 +4235,18 @@ checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation 0.9.4",
|
||||
"system-configuration-sys",
|
||||
"system-configuration-sys 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"core-foundation 0.9.4",
|
||||
"system-configuration-sys 0.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4173,6 +4259,16 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration-sys"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-deps"
|
||||
version = "5.0.0"
|
||||
@@ -4665,7 +4761,17 @@ version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"rustls 0.21.12",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
|
||||
dependencies = [
|
||||
"rustls 0.23.22",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
@@ -4763,14 +4869,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.4.13"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
|
||||
checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"pin-project",
|
||||
"pin-project-lite",
|
||||
"sync_wrapper 1.0.2",
|
||||
"tokio",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
@@ -4778,15 +4884,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tower-layer"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
|
||||
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
|
||||
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
@@ -5024,6 +5130,15 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.13.3+wasi-0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.92"
|
||||
@@ -5306,6 +5421,36 @@ version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278"
|
||||
|
||||
[[package]]
|
||||
name = "windows-registry"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
|
||||
dependencies = [
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
|
||||
dependencies = [
|
||||
"windows-result",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.42.0"
|
||||
@@ -5624,6 +5769,15 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "write16"
|
||||
version = "1.0.0"
|
||||
@@ -5765,6 +5919,26 @@ dependencies = [
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa91407dacce3a68c56de03abe2760159582b846c6a4acd2f456618087f12713"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06718a168365cad3d5ff0bb133aad346959a2074bd4a85c121255a11304a8626"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.5"
|
||||
@@ -5786,6 +5960,12 @@ dependencies = [
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.10.4"
|
||||
|
||||
+7
-4
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mindwork-ai-studio"
|
||||
version = "0.9.27"
|
||||
version = "0.9.28"
|
||||
edition = "2021"
|
||||
description = "MindWork AI Studio"
|
||||
authors = ["Thorsten Sommer"]
|
||||
@@ -20,8 +20,8 @@ flexi_logger = "0.29.8"
|
||||
log = { version = "0.4", features = ["kv"] }
|
||||
once_cell = "1.20"
|
||||
rocket = { version = "0.5.1", features = ["json", "tls"] }
|
||||
rand = "0.8"
|
||||
rand_chacha = "0.3.1"
|
||||
rand = "0.9"
|
||||
rand_chacha = "0.9"
|
||||
base64 = "0.22.1"
|
||||
cipher = { version = "0.4.4", features = ["std"] }
|
||||
aes = "0.8.4"
|
||||
@@ -36,7 +36,10 @@ url = "2.5"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
# See issue https://github.com/tauri-apps/tauri/issues/4470
|
||||
reqwest = { version = "0.12", features = ["native-tls-vendored"] }
|
||||
reqwest = { version = "0.12.12", features = ["native-tls-vendored"] }
|
||||
|
||||
# Fixes security vulnerability downstream, where the upstream is not fixed yet:
|
||||
openssl = "0.10.70"
|
||||
|
||||
[features]
|
||||
custom-protocol = ["tauri/custom-protocol"]
|
||||
@@ -8,7 +8,7 @@ use rocket::request::FromRequest;
|
||||
/// The API token used to authenticate requests.
|
||||
pub static API_TOKEN: Lazy<APIToken> = Lazy::new(|| {
|
||||
let mut token = [0u8; 32];
|
||||
let mut rng = rand_chacha::ChaChaRng::from_entropy();
|
||||
let mut rng = rand_chacha::ChaChaRng::from_os_rng();
|
||||
rng.fill_bytes(&mut token);
|
||||
|
||||
let token = APIToken::from_bytes(token.to_vec());
|
||||
|
||||
@@ -32,7 +32,7 @@ pub static ENCRYPTION: Lazy<Encryption> = Lazy::new(|| {
|
||||
// We use a cryptographically secure pseudo-random number generator
|
||||
// to generate the secret password & salt. ChaCha20Rng is the algorithm
|
||||
// of our choice:
|
||||
let mut rng = rand_chacha::ChaChaRng::from_entropy();
|
||||
let mut rng = rand_chacha::ChaChaRng::from_os_rng();
|
||||
|
||||
// Fill the secret key & salt with random bytes:
|
||||
rng.fill_bytes(&mut secret_key);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
},
|
||||
"package": {
|
||||
"productName": "MindWork AI Studio",
|
||||
"version": "0.9.27"
|
||||
"version": "0.9.28"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
|
||||
Reference in new issue
Block a user