mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-21 21:38:00 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c23f0f49f9 | ||
|
|
7a08baf99b | ||
|
|
cfc2103b43 | ||
|
|
f8dbc186fb | ||
|
|
6041b42510 | ||
|
|
10dc03f33b | ||
|
|
437ea2c243 | ||
|
|
b0d4615506 | ||
|
|
c4d9af18cd | ||
|
|
9996b810e2 | ||
|
|
53ec4c2162 | ||
|
|
38facf3e04 | ||
|
|
68404199ba | ||
|
|
08b8f42c25 | ||
|
|
3c41fe2771 |
No files matched your search
@@ -167,6 +167,12 @@ jobs:
|
||||
sed -i '' "10s/.*/${{ matrix.dotnet_runtime }}/" metadata.txt
|
||||
fi
|
||||
|
||||
# Next line is the necessary PDFium version.
|
||||
# The format is '137.0.7123.0'. What we need
|
||||
# is the '7123' part:
|
||||
pdfium_version=$(sed -n '11p' metadata.txt)
|
||||
pdfium_version=$(echo $pdfium_version | cut -d'.' -f3)
|
||||
|
||||
# Write the metadata to the environment:
|
||||
echo "APP_VERSION=${app_version}" >> $GITHUB_ENV
|
||||
echo "FORMATTED_APP_VERSION=${formatted_app_version}" >> $GITHUB_ENV
|
||||
@@ -178,6 +184,7 @@ jobs:
|
||||
echo "MUD_BLAZOR_VERSION=${mud_blazor_version}" >> $GITHUB_ENV
|
||||
echo "TAURI_VERSION=${tauri_version}" >> $GITHUB_ENV
|
||||
echo "ARCHITECTURE=${{ matrix.dotnet_runtime }}" >> $GITHUB_ENV
|
||||
echo "PDFIUM_VERSION=${pdfium_version}" >> $GITHUB_ENV
|
||||
|
||||
# Log the metadata:
|
||||
echo "App version: '${formatted_app_version}'"
|
||||
@@ -189,6 +196,7 @@ jobs:
|
||||
echo "MudBlazor version: '${mud_blazor_version}'"
|
||||
echo "Tauri version: '${tauri_version}'"
|
||||
echo "Architecture: '${{ matrix.dotnet_runtime }}'"
|
||||
echo "PDFium version: '${pdfium_version}'"
|
||||
|
||||
- name: Read and format metadata (Windows)
|
||||
if: matrix.platform == 'windows-latest'
|
||||
@@ -227,6 +235,12 @@ jobs:
|
||||
# Write the changed metadata back to the file:
|
||||
Set-Content -Path metadata.txt -Value $metadata
|
||||
|
||||
# Next line is the necessary PDFium version.
|
||||
# The format is '137.0.7123.0'. What we need
|
||||
# is the '7123' part:
|
||||
$pdfium_version = $metadata[10]
|
||||
$pdfium_version = $pdfium_version.Split('.')[2]
|
||||
|
||||
# Write the metadata to the environment:
|
||||
Write-Output "APP_VERSION=${app_version}" >> $env:GITHUB_ENV
|
||||
Write-Output "FORMATTED_APP_VERSION=${formatted_app_version}" >> $env:GITHUB_ENV
|
||||
@@ -237,6 +251,7 @@ jobs:
|
||||
Write-Output "RUST_VERSION=${rust_version}" >> $env:GITHUB_ENV
|
||||
Write-Output "MUD_BLAZOR_VERSION=${mud_blazor_version}" >> $env:GITHUB_ENV
|
||||
Write-Output "ARCHITECTURE=${{ matrix.dotnet_runtime }}" >> $env:GITHUB_ENV
|
||||
Write-Output "PDFIUM_VERSION=${pdfium_version}" >> $env:GITHUB_ENV
|
||||
|
||||
# Log the metadata:
|
||||
Write-Output "App version: '${formatted_app_version}'"
|
||||
@@ -248,6 +263,7 @@ jobs:
|
||||
Write-Output "MudBlazor version: '${mud_blazor_version}'"
|
||||
Write-Output "Tauri version: '${tauri_version}'"
|
||||
Write-Output "Architecture: '${{ matrix.dotnet_runtime }}'"
|
||||
Write-Output "PDFium version: '${pdfium_version}'"
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
@@ -255,6 +271,117 @@ jobs:
|
||||
dotnet-version: ${{ env.DOTNET_SDK_VERSION }}
|
||||
cache: true
|
||||
cache-dependency-path: 'app/MindWork AI Studio/packages.lock.json'
|
||||
|
||||
- name: Deploy PDFium (Unix)
|
||||
if: matrix.platform != 'windows-latest'
|
||||
env:
|
||||
PDFIUM_VERSION: ${{ env.PDFIUM_VERSION }}
|
||||
DOTNET_RUNTIME: ${{ matrix.dotnet_runtime }}
|
||||
run: |
|
||||
set -e
|
||||
|
||||
# Target directory:
|
||||
TLIB_DIR="runtime/resources/libraries"
|
||||
mkdir -p "$TLIB_DIR"
|
||||
|
||||
case "${DOTNET_RUNTIME}" in
|
||||
linux-x64)
|
||||
PDFIUM_FILE="linux-x64.tgz"
|
||||
LIB_SOURCE="lib/libpdfium.so"
|
||||
LIB_TARGET="libpdfium.so"
|
||||
;;
|
||||
linux-arm64)
|
||||
PDFIUM_FILE="linux-arm64.tgz"
|
||||
LIB_SOURCE="lib/libpdfium.so"
|
||||
LIB_TARGET="libpdfium.so"
|
||||
;;
|
||||
osx-x64)
|
||||
PDFIUM_FILE="mac-x64.tgz"
|
||||
LIB_SOURCE="lib/libpdfium.dylib"
|
||||
LIB_TARGET="libpdfium.dylib"
|
||||
;;
|
||||
osx-arm64)
|
||||
PDFIUM_FILE="mac-arm64.tgz"
|
||||
LIB_SOURCE="lib/libpdfium.dylib"
|
||||
LIB_TARGET="libpdfium.dylib"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown platform: ${DOTNET_RUNTIME}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
PDFIUM_URL="https://github.com/bblanchon/pdfium-binaries/releases/download/chromium%2F${PDFIUM_VERSION}/pdfium-${PDFIUM_FILE}"
|
||||
|
||||
echo "Download PDFium $PDFIUM_URL ..."
|
||||
TMP=$(mktemp -d)
|
||||
ARCHIVE="${TMP}/pdfium.tgz"
|
||||
|
||||
curl -fsSL -o "$ARCHIVE" "$PDFIUM_URL"
|
||||
|
||||
echo "Extracting PDFium ..."
|
||||
tar xzf "$ARCHIVE" -C "$TMP"
|
||||
SRC="${TMP}/${LIB_SOURCE}"
|
||||
|
||||
if [ ! -f "$SRC" ]; then
|
||||
echo "Was not able to find PDFium source: $SRC"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Copy PDFium from ${LIB_TARGET} to ${TLIB_DIR}/"
|
||||
cp -f "$SRC" "$TLIB_DIR/$LIB_TARGET"
|
||||
|
||||
echo "Cleaning up ..."
|
||||
rm -fr "$TMP"
|
||||
|
||||
- name: Install PDFium (Windows)
|
||||
if: matrix.platform == 'windows-latest'
|
||||
env:
|
||||
PDFIUM_VERSION: ${{ env.PDFIUM_VERSION }}
|
||||
DOTNET_RUNTIME: ${{ matrix.dotnet_runtime }}
|
||||
run: |
|
||||
$TLIB_DIR = "runtime\resources\libraries"
|
||||
New-Item -ItemType Directory -Force -Path $TLIB_DIR | Out-Null
|
||||
|
||||
switch ($env:DOTNET_RUNTIME) {
|
||||
"win-x64" {
|
||||
$PDFIUM_FILE = "win-x64.tgz"
|
||||
$LIB_SOURCE = "bin\pdfium.dll"
|
||||
$LIB_TARGET = "pdfium.dll"
|
||||
}
|
||||
"win-arm64" {
|
||||
$PDFIUM_FILE = "win-arm64.tgz"
|
||||
$LIB_SOURCE = "bin\pdfium.dll"
|
||||
$LIB_TARGET = "pdfium.dll"
|
||||
}
|
||||
default {
|
||||
Write-Error "Unknown platform: $($env:DOTNET_RUNTIME)"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
$PDFIUM_URL = "https://github.com/bblanchon/pdfium-binaries/releases/download/chromium%2F$($env:PDFIUM_VERSION)/pdfium-$PDFIUM_FILE"
|
||||
Write-Host "Download $PDFIUM_URL ..."
|
||||
$TMP = New-TemporaryFile | Split-Path
|
||||
$ARCHIVE = Join-Path $TMP "pdfium.tgz"
|
||||
|
||||
Invoke-WebRequest -Uri $PDFIUM_URL -OutFile $ARCHIVE
|
||||
|
||||
Write-Host "Extracting PDFium ..."
|
||||
tar -xzf $ARCHIVE -C $TMP
|
||||
|
||||
$SRC = Join-Path $TMP $LIB_SOURCE
|
||||
if (!(Test-Path $SRC)) {
|
||||
Write-Error "Cannot find PDFium source: $SRC"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$DEST = Join-Path $TLIB_DIR $LIB_TARGET
|
||||
Copy-Item -Path $SRC -Destination $DEST -Force
|
||||
|
||||
Write-Host "Cleaning up ..."
|
||||
Remove-Item $ARCHIVE -Force
|
||||
Remove-Item $TMP -Recurse -Force
|
||||
|
||||
- name: Build .NET project
|
||||
run: |
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# Ignore pdfium library:
|
||||
libpdfium.dylib
|
||||
libpdfium.so
|
||||
libpdfium.dll
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
|
||||
@@ -17,5 +17,10 @@ public sealed class CheckRidsCommand
|
||||
{
|
||||
Console.WriteLine($"- {rid}");
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("The RID for the current OS and CPU is:");
|
||||
var currentRid = Environment.GetCurrentRid();
|
||||
Console.WriteLine($"- {currentRid}");
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,29 @@ namespace Build.Commands;
|
||||
|
||||
public sealed partial class CollectI18NKeysCommand
|
||||
{
|
||||
private const string START_TAG1 = """
|
||||
T("
|
||||
""";
|
||||
|
||||
private const string START_TAG2 = """
|
||||
TB("
|
||||
""";
|
||||
|
||||
private const string START_TAG3 = """
|
||||
T(@"
|
||||
""";
|
||||
|
||||
private const string END_TAG = """
|
||||
")
|
||||
""";
|
||||
|
||||
private static readonly (string Tag, int Length)[] START_TAGS =
|
||||
[
|
||||
(START_TAG1, START_TAG1.Length),
|
||||
(START_TAG2, START_TAG2.Length),
|
||||
(START_TAG3, START_TAG3.Length)
|
||||
];
|
||||
|
||||
[Command("collect-i18n", Description = "Collect I18N keys")]
|
||||
public async Task CollectI18NKeys()
|
||||
{
|
||||
@@ -133,36 +156,22 @@ public sealed partial class CollectI18NKeysCommand
|
||||
|
||||
private List<string> FindAllTextTags(ReadOnlySpan<char> fileContent)
|
||||
{
|
||||
const string START_TAG1 = """
|
||||
T("
|
||||
""";
|
||||
|
||||
const string START_TAG2 = """
|
||||
TB("
|
||||
""";
|
||||
|
||||
const string END_TAG = """
|
||||
")
|
||||
""";
|
||||
|
||||
(int Index, int Len) FindNextStart(ReadOnlySpan<char> content)
|
||||
{
|
||||
var startIdx1 = content.IndexOf(START_TAG1);
|
||||
var startIdx2 = content.IndexOf(START_TAG2);
|
||||
|
||||
if (startIdx1 == -1 && startIdx2 == -1)
|
||||
return (-1, 0);
|
||||
|
||||
if (startIdx1 == -1)
|
||||
return (startIdx2, START_TAG2.Length);
|
||||
|
||||
if (startIdx2 == -1)
|
||||
return (startIdx1, START_TAG1.Length);
|
||||
|
||||
if (startIdx1 < startIdx2)
|
||||
return (startIdx1, START_TAG1.Length);
|
||||
|
||||
return (startIdx2, START_TAG2.Length);
|
||||
var bestIndex = -1;
|
||||
var bestLength = 0;
|
||||
|
||||
foreach (var (tag, length) in START_TAGS)
|
||||
{
|
||||
var index = content.IndexOf(tag);
|
||||
if (index != -1 && (bestIndex == -1 || index < bestIndex))
|
||||
{
|
||||
bestIndex = index;
|
||||
bestLength = length;
|
||||
}
|
||||
}
|
||||
|
||||
return (bestIndex, bestLength);
|
||||
}
|
||||
|
||||
var matches = new List<string>();
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace Build.Commands;
|
||||
|
||||
public record Library(string Path, string Filename);
|
||||
@@ -0,0 +1,107 @@
|
||||
using System.Formats.Tar;
|
||||
using System.IO.Compression;
|
||||
|
||||
using SharedTools;
|
||||
|
||||
namespace Build.Commands;
|
||||
|
||||
public static class Pdfium
|
||||
{
|
||||
public static async Task InstallAsync(RID rid, string version)
|
||||
{
|
||||
Console.Write($"- Installing Pdfium {version} for {rid.ToUserFriendlyName()} ...");
|
||||
|
||||
var cwd = Environment.GetRustRuntimeDirectory();
|
||||
var pdfiumTmpDownloadPath = Path.GetTempFileName();
|
||||
var pdfiumTmpExtractPath = Directory.CreateTempSubdirectory();
|
||||
var pdfiumUrl = GetPdfiumDownloadUrl(rid, version);
|
||||
|
||||
//
|
||||
// Download the file:
|
||||
//
|
||||
Console.Write(" downloading ...");
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
var response = await client.GetAsync(pdfiumUrl);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
Console.WriteLine($" failed to download Pdfium {version} for {rid.ToUserFriendlyName()}");
|
||||
return;
|
||||
}
|
||||
|
||||
await using var fileStream = File.Create(pdfiumTmpDownloadPath);
|
||||
await response.Content.CopyToAsync(fileStream);
|
||||
}
|
||||
|
||||
//
|
||||
// Extract the downloaded file:
|
||||
//
|
||||
Console.Write(" extracting ...");
|
||||
await using(var tgzStream = File.Open(pdfiumTmpDownloadPath, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
await using var uncompressedStream = new GZipStream(tgzStream, CompressionMode.Decompress);
|
||||
await TarFile.ExtractToDirectoryAsync(uncompressedStream, pdfiumTmpExtractPath.FullName, true);
|
||||
}
|
||||
|
||||
//
|
||||
// Copy the library to the target directory:
|
||||
//
|
||||
Console.Write(" deploying ...");
|
||||
var library = GetLibraryPath(rid);
|
||||
if (string.IsNullOrEmpty(library.Path))
|
||||
{
|
||||
Console.WriteLine($" failed to find the library path for {rid.ToUserFriendlyName()}");
|
||||
return;
|
||||
}
|
||||
|
||||
var pdfiumLibSourcePath = Path.Join(pdfiumTmpExtractPath.FullName, library.Path);
|
||||
var pdfiumLibTargetPath = Path.Join(cwd, "resources", "libraries", library.Filename);
|
||||
if (!File.Exists(pdfiumLibSourcePath))
|
||||
{
|
||||
Console.WriteLine($" failed to find the library file '{pdfiumLibSourcePath}'");
|
||||
return;
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(Path.Join(cwd, "resources", "libraries"));
|
||||
if (File.Exists(pdfiumLibTargetPath))
|
||||
File.Delete(pdfiumLibTargetPath);
|
||||
|
||||
File.Copy(pdfiumLibSourcePath, pdfiumLibTargetPath);
|
||||
|
||||
//
|
||||
// Cleanup:
|
||||
//
|
||||
Console.Write(" cleaning up ...");
|
||||
File.Delete(pdfiumTmpDownloadPath);
|
||||
Directory.Delete(pdfiumTmpExtractPath.FullName, true);
|
||||
|
||||
Console.WriteLine(" done.");
|
||||
}
|
||||
|
||||
private static Library GetLibraryPath(RID rid) => rid switch
|
||||
{
|
||||
RID.LINUX_ARM64 or RID.LINUX_X64 => new(Path.Join("lib", "libpdfium.so"), "libpdfium.so"),
|
||||
RID.OSX_ARM64 or RID.OSX_X64 => new(Path.Join("lib", "libpdfium.dylib"), "libpdfium.dylib"),
|
||||
RID.WIN_ARM64 or RID.WIN_X64 => new(Path.Join("bin", "pdfium.dll"), "pdfium.dll"),
|
||||
|
||||
_ => new(string.Empty, string.Empty),
|
||||
};
|
||||
|
||||
private static string GetPdfiumDownloadUrl(RID rid, string version)
|
||||
{
|
||||
var baseUrl = $"https://github.com/bblanchon/pdfium-binaries/releases/download/chromium%2F{version}/pdfium-";
|
||||
return rid switch
|
||||
{
|
||||
RID.LINUX_ARM64 => $"{baseUrl}linux-arm64.tgz",
|
||||
RID.LINUX_X64 => $"{baseUrl}linux-x64.tgz",
|
||||
|
||||
RID.OSX_ARM64 => $"{baseUrl}mac-arm64.tgz",
|
||||
RID.OSX_X64 => $"{baseUrl}mac-x64.tgz",
|
||||
|
||||
RID.WIN_ARM64 => $"{baseUrl}win-arm64.zip",
|
||||
RID.WIN_X64 => $"{baseUrl}win-x64.zip",
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -105,105 +105,102 @@ public sealed partial class UpdateMetadataCommands
|
||||
// Build the .NET project:
|
||||
//
|
||||
var pathApp = Environment.GetAIStudioDirectory();
|
||||
var rids = Environment.GetRidsForCurrentOS();
|
||||
foreach (var rid in rids)
|
||||
{
|
||||
Console.WriteLine("==============================");
|
||||
await this.UpdateArchitecture(rid);
|
||||
|
||||
Console.Write($"- Start .NET build for '{rid.AsMicrosoftRid()}' ...");
|
||||
await this.ReadCommandOutput(pathApp, "dotnet", $"clean --configuration release --runtime {rid.AsMicrosoftRid()}");
|
||||
var dotnetBuildOutput = await this.ReadCommandOutput(pathApp, "dotnet", $"publish --configuration release --runtime {rid.AsMicrosoftRid()} --disable-build-servers --force");
|
||||
var dotnetBuildOutputLines = dotnetBuildOutput.Split([global::System.Environment.NewLine], StringSplitOptions.RemoveEmptyEntries);
|
||||
var foundIssue = false;
|
||||
foreach (var buildOutputLine in dotnetBuildOutputLines)
|
||||
{
|
||||
if(buildOutputLine.Contains(" error ") || buildOutputLine.Contains("#warning"))
|
||||
{
|
||||
if(!foundIssue)
|
||||
{
|
||||
foundIssue = true;
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("- Build has issues:");
|
||||
}
|
||||
|
||||
Console.Write(" - ");
|
||||
Console.WriteLine(buildOutputLine);
|
||||
}
|
||||
}
|
||||
|
||||
if(foundIssue)
|
||||
Console.WriteLine();
|
||||
else
|
||||
{
|
||||
Console.WriteLine(" completed successfully.");
|
||||
}
|
||||
|
||||
//
|
||||
// Prepare the .NET artifact to be used by Tauri as sidecar:
|
||||
//
|
||||
var os = Environment.GetOS();
|
||||
var tauriSidecarArtifactName = rid switch
|
||||
{
|
||||
RID.WIN_X64 => "mindworkAIStudioServer-x86_64-pc-windows-msvc.exe",
|
||||
RID.WIN_ARM64 => "mindworkAIStudioServer-aarch64-pc-windows-msvc.exe",
|
||||
|
||||
RID.LINUX_X64 => "mindworkAIStudioServer-x86_64-unknown-linux-gnu",
|
||||
RID.LINUX_ARM64 => "mindworkAIStudioServer-aarch64-unknown-linux-gnu",
|
||||
|
||||
RID.OSX_ARM64 => "mindworkAIStudioServer-aarch64-apple-darwin",
|
||||
RID.OSX_X64 => "mindworkAIStudioServer-x86_64-apple-darwin",
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
if (string.IsNullOrWhiteSpace(tauriSidecarArtifactName))
|
||||
{
|
||||
Console.WriteLine($"- Error: Unsupported rid '{rid.AsMicrosoftRid()}'.");
|
||||
return;
|
||||
}
|
||||
var rid = Environment.GetCurrentRid();
|
||||
|
||||
var dotnetArtifactPath = Path.Combine(pathApp, "bin", "dist");
|
||||
if(!Directory.Exists(dotnetArtifactPath))
|
||||
Directory.CreateDirectory(dotnetArtifactPath);
|
||||
|
||||
var dotnetArtifactFilename = os switch
|
||||
Console.WriteLine("==============================");
|
||||
await this.UpdateArchitecture(rid);
|
||||
|
||||
var pdfiumVersion = await this.ReadPdfiumVersion();
|
||||
await Pdfium.InstallAsync(rid, pdfiumVersion);
|
||||
|
||||
Console.Write($"- Start .NET build for {rid.ToUserFriendlyName()} ...");
|
||||
await this.ReadCommandOutput(pathApp, "dotnet", $"clean --configuration release --runtime {rid.AsMicrosoftRid()}");
|
||||
var dotnetBuildOutput = await this.ReadCommandOutput(pathApp, "dotnet", $"publish --configuration release --runtime {rid.AsMicrosoftRid()} --disable-build-servers --force");
|
||||
var dotnetBuildOutputLines = dotnetBuildOutput.Split([global::System.Environment.NewLine], StringSplitOptions.RemoveEmptyEntries);
|
||||
var foundIssue = false;
|
||||
foreach (var buildOutputLine in dotnetBuildOutputLines)
|
||||
{
|
||||
if(buildOutputLine.Contains(" error ") || buildOutputLine.Contains("#warning"))
|
||||
{
|
||||
"windows" => "mindworkAIStudio.exe",
|
||||
_ => "mindworkAIStudio",
|
||||
};
|
||||
|
||||
var dotnetPublishedPath = Path.Combine(pathApp, "bin", "release", Environment.DOTNET_VERSION, rid.AsMicrosoftRid(), "publish", dotnetArtifactFilename);
|
||||
var finalDestination = Path.Combine(dotnetArtifactPath, tauriSidecarArtifactName);
|
||||
|
||||
if(File.Exists(dotnetPublishedPath))
|
||||
Console.WriteLine("- Published .NET artifact found.");
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"- Error: Published .NET artifact not found: '{dotnetPublishedPath}'.");
|
||||
return;
|
||||
}
|
||||
if(!foundIssue)
|
||||
{
|
||||
foundIssue = true;
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("- Build has issues:");
|
||||
}
|
||||
|
||||
Console.Write($"- Move the .NET artifact to the Tauri sidecar destination ...");
|
||||
try
|
||||
{
|
||||
File.Move(dotnetPublishedPath, finalDestination, true);
|
||||
Console.WriteLine(" done.");
|
||||
Console.Write(" - ");
|
||||
Console.WriteLine(buildOutputLine);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(" failed.");
|
||||
Console.WriteLine($" - Error: {e.Message}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(foundIssue)
|
||||
Console.WriteLine();
|
||||
else
|
||||
{
|
||||
Console.WriteLine(" completed successfully.");
|
||||
}
|
||||
|
||||
//
|
||||
// Prepare the .NET artifact to be used by Tauri as sidecar:
|
||||
//
|
||||
var os = Environment.GetOS();
|
||||
var tauriSidecarArtifactName = rid switch
|
||||
{
|
||||
RID.WIN_X64 => "mindworkAIStudioServer-x86_64-pc-windows-msvc.exe",
|
||||
RID.WIN_ARM64 => "mindworkAIStudioServer-aarch64-pc-windows-msvc.exe",
|
||||
|
||||
RID.LINUX_X64 => "mindworkAIStudioServer-x86_64-unknown-linux-gnu",
|
||||
RID.LINUX_ARM64 => "mindworkAIStudioServer-aarch64-unknown-linux-gnu",
|
||||
|
||||
RID.OSX_ARM64 => "mindworkAIStudioServer-aarch64-apple-darwin",
|
||||
RID.OSX_X64 => "mindworkAIStudioServer-x86_64-apple-darwin",
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
if (string.IsNullOrWhiteSpace(tauriSidecarArtifactName))
|
||||
{
|
||||
Console.WriteLine($"- Error: Unsupported rid '{rid.AsMicrosoftRid()}'.");
|
||||
return;
|
||||
}
|
||||
|
||||
var dotnetArtifactPath = Path.Combine(pathApp, "bin", "dist");
|
||||
if(!Directory.Exists(dotnetArtifactPath))
|
||||
Directory.CreateDirectory(dotnetArtifactPath);
|
||||
|
||||
var dotnetArtifactFilename = os switch
|
||||
{
|
||||
"windows" => "mindworkAIStudio.exe",
|
||||
_ => "mindworkAIStudio",
|
||||
};
|
||||
|
||||
var dotnetPublishedPath = Path.Combine(pathApp, "bin", "release", Environment.DOTNET_VERSION, rid.AsMicrosoftRid(), "publish", dotnetArtifactFilename);
|
||||
var finalDestination = Path.Combine(dotnetArtifactPath, tauriSidecarArtifactName);
|
||||
|
||||
if(File.Exists(dotnetPublishedPath))
|
||||
Console.WriteLine("- Published .NET artifact found.");
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"- Error: Published .NET artifact not found: '{dotnetPublishedPath}'.");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.Write($"- Move the .NET artifact to the Tauri sidecar destination ...");
|
||||
try
|
||||
{
|
||||
File.Move(dotnetPublishedPath, finalDestination, true);
|
||||
Console.WriteLine(" done.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(" failed.");
|
||||
Console.WriteLine($" - Error: {e.Message}");
|
||||
}
|
||||
|
||||
//
|
||||
// Build the Rust project / runtime:
|
||||
//
|
||||
|
||||
Console.WriteLine("==============================");
|
||||
Console.WriteLine("- Start building the Rust runtime ...");
|
||||
|
||||
var pathRuntime = Environment.GetRustRuntimeDirectory();
|
||||
@@ -314,6 +311,18 @@ public sealed partial class UpdateMetadataCommands
|
||||
await File.WriteAllTextAsync(changelogCodePath, changelogCode, Environment.UTF8_NO_BOM);
|
||||
Console.WriteLine(" done.");
|
||||
}
|
||||
|
||||
private async Task<string> ReadPdfiumVersion()
|
||||
{
|
||||
const int PDFIUM_VERSION_INDEX = 10;
|
||||
|
||||
var pathMetadata = Environment.GetMetadataPath();
|
||||
var lines = await File.ReadAllLinesAsync(pathMetadata, Encoding.UTF8);
|
||||
var currentPdfiumVersion = lines[PDFIUM_VERSION_INDEX].Trim();
|
||||
var shortVersion = currentPdfiumVersion.Split('.')[2];
|
||||
|
||||
return shortVersion;
|
||||
}
|
||||
|
||||
private async Task UpdateArchitecture(RID rid)
|
||||
{
|
||||
@@ -321,7 +330,7 @@ public sealed partial class UpdateMetadataCommands
|
||||
|
||||
var pathMetadata = Environment.GetMetadataPath();
|
||||
var lines = await File.ReadAllLinesAsync(pathMetadata, Encoding.UTF8);
|
||||
Console.Write("- Updating architecture ...");
|
||||
Console.Write($"- Updating architecture to {rid.ToUserFriendlyName()} ...");
|
||||
lines[ARCHITECTURE_INDEX] = rid.AsMicrosoftRid();
|
||||
|
||||
await File.WriteAllLinesAsync(pathMetadata, lines, Environment.UTF8_NO_BOM);
|
||||
|
||||
@@ -17,7 +17,7 @@ public sealed class UpdateWebAssetsCommand
|
||||
Console.WriteLine("=========================");
|
||||
Console.Write("- Updating web assets ...");
|
||||
|
||||
var rid = Environment.GetRidsForCurrentOS().First();
|
||||
var rid = Environment.GetCurrentRid();
|
||||
var cwd = Environment.GetAIStudioDirectory();
|
||||
var contentPath = Path.Join(cwd, "bin", "release", Environment.DOTNET_VERSION, rid.AsMicrosoftRid(), "publish", "wwwroot", "_content");
|
||||
var isMudBlazorDirectoryPresent = Directory.Exists(Path.Join(contentPath, "MudBlazor"));
|
||||
|
||||
@@ -76,4 +76,38 @@ public static class Environment
|
||||
Console.WriteLine($"Error: Unsupported OS '{RuntimeInformation.OSDescription}'");
|
||||
return [];
|
||||
}
|
||||
|
||||
public static RID GetCurrentRid()
|
||||
{
|
||||
var arch = RuntimeInformation.ProcessArchitecture;
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
return arch switch
|
||||
{
|
||||
Architecture.X64 => RID.WIN_X64,
|
||||
Architecture.Arm64 => RID.WIN_ARM64,
|
||||
|
||||
_ => RID.NONE,
|
||||
};
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
return arch switch
|
||||
{
|
||||
Architecture.X64 => RID.OSX_X64,
|
||||
Architecture.Arm64 => RID.OSX_ARM64,
|
||||
|
||||
_ => RID.NONE,
|
||||
};
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
return arch switch
|
||||
{
|
||||
Architecture.X64 => RID.LINUX_X64,
|
||||
Architecture.Arm64 => RID.LINUX_ARM64,
|
||||
|
||||
_ => RID.NONE,
|
||||
};
|
||||
|
||||
Console.WriteLine($"Error: Unsupported OS '{RuntimeInformation.OSDescription}'");
|
||||
return RID.NONE;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LM/@EntryIndexedValue">LM</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MSG/@EntryIndexedValue">MSG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OS/@EntryIndexedValue">OS</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PDF/@EntryIndexedValue">PDF</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RAG/@EntryIndexedValue">RAG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RID/@EntryIndexedValue">RID</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=TB/@EntryIndexedValue">TB</s:String>
|
||||
|
||||
@@ -2,14 +2,16 @@ namespace AIStudio.Assistants.EMail;
|
||||
|
||||
public static class WritingStylesExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(WritingStylesExtensions).Namespace, nameof(WritingStylesExtensions));
|
||||
|
||||
public static string Name(this WritingStyles style) => style switch
|
||||
{
|
||||
WritingStyles.ACADEMIC => "Academic",
|
||||
WritingStyles.PERSONAL => "Personal",
|
||||
WritingStyles.BUSINESS_FORMAL => "Business formal",
|
||||
WritingStyles.BUSINESS_INFORMAL => "Business informal",
|
||||
WritingStyles.ACADEMIC => TB("Academic"),
|
||||
WritingStyles.PERSONAL => TB("Personal"),
|
||||
WritingStyles.BUSINESS_FORMAL => TB("Business formal"),
|
||||
WritingStyles.BUSINESS_INFORMAL => TB("Business informal"),
|
||||
|
||||
_ => "Not specified",
|
||||
_ => TB("Not specified"),
|
||||
};
|
||||
|
||||
public static string Prompt(this WritingStyles style) => style switch
|
||||
|
||||
@@ -182,7 +182,7 @@ public partial class AssistantI18N : AssistantBaseCore<SettingsDialogI18N>
|
||||
var filePath = Path.Join(Environment.CurrentDirectory, "Assistants", "I18N");
|
||||
var resourceFileProvider = new PhysicalFileProvider(filePath);
|
||||
#else
|
||||
var resourceFileProvider = new ManifestEmbeddedFileProvider(Assembly.GetAssembly(type: typeof(Program))!, "Assistants.I18N");
|
||||
var resourceFileProvider = new ManifestEmbeddedFileProvider(Assembly.GetAssembly(type: typeof(Program))!, "Assistants/I18N");
|
||||
#endif
|
||||
|
||||
var file = resourceFileProvider.GetFileInfo("allTexts.lua");
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,4 +1,5 @@
|
||||
@attribute [Route(Routes.ASSISTANT_LEGAL_CHECK)]
|
||||
@using AIStudio.Settings.DataModel
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogLegalCheck>
|
||||
|
||||
@if (!this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader)
|
||||
@@ -6,6 +7,11 @@
|
||||
<ReadWebContent @bind-Content="@this.inputLegalDocument" ProviderSettings="@this.providerSettings" @bind-AgentIsRunning="@this.isAgentRunning" Preselect="@(this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions && this.SettingsManager.ConfigurationData.LegalCheck.PreselectWebContentReader)" PreselectContentCleanerAgent="@(this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions && this.SettingsManager.ConfigurationData.LegalCheck.PreselectContentCleanerAgent)"/>
|
||||
}
|
||||
|
||||
@if (PreviewFeatures.PRE_READ_PDF_2025.IsEnabled(this.SettingsManager))
|
||||
{
|
||||
<ReadPDFContent @bind-PDFContent="@this.inputLegalDocument"/>
|
||||
}
|
||||
|
||||
<MudTextField T="string" Disabled="@this.isAgentRunning" @bind-Text="@this.inputLegalDocument" Validation="@this.ValidatingLegalDocument" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="@T("Legal document")" Variant="Variant.Outlined" Lines="12" AutoGrow="@true" MaxLines="24" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
<MudTextField T="string" Disabled="@this.isAgentRunning" @bind-Text="@this.inputQuestions" Validation="@this.ValidatingQuestions" AdornmentIcon="@Icons.Material.Filled.QuestionAnswer" Adornment="Adornment.Start" Label="@T("Your questions")" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||
@@ -2,12 +2,14 @@ namespace AIStudio.Assistants.RewriteImprove;
|
||||
|
||||
public static class SentenceStructureExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(SentenceStructureExtensions).Namespace, nameof(SentenceStructureExtensions));
|
||||
|
||||
public static string Name(this SentenceStructure sentenceStructure) => sentenceStructure switch
|
||||
{
|
||||
SentenceStructure.ACTIVE => "Active voice",
|
||||
SentenceStructure.PASSIVE => "Passive voice",
|
||||
SentenceStructure.ACTIVE => TB("Active voice"),
|
||||
SentenceStructure.PASSIVE => TB("Passive voice"),
|
||||
|
||||
_ => "Not Specified",
|
||||
_ => TB("Not Specified"),
|
||||
};
|
||||
|
||||
public static string Prompt(this SentenceStructure sentenceStructure) => sentenceStructure switch
|
||||
|
||||
@@ -13,4 +13,5 @@ public enum WritingStyles
|
||||
MARKETING,
|
||||
ACADEMIC,
|
||||
LEGAL,
|
||||
CHANGELOG,
|
||||
}
|
||||
@@ -2,19 +2,22 @@ namespace AIStudio.Assistants.RewriteImprove;
|
||||
|
||||
public static class WritingStylesExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(WritingStylesExtensions).Namespace, nameof(WritingStylesExtensions));
|
||||
|
||||
public static string Name(this WritingStyles style) => style switch
|
||||
{
|
||||
WritingStyles.EVERYDAY => "Everyday (personal texts, social media)",
|
||||
WritingStyles.BUSINESS => "Business (business emails, reports, presentations)",
|
||||
WritingStyles.SCIENTIFIC => "Scientific (scientific papers, research reports)",
|
||||
WritingStyles.JOURNALISTIC => "Journalistic (magazines, newspapers, news)",
|
||||
WritingStyles.LITERARY => "Literary (fiction, poetry)",
|
||||
WritingStyles.TECHNICAL => "Technical (manuals, documentation)",
|
||||
WritingStyles.MARKETING => "Marketing (advertisements, sales texts)",
|
||||
WritingStyles.ACADEMIC => "Academic (essays, seminar papers)",
|
||||
WritingStyles.LEGAL => "Legal (legal texts, contracts)",
|
||||
WritingStyles.EVERYDAY => TB("Everyday (personal texts, social media)"),
|
||||
WritingStyles.BUSINESS => TB("Business (business emails, reports, presentations)"),
|
||||
WritingStyles.SCIENTIFIC => TB("Scientific (scientific papers, research reports)"),
|
||||
WritingStyles.JOURNALISTIC => TB("Journalistic (magazines, newspapers, news)"),
|
||||
WritingStyles.LITERARY => TB("Literary (fiction, poetry)"),
|
||||
WritingStyles.TECHNICAL => TB("Technical (manuals, documentation)"),
|
||||
WritingStyles.MARKETING => TB("Marketing (advertisements, sales texts)"),
|
||||
WritingStyles.ACADEMIC => TB("Academic (essays, seminar papers)"),
|
||||
WritingStyles.LEGAL => TB("Legal (legal texts, contracts)"),
|
||||
WritingStyles.CHANGELOG => TB("Changelog (release notes, version history)"),
|
||||
|
||||
_ => "Not specified",
|
||||
_ => TB("Not specified"),
|
||||
};
|
||||
|
||||
public static string Prompt(this WritingStyles style) => style switch
|
||||
@@ -28,6 +31,7 @@ public static class WritingStylesExtensions
|
||||
WritingStyles.MARKETING => "Use a marketing style like for advertisements, sales texts, and promotional content. Most important is persuasiveness and engagement.",
|
||||
WritingStyles.ACADEMIC => "Use a academic style like for essays, seminar papers, and academic writing. Most important is clarity and objectivity.",
|
||||
WritingStyles.LEGAL => "Use a legal style like for legal texts, contracts, and official documents. Most important is precision and legal correctness. Use formal legal language.",
|
||||
WritingStyles.CHANGELOG => "Use a changelog style like for release notes, version history, and software updates. Most important is clarity and conciseness. The changelog is structured as a Markdown list. Most list items start with one of the following verbs: Added, Changed, Deprecated, Removed, Fixed, Refactored, Improved, or Upgraded -- these verbs should also translated to the target language. Also, changelogs use past tense.",
|
||||
|
||||
_ => "Keep the style of the text as it is.",
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@attribute [Route(Routes.ASSISTANT_SUMMARIZER)]
|
||||
@using AIStudio.Settings.DataModel
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogTextSummarizer>
|
||||
|
||||
@if (!this.SettingsManager.ConfigurationData.TextSummarizer.HideWebContentReader)
|
||||
@@ -6,6 +7,11 @@
|
||||
<ReadWebContent @bind-Content="@this.inputText" ProviderSettings="@this.providerSettings" @bind-AgentIsRunning="@this.isAgentRunning" Preselect="@(this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions && this.SettingsManager.ConfigurationData.TextSummarizer.PreselectWebContentReader)" PreselectContentCleanerAgent="@(this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions && this.SettingsManager.ConfigurationData.TextSummarizer.PreselectContentCleanerAgent)"/>
|
||||
}
|
||||
|
||||
@if (PreviewFeatures.PRE_READ_PDF_2025.IsEnabled(this.SettingsManager))
|
||||
{
|
||||
<ReadPDFContent @bind-PDFContent="@this.inputText"/>
|
||||
}
|
||||
|
||||
<MudTextField T="string" Disabled="@this.isAgentRunning" @bind-Text="@this.inputText" Validation="@this.ValidatingText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="@T("Your input")" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.Name())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="@T("Target language")" AllowOther="@true" @bind-OtherInput="@this.customTargetLanguage" OtherValue="CommonLanguages.OTHER" LabelOther="@T("Custom target language")" ValidateOther="@this.ValidateCustomLanguage" />
|
||||
<EnumSelection T="Complexity" NameFunc="@(complexity => complexity.Name())" @bind-Value="@this.selectedComplexity" Icon="@Icons.Material.Filled.Layers" Label="@T("Target complexity")" AllowOther="@true" @bind-OtherInput="@this.expertInField" OtherValue="Complexity.SCIENTIFIC_LANGUAGE_OTHER_EXPERTS" LabelOther="@T("Your expertise")" ValidateOther="@this.ValidateExpertInField" />
|
||||
|
||||
@@ -2,18 +2,20 @@ namespace AIStudio.Assistants.TextSummarizer;
|
||||
|
||||
public static class ComplexityExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(ComplexityExtensions).Namespace, nameof(ComplexityExtensions));
|
||||
|
||||
public static string Name(this Complexity complexity) => complexity switch
|
||||
{
|
||||
Complexity.NO_CHANGE => "No change in complexity",
|
||||
Complexity.NO_CHANGE => TB("No change in complexity"),
|
||||
|
||||
Complexity.SIMPLE_LANGUAGE => "Simple language, e.g., for children",
|
||||
Complexity.TEEN_LANGUAGE => "Teen language, e.g., for teenagers",
|
||||
Complexity.EVERYDAY_LANGUAGE => "Everyday language, e.g., for adults",
|
||||
Complexity.POPULAR_SCIENCE_LANGUAGE => "Popular science language, e.g., for people interested in science",
|
||||
Complexity.SCIENTIFIC_LANGUAGE_FIELD_EXPERTS => "Scientific language for experts in this field",
|
||||
Complexity.SCIENTIFIC_LANGUAGE_OTHER_EXPERTS => "Scientific language for experts from other fields (interdisciplinary)",
|
||||
Complexity.SIMPLE_LANGUAGE => TB("Simple language, e.g., for children"),
|
||||
Complexity.TEEN_LANGUAGE => TB("Teen language, e.g., for teenagers"),
|
||||
Complexity.EVERYDAY_LANGUAGE => TB("Everyday language, e.g., for adults"),
|
||||
Complexity.POPULAR_SCIENCE_LANGUAGE => TB("Popular science language, e.g., for people interested in science"),
|
||||
Complexity.SCIENTIFIC_LANGUAGE_FIELD_EXPERTS => TB("Scientific language for experts in this field"),
|
||||
Complexity.SCIENTIFIC_LANGUAGE_OTHER_EXPERTS => TB("Scientific language for experts from other fields (interdisciplinary)"),
|
||||
|
||||
_ => "No change in complexity",
|
||||
_ => TB("No change in complexity"),
|
||||
};
|
||||
|
||||
public static string Prompt(this Complexity complexity, string expertInField) => complexity switch
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@attribute [Route(Routes.ASSISTANT_TRANSLATION)]
|
||||
@using AIStudio.Settings.DataModel
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogTranslation>
|
||||
|
||||
@if (!this.SettingsManager.ConfigurationData.Translation.HideWebContentReader)
|
||||
@@ -6,6 +7,11 @@
|
||||
<ReadWebContent @bind-Content="@this.inputText" ProviderSettings="@this.providerSettings" @bind-AgentIsRunning="@this.isAgentRunning" Preselect="@(this.SettingsManager.ConfigurationData.Translation.PreselectOptions && this.SettingsManager.ConfigurationData.Translation.PreselectWebContentReader)" PreselectContentCleanerAgent="@(this.SettingsManager.ConfigurationData.Translation.PreselectOptions && this.SettingsManager.ConfigurationData.Translation.PreselectContentCleanerAgent)"/>
|
||||
}
|
||||
|
||||
@if (PreviewFeatures.PRE_READ_PDF_2025.IsEnabled(this.SettingsManager))
|
||||
{
|
||||
<ReadPDFContent @bind-PDFContent="@this.inputText"/>
|
||||
}
|
||||
|
||||
<MudTextSwitch Label="@T("Live translation")" @bind-Value="@this.liveTranslation" LabelOn="@T("Live translation")" LabelOff="@T("No live translation")"/>
|
||||
@if (this.liveTranslation)
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@ public partial class Changelog
|
||||
|
||||
public static readonly Log[] LOGS =
|
||||
[
|
||||
new (217, "v0.9.42, build 217 (2025-05-04 13:03 UTC)", "v0.9.42.md"),
|
||||
new (216, "v0.9.41, build 216 (2025-04-27 14:18 UTC)", "v0.9.41.md"),
|
||||
new (215, "v0.9.40, build 215 (2025-04-20 13:30 UTC)", "v0.9.40.md"),
|
||||
new (214, "v0.9.39, build 214 (2025-04-07 17:39 UTC)", "v0.9.39.md"),
|
||||
|
||||
@@ -270,7 +270,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
|
||||
private bool CanThreadBeSaved => this.ChatThread is not null && this.ChatThread.Blocks.Count > 0;
|
||||
|
||||
private string TooltipAddChatToWorkspace => string.Format(T(@"Start new chat in workspace ""{0}"""), this.currentWorkspaceName);
|
||||
private string TooltipAddChatToWorkspace => string.Format(T("""
|
||||
Start new chat in workspace "{0}"
|
||||
"""), this.currentWorkspaceName);
|
||||
|
||||
private string UserInputStyle => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence ? this.Provider.UsedLLMProvider.GetConfidence(this.SettingsManager).SetColorStyle(this.SettingsManager) : string.Empty;
|
||||
|
||||
@@ -291,6 +293,12 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
// We select the first 10 words of the user input:
|
||||
var words = firstUserInput.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
var threadName = string.Join(' ', words.Take(10));
|
||||
threadName = threadName.Trim();
|
||||
|
||||
// Remove all line breaks:
|
||||
threadName = threadName.Replace("\r", string.Empty);
|
||||
threadName = threadName.Replace("\n", " ");
|
||||
threadName = threadName.Replace("\t", " ");
|
||||
|
||||
// If the thread name is empty, we use a default name:
|
||||
if (string.IsNullOrWhiteSpace(threadName))
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
@inherits MSGComponentBase
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Description" OnClick="async () => await this.SelectFile()" Variant="Variant.Filled" Class="mb-3">
|
||||
@T("Use PDF content as input")
|
||||
</MudButton>
|
||||
@@ -0,0 +1,31 @@
|
||||
using AIStudio.Tools.Rust;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class ReadPDFContent : MSGComponentBase
|
||||
{
|
||||
[Parameter]
|
||||
public string PDFContent { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<string> PDFContentChanged { get; set; }
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
private async Task SelectFile()
|
||||
{
|
||||
var pdfFile = await this.RustService.SelectFile(T("Select PDF file"), FileTypeFilter.PDF);
|
||||
if (pdfFile.UserCancelled)
|
||||
return;
|
||||
|
||||
if(!File.Exists(pdfFile.SelectedFilePath))
|
||||
return;
|
||||
|
||||
var pdfText = await this.RustService.GetPDFText(pdfFile.SelectedFilePath);
|
||||
await this.PDFContentChanged.InvokeAsync(pdfText);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
@inherits MSGComponentBase
|
||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||
<MudPaper Class="pa-3 mb-3 border-dashed border rounded-lg">
|
||||
<MudTextSwitch Label="@T("Read content from web?")" Disabled="@this.AgentIsRunning" @bind-Value="@this.showWebContentReader" LabelOn="@T("Show web content options")" LabelOff="@T("Hide web content options")" />
|
||||
@if (this.showWebContentReader)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.Rust;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
@@ -22,6 +23,9 @@ public partial class SelectFile : ComponentBase
|
||||
[Parameter]
|
||||
public string FileDialogTitle { get; set; } = "Select File";
|
||||
|
||||
[Parameter]
|
||||
public FileTypeFilter? Filter { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<string, string?> Validation { get; set; } = _ => null;
|
||||
|
||||
@@ -55,7 +59,7 @@ public partial class SelectFile : ComponentBase
|
||||
|
||||
private async Task OpenFileDialog()
|
||||
{
|
||||
var response = await this.RustService.SelectFile(this.FileDialogTitle, string.IsNullOrWhiteSpace(this.File) ? null : this.File);
|
||||
var response = await this.RustService.SelectFile(this.FileDialogTitle, this.Filter, string.IsNullOrWhiteSpace(this.File) ? null : this.File);
|
||||
this.Logger.LogInformation($"The user selected the file '{response.SelectedFilePath}'.");
|
||||
|
||||
if (!response.UserCancelled)
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
@if (PreviewFeatures.PRE_PLUGINS_2025.IsEnabled(this.SettingsManager))
|
||||
{
|
||||
<ConfigurationSelect OptionDescription="@T("Language behavior")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.LanguageBehavior)" Data="@ConfigurationSelectDataFactory.GetLangBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.LanguageBehavior = selectedValue)" OptionHelp="@T("Select the language behavior for the app. The default is to use the system language. You might want to choose a language manually?")"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Language behavior")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.LanguageBehavior)" Data="@ConfigurationSelectDataFactory.GetLangBehaviorData()" SelectionUpdate="@(selectedValue => _ = this.UpdateLangBehaviour(selectedValue))" OptionHelp="@T("Select the language behavior for the app. The default is to use the system language. You might want to choose a language manually?")"/>
|
||||
|
||||
@if (this.SettingsManager.ConfigurationData.App.LanguageBehavior is LangBehavior.MANUAL)
|
||||
{
|
||||
<ConfigurationSelect OptionDescription="@T("Language")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.LanguagePluginId)" Data="@ConfigurationSelectDataFactory.GetLanguagesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.LanguagePluginId = selectedValue)" OptionHelp="@T("Select the language for the app.")"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Language")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.LanguagePluginId)" Data="@ConfigurationSelectDataFactory.GetLanguagesData()" SelectionUpdate="@(selectedValue => _ = this.UpdateManuallySelectedLanguage(selectedValue))" OptionHelp="@T("Select the language for the app.")"/>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,4 +9,16 @@ public partial class SettingsPanelApp : SettingsPanelBase
|
||||
this.SettingsManager.ConfigurationData.App.PreviewVisibility = previewVisibility;
|
||||
this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures = previewVisibility.FilterPreviewFeatures(this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures);
|
||||
}
|
||||
|
||||
private async Task UpdateLangBehaviour(LangBehavior behavior)
|
||||
{
|
||||
this.SettingsManager.ConfigurationData.App.LanguageBehavior = behavior;
|
||||
await this.MessageBus.SendMessage<bool>(this, Event.PLUGINS_RELOADED);
|
||||
}
|
||||
|
||||
private async Task UpdateManuallySelectedLanguage(Guid pluginId)
|
||||
{
|
||||
this.SettingsManager.ConfigurationData.App.LanguagePluginId = pluginId;
|
||||
await this.MessageBus.SendMessage<bool>(this, Event.PLUGINS_RELOADED);
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,7 @@
|
||||
@* ReSharper restore Asp.Entity *@
|
||||
}
|
||||
|
||||
<MudField FullWidth="true" Label="Model selection" Variant="Variant.Outlined" Class="mb-3">
|
||||
<MudField FullWidth="true" Label="@T("Model selection")" Variant="Variant.Outlined" Class="mb-3">
|
||||
<MudStack Row="@true" AlignItems="AlignItems.Center" StretchItems="StretchItems.End">
|
||||
@if (this.DataLLMProvider.IsLLMModelProvidedManually())
|
||||
{
|
||||
|
||||
@@ -206,6 +206,10 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
break;
|
||||
|
||||
case Event.PLUGINS_RELOADED:
|
||||
this.Lang = await this.SettingsManager.GetActiveLanguagePlugin();
|
||||
I18N.Init(this.Lang);
|
||||
this.LoadNavItems();
|
||||
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -47,11 +47,11 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CodeBeam.MudBlazor.Extensions" Version="8.0.0" />
|
||||
<PackageReference Include="CodeBeam.MudBlazor.Extensions" Version="8.1.0" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.12.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.4" />
|
||||
<PackageReference Include="MudBlazor" Version="8.5.1" />
|
||||
<PackageReference Include="MudBlazor.Markdown" Version="8.5.1" />
|
||||
<PackageReference Include="MudBlazor" Version="8.6.0" />
|
||||
<PackageReference Include="MudBlazor.Markdown" Version="8.5.1.1" />
|
||||
<PackageReference Include="ReverseMarkdown" Version="4.6.0" />
|
||||
<PackageReference Include="LuaCSharp" Version="0.4.2" />
|
||||
</ItemGroup>
|
||||
@@ -80,6 +80,7 @@
|
||||
<MetaTauriVersion>$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 7 ])</MetaTauriVersion>
|
||||
<MetaAppCommitHash>$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 8 ])</MetaAppCommitHash>
|
||||
<MetaArchitecture>$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 9 ])</MetaArchitecture>
|
||||
<MetaPdfiumVersion>$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 10 ])</MetaPdfiumVersion>
|
||||
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
|
||||
@@ -104,6 +105,9 @@
|
||||
<AssemblyAttribute Include="AIStudio.Tools.Metadata.MetaDataArchitecture">
|
||||
<_Parameter1>$(MetaArchitecture)</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
<AssemblyAttribute Include="AIStudio.Tools.Metadata.MetaDataLibraries">
|
||||
<_Parameter1>$(MetaPdfiumVersion)</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
|
||||
</Target>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Build" Text="@VersionDotnetSdk"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Memory" Text="@VersionDotnetRuntime"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Build" Text="@VersionRust"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.DocumentScanner" Text="@VersionPdfium"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Widgets" Text="@MudBlazorVersion"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Memory" Text="@TauriVersion"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Translate" Text="@this.OSLanguage"/>
|
||||
@@ -111,6 +112,7 @@
|
||||
<ThirdPartyComponent Name="rcgen" Developer="RustTLS developers, est31 & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rustls/rcgen/blob/main/LICENSE" RepositoryUrl="https://github.com/rustls/rcgen" UseCase="@T("For the secure communication between the user interface and the runtime, we need to create certificates. This Rust library is great for this purpose.")"/>
|
||||
<ThirdPartyComponent Name="file-format" Developer="Mickaël Malécot & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/mmalecot/file-format/blob/main/LICENSE-MIT" RepositoryUrl="https://github.com/mmalecot/file-format" UseCase="@T("This library is used to determine the file type of a file. This is necessary, e.g., when we want to stream a file.")"/>
|
||||
<ThirdPartyComponent Name="calamine" Developer="Johann Tuffe, Joel Natividad, Eric Jolibois, Dmitriy & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/tafia/calamine/blob/master/LICENSE-MIT.md" RepositoryUrl="https://github.com/tafia/calamine" UseCase="@T("This library is used to read Excel and OpenDocument spreadsheet files. This is necessary, e.g., for using spreadsheets as a data source for a chat.")"/>
|
||||
<ThirdPartyComponent Name="PDFium" Developer="Lei Zhang, Tom Sepez, Dan Sinclair, and Foxit, Google, Chromium, Collabora, Ada, DocsCorp, Dropbox, Microsoft, and PSPDFKit Teams & Open Source Community" LicenseName="Apache-2.0" LicenseUrl="https://pdfium.googlesource.com/pdfium/+/refs/heads/main/LICENSE" RepositoryUrl="https://pdfium.googlesource.com/pdfium" UseCase="@T("This library is used to read PDF files. This is necessary, e.g., for using PDFs as a data source for a chat.")"/>
|
||||
<ThirdPartyComponent Name="pdfium-render" Developer="Alastair Carey, Dorian Rudolph & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/ajrcarey/pdfium-render/blob/master/LICENSE.md" RepositoryUrl="https://github.com/ajrcarey/pdfium-render" UseCase="@T("This library is used to read PDF files. This is necessary, e.g., for using PDFs as a data source for a chat.")"/>
|
||||
<ThirdPartyComponent Name="sys-locale" Developer="1Password Team, ComplexSpaces & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/1Password/sys-locale/blob/main/LICENSE-MIT" RepositoryUrl="https://github.com/1Password/sys-locale" UseCase="@T("This library is used to determine the language of the operating system. This is necessary to set the language of the user interface.")"/>
|
||||
<ThirdPartyComponent Name="Lua-CSharp" Developer="Yusuke Nakada & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/nuskey8/Lua-CSharp/blob/main/LICENSE" RepositoryUrl="https://github.com/nuskey8/Lua-CSharp" UseCase="@T("We use Lua as the language for plugins. Lua-CSharp lets Lua scripts communicate with AI Studio and vice versa. Thank you, Yusuke Nakada, for this great library.")" />
|
||||
|
||||
@@ -21,7 +21,8 @@ public partial class About : MSGComponentBase
|
||||
|
||||
private static readonly Assembly ASSEMBLY = Assembly.GetExecutingAssembly();
|
||||
private static readonly MetaDataAttribute META_DATA = ASSEMBLY.GetCustomAttribute<MetaDataAttribute>()!;
|
||||
private static readonly MetaDataArchitecture META_DATA_ARCH = ASSEMBLY.GetCustomAttribute<MetaDataArchitecture>()!;
|
||||
private static readonly MetaDataArchitectureAttribute META_DATA_ARCH = ASSEMBLY.GetCustomAttribute<MetaDataArchitectureAttribute>()!;
|
||||
private static readonly MetaDataLibrariesAttribute META_DATA_LIBRARIES = ASSEMBLY.GetCustomAttribute<MetaDataLibrariesAttribute>()!;
|
||||
|
||||
private string osLanguage = string.Empty;
|
||||
|
||||
@@ -40,6 +41,8 @@ public partial class About : MSGComponentBase
|
||||
private string VersionDotnetSdk => $"{T("Used .NET SDK")}: v{META_DATA.DotnetSdkVersion}";
|
||||
|
||||
private string BuildTime => $"{T("Build time")}: {META_DATA.BuildTime}";
|
||||
|
||||
private string VersionPdfium => $"{T("Used PDFium version")}: v{META_DATA_LIBRARIES.PdfiumVersion}";
|
||||
|
||||
private GetLogPathsResponse logPaths;
|
||||
|
||||
|
||||
+984
-5
File diff suppressed because it is too large.
Load diff
+982
-4
File diff suppressed because it is too large.
Load diff
@@ -111,6 +111,7 @@ internal sealed class Program
|
||||
config.SnackbarConfiguration.SnackbarVariant = Variant.Outlined;
|
||||
});
|
||||
|
||||
builder.Services.AddMemoryCache(); // Needed for the Markdown library
|
||||
builder.Services.AddMudMarkdownServices();
|
||||
builder.Services.AddSingleton(new MudTheme());
|
||||
builder.Services.AddSingleton(MessageBus.INSTANCE);
|
||||
|
||||
@@ -142,7 +142,6 @@ public sealed class ProviderAlibabaCloud(ILogger logger) : BaseProvider("https:/
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
private async Task<IEnumerable<Model>> LoadModels(string[] prefixes, CancellationToken token, string? apiKeyProvisional = null)
|
||||
{
|
||||
var secretKey = apiKeyProvisional switch
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Text.Json;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
namespace AIStudio.Provider;
|
||||
@@ -13,6 +14,8 @@ namespace AIStudio.Provider;
|
||||
/// </summary>
|
||||
public abstract class BaseProvider : IProvider, ISecretId
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(BaseProvider).Namespace, nameof(BaseProvider));
|
||||
|
||||
/// <summary>
|
||||
/// The HTTP client to use it for all requests.
|
||||
/// </summary>
|
||||
@@ -119,7 +122,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
|
||||
if (nextResponse.StatusCode is HttpStatusCode.Forbidden)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Block, $"Tried to communicate with the LLM provider '{this.InstanceName}'. You might not be able to use this provider from your location. The provider message is: '{nextResponse.ReasonPhrase}'"));
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Block, string.Format(TB("Tried to communicate with the LLM provider '{0}'. You might not be able to use this provider from your location. The provider message is: '{1}'"), this.InstanceName, nextResponse.ReasonPhrase)));
|
||||
this.logger.LogError($"Failed request with status code {nextResponse.StatusCode} (message = '{nextResponse.ReasonPhrase}').");
|
||||
errorMessage = nextResponse.ReasonPhrase;
|
||||
break;
|
||||
@@ -127,7 +130,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
|
||||
if(nextResponse.StatusCode is HttpStatusCode.BadRequest)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, $"Tried to communicate with the LLM provider '{this.InstanceName}'. The required message format might be changed. The provider message is: '{nextResponse.ReasonPhrase}'"));
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("Tried to communicate with the LLM provider '{0}'. The required message format might be changed. The provider message is: '{1}'"), this.InstanceName, nextResponse.ReasonPhrase)));
|
||||
this.logger.LogError($"Failed request with status code {nextResponse.StatusCode} (message = '{nextResponse.ReasonPhrase}').");
|
||||
errorMessage = nextResponse.ReasonPhrase;
|
||||
break;
|
||||
@@ -135,7 +138,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
|
||||
if(nextResponse.StatusCode is HttpStatusCode.NotFound)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, $"Tried to communicate with the LLM provider '{this.InstanceName}'. Something was not found. The provider message is: '{nextResponse.ReasonPhrase}'"));
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("Tried to communicate with the LLM provider '{0}'. Something was not found. The provider message is: '{1}'"), this.InstanceName, nextResponse.ReasonPhrase)));
|
||||
this.logger.LogError($"Failed request with status code {nextResponse.StatusCode} (message = '{nextResponse.ReasonPhrase}').");
|
||||
errorMessage = nextResponse.ReasonPhrase;
|
||||
break;
|
||||
@@ -143,7 +146,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
|
||||
if(nextResponse.StatusCode is HttpStatusCode.Unauthorized)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Key, $"Tried to communicate with the LLM provider '{this.InstanceName}'. The API key might be invalid. The provider message is: '{nextResponse.ReasonPhrase}'"));
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Key, string.Format(TB("Tried to communicate with the LLM provider '{0}'. The API key might be invalid. The provider message is: '{1}'"), this.InstanceName, nextResponse.ReasonPhrase)));
|
||||
this.logger.LogError($"Failed request with status code {nextResponse.StatusCode} (message = '{nextResponse.ReasonPhrase}').");
|
||||
errorMessage = nextResponse.ReasonPhrase;
|
||||
break;
|
||||
@@ -151,7 +154,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
|
||||
if(nextResponse.StatusCode is HttpStatusCode.InternalServerError)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, $"Tried to communicate with the LLM provider '{this.InstanceName}'. The server might be down or having issues. The provider message is: '{nextResponse.ReasonPhrase}'"));
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("Tried to communicate with the LLM provider '{0}'. The server might be down or having issues. The provider message is: '{1}'"), this.InstanceName, nextResponse.ReasonPhrase)));
|
||||
this.logger.LogError($"Failed request with status code {nextResponse.StatusCode} (message = '{nextResponse.ReasonPhrase}').");
|
||||
errorMessage = nextResponse.ReasonPhrase;
|
||||
break;
|
||||
@@ -159,7 +162,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
|
||||
if(nextResponse.StatusCode is HttpStatusCode.ServiceUnavailable)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, $"Tried to communicate with the LLM provider '{this.InstanceName}'. The provider is overloaded. The message is: '{nextResponse.ReasonPhrase}'"));
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("Tried to communicate with the LLM provider '{0}'. The provider is overloaded. The message is: '{1}'"), this.InstanceName, nextResponse.ReasonPhrase)));
|
||||
this.logger.LogError($"Failed request with status code {nextResponse.StatusCode} (message = '{nextResponse.ReasonPhrase}').");
|
||||
errorMessage = nextResponse.ReasonPhrase;
|
||||
break;
|
||||
@@ -176,7 +179,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
|
||||
if(retry >= MAX_RETRIES || !string.IsNullOrWhiteSpace(errorMessage))
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, $"Tried to communicate with the LLM provider '{this.InstanceName}'. Even after {MAX_RETRIES} retries, there were some problems with the request. The provider message is: '{errorMessage}'"));
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("Tried to communicate with the LLM provider '{0}'. Even after {1} retries, there were some problems with the request. The provider message is: '{2}'"), this.InstanceName, MAX_RETRIES, errorMessage)));
|
||||
return new HttpRateLimitedStreamResult(false, true, errorMessage ?? $"Failed after {MAX_RETRIES} retries; no provider message available", response);
|
||||
}
|
||||
|
||||
@@ -204,7 +207,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Stream, $"Tried to communicate with the LLM provider '{this.InstanceName}'. There were some problems with the request. The provider message is: '{e.Message}'"));
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Stream, string.Format(TB("Tried to communicate with the LLM provider '{0}'. There were some problems with the request. The provider message is: '{1}'"), this.InstanceName, e.Message)));
|
||||
this.logger.LogError($"Failed to stream chat completion from {providerName} '{this.InstanceName}': {e.Message}");
|
||||
}
|
||||
|
||||
@@ -221,7 +224,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Stream, $"Tried to stream the LLM provider '{this.InstanceName}' answer. There were some problems with the stream. The message is: '{e.Message}'"));
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Stream, string.Format(TB("Tried to stream the LLM provider '{0}' answer. There were some problems with the stream. The message is: '{1}'"), this.InstanceName, e.Message)));
|
||||
this.logger.LogWarning($"Failed to read the end-of-stream state from {providerName} '{this.InstanceName}': {e.Message}");
|
||||
break;
|
||||
}
|
||||
@@ -242,7 +245,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Stream, $"Tried to stream the LLM provider '{this.InstanceName}' answer. Was not able to read the stream. The message is: '{e.Message}'"));
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Stream, string.Format(TB("Tried to stream the LLM provider '{0}' answer. Was not able to read the stream. The message is: '{1}'"), this.InstanceName, e.Message)));
|
||||
this.logger.LogError($"Failed to read the stream from {providerName} '{this.InstanceName}': {e.Message}");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Provider;
|
||||
|
||||
public sealed record Confidence
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(Confidence).Namespace, nameof(Confidence));
|
||||
|
||||
public ConfidenceLevel Level { get; private init; } = ConfidenceLevel.UNKNOWN;
|
||||
|
||||
public string Region { get; private init; } = string.Empty;
|
||||
@@ -29,42 +32,42 @@ public sealed record Confidence
|
||||
public static readonly Confidence NONE = new()
|
||||
{
|
||||
Level = ConfidenceLevel.NONE,
|
||||
Description = "No provider selected. Please select a provider to get see its confidence level.",
|
||||
Description = TB("No provider selected. Please select a provider to get see its confidence level."),
|
||||
};
|
||||
|
||||
public static readonly Confidence USA_HUB = new()
|
||||
{
|
||||
Level = ConfidenceLevel.UNKNOWN,
|
||||
Description = "The provider operates its service from the USA and is subject to **U.S. jurisdiction**. In case of suspicion, authorities in the USA can access your data. Please inform yourself about the use of your data. We do not know if your data is safe.",
|
||||
Description = TB("The provider operates its service from the USA and is subject to **U.S. jurisdiction**. In case of suspicion, authorities in the USA can access your data. Please inform yourself about the use of your data. We do not know if your data is safe."),
|
||||
};
|
||||
|
||||
public static readonly Confidence UNKNOWN = new()
|
||||
{
|
||||
Level = ConfidenceLevel.UNKNOWN,
|
||||
Description = "The trust level of this provider **has not yet** been thoroughly **investigated and evaluated**. We do not know if your data is safe.",
|
||||
Description = TB("The trust level of this provider **has not yet** been thoroughly **investigated and evaluated**. We do not know if your data is safe."),
|
||||
};
|
||||
|
||||
public static readonly Confidence USA_NO_TRAINING = new()
|
||||
{
|
||||
Level = ConfidenceLevel.MODERATE,
|
||||
Description = "The provider operates its service from the USA and is subject to **US jurisdiction**. In case of suspicion, authorities in the USA can access your data. However, **your data is not used for training** purposes.",
|
||||
Description = TB("The provider operates its service from the USA and is subject to **US jurisdiction**. In case of suspicion, authorities in the USA can access your data. However, **your data is not used for training** purposes."),
|
||||
};
|
||||
|
||||
public static readonly Confidence CHINA_NO_TRAINING = new()
|
||||
{
|
||||
Level = ConfidenceLevel.MODERATE,
|
||||
Description = "The provider operates its service from China. In case of suspicion, authorities in the respective countries of operation may access your data. However, **your data is not used for training** purposes.",
|
||||
Description = TB("The provider operates its service from China. In case of suspicion, authorities in the respective countries of operation may access your data. However, **your data is not used for training** purposes."),
|
||||
};
|
||||
|
||||
public static readonly Confidence GDPR_NO_TRAINING = new()
|
||||
{
|
||||
Level = ConfidenceLevel.MEDIUM,
|
||||
Description = "The provider is located in the EU and is subject to the **GDPR** (General Data Protection Regulation). Additionally, the provider states that **your data is not used for training**.",
|
||||
Description = TB("The provider is located in the EU and is subject to the **GDPR** (General Data Protection Regulation). Additionally, the provider states that **your data is not used for training**."),
|
||||
};
|
||||
|
||||
public static readonly Confidence SELF_HOSTED = new()
|
||||
{
|
||||
Level = ConfidenceLevel.HIGH,
|
||||
Description = "You or your organization operate the LLM locally or within your trusted network. In terms of data processing and security, this is the best possible way.",
|
||||
Description = TB("You or your organization operate the LLM locally or within your trusted network. In terms of data processing and security, this is the best possible way."),
|
||||
};
|
||||
}
|
||||
@@ -1,21 +1,24 @@
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Provider;
|
||||
|
||||
public static class ConfidenceLevelExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ConfidenceLevelExtensions).Namespace, nameof(ConfidenceLevelExtensions));
|
||||
|
||||
public static string GetName(this ConfidenceLevel level) => level switch
|
||||
{
|
||||
ConfidenceLevel.NONE => "No provider selected",
|
||||
ConfidenceLevel.NONE => TB("No provider selected"),
|
||||
|
||||
ConfidenceLevel.UNTRUSTED => "Untrusted",
|
||||
ConfidenceLevel.VERY_LOW => "Very Low",
|
||||
ConfidenceLevel.LOW => "Low",
|
||||
ConfidenceLevel.MODERATE => "Moderate",
|
||||
ConfidenceLevel.MEDIUM => "Medium",
|
||||
ConfidenceLevel.HIGH => "High",
|
||||
ConfidenceLevel.UNTRUSTED => TB("Untrusted"),
|
||||
ConfidenceLevel.VERY_LOW => TB("Very Low"),
|
||||
ConfidenceLevel.LOW => TB("Low"),
|
||||
ConfidenceLevel.MODERATE => TB("Moderate"),
|
||||
ConfidenceLevel.MEDIUM => TB("Medium"),
|
||||
ConfidenceLevel.HIGH => TB("High"),
|
||||
|
||||
_ => "Unknown confidence level",
|
||||
_ => TB("Unknown confidence level"),
|
||||
};
|
||||
|
||||
public static string GetColor(this ConfidenceLevel level, SettingsManager settingsManager) => (level, settingsManager.IsDarkMode) switch
|
||||
|
||||
@@ -12,6 +12,7 @@ using AIStudio.Provider.OpenAI;
|
||||
using AIStudio.Provider.SelfHosted;
|
||||
using AIStudio.Provider.X;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
using Host = AIStudio.Provider.SelfHosted.Host;
|
||||
|
||||
@@ -19,6 +20,8 @@ namespace AIStudio.Provider;
|
||||
|
||||
public static class LLMProvidersExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(LLMProvidersExtensions).Namespace, nameof(LLMProvidersExtensions));
|
||||
|
||||
/// <summary>
|
||||
/// Returns the human-readable name of the provider.
|
||||
/// </summary>
|
||||
@@ -26,7 +29,7 @@ public static class LLMProvidersExtensions
|
||||
/// <returns>The human-readable name of the provider.</returns>
|
||||
public static string ToName(this LLMProviders llmProvider) => llmProvider switch
|
||||
{
|
||||
LLMProviders.NONE => "No provider selected",
|
||||
LLMProviders.NONE => TB("No provider selected"),
|
||||
|
||||
LLMProviders.OPEN_AI => "OpenAI",
|
||||
LLMProviders.ANTHROPIC => "Anthropic",
|
||||
@@ -40,12 +43,12 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.FIREWORKS => "Fireworks.ai",
|
||||
LLMProviders.HUGGINGFACE => "Hugging Face",
|
||||
|
||||
LLMProviders.SELF_HOSTED => "Self-hosted",
|
||||
LLMProviders.SELF_HOSTED => TB("Self-hosted"),
|
||||
|
||||
LLMProviders.HELMHOLTZ => "Helmholtz Blablador",
|
||||
LLMProviders.GWDG => "GWDG SAIA",
|
||||
|
||||
_ => "Unknown",
|
||||
_ => TB("Unknown"),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -127,7 +127,8 @@ public sealed class ProviderOpenAI(ILogger logger) : BaseProvider("https://api.o
|
||||
!model.Id.Contains("realtime", StringComparison.OrdinalIgnoreCase) &&
|
||||
!model.Id.Contains("audio", StringComparison.OrdinalIgnoreCase) &&
|
||||
!model.Id.Contains("tts", StringComparison.OrdinalIgnoreCase) &&
|
||||
!model.Id.Contains("transcribe", StringComparison.OrdinalIgnoreCase));
|
||||
!model.Id.Contains("transcribe", StringComparison.OrdinalIgnoreCase) &&
|
||||
!model.Id.Contains("o1-pro", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
+40
-38
@@ -26,6 +26,8 @@ public readonly record struct ConfigurationSelectData<T>(string Name, T Value);
|
||||
/// </summary>
|
||||
public static class ConfigurationSelectDataFactory
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ConfigurationSelectDataFactory).Namespace, nameof(ConfigurationSelectDataFactory));
|
||||
|
||||
public static IEnumerable<ConfigurationSelectData<LangBehavior>> GetLangBehaviorData()
|
||||
{
|
||||
foreach (var behavior in Enum.GetValues<LangBehavior>())
|
||||
@@ -43,65 +45,65 @@ public static class ConfigurationSelectDataFactory
|
||||
|
||||
public static IEnumerable<ConfigurationSelectData<LoadingChatProviderBehavior>> GetLoadingChatProviderBehavior()
|
||||
{
|
||||
yield return new("When possible, use the LLM provider which was used for each chat in the first place", LoadingChatProviderBehavior.USE_CHAT_PROVIDER_IF_AVAILABLE);
|
||||
yield return new("Use the latest LLM provider, which was used before; use the default chat provider initially", LoadingChatProviderBehavior.ALWAYS_USE_LATEST_CHAT_PROVIDER);
|
||||
yield return new("Always use the default chat provider when loading chats", LoadingChatProviderBehavior.ALWAYS_USE_DEFAULT_CHAT_PROVIDER);
|
||||
yield return new(TB("When possible, use the LLM provider which was used for each chat in the first place"), LoadingChatProviderBehavior.USE_CHAT_PROVIDER_IF_AVAILABLE);
|
||||
yield return new(TB("Use the latest LLM provider, which was used before; use the default chat provider initially"), LoadingChatProviderBehavior.ALWAYS_USE_LATEST_CHAT_PROVIDER);
|
||||
yield return new(TB("Always use the default chat provider when loading chats"), LoadingChatProviderBehavior.ALWAYS_USE_DEFAULT_CHAT_PROVIDER);
|
||||
}
|
||||
|
||||
public static IEnumerable<ConfigurationSelectData<AddChatProviderBehavior>> GetAddChatProviderBehavior()
|
||||
{
|
||||
yield return new("Use the latest LLM provider, which was used before; use the default chat provider initially", AddChatProviderBehavior.ADDED_CHATS_USE_LATEST_PROVIDER);
|
||||
yield return new("Always use the default chat provider for new chats", AddChatProviderBehavior.ADDED_CHATS_USE_DEFAULT_PROVIDER);
|
||||
yield return new(TB("Use the latest LLM provider, which was used before; use the default chat provider initially"), AddChatProviderBehavior.ADDED_CHATS_USE_LATEST_PROVIDER);
|
||||
yield return new(TB("Always use the default chat provider for new chats"), AddChatProviderBehavior.ADDED_CHATS_USE_DEFAULT_PROVIDER);
|
||||
}
|
||||
|
||||
public static IEnumerable<ConfigurationSelectData<SendBehavior>> GetSendBehaviorData()
|
||||
{
|
||||
yield return new("No key is sending the input", SendBehavior.NO_KEY_IS_SENDING);
|
||||
yield return new("Modifier key + enter is sending the input", SendBehavior.MODIFER_ENTER_IS_SENDING);
|
||||
yield return new("Enter is sending the input", SendBehavior.ENTER_IS_SENDING);
|
||||
yield return new(TB("No key is sending the input"), SendBehavior.NO_KEY_IS_SENDING);
|
||||
yield return new(TB("Modifier key + enter is sending the input"), SendBehavior.MODIFER_ENTER_IS_SENDING);
|
||||
yield return new(TB("Enter is sending the input"), SendBehavior.ENTER_IS_SENDING);
|
||||
}
|
||||
|
||||
public static IEnumerable<ConfigurationSelectData<UpdateBehavior>> GetUpdateBehaviorData()
|
||||
{
|
||||
yield return new("No automatic update checks", UpdateBehavior.NO_CHECK);
|
||||
yield return new("Once at startup", UpdateBehavior.ONCE_STARTUP);
|
||||
yield return new("Check every hour", UpdateBehavior.HOURLY);
|
||||
yield return new("Check every day", UpdateBehavior.DAILY);
|
||||
yield return new ("Check every week", UpdateBehavior.WEEKLY);
|
||||
yield return new(TB("No automatic update checks"), UpdateBehavior.NO_CHECK);
|
||||
yield return new(TB("Once at startup"), UpdateBehavior.ONCE_STARTUP);
|
||||
yield return new(TB("Check every hour"), UpdateBehavior.HOURLY);
|
||||
yield return new(TB("Check every day"), UpdateBehavior.DAILY);
|
||||
yield return new (TB("Check every week"), UpdateBehavior.WEEKLY);
|
||||
}
|
||||
|
||||
public static IEnumerable<ConfigurationSelectData<WorkspaceStorageBehavior>> GetWorkspaceStorageBehaviorData()
|
||||
{
|
||||
yield return new("Disable workspaces", WorkspaceStorageBehavior.DISABLE_WORKSPACES);
|
||||
yield return new("Store chats automatically", WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY);
|
||||
yield return new("Store chats manually", WorkspaceStorageBehavior.STORE_CHATS_MANUALLY);
|
||||
yield return new(TB("Disable workspaces"), WorkspaceStorageBehavior.DISABLE_WORKSPACES);
|
||||
yield return new(TB("Store chats automatically"), WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY);
|
||||
yield return new(TB("Store chats manually"), WorkspaceStorageBehavior.STORE_CHATS_MANUALLY);
|
||||
}
|
||||
|
||||
public static IEnumerable<ConfigurationSelectData<WorkspaceStorageTemporaryMaintenancePolicy>> GetWorkspaceStorageTemporaryMaintenancePolicyData()
|
||||
{
|
||||
yield return new("No automatic maintenance for temporary chats", WorkspaceStorageTemporaryMaintenancePolicy.NO_AUTOMATIC_MAINTENANCE);
|
||||
yield return new("Delete temporary chats older than 7 days", WorkspaceStorageTemporaryMaintenancePolicy.DELETE_OLDER_THAN_7_DAYS);
|
||||
yield return new("Delete temporary chats older than 30 days", WorkspaceStorageTemporaryMaintenancePolicy.DELETE_OLDER_THAN_30_DAYS);
|
||||
yield return new("Delete temporary chats older than 90 days", WorkspaceStorageTemporaryMaintenancePolicy.DELETE_OLDER_THAN_90_DAYS);
|
||||
yield return new("Delete temporary chats older than 180 days", WorkspaceStorageTemporaryMaintenancePolicy.DELETE_OLDER_THAN_180_DAYS);
|
||||
yield return new("Delete temporary chats older than 1 year", WorkspaceStorageTemporaryMaintenancePolicy.DELETE_OLDER_THAN_365_DAYS);
|
||||
yield return new(TB("No automatic maintenance for disappearing chats; old chats will never be deleted"), WorkspaceStorageTemporaryMaintenancePolicy.NO_AUTOMATIC_MAINTENANCE);
|
||||
yield return new(TB("Delete disappearing chats older than 7 days"), WorkspaceStorageTemporaryMaintenancePolicy.DELETE_OLDER_THAN_7_DAYS);
|
||||
yield return new(TB("Delete disappearing chats older than 30 days"), WorkspaceStorageTemporaryMaintenancePolicy.DELETE_OLDER_THAN_30_DAYS);
|
||||
yield return new(TB("Delete disappearing chats older than 90 days"), WorkspaceStorageTemporaryMaintenancePolicy.DELETE_OLDER_THAN_90_DAYS);
|
||||
yield return new(TB("Delete disappearing chats older than 180 days"), WorkspaceStorageTemporaryMaintenancePolicy.DELETE_OLDER_THAN_180_DAYS);
|
||||
yield return new(TB("Delete disappearing chats older than 1 year"), WorkspaceStorageTemporaryMaintenancePolicy.DELETE_OLDER_THAN_365_DAYS);
|
||||
}
|
||||
|
||||
public static IEnumerable<ConfigurationSelectData<WorkspaceDisplayBehavior>> GetWorkspaceDisplayBehaviorData()
|
||||
{
|
||||
yield return new("Toggle the overlay: the chat uses all the space, workspaces are temporarily shown", WorkspaceDisplayBehavior.TOGGLE_OVERLAY);
|
||||
yield return new("Toggle the sidebar: show the workspaces next to the chat when desired", WorkspaceDisplayBehavior.TOGGLE_SIDEBAR);
|
||||
yield return new("Sidebar is always visible: show the workspaces next to the chat all the time", WorkspaceDisplayBehavior.SIDEBAR_ALWAYS_VISIBLE);
|
||||
yield return new(TB("Toggle the overlay: the chat uses all the space, workspaces are temporarily shown"), WorkspaceDisplayBehavior.TOGGLE_OVERLAY);
|
||||
yield return new(TB("Toggle the sidebar: show the workspaces next to the chat when desired"), WorkspaceDisplayBehavior.TOGGLE_SIDEBAR);
|
||||
yield return new(TB("Sidebar is always visible: show the workspaces next to the chat all the time"), WorkspaceDisplayBehavior.SIDEBAR_ALWAYS_VISIBLE);
|
||||
}
|
||||
|
||||
public static IEnumerable<ConfigurationSelectData<PreviewVisibility>> GetPreviewVisibility()
|
||||
{
|
||||
yield return new("All preview features are hidden", PreviewVisibility.NONE);
|
||||
yield return new("Also show features ready for release; these should be stable", PreviewVisibility.RELEASE_CANDIDATE);
|
||||
yield return new("Also show features in beta: these are almost ready for release; expect some bugs", PreviewVisibility.BETA);
|
||||
yield return new("Also show features in alpha: these are in development; expect bugs and missing features", PreviewVisibility.ALPHA);
|
||||
yield return new("Show also prototype features: these are works in progress; expect bugs and missing features", PreviewVisibility.PROTOTYPE);
|
||||
yield return new("Show also experimental features: these are experimental; expect bugs, missing features, many changes", PreviewVisibility.EXPERIMENTAL);
|
||||
yield return new(TB("All preview features are hidden"), PreviewVisibility.NONE);
|
||||
yield return new(TB("Also show features ready for release; these should be stable"), PreviewVisibility.RELEASE_CANDIDATE);
|
||||
yield return new(TB("Also show features in beta: these are almost ready for release; expect some bugs"), PreviewVisibility.BETA);
|
||||
yield return new(TB("Also show features in alpha: these are in development; expect bugs and missing features"), PreviewVisibility.ALPHA);
|
||||
yield return new(TB("Show also prototype features: these are works in progress; expect bugs and missing features"), PreviewVisibility.PROTOTYPE);
|
||||
yield return new(TB("Show also experimental features: these are experimental; expect bugs, missing features, many changes"), PreviewVisibility.EXPERIMENTAL);
|
||||
}
|
||||
|
||||
public static IEnumerable<ConfigurationSelectData<PreviewFeatures>> GetPreviewFeaturesData(SettingsManager settingsManager)
|
||||
@@ -118,10 +120,10 @@ public static class ConfigurationSelectDataFactory
|
||||
|
||||
public static IEnumerable<ConfigurationSelectData<NavBehavior>> GetNavBehaviorData()
|
||||
{
|
||||
yield return new("Navigation expands on mouse hover", NavBehavior.EXPAND_ON_HOVER);
|
||||
yield return new("Navigation never expands, but there are tooltips", NavBehavior.NEVER_EXPAND_USE_TOOLTIPS);
|
||||
yield return new("Navigation never expands, no tooltips", NavBehavior.NEVER_EXPAND_NO_TOOLTIPS);
|
||||
yield return new("Always expand navigation", NavBehavior.ALWAYS_EXPAND);
|
||||
yield return new(TB("Navigation expands on mouse hover"), NavBehavior.EXPAND_ON_HOVER);
|
||||
yield return new(TB("Navigation never expands, but there are tooltips"), NavBehavior.NEVER_EXPAND_USE_TOOLTIPS);
|
||||
yield return new(TB("Navigation never expands, no tooltips"), NavBehavior.NEVER_EXPAND_NO_TOOLTIPS);
|
||||
yield return new(TB("Always expand navigation"), NavBehavior.ALWAYS_EXPAND);
|
||||
}
|
||||
|
||||
public static IEnumerable<ConfigurationSelectData<IconSources>> GetIconSourcesData()
|
||||
@@ -140,7 +142,7 @@ public static class ConfigurationSelectDataFactory
|
||||
{
|
||||
foreach (var language in Enum.GetValues<CommonLanguages>())
|
||||
if(language is CommonLanguages.AS_IS)
|
||||
yield return new("Not yet specified", language);
|
||||
yield return new(TB("Not yet specified"), language);
|
||||
else
|
||||
yield return new(language.Name(), language);
|
||||
}
|
||||
@@ -149,7 +151,7 @@ public static class ConfigurationSelectDataFactory
|
||||
{
|
||||
foreach (var language in Enum.GetValues<CommonLanguages>())
|
||||
if(language is CommonLanguages.AS_IS)
|
||||
yield return new("Do not specify the language", language);
|
||||
yield return new(TB("Do not specify the language"), language);
|
||||
else
|
||||
yield return new(language.Name(), language);
|
||||
}
|
||||
@@ -211,7 +213,7 @@ public static class ConfigurationSelectDataFactory
|
||||
foreach (var level in Enum.GetValues<ConfidenceLevel>())
|
||||
{
|
||||
if(level is ConfidenceLevel.NONE)
|
||||
yield return new("No minimum confidence level chosen", level);
|
||||
yield return new(TB("No minimum confidence level chosen"), level);
|
||||
|
||||
if(level < minimumLevel)
|
||||
continue;
|
||||
@@ -1,25 +1,29 @@
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Settings.DataModel;
|
||||
|
||||
public static class DataSourceSecurityExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(DataSourceSecurityExtensions).Namespace, nameof(DataSourceSecurityExtensions));
|
||||
|
||||
public static string ToSelectionText(this DataSourceSecurity security) => security switch
|
||||
{
|
||||
DataSourceSecurity.NOT_SPECIFIED => "Please select a security policy",
|
||||
DataSourceSecurity.NOT_SPECIFIED => TB("Please select a security policy"),
|
||||
|
||||
DataSourceSecurity.ALLOW_ANY => "This data source can be used with any LLM provider. Your data may be sent to a cloud-based provider.",
|
||||
DataSourceSecurity.SELF_HOSTED => "This data source can only be used with a self-hosted LLM provider. Your data will not be sent to any cloud-based provider.",
|
||||
DataSourceSecurity.ALLOW_ANY => TB("This data source can be used with any LLM provider. Your data may be sent to a cloud-based provider."),
|
||||
DataSourceSecurity.SELF_HOSTED => TB("This data source can only be used with a self-hosted LLM provider. Your data will not be sent to any cloud-based provider."),
|
||||
|
||||
_ => "Unknown security policy"
|
||||
_ => TB("Unknown security policy")
|
||||
};
|
||||
|
||||
public static string ToInfoText(this DataSourceSecurity security) => security switch
|
||||
{
|
||||
DataSourceSecurity.NOT_SPECIFIED => "The security of the data source is not specified yet. You cannot use this data source until you specify a security policy.",
|
||||
DataSourceSecurity.NOT_SPECIFIED => TB("The security of the data source is not specified yet. You cannot use this data source until you specify a security policy."),
|
||||
|
||||
DataSourceSecurity.ALLOW_ANY => "This data source can be used with any LLM provider. Your data may be sent to a cloud-based provider.",
|
||||
DataSourceSecurity.SELF_HOSTED => "This data source can only be used with a self-hosted LLM provider. Your data will not be sent to any cloud-based provider.",
|
||||
DataSourceSecurity.ALLOW_ANY => TB("This data source can be used with any LLM provider. Your data may be sent to a cloud-based provider."),
|
||||
DataSourceSecurity.SELF_HOSTED => TB("This data source can only be used with a self-hosted LLM provider. Your data will not be sent to any cloud-based provider."),
|
||||
|
||||
_ => "Unknown security policy"
|
||||
_ => TB("Unknown security policy")
|
||||
};
|
||||
|
||||
public static TextColor GetColor(this DataSourceSecurity security) => security switch
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Settings.DataModel;
|
||||
|
||||
/// <summary>
|
||||
@@ -5,6 +7,8 @@ namespace AIStudio.Settings.DataModel;
|
||||
/// </summary>
|
||||
public static class DataSourceTypeExtension
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(DataSourceTypeExtension).Namespace, nameof(DataSourceTypeExtension));
|
||||
|
||||
/// <summary>
|
||||
/// Get the display name of the data source type.
|
||||
/// </summary>
|
||||
@@ -12,10 +16,10 @@ public static class DataSourceTypeExtension
|
||||
/// <returns>The display name of the data source type.</returns>
|
||||
public static string GetDisplayName(this DataSourceType type) => type switch
|
||||
{
|
||||
DataSourceType.LOCAL_FILE => "Local File",
|
||||
DataSourceType.LOCAL_DIRECTORY => "Local Directory",
|
||||
DataSourceType.ERI_V1 => "External ERI Server (v1)",
|
||||
DataSourceType.LOCAL_FILE => TB("Local File"),
|
||||
DataSourceType.LOCAL_DIRECTORY => TB("Local Directory"),
|
||||
DataSourceType.ERI_V1 => TB("External ERI Server (v1)"),
|
||||
|
||||
_ => "None",
|
||||
_ => TB("None"),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Settings.DataModel;
|
||||
|
||||
public static class LangBehaviorExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(LangBehaviorExtensions).Namespace, nameof(LangBehaviorExtensions));
|
||||
|
||||
public static string Name(this LangBehavior langBehavior) => langBehavior switch
|
||||
{
|
||||
LangBehavior.AUTO => TB("Choose the language automatically, based on your system language."),
|
||||
LangBehavior.MANUAL => TB("Choose the language manually."),
|
||||
|
||||
_ => TB("Unknown option")
|
||||
};
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace AIStudio.Settings.DataModel;
|
||||
|
||||
public static class LangBehaviorExtensions
|
||||
{
|
||||
public static string Name(this LangBehavior langBehavior) => langBehavior switch
|
||||
{
|
||||
LangBehavior.AUTO => "Choose the language automatically, based on your system language.",
|
||||
LangBehavior.MANUAL => "Choose the language manually.",
|
||||
|
||||
_ => "Unknown option"
|
||||
};
|
||||
}
|
||||
@@ -10,4 +10,5 @@ public enum PreviewFeatures
|
||||
PRE_RAG_2024,
|
||||
|
||||
PRE_PLUGINS_2025,
|
||||
PRE_READ_PDF_2025,
|
||||
}
|
||||
@@ -1,14 +1,20 @@
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Settings.DataModel;
|
||||
|
||||
public static class PreviewFeaturesExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(PreviewFeaturesExtensions).Namespace, nameof(PreviewFeaturesExtensions));
|
||||
|
||||
public static string GetPreviewDescription(this PreviewFeatures feature) => feature switch
|
||||
{
|
||||
PreviewFeatures.PRE_WRITER_MODE_2024 => "Writer Mode: Experiments about how to write long texts using AI",
|
||||
PreviewFeatures.PRE_RAG_2024 => "RAG: Preview of our RAG implementation where you can refer your files or integrate enterprise data within your company",
|
||||
PreviewFeatures.PRE_PLUGINS_2025 => "Plugins: Preview of our plugin system where you can extend the functionality of the app",
|
||||
PreviewFeatures.PRE_WRITER_MODE_2024 => TB("Writer Mode: Experiments about how to write long texts using AI"),
|
||||
PreviewFeatures.PRE_RAG_2024 => TB("RAG: Preview of our RAG implementation where you can refer your files or integrate enterprise data within your company"),
|
||||
|
||||
_ => "Unknown preview feature"
|
||||
PreviewFeatures.PRE_PLUGINS_2025 => TB("Plugins: Preview of our plugin system where you can extend the functionality of the app"),
|
||||
PreviewFeatures.PRE_READ_PDF_2025 => TB("Read PDF: Preview of our PDF reading system where you can read and extract text from PDF files"),
|
||||
|
||||
_ => TB("Unknown preview feature")
|
||||
};
|
||||
|
||||
public static bool IsEnabled(this PreviewFeatures feature, SettingsManager settingsManager) => settingsManager.ConfigurationData.App.EnabledPreviewFeatures.Contains(feature);
|
||||
|
||||
@@ -19,6 +19,7 @@ public static class PreviewVisibilityExtensions
|
||||
|
||||
if (visibility >= PreviewVisibility.PROTOTYPE)
|
||||
{
|
||||
features.Add(PreviewFeatures.PRE_READ_PDF_2025);
|
||||
features.Add(PreviewFeatures.PRE_RAG_2024);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Settings.DataModel;
|
||||
|
||||
public static class SendToChatDataSourceBehaviorExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(SendToChatDataSourceBehaviorExtensions).Namespace, nameof(SendToChatDataSourceBehaviorExtensions));
|
||||
|
||||
public static string Description(this SendToChatDataSourceBehavior behavior) => behavior switch
|
||||
{
|
||||
SendToChatDataSourceBehavior.NO_DATA_SOURCES => "Use no data sources, when sending an assistant result to a chat",
|
||||
SendToChatDataSourceBehavior.APPLY_STANDARD_CHAT_DATA_SOURCE_OPTIONS => "Apply standard chat data source options, when sending an assistant result to a chat",
|
||||
SendToChatDataSourceBehavior.NO_DATA_SOURCES => TB("Use no data sources, when sending an assistant result to a chat"),
|
||||
SendToChatDataSourceBehavior.APPLY_STANDARD_CHAT_DATA_SOURCE_OPTIONS => TB("Apply standard chat data source options, when sending an assistant result to a chat"),
|
||||
|
||||
_ => "Unknown behavior",
|
||||
_ => TB("Unknown behavior"),
|
||||
};
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Settings.DataModel;
|
||||
|
||||
public static class ThemesExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ThemesExtensions).Namespace, nameof(ThemesExtensions));
|
||||
|
||||
public static string GetName(this Themes theme) => theme switch
|
||||
{
|
||||
Themes.SYSTEM => "Synchronized with the operating system settings",
|
||||
Themes.LIGHT => "Always use light theme",
|
||||
Themes.DARK => "Always use dark theme",
|
||||
Themes.SYSTEM => TB("Synchronized with the operating system settings"),
|
||||
Themes.LIGHT => TB("Always use light theme"),
|
||||
Themes.DARK => TB("Always use dark theme"),
|
||||
|
||||
_ => "Unknown setting",
|
||||
_ => TB("Unknown setting"),
|
||||
};
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Settings;
|
||||
|
||||
public readonly record struct Profile(uint Num, string Id, string Name, string NeedToKnow, string Actions)
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(Profile).Namespace, nameof(Profile));
|
||||
|
||||
public static readonly Profile NO_PROFILE = new()
|
||||
{
|
||||
Name = "Use no profile",
|
||||
Name = TB("Use no profile"),
|
||||
NeedToKnow = string.Empty,
|
||||
Actions = string.Empty,
|
||||
Id = Guid.Empty.ToString(),
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace AIStudio.Settings;
|
||||
/// <summary>
|
||||
/// The settings manager.
|
||||
/// </summary>
|
||||
public sealed class SettingsManager(ILogger<SettingsManager> logger, RustService rustService)
|
||||
public sealed class SettingsManager
|
||||
{
|
||||
private const string SETTINGS_FILENAME = "settings.json";
|
||||
|
||||
@@ -23,9 +23,19 @@ public sealed class SettingsManager(ILogger<SettingsManager> logger, RustService
|
||||
Converters = { new TolerantEnumConverter() },
|
||||
};
|
||||
|
||||
private readonly ILogger<SettingsManager> logger = logger;
|
||||
private readonly RustService rustService = rustService;
|
||||
|
||||
private readonly ILogger<SettingsManager> logger;
|
||||
private readonly RustService rustService;
|
||||
|
||||
/// <summary>
|
||||
/// The settings manager.
|
||||
/// </summary>
|
||||
public SettingsManager(ILogger<SettingsManager> logger, RustService rustService)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.rustService = rustService;
|
||||
this.logger.LogInformation("Settings manager created.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The directory where the configuration files are stored.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
using AIStudio.Tools.ERIClient.DataModel;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
public static class AuthMethodsV1Extensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(AuthMethodsV1Extensions).Namespace, nameof(AuthMethodsV1Extensions));
|
||||
|
||||
public static string DisplayName(this AuthMethod authMethod) => authMethod switch
|
||||
{
|
||||
AuthMethod.NONE => "None",
|
||||
AuthMethod.USERNAME_PASSWORD => "Username & Password",
|
||||
AuthMethod.KERBEROS => "SSO (Kerberos)",
|
||||
AuthMethod.TOKEN => "Access Token",
|
||||
AuthMethod.NONE => TB("None"),
|
||||
AuthMethod.USERNAME_PASSWORD => TB("Username & Password"),
|
||||
AuthMethod.KERBEROS => TB("SSO (Kerberos)"),
|
||||
AuthMethod.TOKEN => TB("Access Token"),
|
||||
|
||||
_ => "Unknown authentication method",
|
||||
_ => TB("Unknown authentication method"),
|
||||
};
|
||||
}
|
||||
@@ -1,23 +1,27 @@
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
public static class CommonLanguageExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(CommonLanguageExtensions).Namespace, nameof(CommonLanguageExtensions));
|
||||
|
||||
public static string Name(this CommonLanguages language) => language switch
|
||||
{
|
||||
CommonLanguages.AS_IS => "Do not change the language",
|
||||
CommonLanguages.AS_IS => TB("Do not change the language"),
|
||||
|
||||
CommonLanguages.EN_US => "English (US)",
|
||||
CommonLanguages.EN_GB => "English (UK)",
|
||||
CommonLanguages.ZH_CN => "Chinese (Simplified)",
|
||||
CommonLanguages.HI_IN => "Hindi (India)",
|
||||
CommonLanguages.ES_ES => "Spanish (Spain)",
|
||||
CommonLanguages.FR_FR => "French (France)",
|
||||
CommonLanguages.DE_DE => "German (Germany)",
|
||||
CommonLanguages.DE_AT => "German (Austria)",
|
||||
CommonLanguages.DE_CH => "German (Switzerland)",
|
||||
CommonLanguages.JA_JP => "Japanese (Japan)",
|
||||
CommonLanguages.EN_US => TB("English (US)"),
|
||||
CommonLanguages.EN_GB => TB("English (UK)"),
|
||||
CommonLanguages.ZH_CN => TB("Chinese (Simplified)"),
|
||||
CommonLanguages.HI_IN => TB("Hindi (India)"),
|
||||
CommonLanguages.ES_ES => TB("Spanish (Spain)"),
|
||||
CommonLanguages.FR_FR => TB("French (France)"),
|
||||
CommonLanguages.DE_DE => TB("German (Germany)"),
|
||||
CommonLanguages.DE_AT => TB("German (Austria)"),
|
||||
CommonLanguages.DE_CH => TB("German (Switzerland)"),
|
||||
CommonLanguages.JA_JP => TB("Japanese (Japan)"),
|
||||
|
||||
_ => "Other",
|
||||
_ => TB("Other"),
|
||||
};
|
||||
|
||||
public static string ToIETFTag(this CommonLanguages language) => language switch
|
||||
@@ -56,7 +60,7 @@ public static class CommonLanguageExtensions
|
||||
public static string NameSelecting(this CommonLanguages language)
|
||||
{
|
||||
if(language is CommonLanguages.AS_IS)
|
||||
return "Please select the target language";
|
||||
return TB("Please select the target language");
|
||||
|
||||
return language.Name();
|
||||
}
|
||||
@@ -64,7 +68,7 @@ public static class CommonLanguageExtensions
|
||||
public static string NameSelectingOptional(this CommonLanguages language)
|
||||
{
|
||||
if(language is CommonLanguages.AS_IS)
|
||||
return "Do not specify the language";
|
||||
return TB("Do not specify the language");
|
||||
|
||||
return language.Name();
|
||||
}
|
||||
|
||||
@@ -2,11 +2,14 @@ using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
public static class ComponentsExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ComponentsExtensions).Namespace, nameof(ComponentsExtensions));
|
||||
|
||||
public static bool AllowSendTo(this Components component) => component switch
|
||||
{
|
||||
Components.NONE => false,
|
||||
@@ -26,22 +29,22 @@ public static class ComponentsExtensions
|
||||
|
||||
public static string Name(this Components component) => component switch
|
||||
{
|
||||
Components.GRAMMAR_SPELLING_ASSISTANT => "Grammar & Spelling Assistant",
|
||||
Components.TEXT_SUMMARIZER_ASSISTANT => "Text Summarizer Assistant",
|
||||
Components.ICON_FINDER_ASSISTANT => "Icon Finder Assistant",
|
||||
Components.TRANSLATION_ASSISTANT => "Translation Assistant",
|
||||
Components.REWRITE_ASSISTANT => "Rewrite Assistant",
|
||||
Components.AGENDA_ASSISTANT => "Agenda Assistant",
|
||||
Components.CODING_ASSISTANT => "Coding Assistant",
|
||||
Components.EMAIL_ASSISTANT => "E-Mail Assistant",
|
||||
Components.LEGAL_CHECK_ASSISTANT => "Legal Check Assistant",
|
||||
Components.SYNONYMS_ASSISTANT => "Synonym Assistant",
|
||||
Components.MY_TASKS_ASSISTANT => "My Tasks Assistant",
|
||||
Components.JOB_POSTING_ASSISTANT => "Job Posting Assistant",
|
||||
Components.ERI_ASSISTANT => "ERI Server",
|
||||
Components.I18N_ASSISTANT => "Localization Assistant",
|
||||
Components.GRAMMAR_SPELLING_ASSISTANT => TB("Grammar & Spelling Assistant"),
|
||||
Components.TEXT_SUMMARIZER_ASSISTANT => TB("Text Summarizer Assistant"),
|
||||
Components.ICON_FINDER_ASSISTANT => TB("Icon Finder Assistant"),
|
||||
Components.TRANSLATION_ASSISTANT => TB("Translation Assistant"),
|
||||
Components.REWRITE_ASSISTANT => TB("Rewrite Assistant"),
|
||||
Components.AGENDA_ASSISTANT => TB("Agenda Assistant"),
|
||||
Components.CODING_ASSISTANT => TB("Coding Assistant"),
|
||||
Components.EMAIL_ASSISTANT => TB("E-Mail Assistant"),
|
||||
Components.LEGAL_CHECK_ASSISTANT => TB("Legal Check Assistant"),
|
||||
Components.SYNONYMS_ASSISTANT => TB("Synonym Assistant"),
|
||||
Components.MY_TASKS_ASSISTANT => TB("My Tasks Assistant"),
|
||||
Components.JOB_POSTING_ASSISTANT => TB("Job Posting Assistant"),
|
||||
Components.ERI_ASSISTANT => TB("ERI Server"),
|
||||
Components.I18N_ASSISTANT => TB("Localization Assistant"),
|
||||
|
||||
Components.CHAT => "New Chat",
|
||||
Components.CHAT => TB("New Chat"),
|
||||
|
||||
_ => Enum.GetName(typeof(Components), component)!,
|
||||
};
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
public static class ConfidenceSchemesExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ConfidenceSchemesExtensions).Namespace, nameof(ConfidenceSchemesExtensions));
|
||||
|
||||
public static string GetListDescription(this ConfidenceSchemes scheme) => scheme switch
|
||||
{
|
||||
ConfidenceSchemes.TRUST_ALL => "Trust all LLM providers",
|
||||
ConfidenceSchemes.TRUST_USA_EUROPE => "Trust LLM providers from the USA and Europe",
|
||||
ConfidenceSchemes.TRUST_USA => "Trust LLM providers from the USA",
|
||||
ConfidenceSchemes.TRUST_EUROPE => "Trust LLM providers from Europe",
|
||||
ConfidenceSchemes.TRUST_ASIA => "Trust LLM providers from Asia",
|
||||
ConfidenceSchemes.LOCAL_TRUST_ONLY => "Trust only local LLM providers",
|
||||
ConfidenceSchemes.TRUST_ALL => TB("Trust all LLM providers"),
|
||||
ConfidenceSchemes.TRUST_USA_EUROPE => TB("Trust LLM providers from the USA and Europe"),
|
||||
ConfidenceSchemes.TRUST_USA => TB("Trust LLM providers from the USA"),
|
||||
ConfidenceSchemes.TRUST_EUROPE => TB("Trust LLM providers from Europe"),
|
||||
ConfidenceSchemes.TRUST_ASIA => TB("Trust LLM providers from Asia"),
|
||||
ConfidenceSchemes.LOCAL_TRUST_ONLY => TB("Trust only local LLM providers"),
|
||||
|
||||
ConfidenceSchemes.CUSTOM => "Configure your own confidence scheme",
|
||||
ConfidenceSchemes.CUSTOM => TB("Configure your own confidence scheme"),
|
||||
|
||||
_ => "Unknown confidence scheme"
|
||||
_ => TB("Unknown confidence scheme"),
|
||||
};
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Tools.ERIClient.DataModel;
|
||||
|
||||
public static class ProviderTypeExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ProviderTypeExtensions).Namespace, nameof(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.",
|
||||
ProviderType.NONE => TB("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 => TB("The related data can be sent to any provider, regardless of where it is hosted (cloud or self-hosted)."),
|
||||
ProviderType.SELF_HOSTED => TB("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.",
|
||||
_ => TB("Unknown configuration. This data source cannot be used at the moment."),
|
||||
};
|
||||
}
|
||||
@@ -3,12 +3,15 @@ using System.Text.Json;
|
||||
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.ERIClient.DataModel;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
namespace AIStudio.Tools.ERIClient;
|
||||
|
||||
public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource), IERIClient
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ERIClientV1).Namespace, nameof(ERIClientV1));
|
||||
|
||||
#region Implementation of IERIClient
|
||||
|
||||
public async Task<APIResponse<List<AuthScheme>>> GetAuthMethodsAsync(CancellationToken cancellationToken = default)
|
||||
@@ -21,7 +24,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
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}"
|
||||
Message = string.Format(TB("Failed to retrieve the authentication methods: there was an issue communicating with the ERI server. Code: {0}, Reason: {1}"), response.StatusCode, response.ReasonPhrase)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -31,7 +34,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to retrieve the authentication methods: the ERI server did not return a valid response."
|
||||
Message = TB("Failed to retrieve the authentication methods: the ERI server did not return a valid response.")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,7 +49,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to retrieve the authentication methods: the request was canceled either by the user or due to a timeout."
|
||||
Message = TB("Failed to retrieve the authentication methods: the request was canceled either by the user or due to a timeout.")
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -54,7 +57,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = $"Failed to retrieve the authentication methods due to an exception: {e.Message}"
|
||||
Message = string.Format(TB("Failed to retrieve the authentication methods due to an exception: {0}"), e.Message)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -76,7 +79,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = $"Failed to authenticate with the ERI server. Code: {noneAuthResponse.StatusCode}, Reason: {noneAuthResponse.ReasonPhrase}"
|
||||
Message = string.Format(TB("Failed to authenticate with the ERI server. Code: {0}, Reason: {1}"), noneAuthResponse.StatusCode, noneAuthResponse.ReasonPhrase)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -86,7 +89,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to authenticate with the ERI server: the response was invalid."
|
||||
Message = TB("Failed to authenticate with the ERI server: the response was invalid.")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -108,7 +111,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to retrieve the password."
|
||||
Message = TB("Failed to retrieve the password.")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -130,7 +133,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = $"Failed to authenticate with the ERI server. Code: {usernamePasswordAuthResponse.StatusCode}, Reason: {usernamePasswordAuthResponse.ReasonPhrase}"
|
||||
Message = string.Format(TB("Failed to authenticate with the ERI server. Code: {0}, Reason: {1}"), usernamePasswordAuthResponse.StatusCode, usernamePasswordAuthResponse.ReasonPhrase)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -140,7 +143,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to authenticate with the server: the response was invalid."
|
||||
Message = TB("Failed to authenticate with the server: the response was invalid.")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -162,7 +165,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to retrieve the access token."
|
||||
Message = TB("Failed to retrieve the access token.")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -181,7 +184,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = $"Failed to authenticate with the ERI server. Code: {tokenAuthResponse.StatusCode}, Reason: {tokenAuthResponse.ReasonPhrase}"
|
||||
Message = string.Format(TB("Failed to authenticate with the ERI server. Code: {0}, Reason: {1}"), tokenAuthResponse.StatusCode, tokenAuthResponse.ReasonPhrase)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -191,7 +194,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to authenticate with the ERI server: the response was invalid."
|
||||
Message = TB("Failed to authenticate with the ERI server: the response was invalid.")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -208,7 +211,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "The authentication method is not supported yet."
|
||||
Message = TB("The authentication method is not supported yet.")
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -217,7 +220,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to authenticate with the ERI server: the request was canceled either by the user or due to a timeout."
|
||||
Message = TB("Failed to authenticate with the ERI server: the request was canceled either by the user or due to a timeout.")
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -225,7 +228,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = $"Failed to authenticate with the ERI server due to an exception: {e.Message}"
|
||||
Message = string.Format(TB("Failed to authenticate with the ERI server due to an exception: {0}"), e.Message)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -243,7 +246,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
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}"
|
||||
Message = string.Format(TB("Failed to retrieve the data source information: there was an issue communicating with the ERI server. Code: {0}, Reason: {1}"), response.StatusCode, response.ReasonPhrase)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -253,7 +256,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to retrieve the data source information: the ERI server did not return a valid response."
|
||||
Message = TB("Failed to retrieve the data source information: the ERI server did not return a valid response.")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -268,7 +271,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to retrieve the data source information: the request was canceled either by the user or due to a timeout."
|
||||
Message = TB("Failed to retrieve the data source information: the request was canceled either by the user or due to a timeout.")
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -276,7 +279,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = $"Failed to retrieve the data source information due to an exception: {e.Message}"
|
||||
Message = string.Format(TB("Failed to retrieve the data source information due to an exception: {0}"), e.Message)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -294,7 +297,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
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}"
|
||||
Message = string.Format(TB("Failed to retrieve the embedding information: there was an issue communicating with the ERI server. Code: {0}, Reason: {1}"), response.StatusCode, response.ReasonPhrase)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -304,7 +307,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to retrieve the embedding information: the ERI server did not return a valid response."
|
||||
Message = TB("Failed to retrieve the embedding information: the ERI server did not return a valid response.")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -319,7 +322,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to retrieve the embedding information: the request was canceled either by the user or due to a timeout."
|
||||
Message = TB("Failed to retrieve the embedding information: the request was canceled either by the user or due to a timeout.")
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -327,7 +330,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = $"Failed to retrieve the embedding information due to an exception: {e.Message}"
|
||||
Message = string.Format(TB("Failed to retrieve the embedding information due to an exception: {0}"), e.Message)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -345,7 +348,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
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}"
|
||||
Message = string.Format(TB("Failed to retrieve the retrieval information: there was an issue communicating with the ERI server. Code: {0}, Reason: {1}"), response.StatusCode, response.ReasonPhrase)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -355,7 +358,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to retrieve the retrieval information: the ERI server did not return a valid response."
|
||||
Message = TB("Failed to retrieve the retrieval information: the ERI server did not return a valid response.")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -370,7 +373,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to retrieve the retrieval information: the request was canceled either by the user or due to a timeout."
|
||||
Message = TB("Failed to retrieve the retrieval information: the request was canceled either by the user or due to a timeout.")
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -378,7 +381,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = $"Failed to retrieve the retrieval information due to an exception: {e.Message}"
|
||||
Message = string.Format(TB("Failed to retrieve the retrieval information due to an exception: {0}"), e.Message)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -399,7 +402,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
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}"
|
||||
Message = string.Format(TB("Failed to execute the retrieval request: there was an issue communicating with the ERI server. Code: {0}, Reason: {1}"), response.StatusCode, response.ReasonPhrase)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -409,7 +412,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to execute the retrieval request: the ERI server did not return a valid response."
|
||||
Message = TB("Failed to execute the retrieval request: the ERI server did not return a valid response.")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -424,7 +427,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to execute the retrieval request: the request was canceled either by the user or due to a timeout."
|
||||
Message = TB("Failed to execute the retrieval request: the request was canceled either by the user or due to a timeout.")
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -432,7 +435,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = $"Failed to execute the retrieval request due to an exception: {e.Message}"
|
||||
Message = string.Format(TB("Failed to execute the retrieval request due to an exception: {0}"), e.Message)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -450,7 +453,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
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}"
|
||||
Message = string.Format(TB("Failed to retrieve the security requirements: there was an issue communicating with the ERI server. Code: {0}, Reason: {1}"), response.StatusCode, response.ReasonPhrase)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -460,7 +463,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to retrieve the security requirements: the ERI server did not return a valid response."
|
||||
Message = TB("Failed to retrieve the security requirements: the ERI server did not return a valid response.")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -475,7 +478,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = "Failed to retrieve the security requirements: the request was canceled either by the user or due to a timeout."
|
||||
Message = TB("Failed to retrieve the security requirements: the request was canceled either by the user or due to a timeout.")
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -483,7 +486,7 @@ public class ERIClientV1(IERIDataSource dataSource) : ERIClientBase(dataSource),
|
||||
return new()
|
||||
{
|
||||
Successful = false,
|
||||
Message = $"Failed to retrieve the security requirements due to an exception: {e.Message}"
|
||||
Message = string.Format(TB("Failed to retrieve the security requirements due to an exception: {0}"), e.Message)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
namespace AIStudio.Tools.Metadata;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Assembly)]
|
||||
public class MetaDataArchitecture(string architecture) : Attribute
|
||||
public class MetaDataArchitectureAttribute(string architecture) : Attribute
|
||||
{
|
||||
public string Architecture => architecture;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace AIStudio.Tools.Metadata;
|
||||
|
||||
public class MetaDataLibrariesAttribute(string pdfiumVersion) : Attribute
|
||||
{
|
||||
public string PdfiumVersion => pdfiumVersion;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
public class I18N : ILang
|
||||
{
|
||||
public static readonly I18N I = new();
|
||||
private static readonly ILogger<I18N> LOG = Program.LOGGER_FACTORY.CreateLogger<I18N>();
|
||||
|
||||
private ILanguagePlugin? language = PluginFactory.BaseLanguage;
|
||||
|
||||
private I18N()
|
||||
{
|
||||
}
|
||||
|
||||
public static void Init(ILanguagePlugin language) => I.language = language;
|
||||
|
||||
#region Implementation of ILang
|
||||
|
||||
public string T(string fallbackEN)
|
||||
{
|
||||
LOG.LogWarning("Using I18N.I.T without namespace and type is probably wrong, because the I18N key collection process of the build system will not find those keys.");
|
||||
if(this.language is not null)
|
||||
return this.GetText(this.language, fallbackEN);
|
||||
|
||||
return fallbackEN;
|
||||
}
|
||||
|
||||
public string T(string fallbackEN, string? typeNamespace, string? typeName)
|
||||
{
|
||||
if(this.language is not null)
|
||||
return this.GetText(this.language, fallbackEN, typeNamespace, typeName);
|
||||
|
||||
return fallbackEN;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -12,8 +12,8 @@ public static class ILangExtensions
|
||||
typeName ??= type.Name;
|
||||
typeNamespace ??= type.Namespace!;
|
||||
|
||||
// When the type's name ends with `1 or 2`, etc. (i.e., generic classes), remove it:
|
||||
if(typeName.Contains("`"))
|
||||
// When the type's name ends with `1 or `2, etc. (i.e., generic classes), remove it:
|
||||
if(typeName.Contains('`'))
|
||||
typeName = typeName[..typeName.IndexOf('`')];
|
||||
|
||||
var ns = $"{typeNamespace}::{typeName}".ToUpperInvariant().Replace(".", "::");
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace AIStudio.Tools.PluginSystem;
|
||||
/// </summary>
|
||||
public abstract partial class PluginBase : IPluginMetadata
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(PluginBase).Namespace, nameof(PluginBase));
|
||||
|
||||
private readonly IReadOnlyCollection<string> baseIssues;
|
||||
protected readonly LuaState state;
|
||||
|
||||
@@ -155,21 +157,21 @@ public abstract partial class PluginBase : IPluginMetadata
|
||||
{
|
||||
if (!this.state.Environment["ID"].TryRead<string>(out var idText))
|
||||
{
|
||||
message = "The field ID does not exist or is not a valid string.";
|
||||
message = TB("The field ID does not exist or is not a valid string.");
|
||||
id = Guid.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Guid.TryParse(idText, out id))
|
||||
{
|
||||
message = "The field ID is not a valid GUID / UUID. The ID must be formatted in the 8-4-4-4-12 format (XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX).";
|
||||
message = TB("The field ID is not a valid GUID / UUID. The ID must be formatted in the 8-4-4-4-12 format (XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX).");
|
||||
id = Guid.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(id == Guid.Empty)
|
||||
{
|
||||
message = "The field ID is empty. The ID must be formatted in the 8-4-4-4-12 format (XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX).";
|
||||
message = TB("The field ID is empty. The ID must be formatted in the 8-4-4-4-12 format (XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX).");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -187,14 +189,14 @@ public abstract partial class PluginBase : IPluginMetadata
|
||||
{
|
||||
if (!this.state.Environment["NAME"].TryRead(out name))
|
||||
{
|
||||
message = "The field NAME does not exist or is not a valid string.";
|
||||
message = TB("The field NAME does not exist or is not a valid string.");
|
||||
name = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
message = "The field NAME is empty. The name must be a non-empty string.";
|
||||
message = TB("The field NAME is empty. The name must be a non-empty string.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -212,14 +214,14 @@ public abstract partial class PluginBase : IPluginMetadata
|
||||
{
|
||||
if (!this.state.Environment["DESCRIPTION"].TryRead(out description))
|
||||
{
|
||||
message = "The field DESCRIPTION does not exist or is not a valid string.";
|
||||
message = TB("The field DESCRIPTION does not exist or is not a valid string.");
|
||||
description = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(string.IsNullOrWhiteSpace(description))
|
||||
{
|
||||
message = "The field DESCRIPTION is empty. The description must be a non-empty string.";
|
||||
message = TB("The field DESCRIPTION is empty. The description must be a non-empty string.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -237,21 +239,21 @@ public abstract partial class PluginBase : IPluginMetadata
|
||||
{
|
||||
if (!this.state.Environment["VERSION"].TryRead<string>(out var versionText))
|
||||
{
|
||||
message = "The field VERSION does not exist or is not a valid string.";
|
||||
message = TB("The field VERSION does not exist or is not a valid string.");
|
||||
version = PluginVersion.NONE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PluginVersion.TryParse(versionText, out version))
|
||||
{
|
||||
message = "The field VERSION is not a valid version number. The version number must be formatted as string in the major.minor.patch format (X.X.X).";
|
||||
message = TB("The field VERSION is not a valid version number. The version number must be formatted as string in the major.minor.patch format (X.X.X).");
|
||||
version = PluginVersion.NONE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(version == PluginVersion.NONE)
|
||||
{
|
||||
message = "The field VERSION is empty. The version number must be formatted as string in the major.minor.patch format (X.X.X).";
|
||||
message = TB("The field VERSION is empty. The version number must be formatted as string in the major.minor.patch format (X.X.X).");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -270,7 +272,7 @@ public abstract partial class PluginBase : IPluginMetadata
|
||||
if (!this.state.Environment["AUTHORS"].TryRead<LuaTable>(out var authorsTable))
|
||||
{
|
||||
authors = [];
|
||||
message = "The table AUTHORS does not exist or is using an invalid syntax.";
|
||||
message = TB("The table AUTHORS does not exist or is using an invalid syntax.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -282,7 +284,7 @@ public abstract partial class PluginBase : IPluginMetadata
|
||||
authors = authorList.ToArray();
|
||||
if(authorList.Count == 0)
|
||||
{
|
||||
message = "The table AUTHORS is empty. At least one author must be specified.";
|
||||
message = TB("The table AUTHORS is empty. At least one author must be specified.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -301,13 +303,13 @@ public abstract partial class PluginBase : IPluginMetadata
|
||||
if (!this.state.Environment["SUPPORT_CONTACT"].TryRead(out contact))
|
||||
{
|
||||
contact = string.Empty;
|
||||
message = "The field SUPPORT_CONTACT does not exist or is not a valid string.";
|
||||
message = TB("The field SUPPORT_CONTACT does not exist or is not a valid string.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(string.IsNullOrWhiteSpace(contact))
|
||||
{
|
||||
message = "The field SUPPORT_CONTACT is empty. The support contact must be a non-empty string.";
|
||||
message = TB("The field SUPPORT_CONTACT is empty. The support contact must be a non-empty string.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -326,14 +328,14 @@ public abstract partial class PluginBase : IPluginMetadata
|
||||
if (!this.state.Environment["SOURCE_URL"].TryRead(out url))
|
||||
{
|
||||
url = string.Empty;
|
||||
message = "The field SOURCE_URL does not exist or is not a valid string.";
|
||||
message = TB("The field SOURCE_URL does not exist or is not a valid string.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!url.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase) && !url.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
url = string.Empty;
|
||||
message = "The field SOURCE_URL is not a valid URL. The URL must start with 'http://' or 'https://'.";
|
||||
message = TB("The field SOURCE_URL is not a valid URL. The URL must start with 'http://' or 'https://'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -352,7 +354,7 @@ public abstract partial class PluginBase : IPluginMetadata
|
||||
if (!this.state.Environment["CATEGORIES"].TryRead<LuaTable>(out var categoriesTable))
|
||||
{
|
||||
categories = [];
|
||||
message = "The table CATEGORIES does not exist or is using an invalid syntax.";
|
||||
message = TB("The table CATEGORIES does not exist or is using an invalid syntax.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -365,7 +367,7 @@ public abstract partial class PluginBase : IPluginMetadata
|
||||
categories = categoryList.ToArray();
|
||||
if(categoryList.Count == 0)
|
||||
{
|
||||
message = $"The table CATEGORIES is empty. At least one category is necessary. Valid categories are: {CommonTools.GetAllEnumValues(PluginCategory.NONE)}.";
|
||||
message = string.Format(TB("The table CATEGORIES is empty. At least one category is necessary. Valid categories are: {0}."), CommonTools.GetAllEnumValues(PluginCategory.NONE));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -384,7 +386,7 @@ public abstract partial class PluginBase : IPluginMetadata
|
||||
if (!this.state.Environment["TARGET_GROUPS"].TryRead<LuaTable>(out var targetGroupsTable))
|
||||
{
|
||||
targetGroups = [];
|
||||
message = "The table TARGET_GROUPS does not exist or is using an invalid syntax.";
|
||||
message = TB("The table TARGET_GROUPS does not exist or is using an invalid syntax.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -397,7 +399,7 @@ public abstract partial class PluginBase : IPluginMetadata
|
||||
targetGroups = targetGroupList.ToArray();
|
||||
if(targetGroups.Length == 0)
|
||||
{
|
||||
message = "The table TARGET_GROUPS is empty or is not a valid table of strings. Valid target groups are: {CommonTools.GetAllEnumValues(PluginTargetGroup.NONE)}.";
|
||||
message = string.Format(TB("The table TARGET_GROUPS is empty or is not a valid table of strings. Valid target groups are: {0}."), CommonTools.GetAllEnumValues(PluginTargetGroup.NONE));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -416,7 +418,7 @@ public abstract partial class PluginBase : IPluginMetadata
|
||||
if (!this.state.Environment["IS_MAINTAINED"].TryRead(out isMaintained))
|
||||
{
|
||||
isMaintained = false;
|
||||
message = "The field IS_MAINTAINED does not exist or is not a valid boolean.";
|
||||
message = TB("The field IS_MAINTAINED does not exist or is not a valid boolean.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -435,7 +437,7 @@ public abstract partial class PluginBase : IPluginMetadata
|
||||
if (!this.state.Environment["DEPRECATION_MESSAGE"].TryRead(out deprecationMessage))
|
||||
{
|
||||
deprecationMessage = string.Empty;
|
||||
message = "The field DEPRECATION_MESSAGE does not exist, is not a valid string. This message is optional: use an empty string to indicate that the plugin is not deprecated.";
|
||||
message = TB("The field DEPRECATION_MESSAGE does not exist, is not a valid string. This message is optional: use an empty string to indicate that the plugin is not deprecated.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -453,7 +455,7 @@ public abstract partial class PluginBase : IPluginMetadata
|
||||
{
|
||||
if (!this.state.Environment["UI_TEXT_CONTENT"].TryRead<LuaTable>(out var textTable))
|
||||
{
|
||||
message = "The UI_TEXT_CONTENT table does not exist or is not a valid table.";
|
||||
message = TB("The UI_TEXT_CONTENT table does not exist or is not a valid table.");
|
||||
pluginContent = [];
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2,37 +2,39 @@ namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
public static class PluginCategoryExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(PluginCategoryExtensions).Namespace, nameof(PluginCategoryExtensions));
|
||||
|
||||
public static string GetName(this PluginCategory type) => type switch
|
||||
{
|
||||
PluginCategory.NONE => "None",
|
||||
PluginCategory.CORE => "AI Studio Core",
|
||||
PluginCategory.NONE => TB("None"),
|
||||
PluginCategory.CORE => TB("AI Studio Core"),
|
||||
|
||||
PluginCategory.BUSINESS => "Business",
|
||||
PluginCategory.INDUSTRY => "Industry",
|
||||
PluginCategory.UTILITY => "Utility",
|
||||
PluginCategory.SOFTWARE_DEVELOPMENT => "Software Development",
|
||||
PluginCategory.GAMING => "Gaming",
|
||||
PluginCategory.EDUCATION => "Education",
|
||||
PluginCategory.ENTERTAINMENT => "Entertainment",
|
||||
PluginCategory.SOCIAL => "Social",
|
||||
PluginCategory.SHOPPING => "Shopping",
|
||||
PluginCategory.TRAVEL => "Travel",
|
||||
PluginCategory.HEALTH => "Health",
|
||||
PluginCategory.FITNESS => "Fitness",
|
||||
PluginCategory.FOOD => "Food",
|
||||
PluginCategory.PARTY => "Party",
|
||||
PluginCategory.SPORTS => "Sports",
|
||||
PluginCategory.NEWS => "News",
|
||||
PluginCategory.WEATHER => "Weather",
|
||||
PluginCategory.MUSIC => "Music",
|
||||
PluginCategory.POLITICAL => "Political",
|
||||
PluginCategory.SCIENCE => "Science",
|
||||
PluginCategory.TECHNOLOGY => "Technology",
|
||||
PluginCategory.ART => "Art",
|
||||
PluginCategory.FICTION => "Fiction",
|
||||
PluginCategory.WRITING => "Writing",
|
||||
PluginCategory.CONTENT_CREATION => "Content Creation",
|
||||
PluginCategory.BUSINESS => TB("Business"),
|
||||
PluginCategory.INDUSTRY => TB("Industry"),
|
||||
PluginCategory.UTILITY => TB("Utility"),
|
||||
PluginCategory.SOFTWARE_DEVELOPMENT => TB("Software Development"),
|
||||
PluginCategory.GAMING => TB("Gaming"),
|
||||
PluginCategory.EDUCATION => TB("Education"),
|
||||
PluginCategory.ENTERTAINMENT => TB("Entertainment"),
|
||||
PluginCategory.SOCIAL => TB("Social"),
|
||||
PluginCategory.SHOPPING => TB("Shopping"),
|
||||
PluginCategory.TRAVEL => TB("Travel"),
|
||||
PluginCategory.HEALTH => TB("Health"),
|
||||
PluginCategory.FITNESS => TB("Fitness"),
|
||||
PluginCategory.FOOD => TB("Food"),
|
||||
PluginCategory.PARTY => TB("Party"),
|
||||
PluginCategory.SPORTS => TB("Sports"),
|
||||
PluginCategory.NEWS => TB("News"),
|
||||
PluginCategory.WEATHER => TB("Weather"),
|
||||
PluginCategory.MUSIC => TB("Music"),
|
||||
PluginCategory.POLITICAL => TB("Political"),
|
||||
PluginCategory.SCIENCE => TB("Science"),
|
||||
PluginCategory.TECHNOLOGY => TB("Technology"),
|
||||
PluginCategory.ART => TB("Art"),
|
||||
PluginCategory.FICTION => TB("Fiction"),
|
||||
PluginCategory.WRITING => TB("Writing"),
|
||||
PluginCategory.CONTENT_CREATION => TB("Content Creation"),
|
||||
|
||||
_ => "Unknown plugin category",
|
||||
_ => TB("Unknown plugin category"),
|
||||
};
|
||||
}
|
||||
@@ -16,7 +16,27 @@ public static partial class PluginFactory
|
||||
return;
|
||||
}
|
||||
|
||||
// A plugin update might remove some resources. Even worse, a plugin
|
||||
// might have changed its name, etc. Thus, we delete the internal
|
||||
// plugin directories before copying the new resources:
|
||||
LOG.LogInformation("Try to delete the internal plugins directory for maintenance.");
|
||||
if (Directory.Exists(INTERNAL_PLUGINS_ROOT))
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.Delete(INTERNAL_PLUGINS_ROOT, true);
|
||||
LOG.LogInformation("Successfully deleted the internal plugins directory for maintenance.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.LogError($"Could not delete the internal plugins directory for maintenance: {INTERNAL_PLUGINS_ROOT}. Error: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
LOG.LogInformation("Start ensuring internal plugins.");
|
||||
if(!Directory.Exists(INTERNAL_PLUGINS_ROOT))
|
||||
Directory.CreateDirectory(INTERNAL_PLUGINS_ROOT);
|
||||
|
||||
foreach (var plugin in Enum.GetValues<InternalPlugin>())
|
||||
{
|
||||
LOG.LogInformation($"Ensure plugin: {plugin}");
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
public sealed class PluginLanguage : PluginBase, ILanguagePlugin
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(PluginLanguage).Namespace, nameof(PluginLanguage));
|
||||
private static readonly ILogger<PluginLanguage> LOGGER = Program.LOGGER_FACTORY.CreateLogger<PluginLanguage>();
|
||||
|
||||
private readonly Dictionary<string, string> content = [];
|
||||
@@ -53,28 +54,28 @@ public sealed class PluginLanguage : PluginBase, ILanguagePlugin
|
||||
{
|
||||
if (!this.state.Environment["IETF_TAG"].TryRead(out readLangCultureTag))
|
||||
{
|
||||
message = "The field IETF_TAG does not exist or is not a valid string.";
|
||||
message = TB("The field IETF_TAG does not exist or is not a valid string.");
|
||||
readLangCultureTag = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(readLangCultureTag))
|
||||
{
|
||||
message = "The field IETF_TAG is empty. Use a valid IETF tag like 'en-US'. The first part is the language, the second part is the country code.";
|
||||
message = TB("The field IETF_TAG is empty. Use a valid IETF tag like 'en-US'. The first part is the language, the second part is the country code.");
|
||||
readLangCultureTag = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (readLangCultureTag.Length != 5)
|
||||
{
|
||||
message = "The field IETF_TAG is not a valid IETF tag. Use a valid IETF tag like 'en-US'. The first part is the language, the second part is the country code.";
|
||||
message = TB("The field IETF_TAG is not a valid IETF tag. Use a valid IETF tag like 'en-US'. The first part is the language, the second part is the country code.");
|
||||
readLangCultureTag = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (readLangCultureTag[2] != '-')
|
||||
{
|
||||
message = "The field IETF_TAG is not a valid IETF tag. Use a valid IETF tag like 'en-US'. The first part is the language, the second part is the country code.";
|
||||
message = TB("The field IETF_TAG is not a valid IETF tag. Use a valid IETF tag like 'en-US'. The first part is the language, the second part is the country code.");
|
||||
readLangCultureTag = string.Empty;
|
||||
return false;
|
||||
}
|
||||
@@ -83,7 +84,7 @@ public sealed class PluginLanguage : PluginBase, ILanguagePlugin
|
||||
for (var i = 0; i < 2; i++)
|
||||
if (!char.IsLower(readLangCultureTag[i]))
|
||||
{
|
||||
message = "The field IETF_TAG is not a valid IETF tag. Use a valid IETF tag like 'en-US'. The first part is the language, the second part is the country code.";
|
||||
message = TB("The field IETF_TAG is not a valid IETF tag. Use a valid IETF tag like 'en-US'. The first part is the language, the second part is the country code.");
|
||||
readLangCultureTag = string.Empty;
|
||||
return false;
|
||||
}
|
||||
@@ -92,7 +93,7 @@ public sealed class PluginLanguage : PluginBase, ILanguagePlugin
|
||||
for (var i = 3; i < 5; i++)
|
||||
if (!char.IsUpper(readLangCultureTag[i]))
|
||||
{
|
||||
message = "The field IETF_TAG is not a valid IETF tag. Use a valid IETF tag like 'en-US'. The first part is the language, the second part is the country code.";
|
||||
message = TB("The field IETF_TAG is not a valid IETF tag. Use a valid IETF tag like 'en-US'. The first part is the language, the second part is the country code.");
|
||||
readLangCultureTag = string.Empty;
|
||||
return false;
|
||||
}
|
||||
@@ -105,14 +106,14 @@ public sealed class PluginLanguage : PluginBase, ILanguagePlugin
|
||||
{
|
||||
if (!this.state.Environment["LANG_NAME"].TryRead(out readLangName))
|
||||
{
|
||||
message = "The field LANG_NAME does not exist or is not a valid string.";
|
||||
message = TB("The field LANG_NAME does not exist or is not a valid string.");
|
||||
readLangName = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(readLangName))
|
||||
{
|
||||
message = "The field LANG_NAME is empty. Use a valid language name.";
|
||||
message = TB("The field LANG_NAME is empty. Use a valid language name.");
|
||||
readLangName = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2,24 +2,26 @@ namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
public static class PluginTargetGroupExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(PluginTargetGroupExtensions).Namespace, nameof(PluginTargetGroupExtensions));
|
||||
|
||||
public static string Name(this PluginTargetGroup group) => group switch
|
||||
{
|
||||
PluginTargetGroup.NONE => "No target group",
|
||||
PluginTargetGroup.NONE => TB("No target group"),
|
||||
|
||||
PluginTargetGroup.EVERYONE => "Everyone",
|
||||
PluginTargetGroup.CHILDREN => "Children",
|
||||
PluginTargetGroup.TEENAGERS => "Teenagers",
|
||||
PluginTargetGroup.STUDENTS => "Students",
|
||||
PluginTargetGroup.ADULTS => "Adults",
|
||||
PluginTargetGroup.EVERYONE => TB("Everyone"),
|
||||
PluginTargetGroup.CHILDREN => TB("Children"),
|
||||
PluginTargetGroup.TEENAGERS => TB("Teenagers"),
|
||||
PluginTargetGroup.STUDENTS => TB("Students"),
|
||||
PluginTargetGroup.ADULTS => TB("Adults"),
|
||||
|
||||
PluginTargetGroup.INDUSTRIAL_WORKERS => "Industrial workers",
|
||||
PluginTargetGroup.OFFICE_WORKERS => "Office workers",
|
||||
PluginTargetGroup.BUSINESS_PROFESSIONALS => "Business professionals",
|
||||
PluginTargetGroup.SOFTWARE_DEVELOPERS => "Software developers",
|
||||
PluginTargetGroup.SCIENTISTS => "Scientists",
|
||||
PluginTargetGroup.TEACHERS => "Teachers",
|
||||
PluginTargetGroup.ARTISTS => "Artists",
|
||||
PluginTargetGroup.INDUSTRIAL_WORKERS => TB("Industrial workers"),
|
||||
PluginTargetGroup.OFFICE_WORKERS => TB("Office workers"),
|
||||
PluginTargetGroup.BUSINESS_PROFESSIONALS => TB("Business professionals"),
|
||||
PluginTargetGroup.SOFTWARE_DEVELOPERS => TB("Software developers"),
|
||||
PluginTargetGroup.SCIENTISTS => TB("Scientists"),
|
||||
PluginTargetGroup.TEACHERS => TB("Teachers"),
|
||||
PluginTargetGroup.ARTISTS => TB("Artists"),
|
||||
|
||||
_ => "Unknown target group",
|
||||
_ => TB("Unknown target group"),
|
||||
};
|
||||
}
|
||||
@@ -2,14 +2,16 @@ namespace AIStudio.Tools.PluginSystem;
|
||||
|
||||
public static class PluginTypeExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(PluginTypeExtensions).Namespace, nameof(PluginTypeExtensions));
|
||||
|
||||
public static string GetName(this PluginType type) => type switch
|
||||
{
|
||||
PluginType.LANGUAGE => "Language plugin",
|
||||
PluginType.ASSISTANT => "Assistant plugin",
|
||||
PluginType.CONFIGURATION => "Configuration plugin",
|
||||
PluginType.THEME => "Theme plugin",
|
||||
PluginType.LANGUAGE => TB("Language plugin"),
|
||||
PluginType.ASSISTANT => TB("Assistant plugin"),
|
||||
PluginType.CONFIGURATION => TB("Configuration plugin"),
|
||||
PluginType.THEME => TB("Theme plugin"),
|
||||
|
||||
_ => "Unknown plugin type",
|
||||
_ => TB("Unknown plugin type"),
|
||||
};
|
||||
|
||||
public static string GetDirectory(this PluginType type) => type switch
|
||||
|
||||
@@ -4,21 +4,24 @@ using AIStudio.Agents;
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Tools.RAG.AugmentationProcesses;
|
||||
|
||||
public sealed class AugmentationOne : IAugmentationProcess
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(AugmentationOne).Namespace, nameof(AugmentationOne));
|
||||
|
||||
#region Implementation of IAugmentationProcess
|
||||
|
||||
/// <inheritdoc />
|
||||
public string TechnicalName => "AugmentationOne";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string UIName => "Standard augmentation process";
|
||||
public string UIName => TB("Standard augmentation process");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Description => "This is the standard augmentation process, which uses all retrieval contexts to augment the chat thread.";
|
||||
public string Description => TB("This is the standard augmentation process, which uses all retrieval contexts to augment the chat thread.");
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ChatThread> ProcessAsync(IProvider provider, IContent lastPrompt, ChatThread chatThread, IReadOnlyList<IRetrievalContext> retrievalContexts, CancellationToken token = default)
|
||||
|
||||
+5
-2
@@ -3,21 +3,24 @@ using AIStudio.Chat;
|
||||
using AIStudio.Components;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Tools.RAG.DataSourceSelectionProcesses;
|
||||
|
||||
public class AgenticSrcSelWithDynHeur : IDataSourceSelectionProcess
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(AgenticSrcSelWithDynHeur).Namespace, nameof(AgenticSrcSelWithDynHeur));
|
||||
|
||||
#region Implementation of IDataSourceSelectionProcess
|
||||
|
||||
/// <inheritdoc />
|
||||
public string TechnicalName => "AgenticSrcSelWithDynHeur";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string UIName => "Automatic AI data source selection with heuristik source reduction";
|
||||
public string UIName => TB("Automatic AI data source selection with heuristik source reduction");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Description => "Automatically selects the appropriate data sources based on the last prompt. Applies a heuristic reduction at the end to reduce the number of data sources.";
|
||||
public string Description => TB("Automatically selects the appropriate data sources based on the last prompt. Applies a heuristic reduction at the end to reduce the number of data sources.");
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<DataSelectionResult> SelectDataSourcesAsync(IProvider provider, IContent lastPrompt, ChatThread chatThread, AllowedSelectedDataSources dataSources, CancellationToken token = default)
|
||||
|
||||
@@ -2,6 +2,7 @@ using AIStudio.Chat;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
using AIStudio.Tools.RAG.AugmentationProcesses;
|
||||
using AIStudio.Tools.RAG.DataSourceSelectionProcesses;
|
||||
using AIStudio.Tools.Services;
|
||||
@@ -10,16 +11,18 @@ namespace AIStudio.Tools.RAG.RAGProcesses;
|
||||
|
||||
public sealed class AISrcSelWithRetCtxVal : IRagProcess
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(AISrcSelWithRetCtxVal).Namespace, nameof(AISrcSelWithRetCtxVal));
|
||||
|
||||
#region Implementation of IRagProcess
|
||||
|
||||
/// <inheritdoc />
|
||||
public string TechnicalName => "AISrcSelWithRetCtxVal";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string UIName => "AI source selection with AI retrieval context validation";
|
||||
public string UIName => TB("AI source selection with AI retrieval context validation");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Description => "This RAG process filters data sources, automatically selects appropriate sources, optionally allows manual source selection, retrieves data, and automatically validates the retrieval context.";
|
||||
public string Description => TB("This RAG process filters data sources, automatically selects appropriate sources, optionally allows manual source selection, retrieves data, and automatically validates the retrieval context.");
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ChatThread> ProcessAsync(IProvider provider, IContent lastPrompt, ChatThread chatThread, CancellationToken token = default)
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// ReSharper disable NotAccessedPositionalProperty.Global
|
||||
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Tools.Rust;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a file type filter for file selection dialogs.
|
||||
/// </summary>
|
||||
/// <param name="FilterName">The name of the filter.</param>
|
||||
/// <param name="FilterExtensions">The file extensions associated with the filter.</param>
|
||||
public readonly record struct FileTypeFilter(string FilterName, string[] FilterExtensions)
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(FileTypeFilter).Namespace, nameof(FileTypeFilter));
|
||||
|
||||
public static FileTypeFilter PDF => new(TB("PDF Files"), ["pdf"]);
|
||||
|
||||
public static FileTypeFilter Text => new(TB("Text Files"), ["txt", "md"]);
|
||||
|
||||
public static FileTypeFilter AllOffice => new(TB("All Office Files"), ["docx", "xlsx", "pptx", "doc", "xls", "ppt", "pdf"]);
|
||||
|
||||
public static FileTypeFilter AllImages => new(TB("All Image Files"), ["jpg", "jpeg", "png", "gif", "bmp", "tiff"]);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace AIStudio.Tools.Rust;
|
||||
|
||||
public sealed class SelectFileOptions
|
||||
{
|
||||
public required string Title { get; init; }
|
||||
|
||||
public PreviousFile? PreviousFile { get; init; }
|
||||
|
||||
public FileTypeFilter? Filter { get; init; }
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
using AIStudio.Tools.Rust;
|
||||
|
||||
namespace AIStudio.Tools.Services;
|
||||
|
||||
public sealed partial class RustService
|
||||
{
|
||||
private static string TB_APIKeys(string fallbackEN) => I18N.I.T(fallbackEN, typeof(RustService).Namespace, $"{nameof(RustService)}.APIKeys");
|
||||
|
||||
/// <summary>
|
||||
/// Try to get the API key for the given secret ID.
|
||||
/// </summary>
|
||||
@@ -12,13 +15,15 @@ public sealed partial class RustService
|
||||
/// <returns>The requested secret.</returns>
|
||||
public async Task<RequestedSecret> GetAPIKey(ISecretId secretId, bool isTrying = false)
|
||||
{
|
||||
static string TB(string fallbackEN) => TB_APIKeys(fallbackEN);
|
||||
|
||||
var secretRequest = new SelectSecretRequest($"provider::{secretId.SecretId}::{secretId.SecretName}::api_key", Environment.UserName, isTrying);
|
||||
var result = await this.http.PostAsJsonAsync("/secrets/get", secretRequest, this.jsonRustSerializerOptions);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
if(!isTrying)
|
||||
this.logger!.LogError($"Failed to get the API key for secret ID '{secretId.SecretId}' due to an API issue: '{result.StatusCode}'");
|
||||
return new RequestedSecret(false, new EncryptedText(string.Empty), "Failed to get the API key due to an API issue.");
|
||||
return new RequestedSecret(false, new EncryptedText(string.Empty), TB("Failed to get the API key due to an API issue."));
|
||||
}
|
||||
|
||||
var secret = await result.Content.ReadFromJsonAsync<RequestedSecret>(this.jsonRustSerializerOptions);
|
||||
@@ -36,13 +41,15 @@ public sealed partial class RustService
|
||||
/// <returns>The store secret response.</returns>
|
||||
public async Task<StoreSecretResponse> SetAPIKey(ISecretId secretId, string key)
|
||||
{
|
||||
static string TB(string fallbackEN) => TB_APIKeys(fallbackEN);
|
||||
|
||||
var encryptedKey = await this.encryptor!.Encrypt(key);
|
||||
var request = new StoreSecretRequest($"provider::{secretId.SecretId}::{secretId.SecretName}::api_key", Environment.UserName, encryptedKey);
|
||||
var result = await this.http.PostAsJsonAsync("/secrets/store", request, this.jsonRustSerializerOptions);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
this.logger!.LogError($"Failed to store the API key for secret ID '{secretId.SecretId}' due to an API issue: '{result.StatusCode}'");
|
||||
return new StoreSecretResponse(false, "Failed to get the API key due to an API issue.");
|
||||
return new StoreSecretResponse(false, TB("Failed to get the API key due to an API issue."));
|
||||
}
|
||||
|
||||
var state = await result.Content.ReadFromJsonAsync<StoreSecretResponse>(this.jsonRustSerializerOptions);
|
||||
@@ -59,12 +66,14 @@ public sealed partial class RustService
|
||||
/// <returns>The delete secret response.</returns>
|
||||
public async Task<DeleteSecretResponse> DeleteAPIKey(ISecretId secretId)
|
||||
{
|
||||
static string TB(string fallbackEN) => TB_APIKeys(fallbackEN);
|
||||
|
||||
var request = new SelectSecretRequest($"provider::{secretId.SecretId}::{secretId.SecretName}::api_key", Environment.UserName, false);
|
||||
var result = await this.http.PostAsJsonAsync("/secrets/delete", request, this.jsonRustSerializerOptions);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
this.logger!.LogError($"Failed to delete the API key for secret ID '{secretId.SecretId}' due to an API issue: '{result.StatusCode}'");
|
||||
return new DeleteSecretResponse{Success = false, WasEntryFound = false, Issue = "Failed to delete the API key due to an API issue."};
|
||||
return new DeleteSecretResponse{Success = false, WasEntryFound = false, Issue = TB("Failed to delete the API key due to an API issue.")};
|
||||
}
|
||||
|
||||
var state = await result.Content.ReadFromJsonAsync<DeleteSecretResponse>(this.jsonRustSerializerOptions);
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
using AIStudio.Tools.Rust;
|
||||
|
||||
namespace AIStudio.Tools.Services;
|
||||
|
||||
public sealed partial class RustService
|
||||
{
|
||||
private static string TB_Clipboard(string fallbackEN) => I18N.I.T(fallbackEN, typeof(RustService).Namespace, $"{nameof(RustService)}.Clipboard");
|
||||
|
||||
/// <summary>
|
||||
/// Tries to copy the given text to the clipboard.
|
||||
/// </summary>
|
||||
@@ -11,7 +14,9 @@ public sealed partial class RustService
|
||||
/// <param name="text">The text to copy to the clipboard.</param>
|
||||
public async Task CopyText2Clipboard(ISnackbar snackbar, string text)
|
||||
{
|
||||
var message = "Successfully copied the text to your clipboard";
|
||||
static string TB(string fallbackEN) => TB_Clipboard(fallbackEN);
|
||||
|
||||
var message = TB("Successfully copied the text to your clipboard");
|
||||
var iconColor = Color.Error;
|
||||
var severity = Severity.Error;
|
||||
try
|
||||
@@ -21,7 +26,7 @@ public sealed partial class RustService
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
this.logger!.LogError($"Failed to copy the text to the clipboard due to an network error: '{response.StatusCode}'");
|
||||
message = "Failed to copy the text to your clipboard.";
|
||||
message = TB("Failed to copy the text to your clipboard.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -29,7 +34,7 @@ public sealed partial class RustService
|
||||
if (!state.Success)
|
||||
{
|
||||
this.logger!.LogError("Failed to copy the text to the clipboard.");
|
||||
message = "Failed to copy the text to your clipboard.";
|
||||
message = TB("Failed to copy the text to your clipboard.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,10 +17,16 @@ public sealed partial class RustService
|
||||
return await result.Content.ReadFromJsonAsync<DirectorySelectionResponse>(this.jsonRustSerializerOptions);
|
||||
}
|
||||
|
||||
public async Task<FileSelectionResponse> SelectFile(string title, string? initialFile = null)
|
||||
public async Task<FileSelectionResponse> SelectFile(string title, FileTypeFilter? filter = null, string? initialFile = null)
|
||||
{
|
||||
PreviousFile? previousFile = initialFile is null ? null : new (initialFile);
|
||||
var result = await this.http.PostAsJsonAsync($"/select/file?title={title}", previousFile, this.jsonRustSerializerOptions);
|
||||
var payload = new SelectFileOptions
|
||||
{
|
||||
Title = title,
|
||||
PreviousFile = initialFile is null ? null : new (initialFile),
|
||||
Filter = filter
|
||||
};
|
||||
|
||||
var result = await this.http.PostAsJsonAsync("/select/file", payload, this.jsonRustSerializerOptions);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
this.logger!.LogError($"Failed to select a file: '{result.StatusCode}'");
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace AIStudio.Tools.Services;
|
||||
|
||||
public sealed partial class RustService
|
||||
{
|
||||
public async Task<string> GetPDFText(string filePath)
|
||||
{
|
||||
var response = await this.http.GetAsync($"/retrieval/fs/read/pdf?file_path={filePath}");
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
this.logger!.LogError($"Failed to read the PDF file due to an network error: '{response.StatusCode}'");
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
using AIStudio.Tools.Rust;
|
||||
|
||||
namespace AIStudio.Tools.Services;
|
||||
|
||||
public sealed partial class RustService
|
||||
{
|
||||
private static string TB_Secrets(string fallbackEN) => I18N.I.T(fallbackEN, typeof(RustService).Namespace, $"{nameof(RustService)}.Secrets");
|
||||
|
||||
/// <summary>
|
||||
/// Try to get the secret data for the given secret ID.
|
||||
/// </summary>
|
||||
@@ -12,13 +15,15 @@ public sealed partial class RustService
|
||||
/// <returns>The requested secret.</returns>
|
||||
public async Task<RequestedSecret> GetSecret(ISecretId secretId, bool isTrying = false)
|
||||
{
|
||||
static string TB(string fallbackEN) => TB_Secrets(fallbackEN);
|
||||
|
||||
var secretRequest = new SelectSecretRequest($"secret::{secretId.SecretId}::{secretId.SecretName}", Environment.UserName, isTrying);
|
||||
var result = await this.http.PostAsJsonAsync("/secrets/get", secretRequest, this.jsonRustSerializerOptions);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
if(!isTrying)
|
||||
this.logger!.LogError($"Failed to get the secret data for secret ID '{secretId.SecretId}' due to an API issue: '{result.StatusCode}'");
|
||||
return new RequestedSecret(false, new EncryptedText(string.Empty), "Failed to get the secret data due to an API issue.");
|
||||
return new RequestedSecret(false, new EncryptedText(string.Empty), TB("Failed to get the secret data due to an API issue."));
|
||||
}
|
||||
|
||||
var secret = await result.Content.ReadFromJsonAsync<RequestedSecret>(this.jsonRustSerializerOptions);
|
||||
@@ -36,13 +41,15 @@ public sealed partial class RustService
|
||||
/// <returns>The store secret response.</returns>
|
||||
public async Task<StoreSecretResponse> SetSecret(ISecretId secretId, string secretData)
|
||||
{
|
||||
static string TB(string fallbackEN) => TB_Secrets(fallbackEN);
|
||||
|
||||
var encryptedSecret = await this.encryptor!.Encrypt(secretData);
|
||||
var request = new StoreSecretRequest($"secret::{secretId.SecretId}::{secretId.SecretName}", Environment.UserName, encryptedSecret);
|
||||
var result = await this.http.PostAsJsonAsync("/secrets/store", request, this.jsonRustSerializerOptions);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
this.logger!.LogError($"Failed to store the secret data for secret ID '{secretId.SecretId}' due to an API issue: '{result.StatusCode}'");
|
||||
return new StoreSecretResponse(false, "Failed to get the secret data due to an API issue.");
|
||||
return new StoreSecretResponse(false, TB("Failed to get the secret data due to an API issue."));
|
||||
}
|
||||
|
||||
var state = await result.Content.ReadFromJsonAsync<StoreSecretResponse>(this.jsonRustSerializerOptions);
|
||||
@@ -59,12 +66,14 @@ public sealed partial class RustService
|
||||
/// <returns>The delete secret response.</returns>
|
||||
public async Task<DeleteSecretResponse> DeleteSecret(ISecretId secretId)
|
||||
{
|
||||
static string TB(string fallbackEN) => TB_Secrets(fallbackEN);
|
||||
|
||||
var request = new SelectSecretRequest($"secret::{secretId.SecretId}::{secretId.SecretName}", Environment.UserName, false);
|
||||
var result = await this.http.PostAsJsonAsync("/secrets/delete", request, this.jsonRustSerializerOptions);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
this.logger!.LogError($"Failed to delete the secret data for secret ID '{secretId.SecretId}' due to an API issue: '{result.StatusCode}'");
|
||||
return new DeleteSecretResponse{Success = false, WasEntryFound = false, Issue = "Failed to delete the secret data due to an API issue."};
|
||||
return new DeleteSecretResponse{Success = false, WasEntryFound = false, Issue = TB("Failed to delete the secret data due to an API issue.")};
|
||||
}
|
||||
|
||||
var state = await result.Content.ReadFromJsonAsync<DeleteSecretResponse>(this.jsonRustSerializerOptions);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
@@ -7,6 +8,8 @@ namespace AIStudio.Tools.Services;
|
||||
|
||||
public sealed class UpdateService : BackgroundService, IMessageBusReceiver
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(UpdateService).Namespace, nameof(UpdateService));
|
||||
|
||||
private static bool IS_INITIALIZED;
|
||||
private static ISnackbar? SNACKBAR;
|
||||
|
||||
@@ -118,7 +121,7 @@ public sealed class UpdateService : BackgroundService, IMessageBusReceiver
|
||||
{
|
||||
if (notifyUserWhenNoUpdate)
|
||||
{
|
||||
SNACKBAR!.Add("No update found.", Severity.Normal, config =>
|
||||
SNACKBAR!.Add(TB("No update found."), Severity.Normal, config =>
|
||||
{
|
||||
config.Icon = Icons.Material.Filled.Update;
|
||||
config.IconSize = Size.Large;
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.ERIClient.DataModel;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Tools.Validation;
|
||||
|
||||
public sealed class DataSourceValidation
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(DataSourceValidation).Namespace, nameof(DataSourceValidation));
|
||||
|
||||
public Func<string> GetSecretStorageIssue { get; init; } = () => string.Empty;
|
||||
|
||||
public Func<string> GetPreviousDataSourceName { get; init; } = () => string.Empty;
|
||||
@@ -26,13 +29,13 @@ public sealed class DataSourceValidation
|
||||
public string? ValidatingHostname(string hostname)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(hostname))
|
||||
return "Please enter a hostname, e.g., http://localhost";
|
||||
return TB("Please enter a hostname, e.g., http://localhost");
|
||||
|
||||
if(!hostname.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase) && !hostname.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase))
|
||||
return "The hostname must start with either http:// or https://";
|
||||
return TB("The hostname must start with either http:// or https://");
|
||||
|
||||
if(!Uri.TryCreate(hostname, UriKind.Absolute, out _))
|
||||
return "The hostname is not a valid HTTP(S) URL.";
|
||||
return TB("The hostname is not a valid HTTP(S) URL.");
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -40,7 +43,7 @@ public sealed class DataSourceValidation
|
||||
public string? ValidatePort(int port)
|
||||
{
|
||||
if(port is < 1 or > 65535)
|
||||
return "The port must be between 1 and 65535.";
|
||||
return TB("The port must be between 1 and 65535.");
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -48,14 +51,14 @@ public sealed class DataSourceValidation
|
||||
public string? ValidateSecurityPolicy(DataSourceSecurity securityPolicy)
|
||||
{
|
||||
if(securityPolicy is DataSourceSecurity.NOT_SPECIFIED)
|
||||
return "Please select your security policy.";
|
||||
return TB("Please select your security policy.");
|
||||
|
||||
var dataSourceSecurity = this.GetSecurityRequirements();
|
||||
if (dataSourceSecurity is null)
|
||||
return null;
|
||||
|
||||
if(dataSourceSecurity.Value.AllowedProviderType is ProviderType.SELF_HOSTED && securityPolicy is not DataSourceSecurity.SELF_HOSTED)
|
||||
return "This data source can only be used with a self-hosted LLM provider. Please change the security policy.";
|
||||
return TB("This data source can only be used with a self-hosted LLM provider. Please change the security policy.");
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -66,7 +69,7 @@ public sealed class DataSourceValidation
|
||||
return null;
|
||||
|
||||
if(string.IsNullOrWhiteSpace(username))
|
||||
return "The username must not be empty.";
|
||||
return TB("The username must not be empty.");
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -84,10 +87,10 @@ public sealed class DataSourceValidation
|
||||
if (string.IsNullOrWhiteSpace(secret))
|
||||
return authMethod switch
|
||||
{
|
||||
AuthMethod.TOKEN => "Please enter your secure access token.",
|
||||
AuthMethod.USERNAME_PASSWORD => "Please enter your password.",
|
||||
AuthMethod.TOKEN => TB("Please enter your secure access token."),
|
||||
AuthMethod.USERNAME_PASSWORD => TB("Please enter your password."),
|
||||
|
||||
_ => "Please enter the secret necessary for authentication."
|
||||
_ => TB("Please enter the secret necessary for authentication.")
|
||||
};
|
||||
|
||||
return null;
|
||||
@@ -96,7 +99,7 @@ public sealed class DataSourceValidation
|
||||
public string? ValidateRetrievalProcess(RetrievalInfo retrievalInfo)
|
||||
{
|
||||
if(retrievalInfo == default)
|
||||
return "Please select one retrieval process.";
|
||||
return TB("Please select one retrieval process.");
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -104,14 +107,14 @@ public sealed class DataSourceValidation
|
||||
public string? ValidatingName(string dataSourceName)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(dataSourceName))
|
||||
return "The name must not be empty.";
|
||||
return TB("The name must not be empty.");
|
||||
|
||||
if (dataSourceName.Length > 40)
|
||||
return "The name must not exceed 40 characters.";
|
||||
return TB("The name must not exceed 40 characters.");
|
||||
|
||||
var lowerName = dataSourceName.ToLowerInvariant();
|
||||
if(lowerName != this.GetPreviousDataSourceName() && this.GetUsedDataSourceNames().Contains(lowerName))
|
||||
return "The name is already used by another data source. Please choose a different name.";
|
||||
return TB("The name is already used by another data source. Please choose a different name.");
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -119,10 +122,10 @@ public sealed class DataSourceValidation
|
||||
public string? ValidatePath(string path)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(path))
|
||||
return "The path must not be empty. Please select a directory.";
|
||||
return TB("The path must not be empty. Please select a directory.");
|
||||
|
||||
if(!Directory.Exists(path))
|
||||
return "The path does not exist. Please select a valid directory.";
|
||||
return TB("The path does not exist. Please select a valid directory.");
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -130,10 +133,10 @@ public sealed class DataSourceValidation
|
||||
public string? ValidateFilePath(string filePath)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(filePath))
|
||||
return "The file path must not be empty. Please select a file.";
|
||||
return TB("The file path must not be empty. Please select a file.");
|
||||
|
||||
if(!File.Exists(filePath))
|
||||
return "The file does not exist. Please select a valid file.";
|
||||
return TB("The file does not exist. Please select a valid file.");
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -141,7 +144,7 @@ public sealed class DataSourceValidation
|
||||
public string? ValidateEmbeddingId(string embeddingId)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(embeddingId))
|
||||
return "Please select an embedding provider.";
|
||||
return TB("Please select an embedding provider.");
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -149,7 +152,7 @@ public sealed class DataSourceValidation
|
||||
public string? ValidateUserAcknowledgedCloudEmbedding(bool value)
|
||||
{
|
||||
if(this.GetSelectedCloudEmbedding() && !value)
|
||||
return "Please acknowledge that you are aware of the cloud embedding implications.";
|
||||
return TB("Please acknowledge that you are aware of the cloud embedding implications.");
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -157,10 +160,10 @@ public sealed class DataSourceValidation
|
||||
public string? ValidateTestedConnection()
|
||||
{
|
||||
if(!this.GetTestedConnection())
|
||||
return "Please test the connection before saving.";
|
||||
return TB("Please test the connection before saving.");
|
||||
|
||||
if(!this.GetTestedConnectionResult())
|
||||
return "The connection test failed. Please check the connection settings.";
|
||||
return TB("The connection test failed. Please check the connection settings.");
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -168,7 +171,7 @@ public sealed class DataSourceValidation
|
||||
public string? ValidateAuthMethod(AuthMethod authMethod)
|
||||
{
|
||||
if(!this.GetAvailableAuthMethods().Contains(authMethod))
|
||||
return "Please select one valid authentication method.";
|
||||
return TB("Please select one valid authentication method.");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Provider.HuggingFace;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
using Host = AIStudio.Provider.SelfHosted.Host;
|
||||
|
||||
namespace AIStudio.Tools.Validation;
|
||||
|
||||
public sealed class ProviderValidation
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ProviderValidation).Namespace, nameof(ProviderValidation));
|
||||
|
||||
public Func<LLMProviders> GetProvider { get; init; } = () => LLMProviders.NONE;
|
||||
|
||||
public Func<string> GetAPIKeyStorageIssue { get; init; } = () => string.Empty;
|
||||
@@ -22,13 +26,13 @@ public sealed class ProviderValidation
|
||||
return null;
|
||||
|
||||
if(string.IsNullOrWhiteSpace(hostname))
|
||||
return "Please enter a hostname, e.g., http://localhost:1234";
|
||||
return TB("Please enter a hostname, e.g., http://localhost:1234");
|
||||
|
||||
if(!hostname.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase) && !hostname.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase))
|
||||
return "The hostname must start with either http:// or https://";
|
||||
return TB("The hostname must start with either http:// or https://");
|
||||
|
||||
if(!Uri.TryCreate(hostname, UriKind.Absolute, out _))
|
||||
return "The hostname is not a valid HTTP(S) URL.";
|
||||
return TB("The hostname is not a valid HTTP(S) URL.");
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -43,7 +47,7 @@ public sealed class ProviderValidation
|
||||
return apiKeyStorageIssue;
|
||||
|
||||
if(string.IsNullOrWhiteSpace(apiKey))
|
||||
return "Please enter an API key.";
|
||||
return TB("Please enter an API key.");
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -51,15 +55,15 @@ public sealed class ProviderValidation
|
||||
public string? ValidatingInstanceName(string instanceName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(instanceName))
|
||||
return "Please enter an instance name.";
|
||||
return TB("Please enter an instance name.");
|
||||
|
||||
if (instanceName.Length > 40)
|
||||
return "The instance name must not exceed 40 characters.";
|
||||
return TB("The instance name must not exceed 40 characters.");
|
||||
|
||||
// The instance name must be unique:
|
||||
var lowerInstanceName = instanceName.ToLowerInvariant();
|
||||
if (lowerInstanceName != this.GetPreviousInstanceName() && this.GetUsedInstanceNames().Contains(lowerInstanceName))
|
||||
return "The instance name must be unique; the chosen name is already in use.";
|
||||
return TB("The instance name must be unique; the chosen name is already in use.");
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -70,7 +74,7 @@ public sealed class ProviderValidation
|
||||
return null;
|
||||
|
||||
if (model == default)
|
||||
return "Please select a model.";
|
||||
return TB("Please select a model.");
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -78,7 +82,7 @@ public sealed class ProviderValidation
|
||||
public string? ValidatingProvider(LLMProviders llmProvider)
|
||||
{
|
||||
if (llmProvider == LLMProviders.NONE)
|
||||
return "Please select a provider.";
|
||||
return TB("Please select a provider.");
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -89,7 +93,7 @@ public sealed class ProviderValidation
|
||||
return null;
|
||||
|
||||
if (host == Host.NONE)
|
||||
return "Please select a host.";
|
||||
return TB("Please select a host.");
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -100,7 +104,7 @@ public sealed class ProviderValidation
|
||||
return null;
|
||||
|
||||
if (inferenceProvider is HFInferenceProvider.NONE)
|
||||
return "Please select an Hugging Face inference provider.";
|
||||
return TB("Please select an Hugging Face inference provider.");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -5,11 +5,14 @@ using System.Text.Json.Serialization;
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
public static class WorkspaceBehaviour
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(WorkspaceBehaviour).Namespace, nameof(WorkspaceBehaviour));
|
||||
|
||||
public static readonly JsonSerializerOptions JSON_OPTIONS = new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
@@ -97,13 +100,13 @@ public static class WorkspaceBehaviour
|
||||
{
|
||||
"Message", (chat.WorkspaceId == Guid.Empty) switch
|
||||
{
|
||||
true => $"Are you sure you want to delete the temporary chat '{chat.Name}'?",
|
||||
false => $"Are you sure you want to delete the chat '{chat.Name}' in the workspace '{workspaceName}'?",
|
||||
true => TB($"Are you sure you want to delete the temporary chat '{chat.Name}'?"),
|
||||
false => TB($"Are you sure you want to delete the chat '{chat.Name}' in the workspace '{workspaceName}'?"),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
var dialogReference = await dialogService.ShowAsync<ConfirmDialog>("Delete Chat", dialogParameters, Dialogs.DialogOptions.FULLSCREEN);
|
||||
var dialogReference = await dialogService.ShowAsync<ConfirmDialog>(TB("Delete Chat"), dialogParameters, Dialogs.DialogOptions.FULLSCREEN);
|
||||
var dialogResult = await dialogReference.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled)
|
||||
return;
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
"net9.0": {
|
||||
"CodeBeam.MudBlazor.Extensions": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "MNxReFDmME1OLhHsD7lrfQZ9cMu5X60PIvidyFueq0ddDKHlagHS6CuYBZRQ62rHqmocdHWOLyJ73t2Im0NnWw==",
|
||||
"requested": "[8.1.0, )",
|
||||
"resolved": "8.1.0",
|
||||
"contentHash": "6f6K8Qr+cjk419URr8+b9nkSr0y300n/DaB0tJdYVRtDsWwoVCvgPwf9kENdDg1HIiYPL524+Q5a9QraL6hAvw==",
|
||||
"dependencies": {
|
||||
"BuildBundlerMinifier": "3.2.449",
|
||||
"CsvHelper": "33.0.1",
|
||||
"Microsoft.AspNetCore.Components": "9.0.1",
|
||||
"Microsoft.AspNetCore.Components.Web": "9.0.1",
|
||||
"Microsoft.AspNetCore.Components": "9.0.4",
|
||||
"Microsoft.AspNetCore.Components.Web": "9.0.4",
|
||||
"MudBlazor": "8.0.0",
|
||||
"ZXing.Net": "0.16.9"
|
||||
}
|
||||
@@ -45,9 +45,9 @@
|
||||
},
|
||||
"MudBlazor": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.5.1, )",
|
||||
"resolved": "8.5.1",
|
||||
"contentHash": "xzGhtY60e1fvvyI7rJSYYV0UEkvY8niKuTaqby19cb8rELd29EpPU439RVL8KjVSMy473BEcpgYWBRAoDPGl8A==",
|
||||
"requested": "[8.6.0, )",
|
||||
"resolved": "8.6.0",
|
||||
"contentHash": "vJROmWdTjiBmyiZBSbD8RcYVFfLbNMEQpKoCOMfY2628KbOLGVcnH1RBziHGyJH9pqc99UoCQS81XLm8X+49ug==",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.Components": "9.0.1",
|
||||
"Microsoft.AspNetCore.Components.Web": "9.0.1",
|
||||
@@ -56,9 +56,9 @@
|
||||
},
|
||||
"MudBlazor.Markdown": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.5.1, )",
|
||||
"resolved": "8.5.1",
|
||||
"contentHash": "UrNr948Nn70CuDoTWaN/HgtaIKO4oKfj7W6Mw8Ei9OG7MCzCwkhUYJLe/CRTKCkt3wINquB/2AAN7ezgEfmbww==",
|
||||
"requested": "[8.5.1.1, )",
|
||||
"resolved": "8.5.1.1",
|
||||
"contentHash": "3FQaHlpLV54W77aVl58JCH2HX0p2QcqGNamtxyVzMKeMa92x+MYm4DFcqqhfbEvNEWTRZ6q22ZCx/NG0UX422Q==",
|
||||
"dependencies": {
|
||||
"Markdig": "0.41.0",
|
||||
"Microsoft.Extensions.Caching.Memory": "9.0.4",
|
||||
@@ -91,52 +91,52 @@
|
||||
},
|
||||
"Microsoft.AspNetCore.Authorization": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.1",
|
||||
"contentHash": "WgLlLBlMczb2+QLNG6sM95OUZ0EBztz60k/N75tjIgpyu0SdpIfYytAmX/7JJAjRTZF0c/CrWaQV+SH9FuGsrA==",
|
||||
"resolved": "9.0.4",
|
||||
"contentHash": "QIV3Jk/zb5wsBNkxj4eK4Qoja6f0hO2pUhWrG8NOmgnclA7zbQvzLLKaGnva7/PtglS8VS5HusZfrnnO8FTpSA==",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.Metadata": "9.0.1",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
|
||||
"Microsoft.Extensions.Options": "9.0.1"
|
||||
"Microsoft.AspNetCore.Metadata": "9.0.4",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "9.0.4",
|
||||
"Microsoft.Extensions.Options": "9.0.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.AspNetCore.Components": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.1",
|
||||
"contentHash": "6pwfbQKNtvPkbF4tCGiAKGyt6BVpu58xAXz7u2YXcUKTNmNxrymbG1mEyMc0EPzVdnquDDqTyfXM3mC1EJycxQ==",
|
||||
"resolved": "9.0.4",
|
||||
"contentHash": "imeT4r1PLXqI3kzf7b06Tzj5H4od8o/u4ZE5JgyrlvwABSZlBQzWN8AgT/lPLqs2VJVxNwcAolx+vxLMl9Dn+g==",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.Authorization": "9.0.1",
|
||||
"Microsoft.AspNetCore.Components.Analyzers": "9.0.1"
|
||||
"Microsoft.AspNetCore.Authorization": "9.0.4",
|
||||
"Microsoft.AspNetCore.Components.Analyzers": "9.0.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.AspNetCore.Components.Analyzers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.1",
|
||||
"contentHash": "I8Rs4LXT5UQxM5Nin2+Oj8aSY2heszSZ3EyTLgt3mxmfiRPrVO7D8NNSsf1voI2Gb0qFJceof/J5c9E+nfNuHw=="
|
||||
"resolved": "9.0.4",
|
||||
"contentHash": "fPrKrrfT5o0dJZ4AgbGsXBl4YNTlSrRcpYMgLTsrmYo7aOkZrdWPmsxVsFRyelfmCW9qHH7ZZK/4cTC8HuS7BA=="
|
||||
},
|
||||
"Microsoft.AspNetCore.Components.Forms": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.1",
|
||||
"contentHash": "KyULVU32bLz74LWDwPEwNUEllTehzWJuM7YAsz80rMKEzvR0K8cRjRzO0fnN/nfydMeLRRlbI0xj8wnEAymLVw==",
|
||||
"resolved": "9.0.4",
|
||||
"contentHash": "FvoNIyz5URP2QonItKbDBzCcdmSXHWfyh0lUAYkNittlLmFAqaIF4TE55h3/SYLI8ISsbAVd44Cax86HBhLu8w==",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.Components": "9.0.1"
|
||||
"Microsoft.AspNetCore.Components": "9.0.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.AspNetCore.Components.Web": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.1",
|
||||
"contentHash": "LI0vjYEd9MaDZPDQxPCn4gGYDkEC5U9rp1nWZo7rPozJxgTG2zU3WERujxTi2LeAC2ZzdXlOVCrUyPQ55LZV2A==",
|
||||
"resolved": "9.0.4",
|
||||
"contentHash": "X02T/wGvT7vCgIXG0R4ZWQUx5/qMaKuWJklBL3eaa8b1NJHyPlox6IpI4AcmMy7r1hM8GwaxKfm34dv5b7n5VA==",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.Components": "9.0.1",
|
||||
"Microsoft.AspNetCore.Components.Forms": "9.0.1",
|
||||
"Microsoft.Extensions.DependencyInjection": "9.0.1",
|
||||
"Microsoft.Extensions.Primitives": "9.0.1",
|
||||
"Microsoft.JSInterop": "9.0.1"
|
||||
"Microsoft.AspNetCore.Components": "9.0.4",
|
||||
"Microsoft.AspNetCore.Components.Forms": "9.0.4",
|
||||
"Microsoft.Extensions.DependencyInjection": "9.0.4",
|
||||
"Microsoft.Extensions.Primitives": "9.0.4",
|
||||
"Microsoft.JSInterop": "9.0.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.AspNetCore.Metadata": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.1",
|
||||
"contentHash": "EZnHifamF7IFEIyjAKMtJM3I/94OIe72i3P09v5oL0twmsmfQwal6Ni3m8lbB5mge3jWFhMozeW+rUdRSqnXRQ=="
|
||||
"resolved": "9.0.4",
|
||||
"contentHash": "3AhnSEW/Zw0E++itsen8YvqT7UoyF7zYD9OZPQmeGpT7YQP4W9GOiwOljfJEC8+4pP8kqgpeRN/p4LkmKLIUlQ=="
|
||||
},
|
||||
"Microsoft.Extensions.Caching.Abstractions": {
|
||||
"type": "Transitive",
|
||||
@@ -160,10 +160,10 @@
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.1",
|
||||
"contentHash": "qZI42ASAe3hr2zMSA6UjM92pO1LeDq5DcwkgSowXXPY8I56M76pEKrnmsKKbxagAf39AJxkH2DY4sb72ixyOrg==",
|
||||
"resolved": "9.0.4",
|
||||
"contentHash": "f2MTUaS2EQ3lX4325ytPAISZqgBfXmY0WvgD80ji6Z20AoDNiCESxsqo6mFRwHJD/jfVKRw9FsW6+86gNre3ug==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1"
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
@@ -219,8 +219,8 @@
|
||||
},
|
||||
"Microsoft.JSInterop": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.1",
|
||||
"contentHash": "/xBwIfb0YoC2Muv6EsHjxpqZw2aKv94+i0g0FWZvqvGv3DeAy+8wipAuECVvKYEs2EIclRD41bjajHLoD6mTtw=="
|
||||
"resolved": "9.0.4",
|
||||
"contentHash": "iuzwm79XoRYTRhf7cOHgMgGAE8ZsAcCGp2VsIDAV05qRzceKSE1S7tQp+8GTzG7+IZpdPpvfaJUwSD/pYIog5A=="
|
||||
},
|
||||
"ZXing.Net": {
|
||||
"type": "Transitive",
|
||||
|
||||
@@ -1 +1,12 @@
|
||||
# v0.9.42, build 217 (2025-05-xx xx:xx UTC)
|
||||
# v0.9.42, build 217 (2025-05-04 13:03 UTC)
|
||||
- Added the writing style "Changelog" to the rewrite & improve text assistant. This helps to create changelogs for your projects.
|
||||
- Added an option to load PDF files directly into the translation, text summarization, and legal check assistants as a preview prototype for testing before release.
|
||||
- Added more text content to the localization system.
|
||||
- Improved the model selection for OpenAI by removing all `o1-pro` models. These models cannot be used right now, since OpenAI introduced a new API, which is not yet supported by MindWork AI Studio.
|
||||
- Improved the internal plugin maintenance so that removed resources are now removed from the file system.
|
||||
- Improved the app settings to apply the chosen language immediately.
|
||||
- Improved chat name generation to prevent the inclusion of line breaks, tabs, and other special characters.
|
||||
- Fixed an issue where empty lines in source code were being ignored by the Markdown renderer. Thanks My Nihongo for fixing this bug in the `MudBlazor.Markdown` repository.
|
||||
- Fixed the localization assistant not being able to load the localization file when used in the release app.
|
||||
- Fixed the localization of the navigation component.
|
||||
- Upgraded Rust & .NET dependencies.
|
||||
@@ -0,0 +1 @@
|
||||
# v0.9.43, build 218 (2025-05-xx xx:xx UTC)
|
||||
+7
-6
@@ -1,10 +1,11 @@
|
||||
0.9.41
|
||||
2025-04-27 14:18:10 UTC
|
||||
216
|
||||
0.9.42
|
||||
2025-05-04 13:03:03 UTC
|
||||
217
|
||||
9.0.105 (commit 35890ecb87)
|
||||
9.0.4 (commit f57e6dc747)
|
||||
1.86.0 (commit 05f9846f8)
|
||||
8.5.1
|
||||
8.6.0
|
||||
1.8.1
|
||||
47b6a896851, release
|
||||
osx-arm64
|
||||
7a08baf99bb, release
|
||||
osx-arm64
|
||||
137.0.7123.0
|
||||
Generated
+25
-11
@@ -188,6 +188,12 @@ dependencies = [
|
||||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atoi_simd"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4790f9e8961209112beb783d85449b508673cf4a6a419c8449b210743ac4dbe9"
|
||||
|
||||
[[package]]
|
||||
name = "atomic"
|
||||
version = "0.5.3"
|
||||
@@ -430,15 +436,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "calamine"
|
||||
version = "0.26.1"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138646b9af2c5d7f1804ea4bf93afc597737d2bd4f7341d67c48b03316976eb1"
|
||||
checksum = "6d80f81ba5c68206b9027e62346d49dc26fb32ffc4fe6ef7022a8ae21d348ccb"
|
||||
dependencies = [
|
||||
"atoi_simd",
|
||||
"byteorder",
|
||||
"codepage",
|
||||
"encoding_rs",
|
||||
"fast-float2",
|
||||
"log",
|
||||
"quick-xml 0.31.0",
|
||||
"quick-xml 0.37.5",
|
||||
"serde",
|
||||
"zip 2.5.0",
|
||||
]
|
||||
@@ -1064,6 +1072,12 @@ dependencies = [
|
||||
"zune-inflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fast-float2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8eb564c5c7423d25c886fb561d1e4ee69f72354d16918afa32c08811f6b6a55"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.1.0"
|
||||
@@ -2610,7 +2624,7 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "mindwork-ai-studio"
|
||||
version = "0.9.41"
|
||||
version = "0.9.42"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"arboard",
|
||||
@@ -3200,9 +3214,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pdfium-render"
|
||||
version = "0.8.30"
|
||||
version = "0.8.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2773a939ec2c736640f5f2e62a325c2e1a997d694961c50f17cadfb4c8682e84"
|
||||
checksum = "a473e1f4c2630f7240b127eb9efebd1d9e55053015b624bfe9dbeae0299947e1"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bytemuck",
|
||||
@@ -3563,20 +3577,20 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.31.0"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
|
||||
checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.32.0"
|
||||
version = "0.37.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2"
|
||||
checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mindwork-ai-studio"
|
||||
version = "0.9.41"
|
||||
version = "0.9.42"
|
||||
edition = "2021"
|
||||
description = "MindWork AI Studio"
|
||||
authors = ["Thorsten Sommer"]
|
||||
@@ -34,8 +34,8 @@ hmac = "0.12.1"
|
||||
sha2 = "0.10.8"
|
||||
rcgen = { version = "0.13.2", features = ["pem"] }
|
||||
file-format = "0.26.0"
|
||||
calamine = "0.26.1"
|
||||
pdfium-render = "0.8.30"
|
||||
calamine = "0.27.0"
|
||||
pdfium-render = "0.8.31"
|
||||
sys-locale = "0.3.2"
|
||||
|
||||
# Fixes security vulnerability downstream, where the upstream is not fixed yet:
|
||||
|
||||
+55
-16
@@ -7,13 +7,14 @@ use rocket::serde::json::Json;
|
||||
use rocket::serde::Serialize;
|
||||
use serde::Deserialize;
|
||||
use tauri::updater::UpdateResponse;
|
||||
use tauri::{Manager, Window};
|
||||
use tauri::{Manager, PathResolver, Window};
|
||||
use tauri::api::dialog::blocking::FileDialogBuilder;
|
||||
use tokio::time;
|
||||
use crate::api_token::APIToken;
|
||||
use crate::dotnet::stop_dotnet_server;
|
||||
use crate::environment::{is_prod, CONFIG_DIRECTORY, DATA_DIRECTORY};
|
||||
use crate::log::switch_to_file_logging;
|
||||
use crate::pdfium::PDFIUM_LIB_PATH;
|
||||
|
||||
/// The Tauri main window.
|
||||
static MAIN_WINDOW: Lazy<Mutex<Option<Window>>> = Lazy::new(|| Mutex::new(None));
|
||||
@@ -38,7 +39,7 @@ pub fn start_tauri() {
|
||||
|
||||
info!(Source = "Bootloader Tauri"; "Reconfigure the file logger to use the app data directory {data_path:?}");
|
||||
switch_to_file_logging(data_path).map_err(|e| error!("Failed to switch logging to file: {e}")).unwrap();
|
||||
|
||||
set_pdfium_path(app.path_resolver());
|
||||
Ok(())
|
||||
})
|
||||
.plugin(tauri_plugin_window_state::Builder::default().build())
|
||||
@@ -266,6 +267,19 @@ pub struct PreviousDirectory {
|
||||
path: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct FileTypeFilter {
|
||||
filter_name: String,
|
||||
filter_extensions: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct SelectFileOptions {
|
||||
title: String,
|
||||
previous_file: Option<PreviousFile>,
|
||||
filter: Option<FileTypeFilter>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct DirectorySelectionResponse {
|
||||
user_cancelled: bool,
|
||||
@@ -273,24 +287,36 @@ pub struct DirectorySelectionResponse {
|
||||
}
|
||||
|
||||
/// Let the user select a file.
|
||||
#[post("/select/file?<title>", data = "<previous_file>")]
|
||||
pub fn select_file(_token: APIToken, title: &str, previous_file: Option<Json<PreviousFile>>) -> Json<FileSelectionResponse> {
|
||||
let file_path = match previous_file {
|
||||
Some(previous) => {
|
||||
let previous_path = previous.file_path.as_str();
|
||||
FileDialogBuilder::new()
|
||||
.set_title(title)
|
||||
.set_directory(previous_path)
|
||||
.pick_file()
|
||||
#[post("/select/file", data = "<payload>")]
|
||||
pub fn select_file(_token: APIToken, payload: Json<SelectFileOptions>) -> Json<FileSelectionResponse> {
|
||||
|
||||
// Create a new file dialog builder:
|
||||
let file_dialog = FileDialogBuilder::new();
|
||||
|
||||
// Set the title of the file dialog:
|
||||
let file_dialog = file_dialog.set_title(&payload.title);
|
||||
|
||||
// Set the file type filter if provided:
|
||||
let file_dialog = match &payload.filter {
|
||||
Some(filter) => {
|
||||
file_dialog.add_filter(&filter.filter_name, &filter.filter_extensions.iter().map(|s| s.as_str()).collect::<Vec<&str>>())
|
||||
},
|
||||
|
||||
None => {
|
||||
FileDialogBuilder::new()
|
||||
.set_title(title)
|
||||
.pick_file()
|
||||
},
|
||||
None => file_dialog,
|
||||
};
|
||||
|
||||
// Set the previous file path if provided:
|
||||
let file_dialog = match &payload.previous_file {
|
||||
Some(previous) => {
|
||||
let previous_path = previous.file_path.as_str();
|
||||
file_dialog.set_directory(previous_path)
|
||||
},
|
||||
|
||||
None => file_dialog,
|
||||
};
|
||||
|
||||
// Show the file dialog and get the selected file path:
|
||||
let file_path = file_dialog.pick_file();
|
||||
match file_path {
|
||||
Some(path) => {
|
||||
info!("User selected file: {path:?}");
|
||||
@@ -319,4 +345,17 @@ pub struct PreviousFile {
|
||||
pub struct FileSelectionResponse {
|
||||
user_cancelled: bool,
|
||||
selected_file_path: String,
|
||||
}
|
||||
|
||||
fn set_pdfium_path(path_resolver: PathResolver) {
|
||||
let pdfium_relative_source_path = String::from("resources/libraries/");
|
||||
let pdfium_source_path = path_resolver.resolve_resource(pdfium_relative_source_path);
|
||||
if pdfium_source_path.is_none() {
|
||||
error!(Source = "Bootloader Tauri"; "Failed to set the PDFium library path.");
|
||||
return;
|
||||
}
|
||||
|
||||
let pdfium_source_path = pdfium_source_path.unwrap();
|
||||
let pdfium_source_path = pdfium_source_path.to_str().unwrap().to_string();
|
||||
*PDFIUM_LIB_PATH.lock().unwrap() = Some(pdfium_source_path.clone());
|
||||
}
|
||||
+36
-10
@@ -15,6 +15,8 @@ use rocket::response::stream::{EventStream, Event};
|
||||
use rocket::tokio::select;
|
||||
use rocket::serde::Serialize;
|
||||
use rocket::get;
|
||||
use crate::api_token::APIToken;
|
||||
use crate::pdfium::PdfiumInit;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Chunk {
|
||||
@@ -39,7 +41,7 @@ type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>
|
||||
type ChunkStream = Pin<Box<dyn Stream<Item = Result<Chunk>> + Send>>;
|
||||
|
||||
#[get("/retrieval/fs/extract?<path>")]
|
||||
pub async fn extract_data(path: String, mut end: Shutdown) -> EventStream![] {
|
||||
pub async fn extract_data(_token: APIToken, path: String, mut end: Shutdown) -> EventStream![] {
|
||||
EventStream! {
|
||||
let stream_result = stream_data(&path).await;
|
||||
match stream_result {
|
||||
@@ -78,7 +80,7 @@ async fn stream_data(file_path: &str) -> Result<ChunkStream> {
|
||||
FileFormat::from_file(&file_path_clone)
|
||||
}).await??;
|
||||
|
||||
let ext = file_path.split('.').last().unwrap_or("");
|
||||
let ext = file_path.split('.').next_back().unwrap_or("");
|
||||
let stream = match ext {
|
||||
DOCX | ODT => {
|
||||
let from = if ext == DOCX { "docx" } else { "odt" };
|
||||
@@ -91,7 +93,7 @@ async fn stream_data(file_path: &str) -> Result<ChunkStream> {
|
||||
|
||||
_ => match fmt.kind() {
|
||||
Kind::Document => match fmt {
|
||||
FileFormat::PortableDocumentFormat => read_pdf(file_path).await?,
|
||||
FileFormat::PortableDocumentFormat => stream_pdf(file_path).await?,
|
||||
FileFormat::MicrosoftWordDocument => {
|
||||
convert_with_pandoc(file_path, "docx", TO_MARKDOWN).await?
|
||||
}
|
||||
@@ -133,7 +135,7 @@ async fn stream_text_file(file_path: &str) -> Result<ChunkStream> {
|
||||
let mut line_number = 0;
|
||||
|
||||
let stream = stream! {
|
||||
while let Ok(Some(line)) = lines.next_line().await { // Korrektur hier
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
line_number += 1;
|
||||
yield Ok(Chunk {
|
||||
content: line,
|
||||
@@ -145,23 +147,47 @@ async fn stream_text_file(file_path: &str) -> Result<ChunkStream> {
|
||||
Ok(Box::pin(stream))
|
||||
}
|
||||
|
||||
async fn read_pdf(file_path: &str) -> Result<ChunkStream> {
|
||||
#[get("/retrieval/fs/read/pdf?<file_path>")]
|
||||
pub fn read_pdf(_token: APIToken, file_path: String) -> String {
|
||||
let pdfium = Pdfium::ai_studio_init();
|
||||
let doc = match pdfium.load_pdf_from_file(&file_path, None) {
|
||||
Ok(document) => document,
|
||||
Err(e) => return e.to_string(),
|
||||
};
|
||||
|
||||
let mut pdf_content = String::new();
|
||||
for page in doc.pages().iter() {
|
||||
let content = match page.text().map(|text_content| text_content.all()) {
|
||||
Ok(content) => content,
|
||||
Err(_) => {
|
||||
continue
|
||||
}
|
||||
};
|
||||
|
||||
pdf_content.push_str(&content);
|
||||
pdf_content.push_str("\n\n");
|
||||
}
|
||||
|
||||
pdf_content
|
||||
}
|
||||
|
||||
async fn stream_pdf(file_path: &str) -> Result<ChunkStream> {
|
||||
let path = file_path.to_owned();
|
||||
let (tx, rx) = mpsc::channel(10);
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let pdfium = Pdfium::default();
|
||||
let pdfium = Pdfium::ai_studio_init();
|
||||
let doc = match pdfium.load_pdf_from_file(&path, None) {
|
||||
Ok(d) => d,
|
||||
Ok(document) => document,
|
||||
Err(e) => {
|
||||
let _ = tx.blocking_send(Err(e.into()));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
for (i, page) in doc.pages().iter().enumerate() {
|
||||
for (num_page, page) in doc.pages().iter().enumerate() {
|
||||
let content = match page.text().map(|t| t.all()) {
|
||||
Ok(c) => c,
|
||||
Ok(text_content) => text_content,
|
||||
Err(e) => {
|
||||
let _ = tx.blocking_send(Err(e.into()));
|
||||
continue;
|
||||
@@ -170,7 +196,7 @@ async fn read_pdf(file_path: &str) -> Result<ChunkStream> {
|
||||
|
||||
if tx.blocking_send(Ok(Chunk {
|
||||
content,
|
||||
metadata: Metadata::Pdf { page_number: i + 1 },
|
||||
metadata: Metadata::Pdf { page_number: num_page + 1 },
|
||||
})).is_err() {
|
||||
break;
|
||||
}
|
||||
|
||||
+3
-1
@@ -9,4 +9,6 @@ pub mod secret;
|
||||
pub mod clipboard;
|
||||
pub mod runtime_api;
|
||||
pub mod certificate;
|
||||
pub mod file_data;
|
||||
pub mod file_data;
|
||||
pub mod metadata;
|
||||
pub mod pdfium;
|
||||
+14
-19
@@ -10,22 +10,12 @@ use mindwork_ai_studio::certificate::{generate_certificate};
|
||||
use mindwork_ai_studio::dotnet::start_dotnet_server;
|
||||
use mindwork_ai_studio::environment::is_dev;
|
||||
use mindwork_ai_studio::log::init_logging;
|
||||
use mindwork_ai_studio::metadata::MetaData;
|
||||
use mindwork_ai_studio::runtime_api::start_runtime_api;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let metadata = include_str!("../../metadata.txt");
|
||||
let mut metadata_lines = metadata.lines();
|
||||
let app_version = metadata_lines.next().unwrap();
|
||||
let build_time = metadata_lines.next().unwrap();
|
||||
let build_number = metadata_lines.next().unwrap();
|
||||
let dotnet_sdk_version = metadata_lines.next().unwrap();
|
||||
let dotnet_version = metadata_lines.next().unwrap();
|
||||
let rust_version = metadata_lines.next().unwrap();
|
||||
let mud_blazor_version = metadata_lines.next().unwrap();
|
||||
let tauri_version = metadata_lines.next().unwrap();
|
||||
let app_commit_hash = metadata_lines.next().unwrap();
|
||||
let architecture = metadata_lines.next().unwrap();
|
||||
let metadata = MetaData::init_from_string(include_str!("../../metadata.txt"));
|
||||
|
||||
init_logging();
|
||||
info!("Starting MindWork AI Studio:");
|
||||
@@ -33,13 +23,18 @@ async fn main() {
|
||||
let working_directory = std::env::current_dir().unwrap();
|
||||
info!(".. The working directory is: '{working_directory:?}'");
|
||||
|
||||
info!(".. Version: v{app_version} (commit {app_commit_hash}, build {build_number}, {architecture})");
|
||||
info!(".. Build time: {build_time}");
|
||||
info!(".. .NET SDK: v{dotnet_sdk_version}");
|
||||
info!(".. .NET: v{dotnet_version}");
|
||||
info!(".. Rust: v{rust_version}");
|
||||
info!(".. MudBlazor: v{mud_blazor_version}");
|
||||
info!(".. Tauri: v{tauri_version}");
|
||||
info!(".. Version: v{app_version} (commit {hash}, build {build_num}, {architecture})",
|
||||
app_version = metadata.app_version,
|
||||
hash = metadata.app_commit_hash,
|
||||
build_num = metadata.build_number,
|
||||
architecture = metadata.architecture);
|
||||
info!(".. Build time: {build_time}", build_time = metadata.build_time);
|
||||
info!(".. .NET SDK: v{sdk_version}", sdk_version = metadata.dotnet_sdk_version);
|
||||
info!(".. .NET: v{dotnet_version}", dotnet_version = metadata.dotnet_version);
|
||||
info!(".. Rust: v{rust_version}", rust_version = metadata.rust_version);
|
||||
info!(".. MudBlazor: v{mud_blazor_version}", mud_blazor_version = metadata.mud_blazor_version);
|
||||
info!(".. Tauri: v{tauri_version}", tauri_version = metadata.tauri_version);
|
||||
info!(".. PDFium: v{pdfium_version}", pdfium_version = metadata.pdfium_version);
|
||||
|
||||
if is_dev() {
|
||||
warn!("Running in development mode.");
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Mutex;
|
||||
|
||||
pub static META_DATA: Lazy<Mutex<Option<MetaData>>> = Lazy::new(|| Mutex::new(None));
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MetaData {
|
||||
pub app_version: String,
|
||||
pub build_time: String,
|
||||
pub build_number: String,
|
||||
pub dotnet_sdk_version: String,
|
||||
pub dotnet_version: String,
|
||||
pub rust_version: String,
|
||||
pub mud_blazor_version: String,
|
||||
pub tauri_version: String,
|
||||
pub app_commit_hash: String,
|
||||
pub architecture: String,
|
||||
pub pdfium_version: String,
|
||||
}
|
||||
|
||||
impl MetaData {
|
||||
/// Creates a new instance of `MetaData` from the given metadata string.
|
||||
pub fn init_from_string(metadata: &str) -> Self {
|
||||
|
||||
// When the metadata is already initialized, return the existing instance:
|
||||
if let Some(metadata) = META_DATA.lock().unwrap().as_ref() {
|
||||
return metadata.clone();
|
||||
}
|
||||
|
||||
let mut metadata_lines = metadata.lines();
|
||||
let app_version = metadata_lines.next().unwrap();
|
||||
let build_time = metadata_lines.next().unwrap();
|
||||
let build_number = metadata_lines.next().unwrap();
|
||||
let dotnet_sdk_version = metadata_lines.next().unwrap();
|
||||
let dotnet_version = metadata_lines.next().unwrap();
|
||||
let rust_version = metadata_lines.next().unwrap();
|
||||
let mud_blazor_version = metadata_lines.next().unwrap();
|
||||
let tauri_version = metadata_lines.next().unwrap();
|
||||
let app_commit_hash = metadata_lines.next().unwrap();
|
||||
let architecture = metadata_lines.next().unwrap();
|
||||
let pdfium_version = metadata_lines.next().unwrap();
|
||||
|
||||
let metadata = MetaData {
|
||||
architecture: architecture.to_string(),
|
||||
app_commit_hash: app_commit_hash.to_string(),
|
||||
app_version: app_version.to_string(),
|
||||
build_number: build_number.to_string(),
|
||||
build_time: build_time.to_string(),
|
||||
dotnet_sdk_version: dotnet_sdk_version.to_string(),
|
||||
dotnet_version: dotnet_version.to_string(),
|
||||
mud_blazor_version: mud_blazor_version.to_string(),
|
||||
rust_version: rust_version.to_string(),
|
||||
tauri_version: tauri_version.to_string(),
|
||||
pdfium_version: pdfium_version.to_string(),
|
||||
};
|
||||
|
||||
*META_DATA.lock().unwrap() = Some(metadata.clone());
|
||||
metadata
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
use std::sync::Mutex;
|
||||
use once_cell::sync::Lazy;
|
||||
use pdfium_render::prelude::Pdfium;
|
||||
|
||||
pub static PDFIUM_LIB_PATH: Lazy<Mutex<Option<String>>> = Lazy::new(|| Mutex::new(None));
|
||||
|
||||
pub trait PdfiumInit {
|
||||
fn ai_studio_init() -> Pdfium;
|
||||
}
|
||||
|
||||
impl PdfiumInit for Pdfium {
|
||||
|
||||
/// Initializes the PDFium library for AI Studio.
|
||||
fn ai_studio_init() -> Pdfium {
|
||||
let lib_path = PDFIUM_LIB_PATH.lock().unwrap();
|
||||
if let Some(path) = lib_path.as_ref() {
|
||||
return Pdfium::new(
|
||||
Pdfium::bind_to_library(Pdfium::pdfium_platform_library_name_at_path(path))
|
||||
.or_else(|_| Pdfium::bind_to_system_library())
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
Pdfium::new(Pdfium::bind_to_system_library().unwrap())
|
||||
}
|
||||
}
|
||||
@@ -79,6 +79,7 @@ pub fn start_runtime_api() {
|
||||
crate::environment::get_config_directory,
|
||||
crate::environment::read_user_language,
|
||||
crate::file_data::extract_data,
|
||||
crate::file_data::read_pdf,
|
||||
crate::log::get_log_paths,
|
||||
])
|
||||
.ignite().await.unwrap()
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
},
|
||||
"package": {
|
||||
"productName": "MindWork AI Studio",
|
||||
"version": "0.9.41"
|
||||
"version": "0.9.42"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
@@ -29,6 +29,9 @@
|
||||
"scope": [
|
||||
"http://localhost"
|
||||
]
|
||||
},
|
||||
"fs": {
|
||||
"scope": ["$RESOURCE/resources/*"]
|
||||
}
|
||||
},
|
||||
"windows": [
|
||||
@@ -57,6 +60,9 @@
|
||||
"externalBin": [
|
||||
"../app/MindWork AI Studio/bin/dist/mindworkAIStudioServer"
|
||||
],
|
||||
"resources": [
|
||||
"resources/*"
|
||||
],
|
||||
"macOS": {
|
||||
"exceptionDomain": "localhost"
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user