mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-02-12 14:41:37 +00:00
Added functionality to download Qdrant and execute it as a background sidecar.
This commit is contained in:
parent
311092ec5e
commit
dec2c27997
125
.github/workflows/build-and-release.yml
vendored
125
.github/workflows/build-and-release.yml
vendored
@ -173,6 +173,9 @@ jobs:
|
|||||||
pdfium_version=$(sed -n '11p' metadata.txt)
|
pdfium_version=$(sed -n '11p' metadata.txt)
|
||||||
pdfium_version=$(echo $pdfium_version | cut -d'.' -f3)
|
pdfium_version=$(echo $pdfium_version | cut -d'.' -f3)
|
||||||
|
|
||||||
|
# Next line is the Qdrant version:
|
||||||
|
qdrant_version=$(sed -n '12p' metadata.txt)
|
||||||
|
|
||||||
# Write the metadata to the environment:
|
# Write the metadata to the environment:
|
||||||
echo "APP_VERSION=${app_version}" >> $GITHUB_ENV
|
echo "APP_VERSION=${app_version}" >> $GITHUB_ENV
|
||||||
echo "FORMATTED_APP_VERSION=${formatted_app_version}" >> $GITHUB_ENV
|
echo "FORMATTED_APP_VERSION=${formatted_app_version}" >> $GITHUB_ENV
|
||||||
@ -185,6 +188,7 @@ jobs:
|
|||||||
echo "TAURI_VERSION=${tauri_version}" >> $GITHUB_ENV
|
echo "TAURI_VERSION=${tauri_version}" >> $GITHUB_ENV
|
||||||
echo "ARCHITECTURE=${{ matrix.dotnet_runtime }}" >> $GITHUB_ENV
|
echo "ARCHITECTURE=${{ matrix.dotnet_runtime }}" >> $GITHUB_ENV
|
||||||
echo "PDFIUM_VERSION=${pdfium_version}" >> $GITHUB_ENV
|
echo "PDFIUM_VERSION=${pdfium_version}" >> $GITHUB_ENV
|
||||||
|
echo "QDRANT_VERSION=${qdrant_version}" >> $GITHUB_ENV
|
||||||
|
|
||||||
# Log the metadata:
|
# Log the metadata:
|
||||||
echo "App version: '${formatted_app_version}'"
|
echo "App version: '${formatted_app_version}'"
|
||||||
@ -197,6 +201,7 @@ jobs:
|
|||||||
echo "Tauri version: '${tauri_version}'"
|
echo "Tauri version: '${tauri_version}'"
|
||||||
echo "Architecture: '${{ matrix.dotnet_runtime }}'"
|
echo "Architecture: '${{ matrix.dotnet_runtime }}'"
|
||||||
echo "PDFium version: '${pdfium_version}'"
|
echo "PDFium version: '${pdfium_version}'"
|
||||||
|
echo "Qdrant version: '${qdrant_version}'"
|
||||||
|
|
||||||
- name: Read and format metadata (Windows)
|
- name: Read and format metadata (Windows)
|
||||||
if: matrix.platform == 'windows-latest'
|
if: matrix.platform == 'windows-latest'
|
||||||
@ -241,6 +246,9 @@ jobs:
|
|||||||
$pdfium_version = $metadata[10]
|
$pdfium_version = $metadata[10]
|
||||||
$pdfium_version = $pdfium_version.Split('.')[2]
|
$pdfium_version = $pdfium_version.Split('.')[2]
|
||||||
|
|
||||||
|
# Next line is the necessary Qdrant version:
|
||||||
|
$qdrant_version = $metadata[12]
|
||||||
|
|
||||||
# Write the metadata to the environment:
|
# Write the metadata to the environment:
|
||||||
Write-Output "APP_VERSION=${app_version}" >> $env:GITHUB_ENV
|
Write-Output "APP_VERSION=${app_version}" >> $env:GITHUB_ENV
|
||||||
Write-Output "FORMATTED_APP_VERSION=${formatted_app_version}" >> $env:GITHUB_ENV
|
Write-Output "FORMATTED_APP_VERSION=${formatted_app_version}" >> $env:GITHUB_ENV
|
||||||
@ -252,6 +260,7 @@ jobs:
|
|||||||
Write-Output "MUD_BLAZOR_VERSION=${mud_blazor_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 "ARCHITECTURE=${{ matrix.dotnet_runtime }}" >> $env:GITHUB_ENV
|
||||||
Write-Output "PDFIUM_VERSION=${pdfium_version}" >> $env:GITHUB_ENV
|
Write-Output "PDFIUM_VERSION=${pdfium_version}" >> $env:GITHUB_ENV
|
||||||
|
Write-Output "QDRANT_VERSION=${qdrant_version}" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
# Log the metadata:
|
# Log the metadata:
|
||||||
Write-Output "App version: '${formatted_app_version}'"
|
Write-Output "App version: '${formatted_app_version}'"
|
||||||
@ -264,6 +273,7 @@ jobs:
|
|||||||
Write-Output "Tauri version: '${tauri_version}'"
|
Write-Output "Tauri version: '${tauri_version}'"
|
||||||
Write-Output "Architecture: '${{ matrix.dotnet_runtime }}'"
|
Write-Output "Architecture: '${{ matrix.dotnet_runtime }}'"
|
||||||
Write-Output "PDFium version: '${pdfium_version}'"
|
Write-Output "PDFium version: '${pdfium_version}'"
|
||||||
|
Write-Output "Qdrant version: '${qdrant_version}'"
|
||||||
|
|
||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v4
|
uses: actions/setup-dotnet@v4
|
||||||
@ -385,6 +395,121 @@ jobs:
|
|||||||
Write-Host "Cleaning up ..."
|
Write-Host "Cleaning up ..."
|
||||||
Remove-Item $ARCHIVE -Force -ErrorAction SilentlyContinue
|
Remove-Item $ARCHIVE -Force -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
# Try to remove the temporary directory, but ignore errors if files are still in use
|
||||||
|
try {
|
||||||
|
Remove-Item $TMP -Recurse -Force -ErrorAction Stop
|
||||||
|
Write-Host "Successfully cleaned up temporary directory: $TMP"
|
||||||
|
} catch {
|
||||||
|
Write-Warning "Could not fully clean up temporary directory: $TMP. This is usually harmless as Windows will clean it up later. Error: $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
- name: Deploy Qdrant (Unix)
|
||||||
|
if: matrix.platform != 'windows-latest'
|
||||||
|
env:
|
||||||
|
QDRANT_VERSION: ${{ env.QDRANT_VERSION }}
|
||||||
|
DOTNET_RUNTIME: ${{ matrix.dotnet_runtime }}
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Target directory:
|
||||||
|
TDB_DIR="runtime/resources/databases/qdrant"
|
||||||
|
mkdir -p "$TDB_DIR"
|
||||||
|
|
||||||
|
case "${DOTNET_RUNTIME}" in
|
||||||
|
linux-x64)
|
||||||
|
QDRANT_FILE="x86_64-unknown-linux-gnu.tar.gz"
|
||||||
|
DB_SOURCE="qdrant"
|
||||||
|
DB_TARGET="qdrant"
|
||||||
|
;;
|
||||||
|
linux-arm64)
|
||||||
|
QDRANT_FILE="aarch64-unknown-linux-musl.tar.gz"
|
||||||
|
DB_SOURCE="qdrant"
|
||||||
|
DB_TARGET="qdrant"
|
||||||
|
;;
|
||||||
|
osx-x64)
|
||||||
|
QDRANT_FILE="x86_64-apple-darwin.tar.gz"
|
||||||
|
DB_SOURCE="qdrant"
|
||||||
|
DB_TARGET="qdrant"
|
||||||
|
;;
|
||||||
|
osx-arm64)
|
||||||
|
QDRANT_FILE="aarch64-apple-darwin.tar.gz"
|
||||||
|
DB_SOURCE="qdrant"
|
||||||
|
DB_TARGET="qdrant"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown platform: ${DOTNET_RUNTIME}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
QDRANT_URL="https://github.com/qdrant/qdrant/releases/download/${QDRANT_VERSON}/qdrant-{QDRANT_FILE}"
|
||||||
|
|
||||||
|
echo "Download Qdrant $QDRANT_URL ..."
|
||||||
|
TMP=$(mktemp -d)
|
||||||
|
ARCHIVE="${TMP}/qdrant.tgz"
|
||||||
|
|
||||||
|
curl -fsSL -o "$ARCHIVE" "$QDRANT_URL"
|
||||||
|
|
||||||
|
echo "Extracting Qdrant ..."
|
||||||
|
tar xzf "$ARCHIVE" -C "$TMP"
|
||||||
|
SRC="${TMP}/${DB_SOURCE}"
|
||||||
|
|
||||||
|
if [ ! -f "$SRC" ]; then
|
||||||
|
echo "Was not able to find Qdrant source: $SRC"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Copy Qdrant from ${DB_TARGET} to ${TDB_DIR}/"
|
||||||
|
cp -f "$SRC" "$TDB_DIR/$DB_TARGET"
|
||||||
|
|
||||||
|
echo "Cleaning up ..."
|
||||||
|
rm -fr "$TMP"
|
||||||
|
|
||||||
|
- name: Install Qdrant (Windows)
|
||||||
|
if: matrix.platform == 'windows-latest'
|
||||||
|
env:
|
||||||
|
QDRANT_VERSION: ${{ env.QDRANT_VERSION }}
|
||||||
|
DOTNET_RUNTIME: ${{ matrix.dotnet_runtime }}
|
||||||
|
run: |
|
||||||
|
$TDB_DIR = "runtime\resources\databases\qdrant"
|
||||||
|
New-Item -ItemType Directory -Force -Path $TDB_DIR | Out-Null
|
||||||
|
|
||||||
|
switch ($env:DOTNET_RUNTIME) {
|
||||||
|
"win-x64" {
|
||||||
|
$QDRANT_FILE = "x86_64-pc-windows-msvc.zip"
|
||||||
|
$DB_SOURCE = "qdrant.exe"
|
||||||
|
$DB_TARGET = "qdrant.exe"
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
Write-Error "Unknown platform: $($env:DOTNET_RUNTIME)"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QDRANT_URL="https://github.com/qdrant/qdrant/releases/download/${QDRANT_VERSON}/qdrant-{QDRANT_FILE}"
|
||||||
|
Write-Host "Download $QDRANT_URL ..."
|
||||||
|
|
||||||
|
# Create a unique temporary directory (not just a file)
|
||||||
|
$TMP = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName())
|
||||||
|
New-Item -ItemType Directory -Path $TMP -Force | Out-Null
|
||||||
|
$ARCHIVE = Join-Path $TMP "qdrant.tgz"
|
||||||
|
|
||||||
|
Invoke-WebRequest -Uri $QDRANT_URL -OutFile $ARCHIVE
|
||||||
|
|
||||||
|
Write-Host "Extracting Qdrant ..."
|
||||||
|
tar -xzf $ARCHIVE -C $TMP
|
||||||
|
|
||||||
|
$SRC = Join-Path $TMP $DB_SOURCE
|
||||||
|
if (!(Test-Path $SRC)) {
|
||||||
|
Write-Error "Cannot find Qdrant source: $SRC"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$DEST = Join-Path $TDB_DIR $DB_TARGET
|
||||||
|
Copy-Item -Path $SRC -Destination $DEST -Force
|
||||||
|
|
||||||
|
Write-Host "Cleaning up ..."
|
||||||
|
Remove-Item $ARCHIVE -Force -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
# Try to remove the temporary directory, but ignore errors if files are still in use
|
# Try to remove the temporary directory, but ignore errors if files are still in use
|
||||||
try {
|
try {
|
||||||
Remove-Item $TMP -Recurse -Force -ErrorAction Stop
|
Remove-Item $TMP -Recurse -Force -ErrorAction Stop
|
||||||
|
|||||||
7
.gitignore
vendored
7
.gitignore
vendored
@ -6,6 +6,13 @@ libpdfium.dylib
|
|||||||
libpdfium.so
|
libpdfium.so
|
||||||
libpdfium.dll
|
libpdfium.dll
|
||||||
|
|
||||||
|
# Ignore qdrant database:
|
||||||
|
qdrant-aarch64-apple-darwin
|
||||||
|
qdrant-x86_64-apple-darwin
|
||||||
|
qdrant-aarch64-unknown-linux-gnu
|
||||||
|
qdrant-x86_64-unknown-linux-gnu
|
||||||
|
qdrant-x86_64-pc-windows-msvc.exe
|
||||||
|
|
||||||
# User-specific files
|
# User-specific files
|
||||||
*.rsuser
|
*.rsuser
|
||||||
*.suo
|
*.suo
|
||||||
|
|||||||
3
app/Build/Commands/Database.cs
Normal file
3
app/Build/Commands/Database.cs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
namespace Build.Commands;
|
||||||
|
|
||||||
|
public record Database(string Path, string Filename);
|
||||||
119
app/Build/Commands/Qdrant.cs
Normal file
119
app/Build/Commands/Qdrant.cs
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
using System.Diagnostics.Eventing.Reader;
|
||||||
|
using System.Formats.Tar;
|
||||||
|
using System.IO.Compression;
|
||||||
|
|
||||||
|
using SharedTools;
|
||||||
|
|
||||||
|
namespace Build.Commands;
|
||||||
|
|
||||||
|
public static class Qdrant
|
||||||
|
{
|
||||||
|
public static async Task InstallAsync(RID rid, string version)
|
||||||
|
{
|
||||||
|
Console.Write($"- Installing Qdrant {version} for {rid.ToUserFriendlyName()} ...");
|
||||||
|
|
||||||
|
var cwd = Environment.GetRustRuntimeDirectory();
|
||||||
|
var qdrantTmpDownloadPath = Path.GetTempFileName();
|
||||||
|
var qdrantTmpExtractPath = Directory.CreateTempSubdirectory();
|
||||||
|
var qdrantUrl = GetQdrantDownloadUrl(rid, version);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Download the file:
|
||||||
|
//
|
||||||
|
Console.Write(" downloading ...");
|
||||||
|
using (var client = new HttpClient())
|
||||||
|
{
|
||||||
|
var response = await client.GetAsync(qdrantUrl);
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
Console.WriteLine($" failed to download Qdrant {version} for {rid.ToUserFriendlyName()} from {qdrantUrl}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await using var fileStream = File.Create(qdrantTmpDownloadPath);
|
||||||
|
await response.Content.CopyToAsync(fileStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Extract the downloaded file:
|
||||||
|
//
|
||||||
|
Console.Write(" extracting ...");
|
||||||
|
await using(var zStream = File.Open(qdrantTmpDownloadPath, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||||
|
{
|
||||||
|
if (rid == RID.WIN_X64)
|
||||||
|
{
|
||||||
|
using var archive = new ZipArchive(zStream, ZipArchiveMode.Read);
|
||||||
|
archive.ExtractToDirectory(qdrantTmpExtractPath.FullName, overwriteFiles: true);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
await using var uncompressedStream = new GZipStream(zStream, CompressionMode.Decompress);
|
||||||
|
await TarFile.ExtractToDirectoryAsync(uncompressedStream, qdrantTmpExtractPath.FullName, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Copy the database to the target directory:
|
||||||
|
//
|
||||||
|
Console.Write(" deploying ...");
|
||||||
|
var database = GetDatabasePath(rid);
|
||||||
|
if (string.IsNullOrWhiteSpace(database.Path))
|
||||||
|
{
|
||||||
|
Console.WriteLine($" failed to find the database path for {rid.ToUserFriendlyName()}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var qdrantDBSourcePath = Path.Join(qdrantTmpExtractPath.FullName, database.Path);
|
||||||
|
var qdrantDBTargetPath = Path.Join(cwd, "resources", "databases", "qdrant",database.Filename);
|
||||||
|
if (!File.Exists(qdrantDBSourcePath))
|
||||||
|
{
|
||||||
|
Console.WriteLine($" failed to find the database file '{qdrantDBSourcePath}'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Directory.CreateDirectory(Path.Join(cwd, "resources", "databases", "qdrant"));
|
||||||
|
if (File.Exists(qdrantDBTargetPath))
|
||||||
|
File.Delete(qdrantDBTargetPath);
|
||||||
|
|
||||||
|
File.Copy(qdrantDBSourcePath, qdrantDBTargetPath);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Cleanup:
|
||||||
|
//
|
||||||
|
Console.Write(" cleaning up ...");
|
||||||
|
File.Delete(qdrantTmpDownloadPath);
|
||||||
|
Directory.Delete(qdrantTmpExtractPath.FullName, true);
|
||||||
|
|
||||||
|
Console.WriteLine(" done.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Database GetDatabasePath(RID rid) => rid switch
|
||||||
|
{
|
||||||
|
RID.LINUX_ARM64 => new("qdrant", "qdrant-aarch64-apple-darwin"),
|
||||||
|
RID.LINUX_X64 => new("qdrant", "qdrant-x86_64-apple-darwin"),
|
||||||
|
|
||||||
|
RID.OSX_ARM64 => new("qdrant", "qdrant-aarch64-unknown-linux-gnu"),
|
||||||
|
RID.OSX_X64 => new("qdrant", "qdrant-x86_64-unknown-linux-gnu"),
|
||||||
|
|
||||||
|
RID.WIN_X64 => new("qdrant.exe", "qdrant-x86_64-pc-windows-msvc.exe"),
|
||||||
|
|
||||||
|
_ => new(string.Empty, string.Empty),
|
||||||
|
};
|
||||||
|
|
||||||
|
private static string GetQdrantDownloadUrl(RID rid, string version)
|
||||||
|
{
|
||||||
|
var baseUrl = $"https://github.com/qdrant/qdrant/releases/download/{version}/qdrant-";
|
||||||
|
return rid switch
|
||||||
|
{
|
||||||
|
RID.LINUX_ARM64 => $"{baseUrl}aarch64-unknown-linux-musl.tar.gz",
|
||||||
|
RID.LINUX_X64 => $"{baseUrl}x86_64-unknown-linux-gnu.tar.gz",
|
||||||
|
|
||||||
|
RID.OSX_ARM64 => $"{baseUrl}aarch64-apple-darwin.tar.gz",
|
||||||
|
RID.OSX_X64 => $"{baseUrl}x86_64-apple-darwin.tar.gz",
|
||||||
|
|
||||||
|
RID.WIN_X64 => $"{baseUrl}x86_64-pc-windows-msvc.zip",
|
||||||
|
#warning We have to handle Qdrant for Windows ARM
|
||||||
|
|
||||||
|
_ => string.Empty,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -112,6 +112,9 @@ public sealed partial class UpdateMetadataCommands
|
|||||||
|
|
||||||
var pdfiumVersion = await this.ReadPdfiumVersion();
|
var pdfiumVersion = await this.ReadPdfiumVersion();
|
||||||
await Pdfium.InstallAsync(rid, pdfiumVersion);
|
await Pdfium.InstallAsync(rid, pdfiumVersion);
|
||||||
|
|
||||||
|
var qdrantVersion = await this.ReadQdrantVersion();
|
||||||
|
await Qdrant.InstallAsync(rid, qdrantVersion);
|
||||||
|
|
||||||
Console.Write($"- Start .NET build for {rid.ToUserFriendlyName()} ...");
|
Console.Write($"- Start .NET build for {rid.ToUserFriendlyName()} ...");
|
||||||
await this.ReadCommandOutput(pathApp, "dotnet", $"clean --configuration release --runtime {rid.AsMicrosoftRid()}");
|
await this.ReadCommandOutput(pathApp, "dotnet", $"clean --configuration release --runtime {rid.AsMicrosoftRid()}");
|
||||||
@ -324,6 +327,16 @@ public sealed partial class UpdateMetadataCommands
|
|||||||
return shortVersion;
|
return shortVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<string> ReadQdrantVersion()
|
||||||
|
{
|
||||||
|
const int QDRANT_VERSION_INDEX = 11;
|
||||||
|
var pathMetadata = Environment.GetMetadataPath();
|
||||||
|
var lines = await File.ReadAllLinesAsync(pathMetadata, Encoding.UTF8);
|
||||||
|
var currentQdrantVersion = lines[QDRANT_VERSION_INDEX].Trim();
|
||||||
|
|
||||||
|
return currentQdrantVersion;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task UpdateArchitecture(RID rid)
|
private async Task UpdateArchitecture(RID rid)
|
||||||
{
|
{
|
||||||
const int ARCHITECTURE_INDEX = 9;
|
const int ARCHITECTURE_INDEX = 9;
|
||||||
|
|||||||
@ -4567,6 +4567,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2777988282"] = "Code in the Rust langu
|
|||||||
-- Show Details
|
-- Show Details
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T27924674"] = "Show Details"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T27924674"] = "Show Details"
|
||||||
|
|
||||||
|
-- Used Qdrant version
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2799791022"] = "Used Qdrant version"
|
||||||
|
|
||||||
-- View our project roadmap and help shape AI Studio's future development.
|
-- View our project roadmap and help shape AI Studio's future development.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2829971158"] = "View our project roadmap and help shape AI Studio's future development."
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2829971158"] = "View our project roadmap and help shape AI Studio's future development."
|
||||||
|
|
||||||
@ -4675,6 +4678,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T855925638"] = "We use this library to
|
|||||||
-- For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose.
|
-- For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T870640199"] = "For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose."
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T870640199"] = "For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose."
|
||||||
|
|
||||||
|
-- Qdrant is a vector similarity search engine and vector database. It provides a production-ready service with a convenient API to store, search, and manage points—vectors with an additional payload Qdrant is tailored to extended filtering support.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T95576615"] = "Qdrant is a vector similarity search engine and vector database. It provides a production-ready service with a convenient API to store, search, and manage points—vectors with an additional payload Qdrant is tailored to extended filtering support."
|
||||||
|
|
||||||
-- Install Pandoc
|
-- Install Pandoc
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T986578435"] = "Install Pandoc"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T986578435"] = "Install Pandoc"
|
||||||
|
|
||||||
|
|||||||
@ -87,6 +87,7 @@
|
|||||||
<MetaAppCommitHash>$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 8 ])</MetaAppCommitHash>
|
<MetaAppCommitHash>$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 8 ])</MetaAppCommitHash>
|
||||||
<MetaArchitecture>$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 9 ])</MetaArchitecture>
|
<MetaArchitecture>$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 9 ])</MetaArchitecture>
|
||||||
<MetaPdfiumVersion>$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 10 ])</MetaPdfiumVersion>
|
<MetaPdfiumVersion>$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 10 ])</MetaPdfiumVersion>
|
||||||
|
<MetaQdrantVersion>$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 11 ])</MetaQdrantVersion>
|
||||||
|
|
||||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||||
|
|
||||||
@ -114,6 +115,9 @@
|
|||||||
<AssemblyAttribute Include="AIStudio.Tools.Metadata.MetaDataLibraries">
|
<AssemblyAttribute Include="AIStudio.Tools.Metadata.MetaDataLibraries">
|
||||||
<_Parameter1>$(MetaPdfiumVersion)</_Parameter1>
|
<_Parameter1>$(MetaPdfiumVersion)</_Parameter1>
|
||||||
</AssemblyAttribute>
|
</AssemblyAttribute>
|
||||||
|
<AssemblyAttribute Include="AIStudio.Tools.Metadata.MetaDataDatabases">
|
||||||
|
<_Parameter1>$(MetaQdrantVersion)</_Parameter1>
|
||||||
|
</AssemblyAttribute>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Target>
|
</Target>
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Build" Text="@this.VersionDotnetSdk"/>
|
<MudListItem T="string" Icon="@Icons.Material.Outlined.Build" Text="@this.VersionDotnetSdk"/>
|
||||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Memory" Text="@this.VersionDotnetRuntime"/>
|
<MudListItem T="string" Icon="@Icons.Material.Outlined.Memory" Text="@this.VersionDotnetRuntime"/>
|
||||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Build" Text="@this.VersionRust"/>
|
<MudListItem T="string" Icon="@Icons.Material.Outlined.Build" Text="@this.VersionRust"/>
|
||||||
|
<MudListItem T="string" Icon="@Icons.Material.Outlined.Storage" Text="@this.VersionQdrant"/>
|
||||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.DocumentScanner" Text="@this.VersionPdfium"/>
|
<MudListItem T="string" Icon="@Icons.Material.Outlined.DocumentScanner" Text="@this.VersionPdfium"/>
|
||||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Article" Text="@this.versionPandoc"/>
|
<MudListItem T="string" Icon="@Icons.Material.Outlined.Article" Text="@this.versionPandoc"/>
|
||||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Widgets" Text="@MudBlazorVersion"/>
|
<MudListItem T="string" Icon="@Icons.Material.Outlined.Widgets" Text="@MudBlazorVersion"/>
|
||||||
@ -194,6 +195,7 @@
|
|||||||
<ThirdPartyComponent Name="CodeBeam.MudBlazor.Extensions" Developer="Mehmet Can Karagöz & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/CodeBeamOrg/CodeBeam.MudBlazor.Extensions/blob/dev/LICENSE" RepositoryUrl="https://github.com/CodeBeamOrg/CodeBeam.MudBlazor.Extensions" UseCase="@T("This library is used to extend the MudBlazor library. It provides additional components that are not part of the MudBlazor library.")"/>
|
<ThirdPartyComponent Name="CodeBeam.MudBlazor.Extensions" Developer="Mehmet Can Karagöz & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/CodeBeamOrg/CodeBeam.MudBlazor.Extensions/blob/dev/LICENSE" RepositoryUrl="https://github.com/CodeBeamOrg/CodeBeam.MudBlazor.Extensions" UseCase="@T("This library is used to extend the MudBlazor library. It provides additional components that are not part of the MudBlazor library.")"/>
|
||||||
<ThirdPartyComponent Name="Rust" Developer="Graydon Hoare, Rust Foundation, Rust developers & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rust-lang/rust/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/rust-lang/rust" UseCase="@T("The .NET backend cannot be started as a desktop app. Therefore, I use a second backend in Rust, which I call runtime. With Rust as the runtime, Tauri can be used to realize a typical desktop app. Thanks to Rust, this app can be offered for Windows, macOS, and Linux desktops. Rust is a great language for developing safe and high-performance software.")"/>
|
<ThirdPartyComponent Name="Rust" Developer="Graydon Hoare, Rust Foundation, Rust developers & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rust-lang/rust/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/rust-lang/rust" UseCase="@T("The .NET backend cannot be started as a desktop app. Therefore, I use a second backend in Rust, which I call runtime. With Rust as the runtime, Tauri can be used to realize a typical desktop app. Thanks to Rust, this app can be offered for Windows, macOS, and Linux desktops. Rust is a great language for developing safe and high-performance software.")"/>
|
||||||
<ThirdPartyComponent Name="Tauri" Developer="Daniel Thompson-Yvetot, Lucas Nogueira, Tensor, Boscop, Serge Zaitsev, George Burton & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/tauri-apps/tauri/blob/dev/LICENSE_MIT" RepositoryUrl="https://github.com/tauri-apps/tauri" UseCase="@T("Tauri is used to host the Blazor user interface. It is a great project that allows the creation of desktop applications using web technologies. I love Tauri!")"/>
|
<ThirdPartyComponent Name="Tauri" Developer="Daniel Thompson-Yvetot, Lucas Nogueira, Tensor, Boscop, Serge Zaitsev, George Burton & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/tauri-apps/tauri/blob/dev/LICENSE_MIT" RepositoryUrl="https://github.com/tauri-apps/tauri" UseCase="@T("Tauri is used to host the Blazor user interface. It is a great project that allows the creation of desktop applications using web technologies. I love Tauri!")"/>
|
||||||
|
<ThirdPartyComponent Name="Qdrant" Developer="Andrey Vasnetsov, Tim Visée, Arnaud Gourlay, Luis Cossío, Ivan Pleshkov, Roman Titov, xzfc, JojiiOfficial & Open Source Community" LicenseName="Apache-2.0" LicenseUrl="https://github.com/qdrant/qdrant/blob/master/LICENSE" RepositoryUrl="https://github.com/qdrant/qdrant" UseCase="@T("Qdrant is a vector similarity search engine and vector database. It provides a production-ready service with a convenient API to store, search, and manage points—vectors with an additional payload Qdrant is tailored to extended filtering support.")"/>
|
||||||
<ThirdPartyComponent Name="Rocket" Developer="Sergio Benitez & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rwf2/Rocket/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/rwf2/Rocket" UseCase="@T("We use Rocket to implement the runtime API. This is necessary because the runtime must be able to communicate with the user interface (IPC). Rocket is a great framework for implementing web APIs in Rust.")"/>
|
<ThirdPartyComponent Name="Rocket" Developer="Sergio Benitez & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rwf2/Rocket/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/rwf2/Rocket" UseCase="@T("We use Rocket to implement the runtime API. This is necessary because the runtime must be able to communicate with the user interface (IPC). Rocket is a great framework for implementing web APIs in Rust.")"/>
|
||||||
<ThirdPartyComponent Name="serde" Developer="Erick Tryzelaar, David Tolnay & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/serde-rs/serde/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/serde-rs/serde" UseCase="@T("Now we have multiple systems, some developed in .NET and others in Rust. The data format JSON is responsible for translating data between both worlds (called data serialization and deserialization). Serde takes on this task in the Rust world. The counterpart in the .NET world is an integral part of .NET and is located in System.Text.Json.")"/>
|
<ThirdPartyComponent Name="serde" Developer="Erick Tryzelaar, David Tolnay & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/serde-rs/serde/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/serde-rs/serde" UseCase="@T("Now we have multiple systems, some developed in .NET and others in Rust. The data format JSON is responsible for translating data between both worlds (called data serialization and deserialization). Serde takes on this task in the Rust world. The counterpart in the .NET world is an integral part of .NET and is located in System.Text.Json.")"/>
|
||||||
<ThirdPartyComponent Name="keyring" Developer="Walther Chen, Daniel Brotsky & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/hwchen/keyring-rs/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/hwchen/keyring-rs" UseCase="@T("In order to use any LLM, each user must store their so-called API key for each LLM provider. This key must be kept secure, similar to a password. The safest way to do this is offered by operating systems like macOS, Windows, and Linux: They have mechanisms to store such data, if available, on special security hardware. Since this is currently not possible in .NET, we use this Rust library.")"/>
|
<ThirdPartyComponent Name="keyring" Developer="Walther Chen, Daniel Brotsky & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/hwchen/keyring-rs/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/hwchen/keyring-rs" UseCase="@T("In order to use any LLM, each user must store their so-called API key for each LLM provider. This key must be kept secure, similar to a password. The safest way to do this is offered by operating systems like macOS, Windows, and Linux: They have mechanisms to store such data, if available, on special security hardware. Since this is currently not possible in .NET, we use this Rust library.")"/>
|
||||||
|
|||||||
@ -30,6 +30,7 @@ public partial class About : MSGComponentBase
|
|||||||
private static readonly MetaDataAttribute META_DATA = ASSEMBLY.GetCustomAttribute<MetaDataAttribute>()!;
|
private static readonly MetaDataAttribute META_DATA = ASSEMBLY.GetCustomAttribute<MetaDataAttribute>()!;
|
||||||
private static readonly MetaDataArchitectureAttribute META_DATA_ARCH = ASSEMBLY.GetCustomAttribute<MetaDataArchitectureAttribute>()!;
|
private static readonly MetaDataArchitectureAttribute META_DATA_ARCH = ASSEMBLY.GetCustomAttribute<MetaDataArchitectureAttribute>()!;
|
||||||
private static readonly MetaDataLibrariesAttribute META_DATA_LIBRARIES = ASSEMBLY.GetCustomAttribute<MetaDataLibrariesAttribute>()!;
|
private static readonly MetaDataLibrariesAttribute META_DATA_LIBRARIES = ASSEMBLY.GetCustomAttribute<MetaDataLibrariesAttribute>()!;
|
||||||
|
private static readonly MetaDataDatabasesAttribute META_DATA_DATABASES = ASSEMBLY.GetCustomAttribute<MetaDataDatabasesAttribute>()!;
|
||||||
|
|
||||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(About).Namespace, nameof(About));
|
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(About).Namespace, nameof(About));
|
||||||
|
|
||||||
@ -53,6 +54,8 @@ public partial class About : MSGComponentBase
|
|||||||
|
|
||||||
private string VersionPdfium => $"{T("Used PDFium version")}: v{META_DATA_LIBRARIES.PdfiumVersion}";
|
private string VersionPdfium => $"{T("Used PDFium version")}: v{META_DATA_LIBRARIES.PdfiumVersion}";
|
||||||
|
|
||||||
|
private string VersionQdrant => $"{T("Used Qdrant version")}: {META_DATA_DATABASES.QdrantVersion}";
|
||||||
|
|
||||||
private string versionPandoc = TB("Determine Pandoc version, please wait...");
|
private string versionPandoc = TB("Determine Pandoc version, please wait...");
|
||||||
private PandocInstallation pandocInstallation;
|
private PandocInstallation pandocInstallation;
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
namespace AIStudio.Tools.Metadata;
|
||||||
|
|
||||||
|
public class MetaDataDatabasesAttribute(string qdrantVersion) : Attribute
|
||||||
|
{
|
||||||
|
public string QdrantVersion => qdrantVersion;
|
||||||
|
}
|
||||||
@ -8,4 +8,5 @@
|
|||||||
1.8.1
|
1.8.1
|
||||||
009bb33d839, release
|
009bb33d839, release
|
||||||
osx-arm64
|
osx-arm64
|
||||||
137.0.7215.0
|
137.0.7215.0
|
||||||
|
v1.16.1
|
||||||
353
runtime/resources/databases/qdrant/config.yaml
Normal file
353
runtime/resources/databases/qdrant/config.yaml
Normal file
@ -0,0 +1,353 @@
|
|||||||
|
log_level: INFO
|
||||||
|
|
||||||
|
# Logging configuration
|
||||||
|
# Qdrant logs to stdout. You may configure to also write logs to a file on disk.
|
||||||
|
# Be aware that this file may grow indefinitely.
|
||||||
|
# logger:
|
||||||
|
# # Logging format, supports `text` and `json`
|
||||||
|
# format: text
|
||||||
|
# on_disk:
|
||||||
|
# enabled: true
|
||||||
|
# log_file: path/to/log/file.log
|
||||||
|
# log_level: INFO
|
||||||
|
# # Logging format, supports `text` and `json`
|
||||||
|
# format: text
|
||||||
|
# buffer_size_bytes: 1024
|
||||||
|
|
||||||
|
storage:
|
||||||
|
|
||||||
|
snapshots_config:
|
||||||
|
# "local" or "s3" - where to store snapshots
|
||||||
|
snapshots_storage: local
|
||||||
|
# s3_config:
|
||||||
|
# bucket: ""
|
||||||
|
# region: ""
|
||||||
|
# access_key: ""
|
||||||
|
# secret_key: ""
|
||||||
|
|
||||||
|
# Where to store temporary files
|
||||||
|
# If null, temporary snapshots are stored in: storage/snapshots_temp/
|
||||||
|
temp_path: null
|
||||||
|
|
||||||
|
# If true - point payloads will not be stored in memory.
|
||||||
|
# It will be read from the disk every time it is requested.
|
||||||
|
# This setting saves RAM by (slightly) increasing the response time.
|
||||||
|
# Note: those payload values that are involved in filtering and are indexed - remain in RAM.
|
||||||
|
#
|
||||||
|
# Default: true
|
||||||
|
on_disk_payload: true
|
||||||
|
|
||||||
|
# Maximum number of concurrent updates to shard replicas
|
||||||
|
# If `null` - maximum concurrency is used.
|
||||||
|
update_concurrency: null
|
||||||
|
|
||||||
|
# Write-ahead-log related configuration
|
||||||
|
wal:
|
||||||
|
# Size of a single WAL segment
|
||||||
|
wal_capacity_mb: 32
|
||||||
|
|
||||||
|
# Number of WAL segments to create ahead of actual data requirement
|
||||||
|
wal_segments_ahead: 0
|
||||||
|
|
||||||
|
# Normal node - receives all updates and answers all queries
|
||||||
|
node_type: "Normal"
|
||||||
|
|
||||||
|
# Listener node - receives all updates, but does not answer search/read queries
|
||||||
|
# Useful for setting up a dedicated backup node
|
||||||
|
# node_type: "Listener"
|
||||||
|
|
||||||
|
performance:
|
||||||
|
# Number of parallel threads used for search operations. If 0 - auto selection.
|
||||||
|
max_search_threads: 0
|
||||||
|
|
||||||
|
# CPU budget, how many CPUs (threads) to allocate for an optimization job.
|
||||||
|
# If 0 - auto selection, keep 1 or more CPUs unallocated depending on CPU size
|
||||||
|
# If negative - subtract this number of CPUs from the available CPUs.
|
||||||
|
# If positive - use this exact number of CPUs.
|
||||||
|
optimizer_cpu_budget: 0
|
||||||
|
|
||||||
|
# Prevent DDoS of too many concurrent updates in distributed mode.
|
||||||
|
# One external update usually triggers multiple internal updates, which breaks internal
|
||||||
|
# timings. For example, the health check timing and consensus timing.
|
||||||
|
# If null - auto selection.
|
||||||
|
update_rate_limit: null
|
||||||
|
|
||||||
|
# Limit for number of incoming automatic shard transfers per collection on this node, does not affect user-requested transfers.
|
||||||
|
# The same value should be used on all nodes in a cluster.
|
||||||
|
# Default is to allow 1 transfer.
|
||||||
|
# If null - allow unlimited transfers.
|
||||||
|
#incoming_shard_transfers_limit: 1
|
||||||
|
|
||||||
|
# Limit for number of outgoing automatic shard transfers per collection on this node, does not affect user-requested transfers.
|
||||||
|
# The same value should be used on all nodes in a cluster.
|
||||||
|
# Default is to allow 1 transfer.
|
||||||
|
# If null - allow unlimited transfers.
|
||||||
|
#outgoing_shard_transfers_limit: 1
|
||||||
|
|
||||||
|
# Enable async scorer which uses io_uring when rescoring.
|
||||||
|
# Only supported on Linux, must be enabled in your kernel.
|
||||||
|
# See: <https://qdrant.tech/articles/io_uring/#and-what-about-qdrant>
|
||||||
|
#async_scorer: false
|
||||||
|
|
||||||
|
optimizers:
|
||||||
|
# The minimal fraction of deleted vectors in a segment, required to perform segment optimization
|
||||||
|
deleted_threshold: 0.2
|
||||||
|
|
||||||
|
# The minimal number of vectors in a segment, required to perform segment optimization
|
||||||
|
vacuum_min_vector_number: 1000
|
||||||
|
|
||||||
|
# Target amount of segments optimizer will try to keep.
|
||||||
|
# Real amount of segments may vary depending on multiple parameters:
|
||||||
|
# - Amount of stored points
|
||||||
|
# - Current write RPS
|
||||||
|
#
|
||||||
|
# It is recommended to select default number of segments as a factor of the number of search threads,
|
||||||
|
# so that each segment would be handled evenly by one of the threads.
|
||||||
|
# If `default_segment_number = 0`, will be automatically selected by the number of available CPUs
|
||||||
|
default_segment_number: 0
|
||||||
|
|
||||||
|
# Do not create segments larger this size (in KiloBytes).
|
||||||
|
# Large segments might require disproportionately long indexation times,
|
||||||
|
# therefore it makes sense to limit the size of segments.
|
||||||
|
#
|
||||||
|
# If indexation speed have more priority for your - make this parameter lower.
|
||||||
|
# If search speed is more important - make this parameter higher.
|
||||||
|
# Note: 1Kb = 1 vector of size 256
|
||||||
|
# If not set, will be automatically selected considering the number of available CPUs.
|
||||||
|
max_segment_size_kb: null
|
||||||
|
|
||||||
|
# Maximum size (in KiloBytes) of vectors allowed for plain index.
|
||||||
|
# Default value based on experiments and observations.
|
||||||
|
# Note: 1Kb = 1 vector of size 256
|
||||||
|
# To explicitly disable vector indexing, set to `0`.
|
||||||
|
# If not set, the default value will be used.
|
||||||
|
indexing_threshold_kb: 10000
|
||||||
|
|
||||||
|
# Interval between forced flushes.
|
||||||
|
flush_interval_sec: 5
|
||||||
|
|
||||||
|
# Max number of threads (jobs) for running optimizations per shard.
|
||||||
|
# Note: each optimization job will also use `max_indexing_threads` threads by itself for index building.
|
||||||
|
# If null - have no limit and choose dynamically to saturate CPU.
|
||||||
|
# If 0 - no optimization threads, optimizations will be disabled.
|
||||||
|
max_optimization_threads: null
|
||||||
|
|
||||||
|
# This section has the same options as 'optimizers' above. All values specified here will overwrite the collections
|
||||||
|
# optimizers configs regardless of the config above and the options specified at collection creation.
|
||||||
|
#optimizers_overwrite:
|
||||||
|
# deleted_threshold: 0.2
|
||||||
|
# vacuum_min_vector_number: 1000
|
||||||
|
# default_segment_number: 0
|
||||||
|
# max_segment_size_kb: null
|
||||||
|
# indexing_threshold_kb: 10000
|
||||||
|
# flush_interval_sec: 5
|
||||||
|
# max_optimization_threads: null
|
||||||
|
|
||||||
|
# Default parameters of HNSW Index. Could be overridden for each collection or named vector individually
|
||||||
|
hnsw_index:
|
||||||
|
# Number of edges per node in the index graph. Larger the value - more accurate the search, more space required.
|
||||||
|
m: 16
|
||||||
|
|
||||||
|
# Number of neighbours to consider during the index building. Larger the value - more accurate the search, more time required to build index.
|
||||||
|
ef_construct: 100
|
||||||
|
|
||||||
|
# Minimal size threshold (in KiloBytes) below which full-scan is preferred over HNSW search.
|
||||||
|
# This measures the total size of vectors being queried against.
|
||||||
|
# When the maximum estimated amount of points that a condition satisfies is smaller than
|
||||||
|
# `full_scan_threshold_kb`, the query planner will use full-scan search instead of HNSW index
|
||||||
|
# traversal for better performance.
|
||||||
|
# Note: 1Kb = 1 vector of size 256
|
||||||
|
full_scan_threshold_kb: 10000
|
||||||
|
|
||||||
|
# Number of parallel threads used for background index building.
|
||||||
|
# If 0 - automatically select.
|
||||||
|
# Best to keep between 8 and 16 to prevent likelihood of building broken/inefficient HNSW graphs.
|
||||||
|
# On small CPUs, less threads are used.
|
||||||
|
max_indexing_threads: 0
|
||||||
|
|
||||||
|
# Store HNSW index on disk. If set to false, index will be stored in RAM. Default: false
|
||||||
|
on_disk: false
|
||||||
|
|
||||||
|
# Custom M param for hnsw graph built for payload index. If not set, default M will be used.
|
||||||
|
payload_m: null
|
||||||
|
|
||||||
|
# Default shard transfer method to use if none is defined.
|
||||||
|
# If null - don't have a shard transfer preference, choose automatically.
|
||||||
|
# If stream_records, snapshot or wal_delta - prefer this specific method.
|
||||||
|
# More info: https://qdrant.tech/documentation/guides/distributed_deployment/#shard-transfer-method
|
||||||
|
shard_transfer_method: null
|
||||||
|
|
||||||
|
# Default parameters for collections
|
||||||
|
collection:
|
||||||
|
# Number of replicas of each shard that network tries to maintain
|
||||||
|
replication_factor: 1
|
||||||
|
|
||||||
|
# How many replicas should apply the operation for us to consider it successful
|
||||||
|
write_consistency_factor: 1
|
||||||
|
|
||||||
|
# Default parameters for vectors.
|
||||||
|
vectors:
|
||||||
|
# Whether vectors should be stored in memory or on disk.
|
||||||
|
on_disk: null
|
||||||
|
|
||||||
|
# shard_number_per_node: 1
|
||||||
|
|
||||||
|
# Default quantization configuration.
|
||||||
|
# More info: https://qdrant.tech/documentation/guides/quantization
|
||||||
|
quantization: null
|
||||||
|
|
||||||
|
# Default strict mode parameters for newly created collections.
|
||||||
|
#strict_mode:
|
||||||
|
# Whether strict mode is enabled for a collection or not.
|
||||||
|
#enabled: false
|
||||||
|
|
||||||
|
# Max allowed `limit` parameter for all APIs that don't have their own max limit.
|
||||||
|
#max_query_limit: null
|
||||||
|
|
||||||
|
# Max allowed `timeout` parameter.
|
||||||
|
#max_timeout: null
|
||||||
|
|
||||||
|
# Allow usage of unindexed fields in retrieval based (eg. search) filters.
|
||||||
|
#unindexed_filtering_retrieve: null
|
||||||
|
|
||||||
|
# Allow usage of unindexed fields in filtered updates (eg. delete by payload).
|
||||||
|
#unindexed_filtering_update: null
|
||||||
|
|
||||||
|
# Max HNSW value allowed in search parameters.
|
||||||
|
#search_max_hnsw_ef: null
|
||||||
|
|
||||||
|
# Whether exact search is allowed or not.
|
||||||
|
#search_allow_exact: null
|
||||||
|
|
||||||
|
# Max oversampling value allowed in search.
|
||||||
|
#search_max_oversampling: null
|
||||||
|
|
||||||
|
# Maximum number of collections allowed to be created
|
||||||
|
# If null - no limit.
|
||||||
|
max_collections: null
|
||||||
|
|
||||||
|
service:
|
||||||
|
# Maximum size of POST data in a single request in megabytes
|
||||||
|
max_request_size_mb: 32
|
||||||
|
|
||||||
|
# Number of parallel workers used for serving the api. If 0 - equal to the number of available cores.
|
||||||
|
# If missing - Same as storage.max_search_threads
|
||||||
|
max_workers: 0
|
||||||
|
|
||||||
|
# Host to bind the service on
|
||||||
|
host: 0.0.0.0
|
||||||
|
|
||||||
|
# HTTP(S) port to bind the service on
|
||||||
|
http_port: 6373
|
||||||
|
|
||||||
|
# gRPC port to bind the service on.
|
||||||
|
# If `null` - gRPC is disabled. Default: null
|
||||||
|
# Comment to disable gRPC:
|
||||||
|
grpc_port: 6344
|
||||||
|
|
||||||
|
# Enable CORS headers in REST API.
|
||||||
|
# If enabled, browsers would be allowed to query REST endpoints regardless of query origin.
|
||||||
|
# More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
||||||
|
# Default: true
|
||||||
|
enable_cors: true
|
||||||
|
|
||||||
|
# Enable HTTPS for the REST and gRPC API
|
||||||
|
enable_tls: false
|
||||||
|
|
||||||
|
# Check user HTTPS client certificate against CA file specified in tls config
|
||||||
|
verify_https_client_certificate: false
|
||||||
|
|
||||||
|
# Set an api-key.
|
||||||
|
# If set, all requests must include a header with the api-key.
|
||||||
|
# example header: `api-key: <API-KEY>`
|
||||||
|
#
|
||||||
|
# If you enable this you should also enable TLS.
|
||||||
|
# (Either above or via an external service like nginx.)
|
||||||
|
# Sending an api-key over an unencrypted channel is insecure.
|
||||||
|
#
|
||||||
|
# Uncomment to enable.
|
||||||
|
# api_key: your_secret_api_key_here
|
||||||
|
|
||||||
|
# Set an api-key for read-only operations.
|
||||||
|
# If set, all requests must include a header with the api-key.
|
||||||
|
# example header: `api-key: <API-KEY>`
|
||||||
|
#
|
||||||
|
# If you enable this you should also enable TLS.
|
||||||
|
# (Either above or via an external service like nginx.)
|
||||||
|
# Sending an api-key over an unencrypted channel is insecure.
|
||||||
|
#
|
||||||
|
# Uncomment to enable.
|
||||||
|
# read_only_api_key: your_secret_read_only_api_key_here
|
||||||
|
|
||||||
|
# Uncomment to enable JWT Role Based Access Control (RBAC).
|
||||||
|
# If enabled, you can generate JWT tokens with fine-grained rules for access control.
|
||||||
|
# Use generated token instead of API key.
|
||||||
|
#
|
||||||
|
# jwt_rbac: true
|
||||||
|
|
||||||
|
# Hardware reporting adds information to the API responses with a
|
||||||
|
# hint on how many resources were used to execute the request.
|
||||||
|
#
|
||||||
|
# Warning: experimental, this feature is still under development and is not supported yet.
|
||||||
|
#
|
||||||
|
# Uncomment to enable.
|
||||||
|
# hardware_reporting: true
|
||||||
|
#
|
||||||
|
# Uncomment to enable.
|
||||||
|
# Prefix for the names of metrics in the /metrics API.
|
||||||
|
# metrics_prefix: qdrant_
|
||||||
|
|
||||||
|
cluster:
|
||||||
|
# Use `enabled: true` to run Qdrant in distributed deployment mode
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
# Configuration of the inter-cluster communication
|
||||||
|
p2p:
|
||||||
|
# Port for internal communication between peers
|
||||||
|
port: 6335
|
||||||
|
|
||||||
|
# Use TLS for communication between peers
|
||||||
|
enable_tls: false
|
||||||
|
|
||||||
|
# Configuration related to distributed consensus algorithm
|
||||||
|
consensus:
|
||||||
|
# How frequently peers should ping each other.
|
||||||
|
# Setting this parameter to lower value will allow consensus
|
||||||
|
# to detect disconnected nodes earlier, but too frequent
|
||||||
|
# tick period may create significant network and CPU overhead.
|
||||||
|
# We encourage you NOT to change this parameter unless you know what you are doing.
|
||||||
|
tick_period_ms: 100
|
||||||
|
|
||||||
|
# Compact consensus operations once we have this amount of applied
|
||||||
|
# operations. Allows peers to join quickly with a consensus snapshot without
|
||||||
|
# replaying a huge amount of operations.
|
||||||
|
# If 0 - disable compaction
|
||||||
|
compact_wal_entries: 128
|
||||||
|
|
||||||
|
# Set to true to prevent service from sending usage statistics to the developers.
|
||||||
|
# Read more: https://qdrant.tech/documentation/guides/telemetry
|
||||||
|
telemetry_disabled: false
|
||||||
|
|
||||||
|
# TLS configuration.
|
||||||
|
# Required if either service.enable_tls or cluster.p2p.enable_tls is true.
|
||||||
|
tls:
|
||||||
|
# Server certificate chain file
|
||||||
|
cert: ./tls/cert.pem
|
||||||
|
|
||||||
|
# Server private key file
|
||||||
|
key: ./tls/key.pem
|
||||||
|
|
||||||
|
# Certificate authority certificate file.
|
||||||
|
# This certificate will be used to validate the certificates
|
||||||
|
# presented by other nodes during inter-cluster communication.
|
||||||
|
#
|
||||||
|
# If verify_https_client_certificate is true, it will verify
|
||||||
|
# HTTPS client certificate
|
||||||
|
#
|
||||||
|
# Required if cluster.p2p.enable_tls is true.
|
||||||
|
ca_cert: ./tls/cacert.pem
|
||||||
|
|
||||||
|
# TTL in seconds to reload certificate from disk, useful for certificate rotations.
|
||||||
|
# Only works for HTTPS endpoints. Does not support gRPC (and intra-cluster communication).
|
||||||
|
# If `null` - TTL is disabled.
|
||||||
|
cert_ttl: 3600
|
||||||
@ -17,6 +17,7 @@ use crate::dotnet::stop_dotnet_server;
|
|||||||
use crate::environment::{is_prod, is_dev, CONFIG_DIRECTORY, DATA_DIRECTORY};
|
use crate::environment::{is_prod, is_dev, CONFIG_DIRECTORY, DATA_DIRECTORY};
|
||||||
use crate::log::switch_to_file_logging;
|
use crate::log::switch_to_file_logging;
|
||||||
use crate::pdfium::PDFIUM_LIB_PATH;
|
use crate::pdfium::PDFIUM_LIB_PATH;
|
||||||
|
use crate::qdrant::start_qdrant_server;
|
||||||
|
|
||||||
/// The Tauri main window.
|
/// The Tauri main window.
|
||||||
static MAIN_WINDOW: Lazy<Mutex<Option<Window>>> = Lazy::new(|| Mutex::new(None));
|
static MAIN_WINDOW: Lazy<Mutex<Option<Window>>> = Lazy::new(|| Mutex::new(None));
|
||||||
@ -94,6 +95,9 @@ pub fn start_tauri() {
|
|||||||
info!(Source = "Bootloader Tauri"; "Reconfigure the file logger to use the app data directory {data_path:?}");
|
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();
|
switch_to_file_logging(data_path).map_err(|e| error!("Failed to switch logging to file: {e}")).unwrap();
|
||||||
set_pdfium_path(app.path_resolver());
|
set_pdfium_path(app.path_resolver());
|
||||||
|
|
||||||
|
start_qdrant_server();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.plugin(tauri_plugin_window_state::Builder::default().build())
|
.plugin(tauri_plugin_window_state::Builder::default().build())
|
||||||
|
|||||||
@ -12,4 +12,5 @@ pub mod certificate;
|
|||||||
pub mod file_data;
|
pub mod file_data;
|
||||||
pub mod metadata;
|
pub mod metadata;
|
||||||
pub mod pdfium;
|
pub mod pdfium;
|
||||||
pub mod pandoc;
|
pub mod pandoc;
|
||||||
|
pub mod qdrant;
|
||||||
@ -38,6 +38,7 @@ async fn main() {
|
|||||||
info!(".. MudBlazor: v{mud_blazor_version}", mud_blazor_version = metadata.mud_blazor_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!(".. Tauri: v{tauri_version}", tauri_version = metadata.tauri_version);
|
||||||
info!(".. PDFium: v{pdfium_version}", pdfium_version = metadata.pdfium_version);
|
info!(".. PDFium: v{pdfium_version}", pdfium_version = metadata.pdfium_version);
|
||||||
|
info!(".. Qdrant: {qdrant_version}", qdrant_version = metadata.qdrant_version);
|
||||||
|
|
||||||
if is_dev() {
|
if is_dev() {
|
||||||
warn!("Running in development mode.");
|
warn!("Running in development mode.");
|
||||||
|
|||||||
@ -16,6 +16,7 @@ pub struct MetaData {
|
|||||||
pub app_commit_hash: String,
|
pub app_commit_hash: String,
|
||||||
pub architecture: String,
|
pub architecture: String,
|
||||||
pub pdfium_version: String,
|
pub pdfium_version: String,
|
||||||
|
pub qdrant_version: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MetaData {
|
impl MetaData {
|
||||||
@ -39,6 +40,7 @@ impl MetaData {
|
|||||||
let app_commit_hash = metadata_lines.next().unwrap();
|
let app_commit_hash = metadata_lines.next().unwrap();
|
||||||
let architecture = metadata_lines.next().unwrap();
|
let architecture = metadata_lines.next().unwrap();
|
||||||
let pdfium_version = metadata_lines.next().unwrap();
|
let pdfium_version = metadata_lines.next().unwrap();
|
||||||
|
let qdrant_version = metadata_lines.next().unwrap();
|
||||||
|
|
||||||
let metadata = MetaData {
|
let metadata = MetaData {
|
||||||
architecture: architecture.to_string(),
|
architecture: architecture.to_string(),
|
||||||
@ -52,6 +54,7 @@ impl MetaData {
|
|||||||
rust_version: rust_version.to_string(),
|
rust_version: rust_version.to_string(),
|
||||||
tauri_version: tauri_version.to_string(),
|
tauri_version: tauri_version.to_string(),
|
||||||
pdfium_version: pdfium_version.to_string(),
|
pdfium_version: pdfium_version.to_string(),
|
||||||
|
qdrant_version: qdrant_version.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
*META_DATA.lock().unwrap() = Some(metadata.clone());
|
*META_DATA.lock().unwrap() = Some(metadata.clone());
|
||||||
|
|||||||
122
runtime/src/qdrant.rs
Normal file
122
runtime/src/qdrant.rs
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use log::{debug, error, info, warn};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use rocket::get;
|
||||||
|
use tauri::api::process::{Command, CommandChild, CommandEvent};
|
||||||
|
use tauri::Url;
|
||||||
|
use crate::api_token::{APIToken};
|
||||||
|
use crate::environment::DATA_DIRECTORY;
|
||||||
|
|
||||||
|
// Qdrant server process started in a separate process and can communicate
|
||||||
|
// via HTTP or gRPC with the .NET server and the runtime process
|
||||||
|
static QDRANT_SERVER: Lazy<Arc<Mutex<Option<CommandChild>>>> = Lazy::new(|| Arc::new(Mutex::new(None)));
|
||||||
|
|
||||||
|
// Qdrant server port (default is 6333 for HTTP and 6334 for gRPC)
|
||||||
|
static QDRANT_SERVER_PORT: Lazy<u16> = Lazy::new(|| {
|
||||||
|
crate::network::get_available_port().unwrap_or(6333)
|
||||||
|
});
|
||||||
|
|
||||||
|
static QDRANT_INITIALIZED: Lazy<Mutex<bool>> = Lazy::new(|| Mutex::new(false));
|
||||||
|
|
||||||
|
#[get("/system/qdrant/port")]
|
||||||
|
pub fn qdrant_port(_token: APIToken) -> String {
|
||||||
|
let qdrant_port = *QDRANT_SERVER_PORT;
|
||||||
|
format!("{qdrant_port}")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Starts the Qdrant server in a separate process.
|
||||||
|
pub fn start_qdrant_server() {
|
||||||
|
|
||||||
|
let base_path = DATA_DIRECTORY.get().unwrap();
|
||||||
|
|
||||||
|
let storage_path = Path::new(base_path).join("databases").join("qdrant").join("storage").to_str().unwrap().to_string();
|
||||||
|
let snapshot_path = Path::new(base_path).join("databases").join("qdrant").join("snapshots").to_str().unwrap().to_string();
|
||||||
|
let init_path = Path::new(base_path).join("databases").join("qdrant").join(".qdrant-initalized").to_str().unwrap().to_string();
|
||||||
|
|
||||||
|
println!("{}", storage_path);
|
||||||
|
println!("{}", snapshot_path);
|
||||||
|
println!("{}", init_path);
|
||||||
|
|
||||||
|
let qdrant_server_environment = HashMap::from_iter([
|
||||||
|
(String::from("QDRANT__SERVICE__HTTP_PORT"), QDRANT_SERVER_PORT.to_string()),
|
||||||
|
(String::from("QDRANT_INIT_FILE_PATH"), init_path),
|
||||||
|
(String::from("QDRANT__STORAGE__STORAGE_PATH"), storage_path),
|
||||||
|
(String::from("QDRANT__STORAGE__SNAPSHOTS_PATH"), snapshot_path),
|
||||||
|
]);
|
||||||
|
let server_spawn_clone = QDRANT_SERVER.clone();
|
||||||
|
tauri::async_runtime::spawn(async move {
|
||||||
|
let (mut rx, child) = Command::new_sidecar("qdrant")
|
||||||
|
.expect("Failed to create sidecar for Qdrant")
|
||||||
|
.args(["--config-path", "resources/databases/qdrant/config.yaml"])
|
||||||
|
.envs(qdrant_server_environment)
|
||||||
|
.spawn()
|
||||||
|
.expect("Failed to spawn Qdrant server process.");
|
||||||
|
|
||||||
|
let server_pid = child.pid();
|
||||||
|
info!(Source = "Bootloader Qdrant"; "Qdrant server process started with PID={server_pid}.");
|
||||||
|
|
||||||
|
// Save the server process to stop it later:
|
||||||
|
*server_spawn_clone.lock().unwrap() = Some(child);
|
||||||
|
|
||||||
|
// Log the output of the Qdrant server:
|
||||||
|
while let Some(event) = rx.recv().await {
|
||||||
|
match event {
|
||||||
|
CommandEvent::Stdout(line) => {
|
||||||
|
let line = line.trim_end();
|
||||||
|
if line.contains("INFO") || line.contains("info") {
|
||||||
|
info!(Source = "Qdrant Server"; "{line}");
|
||||||
|
} else if line.contains("WARN") || line.contains("warning") {
|
||||||
|
warn!(Source = "Qdrant Server"; "{line}");
|
||||||
|
} else if line.contains("ERROR") || line.contains("error") {
|
||||||
|
error!(Source = "Qdrant Server"; "{line}");
|
||||||
|
} else {
|
||||||
|
debug!(Source = "Qdrant Server"; "{line}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CommandEvent::Stderr(line) => {
|
||||||
|
error!(Source = "Qdrant Server (stderr)"; "{line}");
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This endpoint is called by the Qdrant server or frontend to signal that Qdrant is ready.
|
||||||
|
#[get("/system/qdrant/ready")]
|
||||||
|
pub async fn qdrant_ready(_token: APIToken) {
|
||||||
|
{
|
||||||
|
let mut initialized = QDRANT_INITIALIZED.lock().unwrap();
|
||||||
|
if *initialized {
|
||||||
|
warn!("Qdrant server was already initialized.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
info!("Qdrant server is ready.");
|
||||||
|
*initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let qdrant_port = *QDRANT_SERVER_PORT;
|
||||||
|
let _url = match Url::parse(format!("http://localhost:{qdrant_port}").as_str()) {
|
||||||
|
Ok(url) => url,
|
||||||
|
Err(msg) => {
|
||||||
|
error!("Error while parsing Qdrant URL: {msg}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stops the Qdrant server process.
|
||||||
|
pub fn stop_qdrant_server() {
|
||||||
|
if let Some(server_process) = QDRANT_SERVER.lock().unwrap().take() {
|
||||||
|
let server_kill_result = server_process.kill();
|
||||||
|
match server_kill_result {
|
||||||
|
Ok(_) => info!("Qdrant server process was stopped."),
|
||||||
|
Err(e) => error!("Failed to stop Qdrant server process: {e}."),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!("Qdrant server process was not started or is already stopped.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -20,6 +20,11 @@
|
|||||||
"name": "../app/MindWork AI Studio/bin/dist/mindworkAIStudioServer",
|
"name": "../app/MindWork AI Studio/bin/dist/mindworkAIStudioServer",
|
||||||
"sidecar": true,
|
"sidecar": true,
|
||||||
"args": true
|
"args": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "resources/databases/qdrant/qdrant",
|
||||||
|
"sidecar": true,
|
||||||
|
"args": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -59,7 +64,8 @@
|
|||||||
"targets": "all",
|
"targets": "all",
|
||||||
"identifier": "com.github.mindwork-ai.ai-studio",
|
"identifier": "com.github.mindwork-ai.ai-studio",
|
||||||
"externalBin": [
|
"externalBin": [
|
||||||
"../app/MindWork AI Studio/bin/dist/mindworkAIStudioServer"
|
"../app/MindWork AI Studio/bin/dist/mindworkAIStudioServer",
|
||||||
|
"resources/databases/qdrant/qdrant"
|
||||||
],
|
],
|
||||||
"resources": [
|
"resources": [
|
||||||
"resources/*"
|
"resources/*"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user