mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2025-07-05 20:02:55 +00:00
Compare commits
161 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9c52812c09 | ||
|
be27fd84b8 | ||
|
61ffddf1ec | ||
|
6d1ecb7678 | ||
|
68f5bb1512 | ||
|
aaedf667fe | ||
|
7df0b3e6e0 | ||
|
1b8ed286e7 | ||
|
87bb04e3c0 | ||
|
181ba07d2d | ||
|
3587f547b5 | ||
|
cf17c643a0 | ||
|
7843426057 | ||
|
4db31fd9cf | ||
|
18b2e22725 | ||
|
b6169e5bfb | ||
|
4aeb51c9f1 | ||
|
7c4ddf1164 | ||
|
793a9cae0a | ||
|
368fc9ce39 | ||
|
2ef563e9ab | ||
|
32e1ab977d | ||
|
7a498d524e | ||
|
ce915292dc | ||
|
6979a73e5c | ||
|
fcf511cea8 | ||
|
e8cfce05fe | ||
|
b88b78a08e | ||
|
9a1b7853aa | ||
|
69610ea908 | ||
|
437da0fd9b | ||
|
5b685193ef | ||
|
1d31fd9a22 | ||
|
e3c272b0c4 | ||
|
1e19c05c57 | ||
|
c08881784d | ||
|
05544eca15 | ||
|
baa80beb92 | ||
|
801179e835 | ||
|
e3110820d2 | ||
|
e11c15dbab | ||
|
fc7a4391cb | ||
|
d2f85f31ea | ||
|
9e207974e9 | ||
|
df062ae6e6 | ||
|
35eebc08a5 | ||
|
206706f77a | ||
|
d87df5b044 | ||
|
b9b850b2c1 | ||
|
9aa0a91465 | ||
|
217d31bc44 | ||
|
038ddec46b | ||
|
d02efce689 | ||
|
2e58ecb97d | ||
|
a548cabe24 | ||
|
4b2da61a90 | ||
|
2ae292009d | ||
|
03e196b66f | ||
|
4ca1fd54d9 | ||
|
c0cf620fe3 | ||
|
47a0598e8f | ||
|
85a867459e | ||
|
ba236c4012 | ||
|
9fa0f95611 | ||
|
f10d9a5d88 | ||
|
1a4e4781cd | ||
|
99b4d616c7 | ||
|
ace125d7cb | ||
|
d6af5a3afd | ||
|
07b858fd72 | ||
|
5d7949211f | ||
|
c23f0f49f9 | ||
|
7a08baf99b | ||
|
cfc2103b43 | ||
|
f8dbc186fb | ||
|
6041b42510 | ||
|
10dc03f33b | ||
|
437ea2c243 | ||
|
b0d4615506 | ||
|
c4d9af18cd | ||
|
9996b810e2 | ||
|
53ec4c2162 | ||
|
38facf3e04 | ||
|
68404199ba | ||
|
08b8f42c25 | ||
|
3c41fe2771 | ||
|
e6a3add08f | ||
|
56600d3dd3 | ||
|
47b6a89685 | ||
|
2494ee2294 | ||
|
90bd450193 | ||
|
d272d619cb | ||
|
cdc155890f | ||
|
81030019c7 | ||
|
201fb2514d | ||
|
45974f9bcf | ||
|
151830ef15 | ||
|
3e13d50302 | ||
|
02c3e4c817 | ||
|
bafd62429d | ||
|
69fc4d764f | ||
|
9cc3c68dda | ||
|
fdca581c90 | ||
|
7bf5b5cf7a | ||
|
1dc1c6563d | ||
|
e594e5c0e8 | ||
|
7bd827e658 | ||
|
03cd299a86 | ||
|
2144cfe059 | ||
|
c10c084da4 | ||
|
f8c60d87ae | ||
|
c6e45c26d6 | ||
|
09020415db | ||
|
b153b9c4e9 | ||
|
c715b38f41 | ||
|
3fc15d9789 | ||
|
171ed37c27 | ||
|
80c58ea749 | ||
|
1ff27fe21f | ||
|
712ed2938f | ||
|
56e99ffaba | ||
|
693fe5e161 | ||
|
ac731f6f78 | ||
|
1993576903 | ||
|
364941701b | ||
|
f321d6982e | ||
|
a3ec2bbe7b | ||
|
be430766c5 | ||
|
6185db733a | ||
|
670b0653b5 | ||
|
f97612de0e | ||
|
c56232cba7 | ||
|
b456319434 | ||
|
ceefc0114b | ||
|
158252dc34 | ||
|
07c1182611 | ||
|
3ce6e168cb | ||
|
ff61df047a | ||
|
d0074a6fc7 | ||
|
b632854cd4 | ||
|
3f8f399cd9 | ||
|
e2b371f9a1 | ||
|
ee2a73ccd8 | ||
|
97454c59e9 | ||
|
d25a47789e | ||
|
820294c14d | ||
|
dd40ca3e5b | ||
|
c67f17cf41 | ||
|
b948909afa | ||
|
3e66d543b9 | ||
|
1ac125b7f5 | ||
|
aa0c2e7870 | ||
|
f5a49ff077 | ||
|
a5863a176c | ||
|
0c81f90b84 | ||
|
81417daa78 | ||
|
780e45911d | ||
|
dad709cccb | ||
|
75b3238e56 | ||
|
293b0ffdb0 | ||
|
08c18cd387 |
.github
.gitignoreREADME.mdapp
.run
Build
Build Script.csproj
MindWork AI Studio.slnMindWork AI Studio.sln.DotSettingsCommands
AppVersion.csCheckRidsCommand.csCollectI18NKeysCommand.csLibrary.csPdfium.csPrepareAction.csUpdateMetadataCommands.csUpdateWebAssetsCommand.cs
GlobalUsings.csProgram.csTools
MindWork AI Studio
Assistants
Agenda
AssistantBase.razorAssistantBase.razor.csAssistantLowerBase.csBiasDay
Coding
AssistantCoding.razorAssistantCoding.razor.csCodingContextItem.razorCodingContextItem.razor.csCommonCodingLanguageExtensions.cs
EMail
ERI
AllowedLLMProvidersExtensions.csAssistantERI.razorAssistantERI.razor.csAuthExtensions.csDataSourcesExtensions.csOperatingSystemExtensions.csProgrammingLanguagesExtensions.cs
GrammarSpelling
I18N
IconFinder
JobPosting
LegalCheck
MyTasks
RewriteImprove
AssistantRewriteImprove.razorAssistantRewriteImprove.razor.csSentenceStructureExtensions.csWritingStyles.csWritingStylesExtensions.cs
Synonym
TextSummarizer
Translation
Chat
ChatRole.csChatRoleExtensions.csChatRoles.csChatThread.csContentBlock.csContentBlockComponent.razorContentBlockComponent.razor.csContentImage.csContentText.csIContent.csStringExtension.csWorkspace.cs
Components
AssistantBlock.razorAssistantBlock.razor.csChangelog.Logs.csChangelog.razorChangelog.razor.csChatComponent.razorChatComponent.razor.csChatTemplateSelection.razorChatTemplateSelection.razor.csCodeBlock.razorCodeBlock.razor.csCodeTabs.razorCodeTabs.razor.csConfidenceInfo.razorConfidenceInfo.razor.csConfigurationBase.razorConfigurationBase.razor.csConfigurationMinConfidenceSelection.razorConfigurationMinConfidenceSelection.razor.cs
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@ -1,2 +1 @@
|
|||||||
github: [MindWorkAI]
|
github: [MindWorkAI]
|
||||||
open_collective: mindwork-ai
|
|
376
.github/workflows/build-and-release.yml
vendored
376
.github/workflows/build-and-release.yml
vendored
@ -103,6 +103,12 @@ jobs:
|
|||||||
dotnet_name_postfix: '-x86_64-unknown-linux-gnu'
|
dotnet_name_postfix: '-x86_64-unknown-linux-gnu'
|
||||||
tauri_bundle: 'appimage deb updater'
|
tauri_bundle: 'appimage deb updater'
|
||||||
|
|
||||||
|
- platform: 'ubuntu-22.04-arm' # for ARM-based Linux
|
||||||
|
rust_target: 'aarch64-unknown-linux-gnu'
|
||||||
|
dotnet_runtime: 'linux-arm64'
|
||||||
|
dotnet_name_postfix: '-aarch64-unknown-linux-gnu'
|
||||||
|
tauri_bundle: 'appimage deb updater'
|
||||||
|
|
||||||
- platform: 'windows-latest' # for x86-based Windows
|
- platform: 'windows-latest' # for x86-based Windows
|
||||||
rust_target: 'x86_64-pc-windows-msvc'
|
rust_target: 'x86_64-pc-windows-msvc'
|
||||||
dotnet_runtime: 'win-x64'
|
dotnet_runtime: 'win-x64'
|
||||||
@ -154,6 +160,19 @@ jobs:
|
|||||||
# Format the app version:
|
# Format the app version:
|
||||||
formatted_app_version="v${app_version}"
|
formatted_app_version="v${app_version}"
|
||||||
|
|
||||||
|
# Set the architecture:
|
||||||
|
if sed --version 2>/dev/null | grep -q GNU; then
|
||||||
|
sed -i "10s/.*/${{ matrix.dotnet_runtime }}/" metadata.txt
|
||||||
|
else
|
||||||
|
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:
|
# 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
|
||||||
@ -164,6 +183,8 @@ jobs:
|
|||||||
echo "RUST_VERSION=${rust_version}" >> $GITHUB_ENV
|
echo "RUST_VERSION=${rust_version}" >> $GITHUB_ENV
|
||||||
echo "MUD_BLAZOR_VERSION=${mud_blazor_version}" >> $GITHUB_ENV
|
echo "MUD_BLAZOR_VERSION=${mud_blazor_version}" >> $GITHUB_ENV
|
||||||
echo "TAURI_VERSION=${tauri_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:
|
# Log the metadata:
|
||||||
echo "App version: '${formatted_app_version}'"
|
echo "App version: '${formatted_app_version}'"
|
||||||
@ -174,6 +195,8 @@ jobs:
|
|||||||
echo "Rust version: '${rust_version}'"
|
echo "Rust version: '${rust_version}'"
|
||||||
echo "MudBlazor version: '${mud_blazor_version}'"
|
echo "MudBlazor version: '${mud_blazor_version}'"
|
||||||
echo "Tauri version: '${tauri_version}'"
|
echo "Tauri version: '${tauri_version}'"
|
||||||
|
echo "Architecture: '${{ matrix.dotnet_runtime }}'"
|
||||||
|
echo "PDFium version: '${pdfium_version}'"
|
||||||
|
|
||||||
- name: Read and format metadata (Windows)
|
- name: Read and format metadata (Windows)
|
||||||
if: matrix.platform == 'windows-latest'
|
if: matrix.platform == 'windows-latest'
|
||||||
@ -206,6 +229,18 @@ jobs:
|
|||||||
# Format the app version:
|
# Format the app version:
|
||||||
$formatted_app_version = "v${app_version}"
|
$formatted_app_version = "v${app_version}"
|
||||||
|
|
||||||
|
# Set the architecture:
|
||||||
|
$metadata[9] = "${{ matrix.dotnet_runtime }}"
|
||||||
|
|
||||||
|
# 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 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
|
||||||
@ -215,6 +250,8 @@ jobs:
|
|||||||
Write-Output "DOTNET_RUNTIME_VERSION=${dotnet_runtime_version}" >> $env:GITHUB_ENV
|
Write-Output "DOTNET_RUNTIME_VERSION=${dotnet_runtime_version}" >> $env:GITHUB_ENV
|
||||||
Write-Output "RUST_VERSION=${rust_version}" >> $env:GITHUB_ENV
|
Write-Output "RUST_VERSION=${rust_version}" >> $env:GITHUB_ENV
|
||||||
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 "PDFIUM_VERSION=${pdfium_version}" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
# Log the metadata:
|
# Log the metadata:
|
||||||
Write-Output "App version: '${formatted_app_version}'"
|
Write-Output "App version: '${formatted_app_version}'"
|
||||||
@ -225,6 +262,8 @@ jobs:
|
|||||||
Write-Output "Rust version: '${rust_version}'"
|
Write-Output "Rust version: '${rust_version}'"
|
||||||
Write-Output "MudBlazor version: '${mud_blazor_version}'"
|
Write-Output "MudBlazor version: '${mud_blazor_version}'"
|
||||||
Write-Output "Tauri version: '${tauri_version}'"
|
Write-Output "Tauri version: '${tauri_version}'"
|
||||||
|
Write-Output "Architecture: '${{ matrix.dotnet_runtime }}'"
|
||||||
|
Write-Output "PDFium version: '${pdfium_version}'"
|
||||||
|
|
||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v4
|
uses: actions/setup-dotnet@v4
|
||||||
@ -233,6 +272,117 @@ jobs:
|
|||||||
cache: true
|
cache: true
|
||||||
cache-dependency-path: 'app/MindWork AI Studio/packages.lock.json'
|
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
|
- name: Build .NET project
|
||||||
run: |
|
run: |
|
||||||
cd "app/MindWork AI Studio"
|
cd "app/MindWork AI Studio"
|
||||||
@ -275,7 +425,13 @@ jobs:
|
|||||||
if: matrix.platform == 'ubuntu-22.04' && contains(matrix.rust_target, 'x86_64')
|
if: matrix.platform == 'ubuntu-22.04' && contains(matrix.rust_target, 'x86_64')
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
|
sudo apt-get install -y libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf libfuse2
|
||||||
|
|
||||||
|
- name: Setup dependencies (Ubuntu-specific, ARM)
|
||||||
|
if: matrix.platform == 'ubuntu-22.04-arm' && contains(matrix.rust_target, 'aarch64')
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf libfuse2
|
||||||
|
|
||||||
- name: Setup Tauri (Unix)
|
- name: Setup Tauri (Unix)
|
||||||
if: matrix.platform != 'windows-latest'
|
if: matrix.platform != 'windows-latest'
|
||||||
@ -304,14 +460,14 @@ jobs:
|
|||||||
- name: Delete previous artifact, which may exist due to caching (Windows - MSI)
|
- name: Delete previous artifact, which may exist due to caching (Windows - MSI)
|
||||||
if: startsWith(matrix.platform, 'windows') && contains(matrix.tauri_bundle, 'msi')
|
if: startsWith(matrix.platform, 'windows') && contains(matrix.tauri_bundle, 'msi')
|
||||||
run: |
|
run: |
|
||||||
rm -Force "runtime/target/${{ matrix.rust_target }}/release/bundle/msi/MindWork AI Studio_*.msi"
|
rm -Force "runtime/target/${{ matrix.rust_target }}/release/bundle/msi/MindWork AI Studio_*.msi" -ErrorAction SilentlyContinue
|
||||||
rm -Force "runtime/target/${{ matrix.rust_target }}/release/bundle/msi/MindWork AI Studio*msi.zip*"
|
rm -Force "runtime/target/${{ matrix.rust_target }}/release/bundle/msi/MindWork AI Studio*msi.zip*" -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
- name: Delete previous artifact, which may exist due to caching (Windows - NSIS)
|
- name: Delete previous artifact, which may exist due to caching (Windows - NSIS)
|
||||||
if: startsWith(matrix.platform, 'windows') && contains(matrix.tauri_bundle, 'nsis')
|
if: startsWith(matrix.platform, 'windows') && contains(matrix.tauri_bundle, 'nsis')
|
||||||
run: |
|
run: |
|
||||||
rm -Force "runtime/target/${{ matrix.rust_target }}/release/bundle/nsis/MindWork AI Studio_*.exe"
|
rm -Force "runtime/target/${{ matrix.rust_target }}/release/bundle/nsis/MindWork AI Studio_*.exe" -ErrorAction SilentlyContinue
|
||||||
rm -Force "runtime/target/${{ matrix.rust_target }}/release/bundle/nsis/MindWork AI Studio*nsis.zip*"
|
rm -Force "runtime/target/${{ matrix.rust_target }}/release/bundle/nsis/MindWork AI Studio*nsis.zip*" -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
- name: Delete previous artifact, which may exist due to caching (Linux - Debian Package)
|
- name: Delete previous artifact, which may exist due to caching (Linux - Debian Package)
|
||||||
if: startsWith(matrix.platform, 'ubuntu') && contains(matrix.tauri_bundle, 'deb')
|
if: startsWith(matrix.platform, 'ubuntu') && contains(matrix.tauri_bundle, 'deb')
|
||||||
@ -400,215 +556,10 @@ jobs:
|
|||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
retention-days: ${{ env.RETENTION_INTERMEDIATE_ASSETS }}
|
retention-days: ${{ env.RETENTION_INTERMEDIATE_ASSETS }}
|
||||||
|
|
||||||
build_linux_arm64:
|
|
||||||
name: Build app (linux-arm64)
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
needs: read_metadata
|
|
||||||
env:
|
|
||||||
SKIP: false # allows disabling this long-running job temporarily
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
lfs: false
|
|
||||||
|
|
||||||
- name: Read and format metadata
|
|
||||||
if: ${{ env.SKIP != 'true' }}
|
|
||||||
id: metadata
|
|
||||||
run: |
|
|
||||||
# Read the lines of the metadata file:
|
|
||||||
app_version=$(sed -n '1p' metadata.txt)
|
|
||||||
build_time=$(sed -n '2p' metadata.txt)
|
|
||||||
build_number=$(sed -n '3p' metadata.txt)
|
|
||||||
|
|
||||||
# Next line is the .NET SDK version.
|
|
||||||
# The format is '8.0.205 (commit 3e1383b780)'.
|
|
||||||
# We extract only the version number, though:
|
|
||||||
dotnet_sdk_version=$(sed -n '4p' metadata.txt | sed 's/[^0-9.]*\([0-9.]*\).*/\1/')
|
|
||||||
|
|
||||||
# Next line is the .NET runtime version.
|
|
||||||
# The format is '8.0.5 (commit 087e15321b)'.
|
|
||||||
# We extract only the version number, though:
|
|
||||||
dotnet_runtime_version=$(sed -n '5p' metadata.txt | sed 's/[^0-9.]*\([0-9.]*\).*/\1/')
|
|
||||||
|
|
||||||
# Next line is the Rust version.
|
|
||||||
# The format is '1.78.0 (commit 9b00956e5)'.
|
|
||||||
# We extract only the version number, though:
|
|
||||||
rust_version=$(sed -n '6p' metadata.txt | sed 's/[^0-9.]*\([0-9.]*\).*/\1/')
|
|
||||||
|
|
||||||
# Next line is the MudBlazor version:
|
|
||||||
mud_blazor_version=$(sed -n '7p' metadata.txt)
|
|
||||||
|
|
||||||
# Next line is the Tauri version:
|
|
||||||
tauri_version=$(sed -n '8p' metadata.txt)
|
|
||||||
|
|
||||||
# Format the app version:
|
|
||||||
formatted_app_version="v${app_version}"
|
|
||||||
|
|
||||||
# Write the metadata to the environment:
|
|
||||||
echo "APP_VERSION=${app_version}" >> $GITHUB_ENV
|
|
||||||
echo "FORMATTED_APP_VERSION=${formatted_app_version}" >> $GITHUB_ENV
|
|
||||||
echo "BUILD_TIME=${build_time}" >> $GITHUB_ENV
|
|
||||||
echo "BUILD_NUMBER=${build_number}" >> $GITHUB_ENV
|
|
||||||
echo "DOTNET_SDK_VERSION=${dotnet_sdk_version}" >> $GITHUB_ENV
|
|
||||||
echo "DOTNET_RUNTIME_VERSION=${dotnet_runtime_version}" >> $GITHUB_ENV
|
|
||||||
echo "RUST_VERSION=${rust_version}" >> $GITHUB_ENV
|
|
||||||
echo "MUD_BLAZOR_VERSION=${mud_blazor_version}" >> $GITHUB_ENV
|
|
||||||
echo "TAURI_VERSION=${tauri_version}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
# Log the metadata:
|
|
||||||
echo "App version: '${formatted_app_version}'"
|
|
||||||
echo "Build time: '${build_time}'"
|
|
||||||
echo "Build number: '${build_number}'"
|
|
||||||
echo ".NET SDK version: '${dotnet_sdk_version}'"
|
|
||||||
echo ".NET runtime version: '${dotnet_runtime_version}'"
|
|
||||||
echo "Rust version: '${rust_version}'"
|
|
||||||
echo "MudBlazor version: '${mud_blazor_version}'"
|
|
||||||
echo "Tauri version: '${tauri_version}'"
|
|
||||||
|
|
||||||
- name: Setup .NET
|
|
||||||
if: ${{ env.SKIP != 'true' }}
|
|
||||||
uses: actions/setup-dotnet@v4
|
|
||||||
with:
|
|
||||||
dotnet-version: ${{ env.DOTNET_SDK_VERSI }}
|
|
||||||
cache: true
|
|
||||||
cache-dependency-path: 'app/MindWork AI Studio/packages.lock.json'
|
|
||||||
|
|
||||||
- name: Build .NET project
|
|
||||||
if: ${{ env.SKIP != 'true' }}
|
|
||||||
run: |
|
|
||||||
cd "app/MindWork AI Studio"
|
|
||||||
dotnet publish --configuration release --runtime linux-arm64 --disable-build-servers --force --output ../../publish/dotnet
|
|
||||||
|
|
||||||
- name: Move & rename the .NET artifact
|
|
||||||
if: ${{ env.SKIP != 'true' }}
|
|
||||||
run: |
|
|
||||||
mkdir -p "app/MindWork AI Studio/bin/dist"
|
|
||||||
cd publish/dotnet
|
|
||||||
mv mindworkAIStudio "../../app/MindWork AI Studio/bin/dist/mindworkAIStudioServer-aarch64-unknown-linux-gnu"
|
|
||||||
|
|
||||||
- name: Cache linux arm64 runner image
|
|
||||||
if: ${{ env.SKIP != 'true' }}
|
|
||||||
uses: actions/cache@v4
|
|
||||||
id: linux_arm_cache
|
|
||||||
with:
|
|
||||||
path: ${{ runner.temp }}/linux_arm_qemu_cache.img
|
|
||||||
key: target-linux-arm64-rust-${{ env.RUST_VERSION }}
|
|
||||||
|
|
||||||
- name: Build linux arm runner image
|
|
||||||
uses: pguyot/arm-runner-action@v2
|
|
||||||
id: build-linux-arm-runner
|
|
||||||
if: ${{ steps.linux_arm_cache.outputs.cache-hit != 'true' && env.SKIP != 'true' }}
|
|
||||||
env:
|
|
||||||
RUST_VERSION: ${{ env.RUST_VERSION }}
|
|
||||||
TAURI_VERSION: ${{ env.TAURI_VERSION }}
|
|
||||||
|
|
||||||
with:
|
|
||||||
base_image: dietpi:rpi_armv8_bullseye
|
|
||||||
cpu: cortex-a53
|
|
||||||
image_additional_mb: 6000 # ~ 6GB
|
|
||||||
optimize_image: false
|
|
||||||
shell: /bin/bash
|
|
||||||
commands: |
|
|
||||||
# Rust complains (rightly) that $HOME doesn't match eid home:
|
|
||||||
export HOME=/root
|
|
||||||
|
|
||||||
# Workaround to CI worker being stuck on Updating crates.io index:
|
|
||||||
export CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse
|
|
||||||
|
|
||||||
# Update and upgrade the system:
|
|
||||||
apt-get update --yes --allow-releaseinfo-change
|
|
||||||
apt-get upgrade --yes
|
|
||||||
apt-get autoremove --yes
|
|
||||||
apt-get install curl wget --yes
|
|
||||||
|
|
||||||
# Install Rust:
|
|
||||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain none -y
|
|
||||||
source "$HOME/.cargo/env"
|
|
||||||
rustup toolchain install $RUST_VERSION
|
|
||||||
|
|
||||||
# Install build tools and tauri-cli requirements:
|
|
||||||
apt-get install --yes libwebkit2gtk-4.0-dev build-essential libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev
|
|
||||||
|
|
||||||
# Setup Tauri:
|
|
||||||
cargo install --version 1.6.2 tauri-cli
|
|
||||||
|
|
||||||
- name: Add the built runner image to the cache
|
|
||||||
if: ${{ steps.linux_arm_cache.outputs.cache-hit != 'true' && env.SKIP != 'true' }}
|
|
||||||
run: |
|
|
||||||
mv ${{ steps.build-linux-arm-runner.outputs.image }} ${{ runner.temp }}/linux_arm_qemu_cache.img
|
|
||||||
|
|
||||||
#
|
|
||||||
# This step does not work, because we start a VM with qemu to run the build.
|
|
||||||
#
|
|
||||||
#- name: Delete previous artifact, which may exist due to caching (Linux - Debian Package)
|
|
||||||
# if: ${{ env.SKIP != 'true' }}
|
|
||||||
# run: |
|
|
||||||
# rm -f result/target/aarch64-unknown-linux-gnu/release/bundle/deb/mind-work-ai-studio_*.deb
|
|
||||||
|
|
||||||
- name: Build Tauri project
|
|
||||||
if: ${{ env.SKIP != 'true' }}
|
|
||||||
uses: pguyot/arm-runner-action@v2
|
|
||||||
id: build-linux-arm
|
|
||||||
|
|
||||||
with:
|
|
||||||
base_image: file://${{ runner.temp }}/linux_arm_qemu_cache.img
|
|
||||||
cpu: cortex-a53
|
|
||||||
optimize_image: false
|
|
||||||
copy_artifact_path: runtime
|
|
||||||
copy_artifact_dest: result
|
|
||||||
bind_mount_repository: true
|
|
||||||
|
|
||||||
#
|
|
||||||
# We do not need to set the PRIVATE_PUBLISH_KEY and PRIVATE_PUBLISH_KEY_PASSWORD here,
|
|
||||||
# because we cannot produce the AppImage on arm64. Only the AppImage supports the automatic
|
|
||||||
# update feature. The Debian package does not support this feature.
|
|
||||||
#
|
|
||||||
#PRIVATE_PUBLISH_KEY: ${{ secrets.PRIVATE_PUBLISH_KEY }}
|
|
||||||
#PRIVATE_PUBLISH_KEY_PASSWORD: ${{ secrets.PRIVATE_PUBLISH_KEY_PASSWORD }}
|
|
||||||
#
|
|
||||||
|
|
||||||
shell: /bin/bash
|
|
||||||
commands: |
|
|
||||||
# Delete all previous artifacts, which may exist due to caching:
|
|
||||||
rm -f result/target/aarch64-unknown-linux-gnu/release/bundle/deb/mind-work-ai-studio_*.deb
|
|
||||||
|
|
||||||
export HOME=/root
|
|
||||||
export CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse
|
|
||||||
source "$HOME/.cargo/env"
|
|
||||||
cd runtime
|
|
||||||
|
|
||||||
# Try to restore the Rust cache from previous build:
|
|
||||||
mkdir -p /rust-cache/target
|
|
||||||
rm -fr target
|
|
||||||
cp -Rp /rust-cache/target target
|
|
||||||
|
|
||||||
cargo tauri build --target aarch64-unknown-linux-gnu --bundles deb
|
|
||||||
|
|
||||||
# Save the built libraries for the next job:
|
|
||||||
rm -fr /rust-cache/target
|
|
||||||
cp -Rp target /rust-cache
|
|
||||||
|
|
||||||
- name: Update the runner image to cache the Rust runtime build
|
|
||||||
if: ${{ env.SKIP != 'true' }}
|
|
||||||
run: |
|
|
||||||
mv ${{ steps.build-linux-arm.outputs.image }} $RUNNER_TEMP/linux_arm_qemu_cache.img
|
|
||||||
|
|
||||||
- name: Upload artifact (Linux - Debian Package)
|
|
||||||
if: ${{ env.SKIP != 'true' && startsWith(github.ref, 'refs/tags/v') }}
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: MindWork AI Studio (Linux - deb linux-arm64)
|
|
||||||
path: |
|
|
||||||
result/target/aarch64-unknown-linux-gnu/release/bundle/deb/mind-work-ai-studio_*.deb
|
|
||||||
if-no-files-found: warn
|
|
||||||
retention-days: ${{ env.RETENTION_INTERMEDIATE_ASSETS }}
|
|
||||||
|
|
||||||
create_release:
|
create_release:
|
||||||
name: Prepare & create release
|
name: Prepare & create release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build_main, read_metadata, build_linux_arm64]
|
needs: [build_main, read_metadata]
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
steps:
|
steps:
|
||||||
- name: Create artifact directory
|
- name: Create artifact directory
|
||||||
@ -682,6 +633,7 @@ jobs:
|
|||||||
# - platform=darwin-aarch64 when path contains 'aarch64-apple-darwin'
|
# - platform=darwin-aarch64 when path contains 'aarch64-apple-darwin'
|
||||||
# - platform=darwin-x86_64 when path contains 'x86_64-apple-darwin'
|
# - platform=darwin-x86_64 when path contains 'x86_64-apple-darwin'
|
||||||
# - platform=linux-x86_64 when path contains 'x86_64-unknown-linux-'
|
# - platform=linux-x86_64 when path contains 'x86_64-unknown-linux-'
|
||||||
|
# - platform=linux-aarch64 when path contains 'aarch64-unknown-linux-'
|
||||||
# - platform=windows-x86_64 when path contains 'x86_64-pc-windows-'
|
# - platform=windows-x86_64 when path contains 'x86_64-pc-windows-'
|
||||||
# - platform=windows-aarch64 when path contains 'aarch64-pc-windows-'
|
# - platform=windows-aarch64 when path contains 'aarch64-pc-windows-'
|
||||||
#
|
#
|
||||||
@ -691,6 +643,8 @@ jobs:
|
|||||||
platform="darwin-x86_64"
|
platform="darwin-x86_64"
|
||||||
elif [[ "$sig_file" == *"amd64.AppImage"* ]]; then
|
elif [[ "$sig_file" == *"amd64.AppImage"* ]]; then
|
||||||
platform="linux-x86_64"
|
platform="linux-x86_64"
|
||||||
|
elif [[ "$sig_file" == *"aarch64.AppImage"* ]]; then
|
||||||
|
platform="linux-aarch64"
|
||||||
elif [[ "$sig_file" == *"x64-setup.nsis"* ]]; then
|
elif [[ "$sig_file" == *"x64-setup.nsis"* ]]; then
|
||||||
platform="windows-x86_64"
|
platform="windows-x86_64"
|
||||||
elif [[ "$sig_file" == *"arm64-setup.nsis"* ]]; then
|
elif [[ "$sig_file" == *"arm64-setup.nsis"* ]]; then
|
||||||
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,3 +1,11 @@
|
|||||||
|
# Ignore any startup.env file:
|
||||||
|
startup.env
|
||||||
|
|
||||||
|
# Ignore pdfium library:
|
||||||
|
libpdfium.dylib
|
||||||
|
libpdfium.so
|
||||||
|
libpdfium.dll
|
||||||
|
|
||||||
# User-specific files
|
# User-specific files
|
||||||
*.rsuser
|
*.rsuser
|
||||||
*.suo
|
*.suo
|
||||||
|
49
README.md
49
README.md
@ -13,8 +13,9 @@ Things we are currently working on:
|
|||||||
- [x] ~~App: Configure embedding providers (PR [#224](https://github.com/MindWorkAI/AI-Studio/pull/224))~~
|
- [x] ~~App: Configure embedding providers (PR [#224](https://github.com/MindWorkAI/AI-Studio/pull/224))~~
|
||||||
- [x] ~~App: Implement an [ERI](https://github.com/MindWorkAI/ERI) server coding assistant (PR [#231](https://github.com/MindWorkAI/AI-Studio/pull/231))~~
|
- [x] ~~App: Implement an [ERI](https://github.com/MindWorkAI/ERI) server coding assistant (PR [#231](https://github.com/MindWorkAI/AI-Studio/pull/231))~~
|
||||||
- [x] ~~App: Management of data sources (local & external data via [ERI](https://github.com/MindWorkAI/ERI)) (PR [#259](https://github.com/MindWorkAI/AI-Studio/pull/259), [#273](https://github.com/MindWorkAI/AI-Studio/pull/273))~~
|
- [x] ~~App: Management of data sources (local & external data via [ERI](https://github.com/MindWorkAI/ERI)) (PR [#259](https://github.com/MindWorkAI/AI-Studio/pull/259), [#273](https://github.com/MindWorkAI/AI-Studio/pull/273))~~
|
||||||
- [ ] Runtime: Extract data from txt / md / pdf / docx / xlsx files
|
- [x] ~~Runtime: Extract data from txt / md / pdf / docx / xlsx files (PR [#374](https://github.com/MindWorkAI/AI-Studio/pull/374))~~
|
||||||
- [ ] (*Optional*) Runtime: Implement internal embedding provider through [fastembed-rs](https://github.com/Anush008/fastembed-rs)
|
- [ ] (*Optional*) Runtime: Implement internal embedding provider through [fastembed-rs](https://github.com/Anush008/fastembed-rs)
|
||||||
|
- [x] ~~App: Implement dialog for checking & handling [pandoc](https://pandoc.org/) installation ([PR #393](https://github.com/MindWorkAI/AI-Studio/pull/393), [PR #487](https://github.com/MindWorkAI/AI-Studio/pull/487))~~
|
||||||
- [ ] App: Implement external embedding providers
|
- [ ] App: Implement external embedding providers
|
||||||
- [ ] App: Implement the process to vectorize one local file using embeddings
|
- [ ] App: Implement the process to vectorize one local file using embeddings
|
||||||
- [ ] Runtime: Integration of the vector database [LanceDB](https://github.com/lancedb/lancedb)
|
- [ ] Runtime: Integration of the vector database [LanceDB](https://github.com/lancedb/lancedb)
|
||||||
@ -24,26 +25,39 @@ Things we are currently working on:
|
|||||||
- [x] ~~App: Integrate data sources in chats (PR [#282](https://github.com/MindWorkAI/AI-Studio/pull/282))~~
|
- [x] ~~App: Integrate data sources in chats (PR [#282](https://github.com/MindWorkAI/AI-Studio/pull/282))~~
|
||||||
|
|
||||||
|
|
||||||
- Since September 2024: Experiments have been started on how we can work on long texts with AI Studio. Let's say you want to write a fantasy novel or create a complex project proposal and use LLM for support. The initial experiments were promising, but not yet satisfactory. We are testing further approaches until a satisfactory solution is found. The current state of our experiment is available as an experimental preview feature through your app configuration. Related PR: ~~[#167](https://github.com/MindWorkAI/AI-Studio/pull/167), [#226](https://github.com/MindWorkAI/AI-Studio/pull/226)~~.
|
- Since September 2024: Experiments have been started on how we can work on long texts with AI Studio. Let's say you want to write a fantasy novel or create a complex project proposal and use LLM for support. The initial experiments were promising, but not yet satisfactory. We are testing further approaches until a satisfactory solution is found. The current state of our experiment is available as an experimental preview feature through your app configuration. Related PR: ~~[PR #167](https://github.com/MindWorkAI/AI-Studio/pull/167), [PR #226](https://github.com/MindWorkAI/AI-Studio/pull/226)~~, [PR #376](https://github.com/MindWorkAI/AI-Studio/pull/376).
|
||||||
|
|
||||||
|
- Since March 2025: We have started developing the plugin system. There will be language plugins to offer AI Studio in other languages, configuration plugins to centrally manage certain providers and rules within an organization, and assistant plugins that allow anyone to develop their own assistants. We are using Lua as the plugin language:
|
||||||
|
- [x] ~~Plan & implement the base plugin system ([PR #322](https://github.com/MindWorkAI/AI-Studio/pull/322))~~
|
||||||
|
- [x] ~~Start the plugin system ([PR #372](https://github.com/MindWorkAI/AI-Studio/pull/372))~~
|
||||||
|
- [x] ~~Added hot-reload support for plugins ([PR #377](https://github.com/MindWorkAI/AI-Studio/pull/377), [PR #391](https://github.com/MindWorkAI/AI-Studio/pull/391))~~
|
||||||
|
- [x] ~~Add support for other languages (I18N) to AI Studio ([PR #381](https://github.com/MindWorkAI/AI-Studio/pull/381), [PR #400](https://github.com/MindWorkAI/AI-Studio/pull/400), [PR #404](https://github.com/MindWorkAI/AI-Studio/pull/404), [PR #429](https://github.com/MindWorkAI/AI-Studio/pull/429), [PR #446](https://github.com/MindWorkAI/AI-Studio/pull/446), [PR #451](https://github.com/MindWorkAI/AI-Studio/pull/451), [PR #455](https://github.com/MindWorkAI/AI-Studio/pull/455), [PR #458](https://github.com/MindWorkAI/AI-Studio/pull/458), [PR #462](https://github.com/MindWorkAI/AI-Studio/pull/462), [PR #469](https://github.com/MindWorkAI/AI-Studio/pull/469), [PR #486](https://github.com/MindWorkAI/AI-Studio/pull/486))~~
|
||||||
|
- [x] ~~Add an I18N assistant to translate all AI Studio texts to a certain language & culture ([PR #422](https://github.com/MindWorkAI/AI-Studio/pull/422))~~
|
||||||
|
- [x] ~~Provide MindWork AI Studio in German ([PR #430](https://github.com/MindWorkAI/AI-Studio/pull/430), [PR #446](https://github.com/MindWorkAI/AI-Studio/pull/446), [PR #451](https://github.com/MindWorkAI/AI-Studio/pull/451), [PR #455](https://github.com/MindWorkAI/AI-Studio/pull/455), [PR #458](https://github.com/MindWorkAI/AI-Studio/pull/458), [PR #462](https://github.com/MindWorkAI/AI-Studio/pull/462), [PR #469](https://github.com/MindWorkAI/AI-Studio/pull/469), [PR #486](https://github.com/MindWorkAI/AI-Studio/pull/486))~~
|
||||||
|
- [x] ~~Add configuration plugins, which allow pre-defining some LLM providers in organizations ([PR #491](https://github.com/MindWorkAI/AI-Studio/pull/491), [PR #493](https://github.com/MindWorkAI/AI-Studio/pull/493), [PR #494](https://github.com/MindWorkAI/AI-Studio/pull/494), [PR #497](https://github.com/MindWorkAI/AI-Studio/pull/497))~~
|
||||||
|
- [ ] Add an app store for plugins, showcasing community-contributed plugins from public GitHub and GitLab repositories. This will enable AI Studio users to discover, install, and update plugins directly within the platform.
|
||||||
|
- [ ] Add assistant plugins
|
||||||
|
|
||||||
Other News:
|
Other News:
|
||||||
|
|
||||||
- October 2024: We've found the first two financial supporters. Huge thanks to `richard-stanton` and `peerschuett` for backing the project. Thanks for having the courage to be the first to support us.
|
- April 2025: We have two active financial supporters: Peer `peerschuett` and Dominic `donework`. Thank you very much for your support. MindWork AI reinvests these donations by passing them on to our AI Studio dependencies ([see here](https://github.com/orgs/MindWorkAI/sponsoring)). In the event that we receive large donations, we will first sign the app ([#56](https://github.com/MindWorkAI/Planning/issues/56)). In case we receive more donations, we will look for and pay staff to develop features for AI Studio.
|
||||||
|
|
||||||
- October 2024: The [German Aerospace Center (DLR)](https://en.wikipedia.org/wiki/German_Aerospace_Center) ([Website](https://www.dlr.de/en)) will use AI Studio at least within the scope of one project and will also contribute to its further development. This is great news.
|
- April 2025: The [German Aerospace Center (DLR)](https://en.wikipedia.org/wiki/German_Aerospace_Center) ([Website](https://www.dlr.de/en)) will use AI Studio at least within the scope of three projects and will also contribute to its further development. This is great news.
|
||||||
|
|
||||||
|
|
||||||
Features we have recently released:
|
Features we have recently released:
|
||||||
|
|
||||||
|
- v0.9.46: Released our plugin system, a German language plugin, early support for enterprise environments, and configuration plugins. Additionally, we added the Pandoc integration for future data processing and file generation.
|
||||||
|
- v0.9.45: Added chat templates to AI Studio, allowing you to create and use a library of system prompts for your chats.
|
||||||
|
- v0.9.44: Added PDF import to the text summarizer, translation, and legal check assistants, allowing you to import PDF files and use them as input for the assistants.
|
||||||
|
- v0.9.40: Added support for the `o4` models from OpenAI. Also, we added Alibaba Cloud & Hugging Face as LLM providers.
|
||||||
|
- v0.9.39: Added the plugin system as a preview feature.
|
||||||
|
- v0.9.31: Added Helmholtz & GWDG as LLM providers. This is a huge improvement for many researchers out there who can use these providers for free. We added DeepSeek as a provider as well.
|
||||||
|
- v0.9.29: Added agents to support the RAG process (selecting the best data sources & validating retrieved data as part of the augmentation process)
|
||||||
|
- v0.9.26+: Added RAG for external data sources using our [ERI interface](https://mindworkai.org/#eri---external-retrieval-interface) as a preview feature.
|
||||||
- v0.9.25: Added [xAI](https://x.ai/) as a new provider. xAI provides their Grok models for generating content.
|
- v0.9.25: Added [xAI](https://x.ai/) as a new provider. xAI provides their Grok models for generating content.
|
||||||
- v0.9.23: Added support for OpenAI `o` models (`o1`, `o1-mini`, `o3`, etc.); added also an [ERI](https://github.com/MindWorkAI/ERI) server coding assistant as a preview feature behind the RAG feature flag. Your own ERI server can be used to gain access to, e.g., your enterprise data from within AI Studio.
|
- v0.9.23: Added support for OpenAI `o` models (`o1`, `o1-mini`, `o3`, etc.); added also an [ERI](https://github.com/MindWorkAI/ERI) server coding assistant as a preview feature behind the RAG feature flag. Your own ERI server can be used to gain access to, e.g., your enterprise data from within AI Studio.
|
||||||
- v0.9.22: Added options for preview features; added embedding provider configuration for RAG (preview) and writer mode (experimental preview).
|
- v0.9.22: Added options for preview features; added embedding provider configuration for RAG (preview) and writer mode (experimental preview).
|
||||||
- v0.9.18: Added the new Anthropic Heiku model; added Groq and Google Gemini as provider options.
|
|
||||||
- v0.9.17: Added the new Anthropic model `claude-3-5-sonnet-20241022`.
|
|
||||||
- v0.9.16: Added workspace display options & improved the layout of the app window.
|
|
||||||
- v0.9.15: Added the bias-of-the-day assistant. Tells you about a cognitive bias every day.
|
|
||||||
- v0.9.13: You can use `ollama` providers secured with API keys.
|
|
||||||
|
|
||||||
## What is AI Studio?
|
## What is AI Studio?
|
||||||
|
|
||||||
@ -56,7 +70,19 @@ MindWork AI Studio is a free desktop app for macOS, Windows, and Linux. It provi
|
|||||||
|
|
||||||
**Key advantages:**
|
**Key advantages:**
|
||||||
- **Free of charge**: The app is free to use, both for personal and commercial purposes.
|
- **Free of charge**: The app is free to use, both for personal and commercial purposes.
|
||||||
- **Independence**: You are not tied to any single provider. Instead, you can choose the provider that best suits their needs. Right now, we support OpenAI (GPT4o, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, and self-hosted models using [llama.cpp](https://github.com/ggerganov/llama.cpp), [ollama](https://github.com/ollama/ollama), [LM Studio](https://lmstudio.ai/), [Groq](https://groq.com/), or [Fireworks](https://fireworks.ai/). For scientists and employees of research institutions, we also support [Helmholtz](https://helmholtz.cloud/services/?serviceID=d7d5c597-a2f6-4bd1-b71e-4d6499d98570) and [GWDG](https://gwdg.de/services/application-services/ai-services/) AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
|
- **Independence**: You are not tied to any single provider. Instead, you can choose the providers that best suit your needs. Right now, we support:
|
||||||
|
- [OpenAI](https://openai.com/) (GPT4o, GPT4.1, o1, o3, o4, etc.)
|
||||||
|
- [Mistral](https://mistral.ai/)
|
||||||
|
- [Anthropic](https://www.anthropic.com/) (Claude)
|
||||||
|
- [Google Gemini](https://gemini.google.com)
|
||||||
|
- [xAI](https://x.ai/) (Grok)
|
||||||
|
- [DeepSeek](https://www.deepseek.com/en)
|
||||||
|
- [Alibaba Cloud](https://www.alibabacloud.com) (Qwen)
|
||||||
|
- [Hugging Face](https://huggingface.co/) using their [inference providers](https://huggingface.co/docs/inference-providers/index) such as Cerebras, Nebius, Sambanova, Novita, Hyperbolic, Together AI, Fireworks, Hugging Face
|
||||||
|
- Self-hosted models using [llama.cpp](https://github.com/ggerganov/llama.cpp), [ollama](https://github.com/ollama/ollama), [LM Studio](https://lmstudio.ai/)
|
||||||
|
- [Groq](https://groq.com/)
|
||||||
|
- [Fireworks](https://fireworks.ai/)
|
||||||
|
- For scientists and employees of research institutions, we also support [Helmholtz](https://helmholtz.cloud/services/?serviceID=d7d5c597-a2f6-4bd1-b71e-4d6499d98570) and [GWDG](https://gwdg.de/services/application-services/ai-services/) AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
|
||||||
- **Assistants**: You just want to quickly translate a text? AI Studio has so-called assistants for such and other tasks. No prompting is necessary when working with these assistants.
|
- **Assistants**: You just want to quickly translate a text? AI Studio has so-called assistants for such and other tasks. No prompting is necessary when working with these assistants.
|
||||||
- **Unrestricted usage**: Unlike services like ChatGPT, which impose limits after intensive use, MindWork AI Studio offers unlimited usage through the providers API.
|
- **Unrestricted usage**: Unlike services like ChatGPT, which impose limits after intensive use, MindWork AI Studio offers unlimited usage through the providers API.
|
||||||
- **Cost-effective**: You only pay for what you use, which can be cheaper than monthly subscription services like ChatGPT Plus, especially if used infrequently. But beware, here be dragons: For extremely intensive usage, the API costs can be significantly higher. Unfortunately, providers currently do not offer a way to display current costs in the app. Therefore, check your account with the respective provider to see how your costs are developing. When available, use prepaid and set a cost limit.
|
- **Cost-effective**: You only pay for what you use, which can be cheaper than monthly subscription services like ChatGPT Plus, especially if used infrequently. But beware, here be dragons: For extremely intensive usage, the API costs can be significantly higher. Unfortunately, providers currently do not offer a way to display current costs in the app. Therefore, check your account with the respective provider to see how your costs are developing. When available, use prepaid and set a cost limit.
|
||||||
@ -97,6 +123,9 @@ Stay tuned for more updates and enhancements to make MindWork AI Studio even mor
|
|||||||
## Building
|
## Building
|
||||||
You want to know how to build MindWork AI Studio from source? [Check out the instructions here](documentation/Build.md).
|
You want to know how to build MindWork AI Studio from source? [Check out the instructions here](documentation/Build.md).
|
||||||
|
|
||||||
|
## Enterprise IT
|
||||||
|
Do you want to manage AI Studio centrally from your IT department? Yes, that’s possible. [Here’s how it works.](documentation/Enterprise%20IT.md)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
MindWork AI Studio is licensed under the `FSL-1.1-MIT` license (functional source license). Here’s a simple rundown of what that means for you:
|
MindWork AI Studio is licensed under the `FSL-1.1-MIT` license (functional source license). Here’s a simple rundown of what that means for you:
|
||||||
- **Permitted Use**: Feel free to use, copy, modify, and share the software for your own projects, educational purposes, research, or even in professional services. The key is to use it in a way that doesn't compete with our offerings.
|
- **Permitted Use**: Feel free to use, copy, modify, and share the software for your own projects, educational purposes, research, or even in professional services. The key is to use it in a way that doesn't compete with our offerings.
|
||||||
|
17
app/.run/Collect I18N content.run.xml
Normal file
17
app/.run/Collect I18N content.run.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Collect I18N content" type="ShConfigurationType">
|
||||||
|
<option name="SCRIPT_TEXT" value="dotnet run collect-i18n" />
|
||||||
|
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||||
|
<option name="SCRIPT_PATH" value="$PROJECT_DIR$/Build" />
|
||||||
|
<option name="SCRIPT_OPTIONS" value="" />
|
||||||
|
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||||
|
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$/Build" />
|
||||||
|
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||||
|
<option name="INTERPRETER_PATH" value="/opt/homebrew/bin/nu" />
|
||||||
|
<option name="INTERPRETER_OPTIONS" value="" />
|
||||||
|
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||||
|
<option name="EXECUTE_SCRIPT_FILE" value="false" />
|
||||||
|
<envs />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
@ -1,5 +1,5 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Tauri Dev" type="ShConfigurationType">
|
<configuration default="false" name="[1] Start Tauri" type="ShConfigurationType">
|
||||||
<option name="SCRIPT_TEXT" value="cargo tauri dev --no-watch" />
|
<option name="SCRIPT_TEXT" value="cargo tauri dev --no-watch" />
|
||||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||||
<option name="SCRIPT_PATH" value="" />
|
<option name="SCRIPT_PATH" value="" />
|
||||||
@ -12,6 +12,8 @@
|
|||||||
<option name="EXECUTE_IN_TERMINAL" value="false" />
|
<option name="EXECUTE_IN_TERMINAL" value="false" />
|
||||||
<option name="EXECUTE_SCRIPT_FILE" value="false" />
|
<option name="EXECUTE_SCRIPT_FILE" value="false" />
|
||||||
<envs />
|
<envs />
|
||||||
<method v="2" />
|
<method v="2">
|
||||||
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Collect I18N content" run_configuration_type="ShConfigurationType" />
|
||||||
|
</method>
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
27
app/.run/[2] Start .NET Server.run.xml
Normal file
27
app/.run/[2] Start .NET Server.run.xml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="[2] Start .NET Server" type="DotNetProject" factoryName=".NET Project">
|
||||||
|
<option name="EXE_PATH" value="$PROJECT_DIR$/MindWork AI Studio/bin/Debug/net9.0/osx-arm64/mindworkAIStudio" />
|
||||||
|
<option name="PROGRAM_PARAMETERS" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/MindWork AI Studio" />
|
||||||
|
<option name="PASS_PARENT_ENVS" value="1" />
|
||||||
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
|
<option name="ENV_FILE_PATHS" value="" />
|
||||||
|
<option name="REDIRECT_INPUT_PATH" value="" />
|
||||||
|
<option name="PTY_MODE" value="Auto" />
|
||||||
|
<option name="USE_MONO" value="0" />
|
||||||
|
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||||
|
<option name="AUTO_ATTACH_CHILDREN" value="0" />
|
||||||
|
<option name="MIXED_MODE_DEBUG" value="0" />
|
||||||
|
<option name="RUNTIME_TYPE" value="coreclr" />
|
||||||
|
<option name="PROJECT_PATH" value="$PROJECT_DIR$/MindWork AI Studio/MindWork AI Studio.csproj" />
|
||||||
|
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||||
|
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||||
|
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||||
|
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||||
|
<option name="PROJECT_TFM" value="net9.0" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Build" />
|
||||||
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Collect I18N content" run_configuration_type="ShConfigurationType" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
21
app/Build/Build Script.csproj
Normal file
21
app/Build/Build Script.csproj
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<RootNamespace>Build</RootNamespace>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<AssemblyName>build</AssemblyName>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Cocona" Version="2.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\SharedTools\SharedTools.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
3
app/Build/Commands/AppVersion.cs
Normal file
3
app/Build/Commands/AppVersion.cs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
namespace Build.Commands;
|
||||||
|
|
||||||
|
public record AppVersion(string VersionText, int Major, int Minor, int Patch);
|
26
app/Build/Commands/CheckRidsCommand.cs
Normal file
26
app/Build/Commands/CheckRidsCommand.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// ReSharper disable ClassNeverInstantiated.Global
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
namespace Build.Commands;
|
||||||
|
|
||||||
|
public sealed class CheckRidsCommand
|
||||||
|
{
|
||||||
|
[Command("check-rids", Description = "Check the RIDs for the current OS")]
|
||||||
|
public void GetRids()
|
||||||
|
{
|
||||||
|
if(!Environment.IsWorkingDirectoryValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var rids = Environment.GetRidsForCurrentOS();
|
||||||
|
Console.WriteLine("The following RIDs are available for the current OS:");
|
||||||
|
foreach (var rid in rids)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"- {rid}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine("The RID for the current OS and CPU is:");
|
||||||
|
var currentRid = Environment.GetCurrentRid();
|
||||||
|
Console.WriteLine($"- {currentRid}");
|
||||||
|
}
|
||||||
|
}
|
281
app/Build/Commands/CollectI18NKeysCommand.cs
Normal file
281
app/Build/Commands/CollectI18NKeysCommand.cs
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
using SharedTools;
|
||||||
|
|
||||||
|
// ReSharper disable ClassNeverInstantiated.Global
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
if(!Environment.IsWorkingDirectoryValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Console.WriteLine("=========================");
|
||||||
|
Console.Write("- Collecting I18N keys ...");
|
||||||
|
|
||||||
|
var cwd = Environment.GetAIStudioDirectory();
|
||||||
|
var binPath = Path.Join(cwd, "bin");
|
||||||
|
var objPath = Path.Join(cwd, "obj");
|
||||||
|
var wwwrootPath = Path.Join(cwd, "wwwroot");
|
||||||
|
var allFiles = Directory.EnumerateFiles(cwd, "*", SearchOption.AllDirectories);
|
||||||
|
var counter = 0;
|
||||||
|
|
||||||
|
var allI18NContent = new Dictionary<string, string>();
|
||||||
|
foreach (var filePath in allFiles)
|
||||||
|
{
|
||||||
|
counter++;
|
||||||
|
if(filePath.StartsWith(binPath, StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(filePath.StartsWith(objPath, StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(filePath.StartsWith(wwwrootPath, StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var content = await File.ReadAllTextAsync(filePath, Encoding.UTF8);
|
||||||
|
var matches = this.FindAllTextTags(content);
|
||||||
|
if (matches.Count == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var ns = this.DetermineNamespace(filePath);
|
||||||
|
var fileInfo = new FileInfo(filePath);
|
||||||
|
var name = fileInfo.Name.Replace(fileInfo.Extension, string.Empty).Replace(".razor", string.Empty);
|
||||||
|
var langNamespace = $"{ns}.{name}".ToUpperInvariant();
|
||||||
|
foreach (var match in matches)
|
||||||
|
{
|
||||||
|
// The key in the format A.B.C.D.T{hash}:
|
||||||
|
var key = $"UI_TEXT_CONTENT.{langNamespace}.T{match.ToFNV32()}";
|
||||||
|
allI18NContent.TryAdd(key, match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($" {counter:###,###} files processed, {allI18NContent.Count:###,###} keys found.");
|
||||||
|
|
||||||
|
Console.Write("- Creating Lua code ...");
|
||||||
|
var luaCode = this.ExportToLuaAssignments(allI18NContent);
|
||||||
|
|
||||||
|
// Build the path, where we want to store the Lua code:
|
||||||
|
var luaPath = Path.Join(cwd, "Assistants", "I18N", "allTexts.lua");
|
||||||
|
|
||||||
|
// Store the Lua code:
|
||||||
|
await File.WriteAllTextAsync(luaPath, luaCode, Encoding.UTF8);
|
||||||
|
|
||||||
|
Console.WriteLine(" done.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ExportToLuaAssignments(Dictionary<string, string> keyValuePairs)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
// Add the mandatory plugin metadata:
|
||||||
|
sb.AppendLine(
|
||||||
|
"""
|
||||||
|
-- The ID for this plugin:
|
||||||
|
ID = "77c2688a-a68f-45cc-820e-fa8f3038a146"
|
||||||
|
|
||||||
|
-- The icon for the plugin:
|
||||||
|
ICON_SVG = ""
|
||||||
|
|
||||||
|
-- The name of the plugin:
|
||||||
|
NAME = "Collected I18N keys"
|
||||||
|
|
||||||
|
-- The description of the plugin:
|
||||||
|
DESCRIPTION = "This plugin is not meant to be used directly. Its a collection of all I18N keys found in the project."
|
||||||
|
|
||||||
|
-- The version of the plugin:
|
||||||
|
VERSION = "1.0.0"
|
||||||
|
|
||||||
|
-- The type of the plugin:
|
||||||
|
TYPE = "LANGUAGE"
|
||||||
|
|
||||||
|
-- The authors of the plugin:
|
||||||
|
AUTHORS = {"MindWork AI Community"}
|
||||||
|
|
||||||
|
-- The support contact for the plugin:
|
||||||
|
SUPPORT_CONTACT = "MindWork AI Community"
|
||||||
|
|
||||||
|
-- The source URL for the plugin:
|
||||||
|
SOURCE_URL = "https://github.com/MindWorkAI/AI-Studio"
|
||||||
|
|
||||||
|
-- The categories for the plugin:
|
||||||
|
CATEGORIES = { "CORE" }
|
||||||
|
|
||||||
|
-- The target groups for the plugin:
|
||||||
|
TARGET_GROUPS = { "EVERYONE" }
|
||||||
|
|
||||||
|
-- The flag for whether the plugin is maintained:
|
||||||
|
IS_MAINTAINED = true
|
||||||
|
|
||||||
|
-- When the plugin is deprecated, this message will be shown to users:
|
||||||
|
DEPRECATION_MESSAGE = ""
|
||||||
|
|
||||||
|
-- The IETF BCP 47 tag for the language. It's the ISO 639 language
|
||||||
|
-- code followed by the ISO 3166-1 country code:
|
||||||
|
IETF_TAG = "en-US"
|
||||||
|
|
||||||
|
-- The language name in the user's language:
|
||||||
|
LANG_NAME = "English (United States)"
|
||||||
|
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add the UI_TEXT_CONTENT table:
|
||||||
|
LuaTable.Create(ref sb, "UI_TEXT_CONTENT", keyValuePairs);
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> FindAllTextTags(ReadOnlySpan<char> fileContent)
|
||||||
|
{
|
||||||
|
(int Index, int Len) FindNextStart(ReadOnlySpan<char> content)
|
||||||
|
{
|
||||||
|
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>();
|
||||||
|
var startIdx = FindNextStart(fileContent);
|
||||||
|
var content = fileContent;
|
||||||
|
while (startIdx.Index > -1)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// In some cases, after the initial " there follow more " characters.
|
||||||
|
// We need to skip them:
|
||||||
|
//
|
||||||
|
content = content[(startIdx.Index + startIdx.Len)..];
|
||||||
|
while(content[0] == '"')
|
||||||
|
content = content[1..];
|
||||||
|
|
||||||
|
var endIdx = content.IndexOf(END_TAG);
|
||||||
|
if (endIdx == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var match = content[..endIdx];
|
||||||
|
while (match[^1] == '"')
|
||||||
|
match = match[..^1];
|
||||||
|
|
||||||
|
matches.Add(match.ToString());
|
||||||
|
startIdx = FindNextStart(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? DetermineNamespace(string filePath)
|
||||||
|
{
|
||||||
|
// Is it a C# file? Then we can read the namespace from it:
|
||||||
|
if (filePath.EndsWith(".cs", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return this.ReadNamespaceFromCSharp(filePath);
|
||||||
|
|
||||||
|
// Is it a Razor file? Then, it depends:
|
||||||
|
if (filePath.EndsWith(".razor", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
// Check if the file contains a namespace declaration:
|
||||||
|
var blazorNamespace = this.ReadNamespaceFromRazor(filePath);
|
||||||
|
if (blazorNamespace != null)
|
||||||
|
return blazorNamespace;
|
||||||
|
|
||||||
|
// Alright, no namespace declaration. Let's check the corresponding C# file:
|
||||||
|
var csFilePath = $"{filePath}.cs";
|
||||||
|
if (File.Exists(csFilePath))
|
||||||
|
{
|
||||||
|
var csNamespace = this.ReadNamespaceFromCSharp(csFilePath);
|
||||||
|
if (csNamespace != null)
|
||||||
|
return csNamespace;
|
||||||
|
|
||||||
|
Console.WriteLine($"- Error: Neither the blazor file '{filePath}' nor the corresponding C# file '{csFilePath}' contain a namespace declaration.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"- Error: The blazor file '{filePath}' does not contain a namespace declaration and the corresponding C# file '{csFilePath}' does not exist.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not a C# or Razor file. We can't determine the namespace:
|
||||||
|
Console.WriteLine($"- Error: The file '{filePath}' is neither a C# nor a Razor file. We can't determine the namespace.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? ReadNamespaceFromCSharp(string filePath)
|
||||||
|
{
|
||||||
|
var content = File.ReadAllText(filePath, Encoding.UTF8);
|
||||||
|
var matches = CSharpNamespaceRegex().Matches(content);
|
||||||
|
|
||||||
|
if (matches.Count == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (matches.Count > 1)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"The file '{filePath}' contains multiple namespaces. This scenario is not supported.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var match = matches[0];
|
||||||
|
return match.Groups[1].Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? ReadNamespaceFromRazor(string filePath)
|
||||||
|
{
|
||||||
|
var content = File.ReadAllText(filePath, Encoding.UTF8);
|
||||||
|
var matches = BlazorNamespaceRegex().Matches(content);
|
||||||
|
|
||||||
|
if (matches.Count == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (matches.Count > 1)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"The file '{filePath}' contains multiple namespaces. This scenario is not supported.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var match = matches[0];
|
||||||
|
return match.Groups[1].Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[GeneratedRegex("""@namespace\s+([a-zA-Z0-9_.]+)""")]
|
||||||
|
private static partial Regex BlazorNamespaceRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex("""namespace\s+([a-zA-Z0-9_.]+)""")]
|
||||||
|
private static partial Regex CSharpNamespaceRegex();
|
||||||
|
}
|
3
app/Build/Commands/Library.cs
Normal file
3
app/Build/Commands/Library.cs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
namespace Build.Commands;
|
||||||
|
|
||||||
|
public record Library(string Path, string Filename);
|
107
app/Build/Commands/Pdfium.cs
Normal file
107
app/Build/Commands/Pdfium.cs
Normal file
@ -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()} from {pdfiumUrl}");
|
||||||
|
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.IsNullOrWhiteSpace(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.tgz",
|
||||||
|
RID.WIN_X64 => $"{baseUrl}win-x64.tgz",
|
||||||
|
|
||||||
|
_ => string.Empty,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
10
app/Build/Commands/PrepareAction.cs
Normal file
10
app/Build/Commands/PrepareAction.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace Build.Commands;
|
||||||
|
|
||||||
|
public enum PrepareAction
|
||||||
|
{
|
||||||
|
NONE,
|
||||||
|
|
||||||
|
PATCH,
|
||||||
|
MINOR,
|
||||||
|
MAJOR,
|
||||||
|
}
|
660
app/Build/Commands/UpdateMetadataCommands.cs
Normal file
660
app/Build/Commands/UpdateMetadataCommands.cs
Normal file
@ -0,0 +1,660 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
using SharedTools;
|
||||||
|
|
||||||
|
namespace Build.Commands;
|
||||||
|
|
||||||
|
// ReSharper disable MemberCanBePrivate.Global
|
||||||
|
// ReSharper disable ClassNeverInstantiated.Global
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
|
||||||
|
public sealed partial class UpdateMetadataCommands
|
||||||
|
{
|
||||||
|
[Command("release", Description = "Prepare & build the next release")]
|
||||||
|
public async Task Release(PrepareAction action)
|
||||||
|
{
|
||||||
|
if(!Environment.IsWorkingDirectoryValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Prepare the metadata for the next release:
|
||||||
|
await this.PerformPrepare(action, true);
|
||||||
|
|
||||||
|
// Build once to allow the Rust compiler to read the changed metadata
|
||||||
|
// and to update all .NET artifacts:
|
||||||
|
await this.Build();
|
||||||
|
|
||||||
|
// Now, we update the web assets (which may were updated by the first build):
|
||||||
|
new UpdateWebAssetsCommand().UpdateWebAssets();
|
||||||
|
|
||||||
|
// Collect the I18N keys from the source code. This step yields a I18N file
|
||||||
|
// that must be part of the final release:
|
||||||
|
await new CollectI18NKeysCommand().CollectI18NKeys();
|
||||||
|
|
||||||
|
// Build the final release, where Rust knows the updated metadata, the .NET
|
||||||
|
// artifacts are already in place, and .NET knows the updated web assets, etc.:
|
||||||
|
await this.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command("update-versions", Description = "The command will update the package versions in the metadata file")]
|
||||||
|
public async Task UpdateVersions()
|
||||||
|
{
|
||||||
|
if(!Environment.IsWorkingDirectoryValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Console.WriteLine("==============================");
|
||||||
|
Console.WriteLine("- Update the main package versions ...");
|
||||||
|
|
||||||
|
await this.UpdateDotnetVersion();
|
||||||
|
await this.UpdateRustVersion();
|
||||||
|
await this.UpdateMudBlazorVersion();
|
||||||
|
await this.UpdateTauriVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command("prepare", Description = "Prepare the metadata for the next release")]
|
||||||
|
public async Task Prepare(PrepareAction action)
|
||||||
|
{
|
||||||
|
if(!Environment.IsWorkingDirectoryValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Console.WriteLine("==============================");
|
||||||
|
Console.Write("- Are you trying to prepare a new release? (y/n) ");
|
||||||
|
var userAnswer = Console.ReadLine();
|
||||||
|
if (userAnswer?.ToLowerInvariant() == "y")
|
||||||
|
{
|
||||||
|
Console.WriteLine("- Please use the 'release' command instead");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.PerformPrepare(action, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PerformPrepare(PrepareAction action, bool internalCall)
|
||||||
|
{
|
||||||
|
if(internalCall)
|
||||||
|
Console.WriteLine("==============================");
|
||||||
|
|
||||||
|
Console.WriteLine("- Prepare the metadata for the next release ...");
|
||||||
|
|
||||||
|
var appVersion = await this.UpdateAppVersion(action);
|
||||||
|
if (!string.IsNullOrWhiteSpace(appVersion.VersionText))
|
||||||
|
{
|
||||||
|
var buildNumber = await this.IncreaseBuildNumber();
|
||||||
|
var buildTime = await this.UpdateBuildTime();
|
||||||
|
await this.UpdateChangelog(buildNumber, appVersion.VersionText, buildTime);
|
||||||
|
await this.CreateNextChangelog(buildNumber, appVersion);
|
||||||
|
await this.UpdateDotnetVersion();
|
||||||
|
await this.UpdateRustVersion();
|
||||||
|
await this.UpdateMudBlazorVersion();
|
||||||
|
await this.UpdateTauriVersion();
|
||||||
|
await this.UpdateProjectCommitHash();
|
||||||
|
await this.UpdateLicenceYear(Path.GetFullPath(Path.Combine(Environment.GetAIStudioDirectory(), "..", "..", "LICENSE.md")));
|
||||||
|
await this.UpdateLicenceYear(Path.GetFullPath(Path.Combine(Environment.GetAIStudioDirectory(), "Pages", "About.razor.cs")));
|
||||||
|
Console.WriteLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command("build", Description = "Build MindWork AI Studio")]
|
||||||
|
public async Task Build()
|
||||||
|
{
|
||||||
|
if(!Environment.IsWorkingDirectoryValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Build the .NET project:
|
||||||
|
//
|
||||||
|
var pathApp = Environment.GetAIStudioDirectory();
|
||||||
|
var rid = Environment.GetCurrentRid();
|
||||||
|
|
||||||
|
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"))
|
||||||
|
{
|
||||||
|
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 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("- Start building the Rust runtime ...");
|
||||||
|
|
||||||
|
var pathRuntime = Environment.GetRustRuntimeDirectory();
|
||||||
|
var rustBuildOutput = await this.ReadCommandOutput(pathRuntime, "cargo", "tauri build --bundles none", true);
|
||||||
|
var rustBuildOutputLines = rustBuildOutput.Split([global::System.Environment.NewLine], StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
var foundRustIssue = false;
|
||||||
|
foreach (var buildOutputLine in rustBuildOutputLines)
|
||||||
|
{
|
||||||
|
if(buildOutputLine.Contains("error", StringComparison.OrdinalIgnoreCase) || buildOutputLine.Contains("warning"))
|
||||||
|
{
|
||||||
|
if(!foundRustIssue)
|
||||||
|
{
|
||||||
|
foundRustIssue = true;
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine("- Build has issues:");
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.Write(" - ");
|
||||||
|
Console.WriteLine(buildOutputLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(foundRustIssue)
|
||||||
|
Console.WriteLine();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine("- Compilation completed successfully.");
|
||||||
|
Console.WriteLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CreateNextChangelog(int currentBuildNumber, AppVersion currentAppVersion)
|
||||||
|
{
|
||||||
|
Console.Write("- Create the next changelog ...");
|
||||||
|
var pathChangelogs = Path.Combine(Environment.GetAIStudioDirectory(), "wwwroot", "changelog");
|
||||||
|
var nextBuildNumber = currentBuildNumber + 1;
|
||||||
|
|
||||||
|
//
|
||||||
|
// We assume that most of the time, there will be patch releases:
|
||||||
|
//
|
||||||
|
var nextMajor = currentAppVersion.Major;
|
||||||
|
var nextMinor = currentAppVersion.Minor;
|
||||||
|
var nextPatch = currentAppVersion.Patch + 1;
|
||||||
|
|
||||||
|
var nextAppVersion = $"{nextMajor}.{nextMinor}.{nextPatch}";
|
||||||
|
var nextChangelogFilename = $"v{nextAppVersion}.md";
|
||||||
|
var nextChangelogFilePath = Path.Combine(pathChangelogs, nextChangelogFilename);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Regarding the next build time: We assume that the next release will take place in one week from now.
|
||||||
|
// Thus, we check how many days this month has left. In the end, we want to predict the year and month
|
||||||
|
// for the next build. Day, hour, minute and second are all set to x.
|
||||||
|
//
|
||||||
|
var nextBuildMonth = (DateTime.Today + TimeSpan.FromDays(7)).Month;
|
||||||
|
var nextBuildYear = (DateTime.Today + TimeSpan.FromDays(7)).Year;
|
||||||
|
var nextBuildTimeString = $"{nextBuildYear}-{nextBuildMonth:00}-xx xx:xx UTC";
|
||||||
|
|
||||||
|
var changelogHeader = $"""
|
||||||
|
# v{nextAppVersion}, build {nextBuildNumber} ({nextBuildTimeString})
|
||||||
|
|
||||||
|
""";
|
||||||
|
|
||||||
|
if(!File.Exists(nextChangelogFilePath))
|
||||||
|
{
|
||||||
|
await File.WriteAllTextAsync(nextChangelogFilePath, changelogHeader, Environment.UTF8_NO_BOM);
|
||||||
|
Console.WriteLine($" done. Changelog '{nextChangelogFilename}' created.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine(" failed.");
|
||||||
|
Console.WriteLine("- Error: The changelog file already exists.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateChangelog(int buildNumber, string appVersion, string buildTime)
|
||||||
|
{
|
||||||
|
Console.Write("- Updating the in-app changelog list ...");
|
||||||
|
var pathChangelogs = Path.Combine(Environment.GetAIStudioDirectory(), "wwwroot", "changelog");
|
||||||
|
var expectedLogFilename = $"v{appVersion}.md";
|
||||||
|
var expectedLogFilePath = Path.Combine(pathChangelogs, expectedLogFilename);
|
||||||
|
|
||||||
|
if(!File.Exists(expectedLogFilePath))
|
||||||
|
{
|
||||||
|
Console.WriteLine(" failed.");
|
||||||
|
Console.WriteLine($"- Error: The changelog file '{expectedLogFilename}' does not exist.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right now, the build time is formatted as "yyyy-MM-dd HH:mm:ss UTC", but must remove the seconds:
|
||||||
|
buildTime = buildTime[..^7] + " UTC";
|
||||||
|
|
||||||
|
const string CODE_START =
|
||||||
|
"""
|
||||||
|
LOGS =
|
||||||
|
[
|
||||||
|
""";
|
||||||
|
|
||||||
|
var changelogCodePath = Path.Join(Environment.GetAIStudioDirectory(), "Components", "Changelog.Logs.cs");
|
||||||
|
var changelogCode = await File.ReadAllTextAsync(changelogCodePath, Encoding.UTF8);
|
||||||
|
var updatedCode =
|
||||||
|
$"""
|
||||||
|
{CODE_START}
|
||||||
|
new ({buildNumber}, "v{appVersion}, build {buildNumber} ({buildTime})", "{expectedLogFilename}"),
|
||||||
|
""";
|
||||||
|
|
||||||
|
changelogCode = changelogCode.Replace(CODE_START, updatedCode);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
const int ARCHITECTURE_INDEX = 9;
|
||||||
|
|
||||||
|
var pathMetadata = Environment.GetMetadataPath();
|
||||||
|
var lines = await File.ReadAllLinesAsync(pathMetadata, Encoding.UTF8);
|
||||||
|
Console.Write($"- Updating architecture to {rid.ToUserFriendlyName()} ...");
|
||||||
|
lines[ARCHITECTURE_INDEX] = rid.AsMicrosoftRid();
|
||||||
|
|
||||||
|
await File.WriteAllLinesAsync(pathMetadata, lines, Environment.UTF8_NO_BOM);
|
||||||
|
Console.WriteLine(" done.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateProjectCommitHash()
|
||||||
|
{
|
||||||
|
const int COMMIT_HASH_INDEX = 8;
|
||||||
|
|
||||||
|
var pathMetadata = Environment.GetMetadataPath();
|
||||||
|
var lines = await File.ReadAllLinesAsync(pathMetadata, Encoding.UTF8);
|
||||||
|
var currentCommitHash = lines[COMMIT_HASH_INDEX].Trim();
|
||||||
|
var headCommitHash = await this.ReadCommandOutput(Environment.GetAIStudioDirectory(), "git", "rev-parse HEAD");
|
||||||
|
var first10Chars = headCommitHash[..11];
|
||||||
|
var updatedCommitHash = $"{first10Chars}, release";
|
||||||
|
|
||||||
|
Console.WriteLine($"- Updating commit hash from '{currentCommitHash}' to '{updatedCommitHash}'.");
|
||||||
|
lines[COMMIT_HASH_INDEX] = updatedCommitHash;
|
||||||
|
|
||||||
|
await File.WriteAllLinesAsync(pathMetadata, lines, Environment.UTF8_NO_BOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<AppVersion> UpdateAppVersion(PrepareAction action)
|
||||||
|
{
|
||||||
|
const int APP_VERSION_INDEX = 0;
|
||||||
|
|
||||||
|
if (action == PrepareAction.NONE)
|
||||||
|
{
|
||||||
|
Console.WriteLine("- No action specified. Skipping app version update.");
|
||||||
|
return new(string.Empty, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var pathMetadata = Environment.GetMetadataPath();
|
||||||
|
var lines = await File.ReadAllLinesAsync(pathMetadata, Encoding.UTF8);
|
||||||
|
var currentAppVersionLine = lines[APP_VERSION_INDEX].Trim();
|
||||||
|
var currentAppVersion = AppVersionRegex().Match(currentAppVersionLine);
|
||||||
|
var currentPatch = int.Parse(currentAppVersion.Groups["patch"].Value);
|
||||||
|
var currentMinor = int.Parse(currentAppVersion.Groups["minor"].Value);
|
||||||
|
var currentMajor = int.Parse(currentAppVersion.Groups["major"].Value);
|
||||||
|
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case PrepareAction.PATCH:
|
||||||
|
currentPatch++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PrepareAction.MINOR:
|
||||||
|
currentPatch = 0;
|
||||||
|
currentMinor++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PrepareAction.MAJOR:
|
||||||
|
currentPatch = 0;
|
||||||
|
currentMinor = 0;
|
||||||
|
currentMajor++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var updatedAppVersion = $"{currentMajor}.{currentMinor}.{currentPatch}";
|
||||||
|
Console.WriteLine($"- Updating app version from '{currentAppVersionLine}' to '{updatedAppVersion}'.");
|
||||||
|
|
||||||
|
lines[APP_VERSION_INDEX] = updatedAppVersion;
|
||||||
|
await File.WriteAllLinesAsync(pathMetadata, lines, Environment.UTF8_NO_BOM);
|
||||||
|
|
||||||
|
return new(updatedAppVersion, currentMajor, currentMinor, currentPatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateLicenceYear(string licenceFilePath)
|
||||||
|
{
|
||||||
|
var currentYear = DateTime.UtcNow.Year.ToString();
|
||||||
|
var lines = await File.ReadAllLinesAsync(licenceFilePath, Encoding.UTF8);
|
||||||
|
|
||||||
|
var found = false;
|
||||||
|
var copyrightYear = string.Empty;
|
||||||
|
var updatedLines = new List<string>(lines.Length);
|
||||||
|
foreach (var line in lines)
|
||||||
|
{
|
||||||
|
var match = FindCopyrightRegex().Match(line);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
copyrightYear = match.Groups["year"].Value;
|
||||||
|
|
||||||
|
if(!found && copyrightYear != currentYear)
|
||||||
|
Console.WriteLine($"- Updating the licence's year in '{Path.GetFileName(licenceFilePath)}' from '{copyrightYear}' to '{currentYear}'.");
|
||||||
|
|
||||||
|
updatedLines.Add(ReplaceCopyrightYearRegex().Replace(line, currentYear));
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
updatedLines.Add(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
await File.WriteAllLinesAsync(licenceFilePath, updatedLines, Environment.UTF8_NO_BOM);
|
||||||
|
if (!found)
|
||||||
|
Console.WriteLine($"- Error: No copyright year found in '{Path.GetFileName(licenceFilePath)}'.");
|
||||||
|
else if (copyrightYear == currentYear)
|
||||||
|
Console.WriteLine($"- The copyright year in '{Path.GetFileName(licenceFilePath)}' is already up to date.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateTauriVersion()
|
||||||
|
{
|
||||||
|
const int TAURI_VERSION_INDEX = 7;
|
||||||
|
|
||||||
|
var pathMetadata = Environment.GetMetadataPath();
|
||||||
|
var lines = await File.ReadAllLinesAsync(pathMetadata, Encoding.UTF8);
|
||||||
|
var currentTauriVersion = lines[TAURI_VERSION_INDEX].Trim();
|
||||||
|
|
||||||
|
var matches = await this.DetermineVersion("Tauri", Environment.GetRustRuntimeDirectory(), TauriVersionRegex(), "cargo", "tree --depth 1");
|
||||||
|
if (matches.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var updatedTauriVersion = matches[0].Groups["version"].Value;
|
||||||
|
if(currentTauriVersion == updatedTauriVersion)
|
||||||
|
{
|
||||||
|
Console.WriteLine("- The Tauri version is already up to date.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"- Updated Tauri version from {currentTauriVersion} to {updatedTauriVersion}.");
|
||||||
|
lines[TAURI_VERSION_INDEX] = updatedTauriVersion;
|
||||||
|
|
||||||
|
await File.WriteAllLinesAsync(pathMetadata, lines, Environment.UTF8_NO_BOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateMudBlazorVersion()
|
||||||
|
{
|
||||||
|
const int MUD_BLAZOR_VERSION_INDEX = 6;
|
||||||
|
|
||||||
|
var pathMetadata = Environment.GetMetadataPath();
|
||||||
|
var lines = await File.ReadAllLinesAsync(pathMetadata, Encoding.UTF8);
|
||||||
|
var currentMudBlazorVersion = lines[MUD_BLAZOR_VERSION_INDEX].Trim();
|
||||||
|
|
||||||
|
var matches = await this.DetermineVersion("MudBlazor", Environment.GetAIStudioDirectory(), MudBlazorVersionRegex(), "dotnet", "list package");
|
||||||
|
if (matches.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var updatedMudBlazorVersion = matches[0].Groups["version"].Value;
|
||||||
|
if(currentMudBlazorVersion == updatedMudBlazorVersion)
|
||||||
|
{
|
||||||
|
Console.WriteLine("- The MudBlazor version is already up to date.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"- Updated MudBlazor version from {currentMudBlazorVersion} to {updatedMudBlazorVersion}.");
|
||||||
|
lines[MUD_BLAZOR_VERSION_INDEX] = updatedMudBlazorVersion;
|
||||||
|
|
||||||
|
await File.WriteAllLinesAsync(pathMetadata, lines, Environment.UTF8_NO_BOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateRustVersion()
|
||||||
|
{
|
||||||
|
const int RUST_VERSION_INDEX = 5;
|
||||||
|
|
||||||
|
var pathMetadata = Environment.GetMetadataPath();
|
||||||
|
var lines = await File.ReadAllLinesAsync(pathMetadata, Encoding.UTF8);
|
||||||
|
var currentRustVersion = lines[RUST_VERSION_INDEX].Trim();
|
||||||
|
var matches = await this.DetermineVersion("Rust", Environment.GetRustRuntimeDirectory(), RustVersionRegex(), "rustc", "-Vv");
|
||||||
|
if (matches.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var updatedRustVersion = matches[0].Groups["version"].Value + " (commit " + matches[0].Groups["commit"].Value + ")";
|
||||||
|
if(currentRustVersion == updatedRustVersion)
|
||||||
|
{
|
||||||
|
Console.WriteLine("- Rust version is already up to date.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"- Updated Rust version from {currentRustVersion} to {updatedRustVersion}.");
|
||||||
|
lines[RUST_VERSION_INDEX] = updatedRustVersion;
|
||||||
|
|
||||||
|
await File.WriteAllLinesAsync(pathMetadata, lines, Environment.UTF8_NO_BOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateDotnetVersion()
|
||||||
|
{
|
||||||
|
const int DOTNET_VERSION_INDEX = 4;
|
||||||
|
const int DOTNET_SDK_VERSION_INDEX = 3;
|
||||||
|
|
||||||
|
var pathMetadata = Environment.GetMetadataPath();
|
||||||
|
var lines = await File.ReadAllLinesAsync(pathMetadata, Encoding.UTF8);
|
||||||
|
var currentDotnetVersion = lines[DOTNET_VERSION_INDEX].Trim();
|
||||||
|
var currentDotnetSdkVersion = lines[DOTNET_SDK_VERSION_INDEX].Trim();
|
||||||
|
|
||||||
|
var matches = await this.DetermineVersion(".NET", Environment.GetAIStudioDirectory(), DotnetVersionRegex(), "dotnet", "--info");
|
||||||
|
if (matches.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var updatedDotnetVersion = matches[0].Groups["hostVersion"].Value + " (commit " + matches[0].Groups["hostCommit"].Value + ")";
|
||||||
|
var updatedDotnetSdkVersion = matches[0].Groups["sdkVersion"].Value + " (commit " + matches[0].Groups["sdkCommit"].Value + ")";
|
||||||
|
if(currentDotnetVersion == updatedDotnetVersion && currentDotnetSdkVersion == updatedDotnetSdkVersion)
|
||||||
|
{
|
||||||
|
Console.WriteLine("- .NET version is already up to date.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"- Updated .NET SDK version from {currentDotnetSdkVersion} to {updatedDotnetSdkVersion}.");
|
||||||
|
Console.WriteLine($"- Updated .NET version from {currentDotnetVersion} to {updatedDotnetVersion}.");
|
||||||
|
|
||||||
|
lines[DOTNET_VERSION_INDEX] = updatedDotnetVersion;
|
||||||
|
lines[DOTNET_SDK_VERSION_INDEX] = updatedDotnetSdkVersion;
|
||||||
|
|
||||||
|
await File.WriteAllLinesAsync(pathMetadata, lines, Environment.UTF8_NO_BOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IList<Match>> DetermineVersion(string name, string workingDirectory, Regex regex, string program, string command)
|
||||||
|
{
|
||||||
|
var processInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
WorkingDirectory = workingDirectory,
|
||||||
|
FileName = program,
|
||||||
|
Arguments = command,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true
|
||||||
|
};
|
||||||
|
|
||||||
|
using var process = new Process();
|
||||||
|
process.StartInfo = processInfo;
|
||||||
|
process.Start();
|
||||||
|
|
||||||
|
var output = await process.StandardOutput.ReadToEndAsync();
|
||||||
|
await process.WaitForExitAsync();
|
||||||
|
|
||||||
|
var matches = regex.Matches(output);
|
||||||
|
if (matches.Count == 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"- Error: Was not able to determine the {name} version.");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> ReadCommandOutput(string workingDirectory, string program, string command, bool showLiveOutput = false)
|
||||||
|
{
|
||||||
|
var processInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
WorkingDirectory = workingDirectory,
|
||||||
|
FileName = program,
|
||||||
|
Arguments = command,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
using var process = new Process();
|
||||||
|
process.StartInfo = processInfo;
|
||||||
|
process.Start();
|
||||||
|
process.BeginOutputReadLine();
|
||||||
|
process.BeginErrorReadLine();
|
||||||
|
|
||||||
|
process.OutputDataReceived += (_, args) =>
|
||||||
|
{
|
||||||
|
if(!string.IsNullOrWhiteSpace(args.Data))
|
||||||
|
{
|
||||||
|
if(showLiveOutput)
|
||||||
|
Console.WriteLine(args.Data);
|
||||||
|
sb.AppendLine(args.Data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
process.ErrorDataReceived += (_, args) =>
|
||||||
|
{
|
||||||
|
if(!string.IsNullOrWhiteSpace(args.Data))
|
||||||
|
{
|
||||||
|
if(showLiveOutput)
|
||||||
|
Console.WriteLine(args.Data);
|
||||||
|
sb.AppendLine(args.Data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await process.WaitForExitAsync();
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<int> IncreaseBuildNumber()
|
||||||
|
{
|
||||||
|
const int BUILD_NUMBER_INDEX = 2;
|
||||||
|
var pathMetadata = Environment.GetMetadataPath();
|
||||||
|
var lines = await File.ReadAllLinesAsync(pathMetadata, Encoding.UTF8);
|
||||||
|
var buildNumber = int.Parse(lines[BUILD_NUMBER_INDEX]) + 1;
|
||||||
|
|
||||||
|
Console.WriteLine($"- Updating build number from '{lines[BUILD_NUMBER_INDEX]}' to '{buildNumber}'.");
|
||||||
|
|
||||||
|
lines[BUILD_NUMBER_INDEX] = buildNumber.ToString();
|
||||||
|
await File.WriteAllLinesAsync(pathMetadata, lines, Environment.UTF8_NO_BOM);
|
||||||
|
return buildNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> UpdateBuildTime()
|
||||||
|
{
|
||||||
|
const int BUILD_TIME_INDEX = 1;
|
||||||
|
var pathMetadata = Environment.GetMetadataPath();
|
||||||
|
var lines = await File.ReadAllLinesAsync(pathMetadata, Encoding.UTF8);
|
||||||
|
var buildTime = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + " UTC";
|
||||||
|
|
||||||
|
Console.WriteLine($"- Updating build time from '{lines[BUILD_TIME_INDEX]}' to '{buildTime}'.");
|
||||||
|
|
||||||
|
lines[BUILD_TIME_INDEX] = buildTime;
|
||||||
|
await File.WriteAllLinesAsync(pathMetadata, lines, Environment.UTF8_NO_BOM);
|
||||||
|
return buildTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
[GeneratedRegex("""(?ms).?(NET\s+SDK|SDK\s+\.NET)\s*:\s+Version:\s+(?<sdkVersion>[0-9.]+).+Commit:\s+(?<sdkCommit>[a-zA-Z0-9]+).+Host:\s+Version:\s+(?<hostVersion>[0-9.]+).+Commit:\s+(?<hostCommit>[a-zA-Z0-9]+)""")]
|
||||||
|
private static partial Regex DotnetVersionRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex("""rustc (?<version>[0-9.]+)(?:-nightly)? \((?<commit>[a-zA-Z0-9]+)""")]
|
||||||
|
private static partial Regex RustVersionRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex("""MudBlazor\s+(?<version>[0-9.]+)""")]
|
||||||
|
private static partial Regex MudBlazorVersionRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex("""tauri\s+v(?<version>[0-9.]+)""")]
|
||||||
|
private static partial Regex TauriVersionRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex("""^\s*Copyright\s+(?<year>[0-9]{4})""")]
|
||||||
|
private static partial Regex FindCopyrightRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex("""([0-9]{4})""")]
|
||||||
|
private static partial Regex ReplaceCopyrightYearRegex();
|
||||||
|
|
||||||
|
[GeneratedRegex("""(?<major>[0-9]+)\.(?<minor>[0-9]+)\.(?<patch>[0-9]+)""")]
|
||||||
|
private static partial Regex AppVersionRegex();
|
||||||
|
}
|
57
app/Build/Commands/UpdateWebAssetsCommand.cs
Normal file
57
app/Build/Commands/UpdateWebAssetsCommand.cs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// ReSharper disable ClassNeverInstantiated.Global
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
|
||||||
|
using SharedTools;
|
||||||
|
|
||||||
|
namespace Build.Commands;
|
||||||
|
|
||||||
|
public sealed class UpdateWebAssetsCommand
|
||||||
|
{
|
||||||
|
[Command("update-web", Description = "Update web assets")]
|
||||||
|
public void UpdateWebAssets()
|
||||||
|
{
|
||||||
|
if(!Environment.IsWorkingDirectoryValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Console.WriteLine("=========================");
|
||||||
|
Console.Write("- Updating web assets ...");
|
||||||
|
|
||||||
|
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"));
|
||||||
|
if (!isMudBlazorDirectoryPresent)
|
||||||
|
{
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine($"- Error: No web assets found for RID '{rid}'. Please publish the project first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var destinationPath = Path.Join(cwd, "wwwroot", "system");
|
||||||
|
if(Directory.Exists(destinationPath))
|
||||||
|
Directory.Delete(destinationPath, true);
|
||||||
|
|
||||||
|
Directory.CreateDirectory(destinationPath);
|
||||||
|
|
||||||
|
var sourcePaths = Directory.EnumerateFiles(contentPath, "*", SearchOption.AllDirectories);
|
||||||
|
var counter = 0;
|
||||||
|
foreach(var sourcePath in sourcePaths)
|
||||||
|
{
|
||||||
|
counter++;
|
||||||
|
var relativePath = sourcePath
|
||||||
|
.Replace(contentPath, "")
|
||||||
|
.TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||||
|
var targetPath = Path.Join(cwd, "wwwroot", "system", relativePath);
|
||||||
|
var targetDirectory = Path.GetDirectoryName(targetPath);
|
||||||
|
if (targetDirectory != null)
|
||||||
|
Directory.CreateDirectory(targetDirectory);
|
||||||
|
|
||||||
|
File.Copy(sourcePath, targetPath, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($" {counter:###,###} web assets updated successfully.");
|
||||||
|
Console.WriteLine();
|
||||||
|
}
|
||||||
|
}
|
7
app/Build/GlobalUsings.cs
Normal file
7
app/Build/GlobalUsings.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// Global using directives
|
||||||
|
|
||||||
|
global using System.Text;
|
||||||
|
|
||||||
|
global using Cocona;
|
||||||
|
|
||||||
|
global using Environment = Build.Tools.Environment;
|
9
app/Build/Program.cs
Normal file
9
app/Build/Program.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using Build.Commands;
|
||||||
|
|
||||||
|
var builder = CoconaApp.CreateBuilder();
|
||||||
|
var app = builder.Build();
|
||||||
|
app.AddCommands<CheckRidsCommand>();
|
||||||
|
app.AddCommands<UpdateMetadataCommands>();
|
||||||
|
app.AddCommands<UpdateWebAssetsCommand>();
|
||||||
|
app.AddCommands<CollectI18NKeysCommand>();
|
||||||
|
app.Run();
|
113
app/Build/Tools/Environment.cs
Normal file
113
app/Build/Tools/Environment.cs
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
using SharedTools;
|
||||||
|
|
||||||
|
namespace Build.Tools;
|
||||||
|
|
||||||
|
public static class Environment
|
||||||
|
{
|
||||||
|
public const string DOTNET_VERSION = "net9.0";
|
||||||
|
public static readonly Encoding UTF8_NO_BOM = new UTF8Encoding(false);
|
||||||
|
|
||||||
|
private static readonly Dictionary<RID, string> ALL_RIDS = Enum.GetValues<RID>().Select(rid => new KeyValuePair<RID, string>(rid, rid.AsMicrosoftRid())).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||||
|
|
||||||
|
public static bool IsWorkingDirectoryValid()
|
||||||
|
{
|
||||||
|
var currentDirectory = Directory.GetCurrentDirectory();
|
||||||
|
var mainFile = Path.Combine(currentDirectory, "Program.cs");
|
||||||
|
var projectFile = Path.Combine(currentDirectory, "Build Script.csproj");
|
||||||
|
|
||||||
|
if (!currentDirectory.EndsWith("Build", StringComparison.Ordinal) || !File.Exists(mainFile) || !File.Exists(projectFile))
|
||||||
|
{
|
||||||
|
Console.WriteLine("The current directory is not a valid working directory for the build script. Go to the /app/Build directory within the git repository.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetAIStudioDirectory()
|
||||||
|
{
|
||||||
|
var currentDirectory = Directory.GetCurrentDirectory();
|
||||||
|
var directory = Path.Combine(currentDirectory, "..", "MindWork AI Studio");
|
||||||
|
return Path.GetFullPath(directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetRustRuntimeDirectory()
|
||||||
|
{
|
||||||
|
var currentDirectory = Directory.GetCurrentDirectory();
|
||||||
|
var directory = Path.Combine(currentDirectory, "..", "..", "runtime");
|
||||||
|
return Path.GetFullPath(directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetMetadataPath()
|
||||||
|
{
|
||||||
|
var currentDirectory = Directory.GetCurrentDirectory();
|
||||||
|
var directory = Path.Combine(currentDirectory, "..", "..", "metadata.txt");
|
||||||
|
return Path.GetFullPath(directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? GetOS()
|
||||||
|
{
|
||||||
|
if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
return "windows";
|
||||||
|
|
||||||
|
if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||||
|
return "linux";
|
||||||
|
|
||||||
|
if(RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
return "darwin";
|
||||||
|
|
||||||
|
Console.WriteLine($"Error: Unsupported OS '{RuntimeInformation.OSDescription}'");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<RID> GetRidsForCurrentOS()
|
||||||
|
{
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
return ALL_RIDS.Where(rid => rid.Value.StartsWith("win-", StringComparison.Ordinal)).Select(n => n.Key);
|
||||||
|
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
return ALL_RIDS.Where(rid => rid.Value.StartsWith("osx-", StringComparison.Ordinal)).Select(n => n.Key);
|
||||||
|
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||||
|
return ALL_RIDS.Where(rid => rid.Value.StartsWith("linux-", StringComparison.Ordinal)).Select(n => n.Key);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MindWork AI Studio", "MindW
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceCodeRules", "SourceCodeRules\SourceCodeRules\SourceCodeRules.csproj", "{0976C1CB-D499-4C86-8ADA-B7A7A4DE0BF8}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceCodeRules", "SourceCodeRules\SourceCodeRules\SourceCodeRules.csproj", "{0976C1CB-D499-4C86-8ADA-B7A7A4DE0BF8}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Build Script", "Build\Build Script.csproj", "{447A5590-68E1-4EF8-9451-A41AF5FBE571}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedTools", "SharedTools\SharedTools.csproj", "{969C74DF-7678-4CD5-B269-D03E1ECA3D2A}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -18,6 +22,14 @@ Global
|
|||||||
{0976C1CB-D499-4C86-8ADA-B7A7A4DE0BF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{0976C1CB-D499-4C86-8ADA-B7A7A4DE0BF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{0976C1CB-D499-4C86-8ADA-B7A7A4DE0BF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{0976C1CB-D499-4C86-8ADA-B7A7A4DE0BF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{0976C1CB-D499-4C86-8ADA-B7A7A4DE0BF8}.Release|Any CPU.Build.0 = Release|Any CPU
|
{0976C1CB-D499-4C86-8ADA-B7A7A4DE0BF8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{447A5590-68E1-4EF8-9451-A41AF5FBE571}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{447A5590-68E1-4EF8-9451-A41AF5FBE571}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{447A5590-68E1-4EF8-9451-A41AF5FBE571}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{447A5590-68E1-4EF8-9451-A41AF5FBE571}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{969C74DF-7678-4CD5-B269-D03E1ECA3D2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{969C74DF-7678-4CD5-B269-D03E1ECA3D2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{969C74DF-7678-4CD5-B269-D03E1ECA3D2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{969C74DF-7678-4CD5-B269-D03E1ECA3D2A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
@ -2,15 +2,24 @@
|
|||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AI/@EntryIndexedValue">AI</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AI/@EntryIndexedValue">AI</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EDI/@EntryIndexedValue">EDI</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EDI/@EntryIndexedValue">EDI</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ERI/@EntryIndexedValue">ERI</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ERI/@EntryIndexedValue">ERI</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FNV/@EntryIndexedValue">FNV</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GWDG/@EntryIndexedValue">GWDG</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GWDG/@EntryIndexedValue">GWDG</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HF/@EntryIndexedValue">HF</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LLM/@EntryIndexedValue">LLM</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LLM/@EntryIndexedValue">LLM</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LM/@EntryIndexedValue">LM</s:String>
|
<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/=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/=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>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UI/@EntryIndexedValue">UI</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UI/@EntryIndexedValue">UI</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=URL/@EntryIndexedValue">URL</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=I18N/@EntryIndexedValue">I18N</s:String>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=agentic/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=agentic/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=groq/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=groq/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=gwdg/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=gwdg/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=huggingface/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=mwais/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=mwais/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=ollama/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=ollama/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=tauri_0027s/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=tauri_0027s/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
@ -1,51 +1,55 @@
|
|||||||
@attribute [Route(Routes.ASSISTANT_AGENDA)]
|
@attribute [Route(Routes.ASSISTANT_AGENDA)]
|
||||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogAgenda>
|
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogAgenda>
|
||||||
|
|
||||||
<MudTextField T="string" @bind-Text="@this.inputName" Validation="@this.ValidateName" Label="Meeting Name" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Tag" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Name the meeting, seminar, etc." Placeholder="Weekly jour fixe" Class="mb-3"/>
|
<MudTextField T="string" @bind-Text="@this.inputName" Validation="@this.ValidateName" Label="@T("Meeting Name")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Tag" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="@T("Name the meeting, seminar, etc.")" Placeholder="@T("Weekly jour fixe")" Class="mb-3"/>
|
||||||
<MudTextField T="string" @bind-Text="@this.inputTopic" Validation="@this.ValidateTopic" Label="Topic" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.EventNote" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Describe the topic of the meeting, seminar, etc. Is it about quantum computing, software engineering, or is it a general business meeting?" Placeholder="Project meeting" Class="mb-3"/>
|
<MudTextField T="string" @bind-Text="@this.inputTopic" Validation="@this.ValidateTopic" Label="@T("Topic")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.EventNote" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="@T("Describe the topic of the meeting, seminar, etc. Is it about quantum computing, software engineering, or is it a general business meeting?")" Placeholder="@T("Project meeting")" Class="mb-3"/>
|
||||||
<MudTextField T="string" @bind-Text="@this.inputContent" Validation="@this.ValidateContent" Label="Content list" Variant="Variant.Outlined" Lines="6" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Bullet list the content of the meeting, seminar, etc. roughly. Use dashes (-) to separate the items." Placeholder="@PLACEHOLDER_CONTENT" Class="mb-3" Immediate="@false" DebounceInterval="1_000" OnDebounceIntervalElapsed="@this.OnContentChanged" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.ListAlt"/>
|
<DebouncedTextField @bind-Text="@this.inputContent" ValidationFunc="@this.ValidateContent" DebounceTime="TimeSpan.FromSeconds(1)" Label="@T("Content list")" Lines="6" Attributes="@USER_INPUT_ATTRIBUTES" HelpText="@T("Bullet list the content of the meeting, seminar, etc. roughly. Use dashes (-) to separate the items.")" Placeholder="@PLACEHOLDER_CONTENT" WhenTextCanged="@this.OnContentChanged" Icon="@Icons.Material.Filled.ListAlt"/>
|
||||||
<MudSelect T="string" Label="(Optional) What topics should be the focus?" MultiSelection="@true" @bind-SelectedValues="@this.selectedFoci" Variant="Variant.Outlined" Class="mb-3" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.ListAlt">
|
<MudSelect T="string" Label="@T("(Optional) What topics should be the focus?")" MultiSelection="@true" @bind-SelectedValues="@this.selectedFoci" Variant="Variant.Outlined" Class="mb-3" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.ListAlt">
|
||||||
@foreach (var contentLine in this.contentLines)
|
@foreach (var contentLine in this.contentLines)
|
||||||
{
|
{
|
||||||
@if(!this.justBriefly.Contains(contentLine))
|
@if(!this.justBriefly.Contains(contentLine))
|
||||||
{
|
{
|
||||||
<MudSelectItem T="string" Value="@contentLine">@contentLine</MudSelectItem>
|
<MudSelectItem T="string" Value="@contentLine">
|
||||||
|
@contentLine
|
||||||
|
</MudSelectItem>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
|
|
||||||
<MudSelect T="string" Label="(Optional) What topics should only be briefly addressed?" MultiSelection="@true" @bind-SelectedValues="@this.justBriefly" Variant="Variant.Outlined" Class="mb-3" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.ListAlt">
|
<MudSelect T="string" Label="@T("(Optional) What topics should only be briefly addressed?")" MultiSelection="@true" @bind-SelectedValues="@this.justBriefly" Variant="Variant.Outlined" Class="mb-3" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.ListAlt">
|
||||||
@foreach (var contentLine in this.contentLines)
|
@foreach (var contentLine in this.contentLines)
|
||||||
{
|
{
|
||||||
@if(!this.selectedFoci.Contains(contentLine))
|
@if(!this.selectedFoci.Contains(contentLine))
|
||||||
{
|
{
|
||||||
<MudSelectItem T="string" Value="@contentLine">@contentLine</MudSelectItem>
|
<MudSelectItem T="string" Value="@contentLine">
|
||||||
|
@contentLine
|
||||||
|
</MudSelectItem>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
|
|
||||||
<MudTextField T="string" @bind-Text="@this.inputObjective" Validation="@this.ValidateObjective" Label="Objective" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Flag" Variant="Variant.Outlined" Lines="3" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Describe the objective(s) of the meeting, seminar, etc. What should be achieved?" Placeholder="Discuss the current project status and plan the next steps." Class="mb-3"/>
|
<MudTextField T="string" @bind-Text="@this.inputObjective" Validation="@this.ValidateObjective" Label="@T("Objective")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Flag" Variant="Variant.Outlined" Lines="3" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="@T("Describe the objective(s) of the meeting, seminar, etc. What should be achieved?")" Placeholder="@T("Discuss the current project status and plan the next steps.")" Class="mb-3"/>
|
||||||
<MudTextField T="string" @bind-Text="@this.inputModerator" Validation="@this.ValidateModerator" Label="Moderator" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Person3" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Who will moderate the meeting, seminar, etc.?" Placeholder="Jane Doe" Class="mb-3" />
|
<MudTextField T="string" @bind-Text="@this.inputModerator" Validation="@this.ValidateModerator" Label="@T("Moderator")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Person3" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="@T("Who will moderate the meeting, seminar, etc.?")" Placeholder="Jane Doe" Class="mb-3" />
|
||||||
<MudTextField T="string" @bind-Text="@this.inputDuration" Validation="@this.ValidateDuration" Label="Duration" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Schedule" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="How long will the meeting, seminar, etc. last? E.g., '2 hours', or '2 days (first day 8 hours, then 4 hours)', etc." Placeholder="2 hours" Class="mb-3"/>
|
<MudTextField T="string" @bind-Text="@this.inputDuration" Validation="@this.ValidateDuration" Label="@T("Duration")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Schedule" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="@T("How long will the meeting, seminar, etc. last? E.g., '2 hours', or '2 days (first day 8 hours, then 4 hours)', etc.")" Placeholder="@T("2 hours")" Class="mb-3"/>
|
||||||
<MudTextField T="string" @bind-Text="@this.inputStartTime" Validation="@this.ValidateStartTime" Label="Start time" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Schedule" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="When will the meeting, seminar, etc. start? E.g., '9:00 AM', or '9:00 AM (CET)', etc. When the meeting is a multi-day event, specify the start time for each day." Placeholder="9:00 AM" Class="mb-3"/>
|
<MudTextField T="string" @bind-Text="@this.inputStartTime" Validation="@this.ValidateStartTime" Label="Start time" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Schedule" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="@T("When will the meeting, seminar, etc. start? E.g., '9:00 AM', or '9:00 AM (CET)', etc. When the meeting is a multi-day event, specify the start time for each day.")" Placeholder="@T("9:00 AM")" Class="mb-3"/>
|
||||||
<MudTextField T="string" @bind-Text="@this.inputWhoIsPresenting" Label="(Optional) Who is presenting?" Variant="Variant.Outlined" Lines="6" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="(Optional) List the persons who will present at the meeting, seminar, etc. Use dashes (-) to separate the items." Placeholder="@PLACEHOLDER_WHO_IS_PRESENTING" Class="mb-3"/>
|
<MudTextField T="string" @bind-Text="@this.inputWhoIsPresenting" Label="@T("(Optional) Who is presenting?")" Variant="Variant.Outlined" Lines="6" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="@T("(Optional) List the persons who will present at the meeting, seminar, etc. Use dashes (-) to separate the items.")" Placeholder="@PLACEHOLDER_WHO_IS_PRESENTING" Class="mb-3"/>
|
||||||
<MudTextSwitch Label="Do the participants need to get to know each other first?" @bind-Value="@this.introduceParticipants" LabelOn="Yes, introduce participants" LabelOff="No, participants know each other" />
|
<MudTextSwitch Label="@T("Do the participants need to get to know each other first?")" @bind-Value="@this.introduceParticipants" LabelOn="@T("Yes, introduce participants")" LabelOff="@T("No, participants know each other")" />
|
||||||
<EnumSelection T="NumberParticipants" @bind-Value="@this.numberParticipants" NameFunc="@(participants => participants.Name())" Icon="@Icons.Material.Filled.Group" Label="Number of participants" ValidateSelection="@this.ValidateNumberParticipants"/>
|
<EnumSelection T="NumberParticipants" @bind-Value="@this.numberParticipants" NameFunc="@(participants => participants.Name())" Icon="@Icons.Material.Filled.Group" Label="@T("Number of participants")" ValidateSelection="@this.ValidateNumberParticipants"/>
|
||||||
<MudTextSwitch Label="Should the participants be involved passively or actively?" @bind-Value="@this.activeParticipation" LabelOn="Active participation, like world café, discussions, etc." LabelOff="Passive participation, like presentations, lectures, etc." />
|
<MudTextSwitch Label="@T("Should the participants be involved passively or actively?")" @bind-Value="@this.activeParticipation" LabelOn="@T("Active participation, like world café, discussions, etc.")" LabelOff="@T("Passive participation, like presentations, lectures, etc.")" />
|
||||||
<MudTextSwitch Label="Is this a virtual event, e.g., a call or webinar?" @bind-Value="@this.isMeetingVirtual" LabelOn="Yes, this is a virtual event" LabelOff="No, this is a physical event" />
|
<MudTextSwitch Label="@T("Is this a virtual event, e.g., a call or webinar?")" @bind-Value="@this.isMeetingVirtual" LabelOn="@T("Yes, this is a virtual event")" LabelOff="@T("No, this is a physical event")" />
|
||||||
@if (!this.isMeetingVirtual)
|
@if (!this.isMeetingVirtual)
|
||||||
{
|
{
|
||||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||||
<MudTextField T="string" @bind-Text="@this.inputLocation" Validation="@this.ValidateLocation" Label="Location" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.MyLocation" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Where will the meeting, seminar, etc. take place?" Placeholder="Hamburg, Germany" Class="mb-3"/>
|
<MudTextField T="string" @bind-Text="@this.inputLocation" Validation="@this.ValidateLocation" Label="@T("Location")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.MyLocation" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="@T("Where will the meeting, seminar, etc. take place?")" Placeholder="@T("Hamburg, Germany")" Class="mb-3"/>
|
||||||
<MudTextSwitch Label="Should there be a joint dinner?" @bind-Value="@this.goingToDinner" LabelOn="Yes, there should be a joint dinner" LabelOff="No, there should be no joint dinner" />
|
<MudTextSwitch Label="@T("Should there be a joint dinner?")" @bind-Value="@this.goingToDinner" LabelOn="@T("Yes, there should be a joint dinner")" LabelOff="@T("No, there should be no joint dinner")" />
|
||||||
<MudTextSwitch Label="Should there be a social activity?" @bind-Value="@this.doingSocialActivity" LabelOn="Yes, there should be a social activity" LabelOff="No, there should be no social activity" />
|
<MudTextSwitch Label="@T("Should there be a social activity?")" @bind-Value="@this.doingSocialActivity" LabelOn="@T("Yes, there should be a social activity")" LabelOff="@T("No, there should be no social activity")" />
|
||||||
<MudTextSwitch Label="Do participants need to arrive and depart?" @bind-Value="@this.needToArriveAndDepart" LabelOn="Yes, participants need to arrive and depart" LabelOff="No, participants do not need to arrive and depart" />
|
<MudTextSwitch Label="@T("Do participants need to arrive and depart?")" @bind-Value="@this.needToArriveAndDepart" LabelOn="@T("Yes, participants need to arrive and depart")" LabelOff="@T("No, participants do not need to arrive and depart")" />
|
||||||
<MudStack Row="@true" Wrap="Wrap.Wrap">
|
<MudStack Row="@true" Wrap="Wrap.Wrap">
|
||||||
<MudTextSlider T="int" Label="Approx. duration of the lunch break" @bind-Value="@this.durationLunchBreak" Min="30" Max="120" Step="5" Unit="minutes"/>
|
<MudTextSlider T="int" Label="@T("Approx. duration of the lunch break")" @bind-Value="@this.durationLunchBreak" Min="30" Max="120" Step="5" Unit="@T("minutes")"/>
|
||||||
<MudTextSlider T="int" Label="Approx. duration of the coffee or tea breaks" @bind-Value="@this.durationBreaks" Min="10" Max="60" Step="5" Unit="minutes"/>
|
<MudTextSlider T="int" Label="@T("Approx. duration of the coffee or tea breaks")" @bind-Value="@this.durationBreaks" Min="10" Max="60" Step="5" Unit="@T("minutes")"/>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
}
|
}
|
||||||
|
|
||||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelecting())" @bind-Value="@this.selectedTargetLanguage" ValidateSelection="@this.ValidateTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Target language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom target language" />
|
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelecting())" @bind-Value="@this.selectedTargetLanguage" ValidateSelection="@this.ValidateTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="@T("Target language")" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="@T("Custom target language")" />
|
||||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
@ -9,14 +9,9 @@ public partial class AssistantAgenda : AssistantBaseCore<SettingsDialogAgenda>
|
|||||||
{
|
{
|
||||||
public override Tools.Components Component => Tools.Components.AGENDA_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.AGENDA_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Agenda Planner";
|
protected override string Title => T("Agenda Planner");
|
||||||
|
|
||||||
protected override string Description =>
|
protected override string Description => T("This agenda planner helps you create a structured agenda for your meeting or seminar. Just provide some basic information about the event, and the assistant will generate an agenda for you. You can also specify the duration, the start time, the location, the target language, and other details.");
|
||||||
"""
|
|
||||||
This agenda planner helps you create a structured agenda for your meeting or seminar. Just provide some basic
|
|
||||||
information about the event, and the assistant will generate an agenda for you. You can also specify the
|
|
||||||
duration, the start time, the location, the target language, and other details.
|
|
||||||
""";
|
|
||||||
|
|
||||||
protected override string SystemPrompt =>
|
protected override string SystemPrompt =>
|
||||||
$"""
|
$"""
|
||||||
@ -98,7 +93,7 @@ public partial class AssistantAgenda : AssistantBaseCore<SettingsDialogAgenda>
|
|||||||
|
|
||||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||||
|
|
||||||
protected override string SubmitText => "Create Agenda";
|
protected override string SubmitText => T("Create Agenda");
|
||||||
|
|
||||||
protected override Func<Task> SubmitAction => this.CreateAgenda;
|
protected override Func<Task> SubmitAction => this.CreateAgenda;
|
||||||
|
|
||||||
@ -235,7 +230,7 @@ public partial class AssistantAgenda : AssistantBaseCore<SettingsDialogAgenda>
|
|||||||
private string? ValidateLocation(string location)
|
private string? ValidateLocation(string location)
|
||||||
{
|
{
|
||||||
if(!this.isMeetingVirtual && string.IsNullOrWhiteSpace(location))
|
if(!this.isMeetingVirtual && string.IsNullOrWhiteSpace(location))
|
||||||
return "Please provide a location for the meeting or the seminar.";
|
return T("Please provide a location for the meeting or the seminar.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -243,7 +238,7 @@ public partial class AssistantAgenda : AssistantBaseCore<SettingsDialogAgenda>
|
|||||||
private string? ValidateNumberParticipants(NumberParticipants selectedSize)
|
private string? ValidateNumberParticipants(NumberParticipants selectedSize)
|
||||||
{
|
{
|
||||||
if(selectedSize is NumberParticipants.NOT_SPECIFIED)
|
if(selectedSize is NumberParticipants.NOT_SPECIFIED)
|
||||||
return "Please select the number of participants.";
|
return T("Please select the number of participants.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -251,7 +246,7 @@ public partial class AssistantAgenda : AssistantBaseCore<SettingsDialogAgenda>
|
|||||||
private string? ValidateTargetLanguage(CommonLanguages language)
|
private string? ValidateTargetLanguage(CommonLanguages language)
|
||||||
{
|
{
|
||||||
if(language is CommonLanguages.AS_IS)
|
if(language is CommonLanguages.AS_IS)
|
||||||
return "Please select a target language for the agenda.";
|
return T("Please select a target language for the agenda.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -259,7 +254,7 @@ public partial class AssistantAgenda : AssistantBaseCore<SettingsDialogAgenda>
|
|||||||
private string? ValidateDuration(string duration)
|
private string? ValidateDuration(string duration)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(duration))
|
if(string.IsNullOrWhiteSpace(duration))
|
||||||
return "Please provide a duration for the meeting or the seminar, e.g. '2 hours', or '2 days (8 hours and 4 hours)', etc.";
|
return T("Please provide a duration for the meeting or the seminar, e.g. '2 hours', or '2 days (8 hours and 4 hours)', etc.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -267,7 +262,7 @@ public partial class AssistantAgenda : AssistantBaseCore<SettingsDialogAgenda>
|
|||||||
private string? ValidateStartTime(string startTime)
|
private string? ValidateStartTime(string startTime)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(startTime))
|
if(string.IsNullOrWhiteSpace(startTime))
|
||||||
return "Please provide a start time for the meeting or the seminar. When the meeting is a multi-day event, specify the start time for each day, e.g. '9:00 AM, 10:00 AM', etc.";
|
return T("Please provide a start time for the meeting or the seminar. When the meeting is a multi-day event, specify the start time for each day, e.g. '9:00 AM, 10:00 AM', etc.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -275,7 +270,7 @@ public partial class AssistantAgenda : AssistantBaseCore<SettingsDialogAgenda>
|
|||||||
private string? ValidateCustomLanguage(string language)
|
private string? ValidateCustomLanguage(string language)
|
||||||
{
|
{
|
||||||
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||||
return "Please provide a custom language.";
|
return T("Please provide a custom language.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -283,7 +278,7 @@ public partial class AssistantAgenda : AssistantBaseCore<SettingsDialogAgenda>
|
|||||||
private string? ValidateTopic(string topic)
|
private string? ValidateTopic(string topic)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(topic))
|
if(string.IsNullOrWhiteSpace(topic))
|
||||||
return "Please provide a topic for the agenda. What is the meeting or the seminar about?";
|
return T("Please provide a topic for the agenda. What is the meeting or the seminar about?");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -291,7 +286,7 @@ public partial class AssistantAgenda : AssistantBaseCore<SettingsDialogAgenda>
|
|||||||
private string? ValidateName(string name)
|
private string? ValidateName(string name)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(name))
|
if(string.IsNullOrWhiteSpace(name))
|
||||||
return "Please provide a name for the meeting or the seminar.";
|
return T("Please provide a name for the meeting or the seminar.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -299,12 +294,12 @@ public partial class AssistantAgenda : AssistantBaseCore<SettingsDialogAgenda>
|
|||||||
private string? ValidateContent(string content)
|
private string? ValidateContent(string content)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(content))
|
if(string.IsNullOrWhiteSpace(content))
|
||||||
return "Please provide some content for the agenda. What are the main points of the meeting or the seminar?";
|
return T("Please provide some content for the agenda. What are the main points of the meeting or the seminar?");
|
||||||
|
|
||||||
var lines = content.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
var lines = content.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||||
foreach (var line in lines)
|
foreach (var line in lines)
|
||||||
if(!line.TrimStart().StartsWith('-'))
|
if(!line.TrimStart().StartsWith('-'))
|
||||||
return "Please start each line of your content list with a dash (-) to create a bullet point list.";
|
return T("Please start each line of your content list with a dash (-) to create a bullet point list.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -312,7 +307,7 @@ public partial class AssistantAgenda : AssistantBaseCore<SettingsDialogAgenda>
|
|||||||
private string? ValidateObjective(string objective)
|
private string? ValidateObjective(string objective)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(objective))
|
if(string.IsNullOrWhiteSpace(objective))
|
||||||
return "Please provide an objective for the meeting or the seminar. What do you want to achieve?";
|
return T("Please provide an objective for the meeting or the seminar. What do you want to achieve?");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -320,7 +315,7 @@ public partial class AssistantAgenda : AssistantBaseCore<SettingsDialogAgenda>
|
|||||||
private string? ValidateModerator(string moderator)
|
private string? ValidateModerator(string moderator)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(moderator))
|
if(string.IsNullOrWhiteSpace(moderator))
|
||||||
return "Please provide a moderator for the meeting or the seminar. Who will lead the discussion?";
|
return T("Please provide a moderator for the meeting or the seminar. Who will lead the discussion?");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -2,22 +2,24 @@ namespace AIStudio.Assistants.Agenda;
|
|||||||
|
|
||||||
public static class NumberParticipantsExtensions
|
public static class NumberParticipantsExtensions
|
||||||
{
|
{
|
||||||
|
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(NumberParticipantsExtensions).Namespace, nameof(NumberParticipantsExtensions));
|
||||||
|
|
||||||
public static string Name(this NumberParticipants numberParticipants) => numberParticipants switch
|
public static string Name(this NumberParticipants numberParticipants) => numberParticipants switch
|
||||||
{
|
{
|
||||||
NumberParticipants.NOT_SPECIFIED => "Please select how many participants are expected",
|
NumberParticipants.NOT_SPECIFIED => TB("Please select how many participants are expected"),
|
||||||
|
|
||||||
NumberParticipants.PEER_TO_PEER => "2 (peer to peer)",
|
NumberParticipants.PEER_TO_PEER => TB("2 (peer to peer)"),
|
||||||
|
|
||||||
NumberParticipants.SMALL_GROUP => "3 - 5 (small group)",
|
NumberParticipants.SMALL_GROUP => TB("3 - 5 (small group)"),
|
||||||
NumberParticipants.LARGE_GROUP => "6 - 12 (large group)",
|
NumberParticipants.LARGE_GROUP => TB("6 - 12 (large group)"),
|
||||||
NumberParticipants.MULTIPLE_SMALL_GROUPS => "13 - 20 (multiple small groups)",
|
NumberParticipants.MULTIPLE_SMALL_GROUPS => TB("13 - 20 (multiple small groups)"),
|
||||||
NumberParticipants.MULTIPLE_LARGE_GROUPS => "21 - 30 (multiple large groups)",
|
NumberParticipants.MULTIPLE_LARGE_GROUPS => TB("21 - 30 (multiple large groups)"),
|
||||||
|
|
||||||
NumberParticipants.SYMPOSIUM => "31 - 100 (symposium)",
|
NumberParticipants.SYMPOSIUM => TB("31 - 100 (symposium)"),
|
||||||
NumberParticipants.CONFERENCE => "101 - 200 (conference)",
|
NumberParticipants.CONFERENCE => TB("101 - 200 (conference)"),
|
||||||
NumberParticipants.CONGRESS => "201 - 1,000 (congress)",
|
NumberParticipants.CONGRESS => TB("201 - 1,000 (congress)"),
|
||||||
|
|
||||||
NumberParticipants.LARGE_EVENT => "1,000+ (large event)",
|
NumberParticipants.LARGE_EVENT => TB("1,000+ (large event)"),
|
||||||
|
|
||||||
_ => "Unknown"
|
_ => "Unknown"
|
||||||
};
|
};
|
||||||
|
@ -4,23 +4,20 @@
|
|||||||
|
|
||||||
<div class="inner-scrolling-context">
|
<div class="inner-scrolling-context">
|
||||||
|
|
||||||
<MudText Typo="Typo.h3" Class="mb-2 mr-3">
|
<MudStack Row="true" AlignItems="AlignItems.Center" Class="mb-2 mr-3" StretchItems="StretchItems.Start">
|
||||||
|
<MudText Typo="Typo.h3">
|
||||||
@(this.Title)
|
@(this.Title)
|
||||||
</MudText>
|
</MudText>
|
||||||
|
|
||||||
|
<MudIconButton Variant="Variant.Text" Icon="@Icons.Material.Filled.Settings" OnClick="() => this.OpenSettingsDialog()"/>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
<InnerScrolling>
|
<InnerScrolling>
|
||||||
<ChildContent>
|
<ChildContent>
|
||||||
<MudForm @ref="@(this.form)" @bind-IsValid="@(this.inputIsValid)" @bind-Errors="@(this.inputIssues)" FieldChanged="@this.TriggerFormChange" Class="pr-2">
|
<MudForm @ref="@(this.form)" @bind-IsValid="@(this.inputIsValid)" @bind-Errors="@(this.inputIssues)" FieldChanged="@this.TriggerFormChange" Class="pr-2">
|
||||||
<MudGrid Class="mb-2">
|
<MudText Typo="Typo.body1" Align="Align.Justify" Class="mb-2">
|
||||||
<MudItem xs="10">
|
|
||||||
<MudText Typo="Typo.body1" Align="Align.Justify">
|
|
||||||
@this.Description
|
@this.Description
|
||||||
</MudText>
|
</MudText>
|
||||||
</MudItem>
|
|
||||||
<MudItem xs="2" Class="d-flex justify-end align-start">
|
|
||||||
<MudIconButton Variant="Variant.Filled" Icon="@Icons.Material.Filled.Settings" OnClick="() => this.OpenSettingsDialog()"/>
|
|
||||||
</MudItem>
|
|
||||||
</MudGrid>
|
|
||||||
|
|
||||||
@if (this.Body is not null)
|
@if (this.Body is not null)
|
||||||
{
|
{
|
||||||
@ -28,10 +25,17 @@
|
|||||||
@this.Body
|
@this.Body
|
||||||
</CascadingValue>
|
</CascadingValue>
|
||||||
|
|
||||||
<MudButton Disabled="@this.SubmitDisabled" Variant="Variant.Filled" Class="mb-3" OnClick="() => this.SubmitAction()" Style="@this.SubmitButtonStyle">
|
<MudStack Row="true" AlignItems="AlignItems.Center" StretchItems="StretchItems.Start" Class="mb-3">
|
||||||
|
<MudButton Disabled="@this.SubmitDisabled" Variant="Variant.Filled" OnClick="async () => await this.Start()" Style="@this.SubmitButtonStyle">
|
||||||
@this.SubmitText
|
@this.SubmitText
|
||||||
</MudButton>
|
</MudButton>
|
||||||
|
@if (this.isProcessing && this.cancellationTokenSource is not null)
|
||||||
|
{
|
||||||
|
<MudTooltip Text="@TB("Stop generation")">
|
||||||
|
<MudIconButton Variant="Variant.Filled" Icon="@Icons.Material.Filled.Stop" Color="Color.Error" OnClick="() => this.CancelStreaming()"/>
|
||||||
|
</MudTooltip>
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
}
|
}
|
||||||
</MudForm>
|
</MudForm>
|
||||||
<Issues IssuesData="@(this.inputIssues)"/>
|
<Issues IssuesData="@(this.inputIssues)"/>
|
||||||
@ -73,7 +77,7 @@
|
|||||||
{
|
{
|
||||||
@if (this.ShowSendTo)
|
@if (this.ShowSendTo)
|
||||||
{
|
{
|
||||||
<MudMenu StartIcon="@Icons.Material.Filled.Apps" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="Send to ..." Variant="Variant.Filled" Style="@this.GetSendToColor()" Class="rounded">
|
<MudMenu AnchorOrigin="Origin.TopLeft" TransformOrigin="Origin.BottomLeft" StartIcon="@Icons.Material.Filled.Apps" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="@TB("Send to ...")" Variant="Variant.Filled" Style="@this.GetSendToColor()" Class="rounded">
|
||||||
@foreach (var assistant in Enum.GetValues<Components>().Where(n => n.AllowSendTo()).OrderBy(n => n.Name().Length))
|
@foreach (var assistant in Enum.GetValues<Components>().Where(n => n.AllowSendTo()).OrderBy(n => n.Name().Length))
|
||||||
{
|
{
|
||||||
<MudMenuItem OnClick="() => this.SendToAssistant(assistant, new())">
|
<MudMenuItem OnClick="() => this.SendToAssistant(assistant, new())">
|
||||||
@ -90,20 +94,20 @@
|
|||||||
{
|
{
|
||||||
case ButtonData buttonData when !string.IsNullOrWhiteSpace(buttonData.Tooltip):
|
case ButtonData buttonData when !string.IsNullOrWhiteSpace(buttonData.Tooltip):
|
||||||
<MudTooltip Text="@buttonData.Tooltip">
|
<MudTooltip Text="@buttonData.Tooltip">
|
||||||
<MudButton Variant="Variant.Filled" Color="@buttonData.Color" StartIcon="@GetButtonIcon(buttonData.Icon)" OnClick="async () => await buttonData.AsyncAction()">
|
<MudButton Variant="Variant.Filled" Color="@buttonData.Color" Disabled="@buttonData.DisabledAction()" StartIcon="@GetButtonIcon(buttonData.Icon)" OnClick="async () => await buttonData.AsyncAction()">
|
||||||
@buttonData.Text
|
@buttonData.Text
|
||||||
</MudButton>
|
</MudButton>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ButtonData buttonData:
|
case ButtonData buttonData:
|
||||||
<MudButton Variant="Variant.Filled" Color="@buttonData.Color" StartIcon="@GetButtonIcon(buttonData.Icon)" OnClick="async () => await buttonData.AsyncAction()">
|
<MudButton Variant="Variant.Filled" Color="@buttonData.Color" Disabled="@buttonData.DisabledAction()" StartIcon="@GetButtonIcon(buttonData.Icon)" OnClick="async () => await buttonData.AsyncAction()">
|
||||||
@buttonData.Text
|
@buttonData.Text
|
||||||
</MudButton>
|
</MudButton>
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SendToButton sendToButton:
|
case SendToButton sendToButton:
|
||||||
<MudMenu StartIcon="@Icons.Material.Filled.Apps" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="Send to ..." Variant="Variant.Filled" Style="@this.GetSendToColor()" Class="rounded">
|
<MudMenu AnchorOrigin="Origin.TopLeft" TransformOrigin="Origin.BottomLeft" StartIcon="@Icons.Material.Filled.Apps" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="@TB("Send to ...")" Variant="Variant.Filled" Style="@this.GetSendToColor()" Class="rounded">
|
||||||
@foreach (var assistant in Enum.GetValues<Components>().Where(n => n.AllowSendTo()).OrderBy(n => n.Name().Length))
|
@foreach (var assistant in Enum.GetValues<Components>().Where(n => n.AllowSendTo()).OrderBy(n => n.Name().Length))
|
||||||
{
|
{
|
||||||
<MudMenuItem OnClick="() => this.SendToAssistant(assistant, sendToButton)">
|
<MudMenuItem OnClick="() => this.SendToAssistant(assistant, sendToButton)">
|
||||||
@ -118,14 +122,14 @@
|
|||||||
@if (this.ShowCopyResult)
|
@if (this.ShowCopyResult)
|
||||||
{
|
{
|
||||||
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.ContentCopy" OnClick="() => this.CopyToClipboard()">
|
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.ContentCopy" OnClick="() => this.CopyToClipboard()">
|
||||||
Copy result
|
@TB("Copy result")
|
||||||
</MudButton>
|
</MudButton>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (this.ShowReset)
|
@if (this.ShowReset)
|
||||||
{
|
{
|
||||||
<MudButton Variant="Variant.Filled" Style="@this.GetResetColor()" StartIcon="@Icons.Material.Filled.Refresh" OnClick="() => this.InnerResetForm()">
|
<MudButton Variant="Variant.Filled" Style="@this.GetResetColor()" StartIcon="@Icons.Material.Filled.Refresh" OnClick="() => this.InnerResetForm()">
|
||||||
Reset
|
@TB("Reset")
|
||||||
</MudButton>
|
</MudButton>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,11 +13,8 @@ using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
|||||||
|
|
||||||
namespace AIStudio.Assistants;
|
namespace AIStudio.Assistants;
|
||||||
|
|
||||||
public abstract partial class AssistantBase<TSettings> : AssistantLowerBase, IMessageBusReceiver, IDisposable where TSettings : IComponent
|
public abstract partial class AssistantBase<TSettings> : AssistantLowerBase where TSettings : IComponent
|
||||||
{
|
{
|
||||||
[Inject]
|
|
||||||
protected SettingsManager SettingsManager { get; init; } = null!;
|
|
||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
private IDialogService DialogService { get; init; } = null!;
|
private IDialogService DialogService { get; init; } = null!;
|
||||||
|
|
||||||
@ -42,9 +39,6 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase, IMe
|
|||||||
[Inject]
|
[Inject]
|
||||||
private MudTheme ColorTheme { get; init; } = null!;
|
private MudTheme ColorTheme { get; init; } = null!;
|
||||||
|
|
||||||
[Inject]
|
|
||||||
private MessageBus MessageBus { get; init; } = null!;
|
|
||||||
|
|
||||||
protected abstract string Title { get; }
|
protected abstract string Title { get; }
|
||||||
|
|
||||||
protected abstract string Description { get; }
|
protected abstract string Description { get; }
|
||||||
@ -95,8 +89,10 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase, IMe
|
|||||||
protected MudForm? form;
|
protected MudForm? form;
|
||||||
protected bool inputIsValid;
|
protected bool inputIsValid;
|
||||||
protected Profile currentProfile = Profile.NO_PROFILE;
|
protected Profile currentProfile = Profile.NO_PROFILE;
|
||||||
|
protected ChatTemplate currentChatTemplate = ChatTemplate.NO_CHAT_TEMPLATE;
|
||||||
protected ChatThread? chatThread;
|
protected ChatThread? chatThread;
|
||||||
protected IContent? lastUserPrompt;
|
protected IContent? lastUserPrompt;
|
||||||
|
protected CancellationTokenSource? cancellationTokenSource;
|
||||||
|
|
||||||
private readonly Timer formChangeTimer = new(TimeSpan.FromSeconds(1.6));
|
private readonly Timer formChangeTimer = new(TimeSpan.FromSeconds(1.6));
|
||||||
|
|
||||||
@ -108,6 +104,8 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase, IMe
|
|||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
await base.OnInitializedAsync();
|
||||||
|
|
||||||
this.formChangeTimer.AutoReset = false;
|
this.formChangeTimer.AutoReset = false;
|
||||||
this.formChangeTimer.Elapsed += async (_, _) =>
|
this.formChangeTimer.Elapsed += async (_, _) =>
|
||||||
{
|
{
|
||||||
@ -118,11 +116,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase, IMe
|
|||||||
this.MightPreselectValues();
|
this.MightPreselectValues();
|
||||||
this.providerSettings = this.SettingsManager.GetPreselectedProvider(this.Component);
|
this.providerSettings = this.SettingsManager.GetPreselectedProvider(this.Component);
|
||||||
this.currentProfile = this.SettingsManager.GetPreselectedProfile(this.Component);
|
this.currentProfile = this.SettingsManager.GetPreselectedProfile(this.Component);
|
||||||
|
this.currentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(this.Component);
|
||||||
this.MessageBus.RegisterComponent(this);
|
|
||||||
this.MessageBus.ApplyFilters(this, [], [ Event.COLOR_THEME_CHANGED ]);
|
|
||||||
|
|
||||||
await base.OnInitializedAsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
@ -145,39 +139,28 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase, IMe
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Implementation of IMessageBusReceiver
|
private string TB(string fallbackEN) => this.T(fallbackEN, typeof(AssistantBase<TSettings>).Namespace, nameof(AssistantBase<TSettings>));
|
||||||
|
|
||||||
public string ComponentName => nameof(AssistantBase<TSettings>);
|
|
||||||
|
|
||||||
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
|
||||||
{
|
|
||||||
switch (triggeredEvent)
|
|
||||||
{
|
|
||||||
case Event.COLOR_THEME_CHANGED:
|
|
||||||
this.StateHasChanged();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
|
|
||||||
{
|
|
||||||
return Task.FromResult<TResult?>(default);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private string SubmitButtonStyle => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence ? this.providerSettings.UsedLLMProvider.GetConfidence(this.SettingsManager).StyleBorder(this.SettingsManager) : string.Empty;
|
private string SubmitButtonStyle => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence ? this.providerSettings.UsedLLMProvider.GetConfidence(this.SettingsManager).StyleBorder(this.SettingsManager) : string.Empty;
|
||||||
|
|
||||||
protected string? ValidatingProvider(AIStudio.Settings.Provider provider)
|
protected string? ValidatingProvider(AIStudio.Settings.Provider provider)
|
||||||
{
|
{
|
||||||
if(provider.UsedLLMProvider == LLMProviders.NONE)
|
if(provider.UsedLLMProvider == LLMProviders.NONE)
|
||||||
return "Please select a provider.";
|
return this.TB("Please select a provider.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task Start()
|
||||||
|
{
|
||||||
|
using (this.cancellationTokenSource = new())
|
||||||
|
{
|
||||||
|
await this.SubmitAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cancellationTokenSource = null;
|
||||||
|
}
|
||||||
|
|
||||||
private void TriggerFormChange(FormFieldChangedEventArgs _)
|
private void TriggerFormChange(FormFieldChangedEventArgs _)
|
||||||
{
|
{
|
||||||
this.formChangeTimer.Stop();
|
this.formChangeTimer.Stop();
|
||||||
@ -215,7 +198,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase, IMe
|
|||||||
SystemPrompt = this.SystemPrompt,
|
SystemPrompt = this.SystemPrompt,
|
||||||
WorkspaceId = Guid.Empty,
|
WorkspaceId = Guid.Empty,
|
||||||
ChatId = Guid.NewGuid(),
|
ChatId = Guid.NewGuid(),
|
||||||
Name = $"Assistant - {this.Title}",
|
Name = string.Format(this.TB("Assistant - {0}"), this.Title),
|
||||||
Seed = this.RNG.Next(),
|
Seed = this.RNG.Next(),
|
||||||
Blocks = [],
|
Blocks = [],
|
||||||
};
|
};
|
||||||
@ -261,6 +244,9 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase, IMe
|
|||||||
|
|
||||||
protected async Task<string> AddAIResponseAsync(DateTimeOffset time, bool hideContentFromUser = false)
|
protected async Task<string> AddAIResponseAsync(DateTimeOffset time, bool hideContentFromUser = false)
|
||||||
{
|
{
|
||||||
|
var manageCancellationLocally = this.cancellationTokenSource is null;
|
||||||
|
this.cancellationTokenSource ??= new CancellationTokenSource();
|
||||||
|
|
||||||
var aiText = new ContentText
|
var aiText = new ContentText
|
||||||
{
|
{
|
||||||
// We have to wait for the remote
|
// We have to wait for the remote
|
||||||
@ -289,15 +275,28 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase, IMe
|
|||||||
// Use the selected provider to get the AI response.
|
// Use the selected provider to get the AI response.
|
||||||
// By awaiting this line, we wait for the entire
|
// By awaiting this line, we wait for the entire
|
||||||
// content to be streamed.
|
// content to be streamed.
|
||||||
this.chatThread = await aiText.CreateFromProviderAsync(this.providerSettings.CreateProvider(this.Logger), this.providerSettings.Model, this.lastUserPrompt, this.chatThread);
|
this.chatThread = await aiText.CreateFromProviderAsync(this.providerSettings.CreateProvider(this.Logger), this.providerSettings.Model, this.lastUserPrompt, this.chatThread, this.cancellationTokenSource!.Token);
|
||||||
|
|
||||||
this.isProcessing = false;
|
this.isProcessing = false;
|
||||||
this.StateHasChanged();
|
this.StateHasChanged();
|
||||||
|
|
||||||
|
if(manageCancellationLocally)
|
||||||
|
{
|
||||||
|
this.cancellationTokenSource.Dispose();
|
||||||
|
this.cancellationTokenSource = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Return the AI response:
|
// Return the AI response:
|
||||||
return aiText.Text;
|
return aiText.Text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task CancelStreaming()
|
||||||
|
{
|
||||||
|
if (this.cancellationTokenSource is not null)
|
||||||
|
if(!this.cancellationTokenSource.IsCancellationRequested)
|
||||||
|
await this.cancellationTokenSource.CancelAsync();
|
||||||
|
}
|
||||||
|
|
||||||
protected async Task CopyToClipboard()
|
protected async Task CopyToClipboard()
|
||||||
{
|
{
|
||||||
await this.RustService.CopyText2Clipboard(this.Snackbar, this.Result2Copy());
|
await this.RustService.CopyText2Clipboard(this.Snackbar, this.Result2Copy());
|
||||||
@ -314,7 +313,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase, IMe
|
|||||||
protected async Task OpenSettingsDialog()
|
protected async Task OpenSettingsDialog()
|
||||||
{
|
{
|
||||||
var dialogParameters = new DialogParameters();
|
var dialogParameters = new DialogParameters();
|
||||||
await this.DialogService.ShowAsync<TSettings>("Open Settings", dialogParameters, DialogOptions.FULLSCREEN);
|
await this.DialogService.ShowAsync<TSettings>(null, dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Task SendToAssistant(Tools.Components destination, SendToButton sendToButton)
|
protected Task SendToAssistant(Tools.Components destination, SendToButton sendToButton)
|
||||||
@ -381,11 +380,10 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase, IMe
|
|||||||
false => $"background-color: {this.ColorTheme.GetCurrentPalette(this.SettingsManager).InfoLighten}",
|
false => $"background-color: {this.ColorTheme.GetCurrentPalette(this.SettingsManager).InfoLighten}",
|
||||||
};
|
};
|
||||||
|
|
||||||
#region Implementation of IDisposable
|
#region Overrides of MSGComponentBase
|
||||||
|
|
||||||
public void Dispose()
|
protected override void DisposeResources()
|
||||||
{
|
{
|
||||||
this.MessageBus.Unregister(this);
|
|
||||||
this.formChangeTimer.Dispose();
|
this.formChangeTimer.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
using Microsoft.AspNetCore.Components;
|
using AIStudio.Components;
|
||||||
|
|
||||||
namespace AIStudio.Assistants;
|
namespace AIStudio.Assistants;
|
||||||
|
|
||||||
public abstract class AssistantLowerBase : ComponentBase
|
public abstract class AssistantLowerBase : MSGComponentBase
|
||||||
{
|
{
|
||||||
protected static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();
|
protected static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();
|
||||||
|
|
||||||
|
@ -5,10 +5,10 @@
|
|||||||
<b>Links:</b>
|
<b>Links:</b>
|
||||||
</MudText>
|
</MudText>
|
||||||
<MudList T="string" Class="mb-6">
|
<MudList T="string" Class="mb-6">
|
||||||
<MudListItem T="string" Icon="@Icons.Material.Filled.Link" Target="_blank" Href="https://en.wikipedia.org/wiki/List_of_cognitive_biases">Wikipedia list of cognitive biases</MudListItem>
|
<MudListItem T="string" Icon="@Icons.Material.Filled.Link" Target="_blank" Href="https://en.wikipedia.org/wiki/List_of_cognitive_biases">@T("Wikipedia list of cognitive biases")</MudListItem>
|
||||||
<MudListItem T="string" Icon="@Icons.Material.Filled.Link" Target="_blank" Href="https://commons.wikimedia.org/wiki/File:Cognitive_Bias_Codex_With_Definitions_1-2,_an_Extension_of_the_work_of_John_Manoogian_by_Brian_Rene_Morrissette.png">Extended bias poster</MudListItem>
|
<MudListItem T="string" Icon="@Icons.Material.Filled.Link" Target="_blank" Href="https://commons.wikimedia.org/wiki/File:Cognitive_Bias_Codex_With_Definitions_1-2,_an_Extension_of_the_work_of_John_Manoogian_by_Brian_Rene_Morrissette.png">@T("Extended bias poster")</MudListItem>
|
||||||
<MudListItem T="string" Icon="@Icons.Material.Filled.Link" Target="_blank" Href="https://betterhumans.pub/cognitive-bias-cheat-sheet-55a472476b18">Blog post of Buster Benson: "Cognitive bias cheat sheet"</MudListItem>
|
<MudListItem T="string" Icon="@Icons.Material.Filled.Link" Target="_blank" Href="https://betterhumans.pub/cognitive-bias-cheat-sheet-55a472476b18">@T("Blog post of Buster Benson:") "Cognitive bias cheat sheet"</MudListItem>
|
||||||
</MudList>
|
</MudList>
|
||||||
|
|
||||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelecting())" @bind-Value="@this.selectedTargetLanguage" ValidateSelection="@this.ValidateTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Target language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom target language" />
|
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelecting())" @bind-Value="@this.selectedTargetLanguage" ValidateSelection="@this.ValidateTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="@T("Target language")" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="@T("Custom target language")" />
|
||||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
@ -10,18 +10,9 @@ public partial class BiasOfTheDayAssistant : AssistantBaseCore<SettingsDialogAss
|
|||||||
{
|
{
|
||||||
public override Tools.Components Component => Tools.Components.BIAS_DAY_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.BIAS_DAY_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Bias of the Day";
|
protected override string Title => T("Bias of the Day");
|
||||||
|
|
||||||
protected override string Description =>
|
protected override string Description => T("""Learn about a different cognitive bias every day. You can also ask the LLM your questions. The idea behind "Bias of the Day" is based on work by Buster Benson, John Manoogian III, and Brian Rene Morrissette. Buster Benson grouped the biases, and the original texts come from Wikipedia. Brian Rene Morrissette condensed them into a shorter version. Finally, John Manoogian III created the original poster based on Benson's work and Morrissette's texts. Thorsten Sommer compared all texts for integration into AI Studio with the current Wikipedia versions, updated them, and added source references. The idea of learning about one bias each day based on John's poster comes from Drew Nelson.""");
|
||||||
"""
|
|
||||||
Learn about a different cognitive bias every day. You can also ask the LLM your questions. The idea behind
|
|
||||||
"Bias of the Day" is based on work by Buster Benson, John Manoogian III, and Brian Rene Morrissette. Buster
|
|
||||||
Benson grouped the biases, and the original texts come from Wikipedia. Brian Rene Morrissette condensed them
|
|
||||||
into a shorter version. Finally, John Manoogian III created the original poster based on Benson's work and
|
|
||||||
Morrissette's texts. Thorsten Sommer compared all texts for integration into AI Studio with the current Wikipedia
|
|
||||||
versions, updated them, and added source references. The idea of learning about one bias each day based on John's
|
|
||||||
poster comes from Drew Nelson.
|
|
||||||
""";
|
|
||||||
|
|
||||||
protected override string SystemPrompt => $"""
|
protected override string SystemPrompt => $"""
|
||||||
You are a friendly, helpful expert on cognitive bias. You studied psychology and
|
You are a friendly, helpful expert on cognitive bias. You studied psychology and
|
||||||
@ -41,7 +32,7 @@ public partial class BiasOfTheDayAssistant : AssistantBaseCore<SettingsDialogAss
|
|||||||
|
|
||||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||||
|
|
||||||
protected override string SubmitText => "Show me the bias of the day";
|
protected override string SubmitText => T("Show me the bias of the day");
|
||||||
|
|
||||||
protected override Func<Task> SubmitAction => this.TellBias;
|
protected override Func<Task> SubmitAction => this.TellBias;
|
||||||
|
|
||||||
@ -79,7 +70,7 @@ public partial class BiasOfTheDayAssistant : AssistantBaseCore<SettingsDialogAss
|
|||||||
private string? ValidateTargetLanguage(CommonLanguages language)
|
private string? ValidateTargetLanguage(CommonLanguages language)
|
||||||
{
|
{
|
||||||
if(language is CommonLanguages.AS_IS)
|
if(language is CommonLanguages.AS_IS)
|
||||||
return "Please select a target language for the bias.";
|
return T("Please select a target language for the bias.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -87,7 +78,7 @@ public partial class BiasOfTheDayAssistant : AssistantBaseCore<SettingsDialogAss
|
|||||||
private string? ValidateCustomLanguage(string language)
|
private string? ValidateCustomLanguage(string language)
|
||||||
{
|
{
|
||||||
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||||
return "Please provide a custom language.";
|
return T("Please provide a custom language.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -6,22 +6,22 @@
|
|||||||
{
|
{
|
||||||
var codingContext = this.codingContexts[contextIndex];
|
var codingContext = this.codingContexts[contextIndex];
|
||||||
var index = contextIndex;
|
var index = contextIndex;
|
||||||
<ExpansionPanel HeaderText="@codingContext.Id" HeaderIcon="@Icons.Material.Filled.Code" ShowEndButton="@true" EndButtonColor="Color.Error" EndButtonIcon="@Icons.Material.Filled.Delete" EndButtonTooltip="Delete context" EndButtonClickAsync="@(() => this.DeleteContext(index))">
|
<ExpansionPanel HeaderText="@codingContext.Id" HeaderIcon="@Icons.Material.Filled.Code" ShowEndButton="@true" EndButtonColor="Color.Error" EndButtonIcon="@Icons.Material.Filled.Delete" EndButtonTooltip="@T("Delete context")" EndButtonClickAsync="@(() => this.DeleteContext(index))">
|
||||||
<CodingContextItem @bind-CodingContext="@codingContext"/>
|
<CodingContextItem @bind-CodingContext="@codingContext"/>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
}
|
}
|
||||||
</MudExpansionPanels>
|
</MudExpansionPanels>
|
||||||
<MudButton Variant="Variant.Filled" OnClick="() => this.AddCodingContext()" Class="mb-3">
|
<MudButton Variant="Variant.Filled" OnClick="() => this.AddCodingContext()" Class="mb-3">
|
||||||
Add context
|
@T("Add context")
|
||||||
</MudButton>
|
</MudButton>
|
||||||
|
|
||||||
<MudStack Row="@false" Class="mb-3">
|
<MudStack Row="@false" Class="mb-3">
|
||||||
<MudTextSwitch Label="Do you want to provide compiler messages?" @bind-Value="@this.provideCompilerMessages" LabelOn="Yes, provide compiler messages" LabelOff="No, there are no compiler messages" />
|
<MudTextSwitch Label="@T("Do you want to provide compiler messages?")" @bind-Value="@this.provideCompilerMessages" LabelOn="@T("Yes, provide compiler messages")" LabelOff="@T("No, there are no compiler messages")" />
|
||||||
@if (this.provideCompilerMessages)
|
@if (this.provideCompilerMessages)
|
||||||
{
|
{
|
||||||
<MudTextField T="string" @bind-Text="@this.compilerMessages" Validation="@this.ValidatingCompilerMessages" AdornmentIcon="@Icons.Material.Filled.Error" Adornment="Adornment.Start" Label="Compiler messages" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
<MudTextField T="string" @bind-Text="@this.compilerMessages" Validation="@this.ValidatingCompilerMessages" AdornmentIcon="@Icons.Material.Filled.Error" Adornment="Adornment.Start" Label="@T("Compiler messages")" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||||
}
|
}
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
|
||||||
<MudTextField T="string" @bind-Text="@this.questions" Validation="@this.ValidateQuestions" AdornmentIcon="@Icons.Material.Filled.QuestionMark" Adornment="Adornment.Start" Label="Your question(s)" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
<MudTextField T="string" @bind-Text="@this.questions" Validation="@this.ValidateQuestions" AdornmentIcon="@Icons.Material.Filled.QuestionMark" Adornment="Adornment.Start" Label="@T("Your question(s)")" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
@ -8,14 +8,9 @@ public partial class AssistantCoding : AssistantBaseCore<SettingsDialogCoding>
|
|||||||
{
|
{
|
||||||
public override Tools.Components Component => Tools.Components.CODING_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.CODING_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Coding Assistant";
|
protected override string Title => T("Coding Assistant");
|
||||||
|
|
||||||
protected override string Description =>
|
protected override string Description => T("This coding assistant supports you in writing code. Provide some coding context by copying and pasting your code into the input fields. You might assign an ID to your code snippet to easily reference it later. When you have compiler messages, you can paste them into the input fields to get help with debugging as well.");
|
||||||
"""
|
|
||||||
This coding assistant supports you in writing code. Provide some coding context by copying and pasting
|
|
||||||
your code into the input fields. You might assign an ID to your code snippet to easily reference it later.
|
|
||||||
When you have compiler messages, you can paste them into the input fields to get help with debugging as well.
|
|
||||||
""";
|
|
||||||
|
|
||||||
protected override string SystemPrompt =>
|
protected override string SystemPrompt =>
|
||||||
"""
|
"""
|
||||||
@ -30,7 +25,7 @@ public partial class AssistantCoding : AssistantBaseCore<SettingsDialogCoding>
|
|||||||
|
|
||||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||||
|
|
||||||
protected override string SubmitText => "Get Support";
|
protected override string SubmitText => T("Get Support");
|
||||||
|
|
||||||
protected override Func<Task> SubmitAction => this.GetSupport;
|
protected override Func<Task> SubmitAction => this.GetSupport;
|
||||||
|
|
||||||
@ -80,7 +75,7 @@ public partial class AssistantCoding : AssistantBaseCore<SettingsDialogCoding>
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(checkCompilerMessages))
|
if(string.IsNullOrWhiteSpace(checkCompilerMessages))
|
||||||
return "Please provide the compiler messages.";
|
return T("Please provide the compiler messages.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -88,7 +83,7 @@ public partial class AssistantCoding : AssistantBaseCore<SettingsDialogCoding>
|
|||||||
private string? ValidateQuestions(string checkQuestions)
|
private string? ValidateQuestions(string checkQuestions)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(checkQuestions))
|
if(string.IsNullOrWhiteSpace(checkQuestions))
|
||||||
return "Please provide your questions.";
|
return T("Please provide your questions.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -97,7 +92,7 @@ public partial class AssistantCoding : AssistantBaseCore<SettingsDialogCoding>
|
|||||||
{
|
{
|
||||||
this.codingContexts.Add(new()
|
this.codingContexts.Add(new()
|
||||||
{
|
{
|
||||||
Id = $"Context {this.codingContexts.Count + 1}",
|
Id = string.Format(T("Context {0}"), this.codingContexts.Count + 1),
|
||||||
Language = this.SettingsManager.ConfigurationData.Coding.PreselectOptions ? this.SettingsManager.ConfigurationData.Coding.PreselectedProgrammingLanguage : default,
|
Language = this.SettingsManager.ConfigurationData.Coding.PreselectOptions ? this.SettingsManager.ConfigurationData.Coding.PreselectedProgrammingLanguage : default,
|
||||||
OtherLanguage = this.SettingsManager.ConfigurationData.Coding.PreselectOptions ? this.SettingsManager.ConfigurationData.Coding.PreselectedOtherProgrammingLanguage : string.Empty,
|
OtherLanguage = this.SettingsManager.ConfigurationData.Coding.PreselectOptions ? this.SettingsManager.ConfigurationData.Coding.PreselectedOtherProgrammingLanguage : string.Empty,
|
||||||
});
|
});
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
<MudTextField T="string" @bind-Text="@this.CodingContext.Id" AdornmentIcon="@Icons.Material.Filled.Numbers" Adornment="Adornment.Start" Label="(Optional) Identifier" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
@inherits MSGComponentBase
|
||||||
|
|
||||||
|
<MudTextField T="string" @bind-Text="@this.CodingContext.Id" AdornmentIcon="@Icons.Material.Filled.Numbers" Adornment="Adornment.Start" Label="@T("(Optional) Identifier")" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||||
<MudStack Row="@true" Class="mb-3">
|
<MudStack Row="@true" Class="mb-3">
|
||||||
<MudSelect T="CommonCodingLanguages" @bind-Value="@this.CodingContext.Language" AdornmentIcon="@Icons.Material.Filled.Code" Adornment="Adornment.Start" Label="Language" Variant="Variant.Outlined" Margin="Margin.Dense">
|
<MudSelect T="CommonCodingLanguages" @bind-Value="@this.CodingContext.Language" AdornmentIcon="@Icons.Material.Filled.Code" Adornment="Adornment.Start" Label="@T("Language")" Variant="Variant.Outlined" Margin="Margin.Dense">
|
||||||
@foreach (var language in Enum.GetValues<CommonCodingLanguages>())
|
@foreach (var language in Enum.GetValues<CommonCodingLanguages>())
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="@language">@language.Name()</MudSelectItem>
|
<MudSelectItem Value="@language">
|
||||||
|
@language.Name()
|
||||||
|
</MudSelectItem>
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
@if (this.CodingContext.Language is CommonCodingLanguages.OTHER)
|
@if (this.CodingContext.Language is CommonCodingLanguages.OTHER)
|
||||||
{
|
{
|
||||||
<MudTextField T="string" @bind-Text="@this.CodingContext.OtherLanguage" Validation="@this.ValidatingOtherLanguage" Label="Other language" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
<MudTextField T="string" @bind-Text="@this.CodingContext.OtherLanguage" Validation="@this.ValidatingOtherLanguage" Label="@T("Other language")" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||||
}
|
}
|
||||||
</MudStack>
|
</MudStack>
|
||||||
<MudTextField T="string" @bind-Text="@this.CodingContext.Code" Validation="@this.ValidatingCode" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="Your code" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" />
|
<MudTextField T="string" @bind-Text="@this.CodingContext.Code" Validation="@this.ValidatingCode" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="@T("Your code")" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" />
|
@ -1,10 +1,10 @@
|
|||||||
using AIStudio.Settings;
|
using AIStudio.Components;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
namespace AIStudio.Assistants.Coding;
|
namespace AIStudio.Assistants.Coding;
|
||||||
|
|
||||||
public partial class CodingContextItem : ComponentBase
|
public partial class CodingContextItem : MSGComponentBase
|
||||||
{
|
{
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public CodingContext CodingContext { get; set; } = new();
|
public CodingContext CodingContext { get; set; } = new();
|
||||||
@ -12,9 +12,6 @@ public partial class CodingContextItem : ComponentBase
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public EventCallback<CodingContext> CodingContextChanged { get; set; }
|
public EventCallback<CodingContext> CodingContextChanged { get; set; }
|
||||||
|
|
||||||
[Inject]
|
|
||||||
protected SettingsManager SettingsManager { get; set; } = null!;
|
|
||||||
|
|
||||||
private static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();
|
private static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();
|
||||||
|
|
||||||
#region Overrides of ComponentBase
|
#region Overrides of ComponentBase
|
||||||
@ -32,7 +29,7 @@ public partial class CodingContextItem : ComponentBase
|
|||||||
private string? ValidatingCode(string code)
|
private string? ValidatingCode(string code)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(code))
|
if(string.IsNullOrWhiteSpace(code))
|
||||||
return $"{this.CodingContext.Id}: Please provide your input.";
|
return string.Format(T("{0}: Please provide your input."), this.CodingContext.Id);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -43,7 +40,7 @@ public partial class CodingContextItem : ComponentBase
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(language))
|
if(string.IsNullOrWhiteSpace(language))
|
||||||
return "Please specify the language.";
|
return T("Please specify the language.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
public static class CommonCodingLanguageExtensions
|
public static class CommonCodingLanguageExtensions
|
||||||
{
|
{
|
||||||
|
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(CommonCodingLanguageExtensions).Namespace, nameof(CommonCodingLanguageExtensions));
|
||||||
|
|
||||||
public static string Name(this CommonCodingLanguages language) => language switch
|
public static string Name(this CommonCodingLanguages language) => language switch
|
||||||
{
|
{
|
||||||
CommonCodingLanguages.NONE => "None",
|
CommonCodingLanguages.NONE => TB("None"),
|
||||||
|
|
||||||
CommonCodingLanguages.BASH => "Bash",
|
CommonCodingLanguages.BASH => "Bash",
|
||||||
CommonCodingLanguages.BLAZOR => ".NET Blazor",
|
CommonCodingLanguages.BLAZOR => ".NET Blazor",
|
||||||
@ -37,7 +39,7 @@ public static class CommonCodingLanguageExtensions
|
|||||||
CommonCodingLanguages.TYPESCRIPT => "TypeScript",
|
CommonCodingLanguages.TYPESCRIPT => "TypeScript",
|
||||||
CommonCodingLanguages.XML => "XML",
|
CommonCodingLanguages.XML => "XML",
|
||||||
|
|
||||||
CommonCodingLanguages.OTHER => "Other",
|
CommonCodingLanguages.OTHER => TB("Other"),
|
||||||
_ => "Unknown"
|
_ => TB("Unknown")
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -1,23 +1,25 @@
|
|||||||
@attribute [Route(Routes.ASSISTANT_EMAIL)]
|
@attribute [Route(Routes.ASSISTANT_EMAIL)]
|
||||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogWritingEMails>
|
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogWritingEMails>
|
||||||
|
|
||||||
<MudTextSwitch Label="Is there a history, a previous conversation?" @bind-Value="@this.provideHistory" LabelOn="Yes, I provide the previous conversation" LabelOff="No, I don't provide a previous conversation" />
|
<MudTextSwitch Label="@T("Is there a history, a previous conversation?")" @bind-Value="@this.provideHistory" LabelOn="@T("Yes, I provide the previous conversation")" LabelOff="@T("No, I don't provide a previous conversation")" />
|
||||||
@if (this.provideHistory)
|
@if (this.provideHistory)
|
||||||
{
|
{
|
||||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||||
<MudTextField T="string" @bind-Text="@this.inputHistory" Validation="@this.ValidateHistory" Label="Previous conversation" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Provide the previous conversation, e.g., the last e-mail, the last chat, etc." Class="mb-3" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.DocumentScanner"/>
|
<MudTextField T="string" @bind-Text="@this.inputHistory" Validation="@this.ValidateHistory" Label="@T("Previous conversation")" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="@T("Provide the previous conversation, e.g., the last e-mail, the last chat, etc.")" Class="mb-3" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.DocumentScanner"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
}
|
}
|
||||||
|
|
||||||
<MudTextField T="string" @bind-Text="@this.inputGreeting" Label="(Optional) The greeting phrase to use" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Person" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Placeholder="Dear Colleagues" Class="mb-3"/>
|
<MudTextField T="string" @bind-Text="@this.inputGreeting" Label="@T("(Optional) The greeting phrase to use")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Person" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Placeholder="@T("Dear Colleagues")" Class="mb-3"/>
|
||||||
<MudTextField T="string" @bind-Text="@this.inputBulletPoints" Validation="@this.ValidateBulletPoints" AdornmentIcon="@Icons.Material.Filled.ListAlt" Adornment="Adornment.Start" Label="Your bullet points" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Bullet list the content of the e-mail roughly. Use dashes (-) to separate the items." Immediate="@false" DebounceInterval="1_000" OnDebounceIntervalElapsed="@this.OnContentChanged" Placeholder="@PLACEHOLDER_BULLET_POINTS"/>
|
<DebouncedTextField @bind-Text="@this.inputBulletPoints" ValidationFunc="@this.ValidateBulletPoints" Icon="@Icons.Material.Filled.ListAlt" Label="@T("Your bullet points")" Lines="6" MaxLines="12" Attributes="@USER_INPUT_ATTRIBUTES" HelpText="@T("Bullet list the content of the e-mail roughly. Use dashes (-) to separate the items.")" DebounceTime="TimeSpan.FromSeconds(1)" WhenTextCanged="@this.OnContentChanged" Placeholder="@PLACEHOLDER_BULLET_POINTS"/>
|
||||||
<MudSelect T="string" Label="(Optional) Are any of your points particularly important?" MultiSelection="@true" @bind-SelectedValues="@this.selectedFoci" Variant="Variant.Outlined" Class="mb-3" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.ListAlt">
|
<MudSelect T="string" Label="@T("(Optional) Are any of your points particularly important?")" MultiSelection="@true" @bind-SelectedValues="@this.selectedFoci" Variant="Variant.Outlined" Class="mb-3" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.ListAlt">
|
||||||
@foreach (var contentLine in this.bulletPointsLines)
|
@foreach (var contentLine in this.bulletPointsLines)
|
||||||
{
|
{
|
||||||
<MudSelectItem T="string" Value="@contentLine">@contentLine</MudSelectItem>
|
<MudSelectItem T="string" Value="@contentLine">
|
||||||
|
@contentLine
|
||||||
|
</MudSelectItem>
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
<MudTextField T="string" @bind-Text="@this.inputName" Label="(Optional) Your name for the closing salutation" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Person" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Your name for the closing salutation of your e-mail." Class="mb-3"/>
|
<MudTextField T="string" @bind-Text="@this.inputName" Label="@T("(Optional) Your name for the closing salutation")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Person" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="@T("Your name for the closing salutation of your e-mail.")" Class="mb-3"/>
|
||||||
<EnumSelection T="WritingStyles" NameFunc="@(style => style.Name())" @bind-Value="@this.selectedWritingStyle" Icon="@Icons.Material.Filled.Edit" Label="Select the writing style" ValidateSelection="@this.ValidateWritingStyle"/>
|
<EnumSelection T="WritingStyles" NameFunc="@(style => style.Name())" @bind-Value="@this.selectedWritingStyle" Icon="@Icons.Material.Filled.Edit" Label="@T("Select the writing style")" ValidateSelection="@this.ValidateWritingStyle"/>
|
||||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelecting())" @bind-Value="@this.selectedTargetLanguage" ValidateSelection="@this.ValidateTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Target language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom target language" />
|
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelecting())" @bind-Value="@this.selectedTargetLanguage" ValidateSelection="@this.ValidateTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="@T("Target language")" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="@T("Custom target language")" />
|
||||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
@ -9,12 +9,9 @@ public partial class AssistantEMail : AssistantBaseCore<SettingsDialogWritingEMa
|
|||||||
{
|
{
|
||||||
public override Tools.Components Component => Tools.Components.EMAIL_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.EMAIL_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "E-Mail";
|
protected override string Title => T("E-Mail");
|
||||||
|
|
||||||
protected override string Description =>
|
protected override string Description => T("Provide a list of bullet points and some basic information for an e-mail. The assistant will generate an e-mail based on that input.");
|
||||||
"""
|
|
||||||
Provide a list of bullet points and some basic information for an e-mail. The assistant will generate an e-mail based on that input.
|
|
||||||
""";
|
|
||||||
|
|
||||||
protected override string SystemPrompt =>
|
protected override string SystemPrompt =>
|
||||||
$"""
|
$"""
|
||||||
@ -25,7 +22,7 @@ public partial class AssistantEMail : AssistantBaseCore<SettingsDialogWritingEMa
|
|||||||
|
|
||||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||||
|
|
||||||
protected override string SubmitText => "Create email";
|
protected override string SubmitText => T("Create email");
|
||||||
|
|
||||||
protected override Func<Task> SubmitAction => this.CreateMail;
|
protected override Func<Task> SubmitAction => this.CreateMail;
|
||||||
|
|
||||||
@ -100,12 +97,12 @@ public partial class AssistantEMail : AssistantBaseCore<SettingsDialogWritingEMa
|
|||||||
private string? ValidateBulletPoints(string content)
|
private string? ValidateBulletPoints(string content)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(content))
|
if(string.IsNullOrWhiteSpace(content))
|
||||||
return "Please provide some content for the e-mail.";
|
return T("Please provide some content for the e-mail.");
|
||||||
|
|
||||||
var lines = content.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
var lines = content.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||||
foreach (var line in lines)
|
foreach (var line in lines)
|
||||||
if(!line.TrimStart().StartsWith('-'))
|
if(!line.TrimStart().StartsWith('-'))
|
||||||
return "Please start each line of your content list with a dash (-) to create a bullet point list.";
|
return T("Please start each line of your content list with a dash (-) to create a bullet point list.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -113,7 +110,7 @@ public partial class AssistantEMail : AssistantBaseCore<SettingsDialogWritingEMa
|
|||||||
private string? ValidateTargetLanguage(CommonLanguages language)
|
private string? ValidateTargetLanguage(CommonLanguages language)
|
||||||
{
|
{
|
||||||
if(language is CommonLanguages.AS_IS)
|
if(language is CommonLanguages.AS_IS)
|
||||||
return "Please select a target language for the e-mail.";
|
return T("Please select a target language for the e-mail.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -121,7 +118,7 @@ public partial class AssistantEMail : AssistantBaseCore<SettingsDialogWritingEMa
|
|||||||
private string? ValidateCustomLanguage(string language)
|
private string? ValidateCustomLanguage(string language)
|
||||||
{
|
{
|
||||||
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||||
return "Please provide a custom language.";
|
return T("Please provide a custom language.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -129,7 +126,7 @@ public partial class AssistantEMail : AssistantBaseCore<SettingsDialogWritingEMa
|
|||||||
private string? ValidateWritingStyle(WritingStyles style)
|
private string? ValidateWritingStyle(WritingStyles style)
|
||||||
{
|
{
|
||||||
if(style == WritingStyles.NONE)
|
if(style == WritingStyles.NONE)
|
||||||
return "Please select a writing style for the e-mail.";
|
return T("Please select a writing style for the e-mail.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -137,7 +134,7 @@ public partial class AssistantEMail : AssistantBaseCore<SettingsDialogWritingEMa
|
|||||||
private string? ValidateHistory(string history)
|
private string? ValidateHistory(string history)
|
||||||
{
|
{
|
||||||
if(this.provideHistory && string.IsNullOrWhiteSpace(history))
|
if(this.provideHistory && string.IsNullOrWhiteSpace(history))
|
||||||
return "Please provide some history for the e-mail.";
|
return T("Please provide some history for the e-mail.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,16 @@ namespace AIStudio.Assistants.EMail;
|
|||||||
|
|
||||||
public static class WritingStylesExtensions
|
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
|
public static string Name(this WritingStyles style) => style switch
|
||||||
{
|
{
|
||||||
WritingStyles.ACADEMIC => "Academic",
|
WritingStyles.ACADEMIC => TB("Academic"),
|
||||||
WritingStyles.PERSONAL => "Personal",
|
WritingStyles.PERSONAL => TB("Personal"),
|
||||||
WritingStyles.BUSINESS_FORMAL => "Business formal",
|
WritingStyles.BUSINESS_FORMAL => TB("Business formal"),
|
||||||
WritingStyles.BUSINESS_INFORMAL => "Business informal",
|
WritingStyles.BUSINESS_INFORMAL => TB("Business informal"),
|
||||||
|
|
||||||
_ => "Not specified",
|
_ => TB("Not specified"),
|
||||||
};
|
};
|
||||||
|
|
||||||
public static string Prompt(this WritingStyles style) => style switch
|
public static string Prompt(this WritingStyles style) => style switch
|
||||||
|
@ -2,12 +2,14 @@ namespace AIStudio.Assistants.ERI;
|
|||||||
|
|
||||||
public static class AllowedLLMProvidersExtensions
|
public static class AllowedLLMProvidersExtensions
|
||||||
{
|
{
|
||||||
|
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(AllowedLLMProvidersExtensions).Namespace, nameof(AllowedLLMProvidersExtensions));
|
||||||
|
|
||||||
public static string Description(this AllowedLLMProviders provider) => provider switch
|
public static string Description(this AllowedLLMProviders provider) => provider switch
|
||||||
{
|
{
|
||||||
AllowedLLMProviders.NONE => "Please select what kind of LLM provider are allowed for this data source",
|
AllowedLLMProviders.NONE => TB("Please select what kind of LLM provider are allowed for this data source"),
|
||||||
AllowedLLMProviders.ANY => "Any LLM provider is allowed: users might choose a cloud-based or a self-hosted provider",
|
AllowedLLMProviders.ANY => TB("Any LLM provider is allowed: users might choose a cloud-based or a self-hosted provider"),
|
||||||
AllowedLLMProviders.SELF_HOSTED => "Self-hosted LLM providers are allowed: users cannot choose any cloud-based provider",
|
AllowedLLMProviders.SELF_HOSTED => TB("Self-hosted LLM providers are allowed: users cannot choose any cloud-based provider"),
|
||||||
|
|
||||||
_ => "Unknown option was selected"
|
_ => TB("Unknown option was selected")
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -4,36 +4,38 @@
|
|||||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogERIServer>
|
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogERIServer>
|
||||||
|
|
||||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||||
You can imagine it like this: Hypothetically, when Wikipedia implemented the ERI, it would vectorize
|
@T("You can imagine it like this: Hypothetically, when Wikipedia implemented the ERI, it would vectorize all pages using an embedding method. All of Wikipedia’s data would remain with Wikipedia, including the vector database (decentralized approach). Then, any AI Studio user could add Wikipedia as a data source to significantly reduce the hallucination of the LLM in knowledge questions.")
|
||||||
all pages using an embedding method. All of Wikipedia’s data would remain with Wikipedia, including the
|
|
||||||
vector database (decentralized approach). Then, any AI Studio user could add Wikipedia as a data source to
|
|
||||||
significantly reduce the hallucination of the LLM in knowledge questions.
|
|
||||||
</MudJustifiedText>
|
</MudJustifiedText>
|
||||||
|
|
||||||
<MudText Typo="Typo.body1">
|
<MudText Typo="Typo.body1">
|
||||||
<b>Related links:</b>
|
<b>
|
||||||
|
@T("Related links:")
|
||||||
|
</b>
|
||||||
</MudText>
|
</MudText>
|
||||||
<MudList T="string" Class="mb-6">
|
<MudList T="string" Class="mb-6">
|
||||||
<MudListItem T="string" Icon="@Icons.Material.Filled.Link" Target="_blank" Href="https://github.com/MindWorkAI/ERI">ERI repository with example implementation in .NET and C#</MudListItem>
|
<MudListItem T="string" Icon="@Icons.Material.Filled.Link" Target="_blank" Href="https://github.com/MindWorkAI/ERI">
|
||||||
<MudListItem T="string" Icon="@Icons.Material.Filled.Link" Target="_blank" Href="https://mindworkai.org/swagger-ui.html">Interactive documentation aka Swagger UI</MudListItem>
|
@T("ERI repository with example implementation in .NET and C#")
|
||||||
|
</MudListItem>
|
||||||
|
<MudListItem T="string" Icon="@Icons.Material.Filled.Link" Target="_blank" Href="https://mindworkai.org/swagger-ui.html">
|
||||||
|
@T("Interactive documentation aka Swagger UI")
|
||||||
|
</MudListItem>
|
||||||
</MudList>
|
</MudList>
|
||||||
|
|
||||||
<PreviewPrototype/>
|
<PreviewPrototype/>
|
||||||
<div class="mb-6"></div>
|
<div class="mb-6"></div>
|
||||||
|
|
||||||
<MudText Typo="Typo.h4" Class="mb-3">
|
<MudText Typo="Typo.h4" Class="mb-3">
|
||||||
ERI server presets
|
@T("ERI server presets")
|
||||||
</MudText>
|
</MudText>
|
||||||
|
|
||||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||||
Here you have the option to save different configurations for various ERI servers and switch between them. This is useful if
|
@T("Here you have the option to save different configurations for various ERI servers and switch between them. This is useful if you are responsible for multiple ERI servers.")
|
||||||
you are responsible for multiple ERI servers.
|
|
||||||
</MudJustifiedText>
|
</MudJustifiedText>
|
||||||
|
|
||||||
@if(this.SettingsManager.ConfigurationData.ERI.ERIServers.Count is 0)
|
@if(this.SettingsManager.ConfigurationData.ERI.ERIServers.Count is 0)
|
||||||
{
|
{
|
||||||
<MudText Typo="Typo.body1" Class="mb-3">
|
<MudText Typo="Typo.body1" Class="mb-3">
|
||||||
You have not yet added any ERI server presets.
|
@T("You have not yet added any ERI server presets.")
|
||||||
</MudText>
|
</MudText>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -50,112 +52,116 @@ else
|
|||||||
|
|
||||||
<MudStack Row="@true" Class="mt-1">
|
<MudStack Row="@true" Class="mt-1">
|
||||||
<MudButton Disabled="@this.AreServerPresetsBlocked" OnClick="@this.AddERIServer" Variant="Variant.Filled" Color="Color.Primary">
|
<MudButton Disabled="@this.AreServerPresetsBlocked" OnClick="@this.AddERIServer" Variant="Variant.Filled" Color="Color.Primary">
|
||||||
Add ERI server preset
|
@T("Add ERI server preset")
|
||||||
</MudButton>
|
</MudButton>
|
||||||
<MudButton OnClick="@this.RemoveERIServer" Disabled="@(this.AreServerPresetsBlocked || this.IsNoneERIServerSelected)" Variant="Variant.Filled" Color="Color.Error">
|
<MudButton OnClick="@this.RemoveERIServer" Disabled="@(this.AreServerPresetsBlocked || this.IsNoneERIServerSelected)" Variant="Variant.Filled" Color="Color.Error">
|
||||||
Delete this server preset
|
@T("Delete this server preset")
|
||||||
</MudButton>
|
</MudButton>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
|
||||||
@if(this.AreServerPresetsBlocked)
|
@if(this.AreServerPresetsBlocked)
|
||||||
{
|
{
|
||||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3 mt-3">
|
<MudJustifiedText Typo="Typo.body1" Class="mb-3 mt-3">
|
||||||
Hint: to allow this assistant to manage multiple presets, you must enable the preselection of values in the settings.
|
@T("Hint: to allow this assistant to manage multiple presets, you must enable the preselection of values in the settings.")
|
||||||
</MudJustifiedText>
|
</MudJustifiedText>
|
||||||
}
|
}
|
||||||
|
|
||||||
<MudText Typo="Typo.h4" Class="mb-3 mt-6">
|
<MudText Typo="Typo.h4" Class="mb-3 mt-6">
|
||||||
Auto save
|
@T("Auto save")
|
||||||
</MudText>
|
</MudText>
|
||||||
|
|
||||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||||
The ERI specification will change over time. You probably want to keep your ERI server up to date. This means you might want to
|
@T("The ERI specification will change over time. You probably want to keep your ERI server up to date. This means you might want to regenerate the code for your ERI server. To avoid having to make all inputs each time, all your inputs and decisions can be automatically saved. Would you like this?")
|
||||||
regenerate the code for your ERI server. To avoid having to make all inputs each time, all your inputs and decisions can be
|
|
||||||
automatically saved. Would you like this?
|
|
||||||
</MudJustifiedText>
|
</MudJustifiedText>
|
||||||
|
|
||||||
@if(this.AreServerPresetsBlocked)
|
@if(this.AreServerPresetsBlocked)
|
||||||
{
|
{
|
||||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||||
Hint: to allow this assistant to automatically save your changes, you must enable the preselection of values in the settings.
|
@T("Hint: to allow this assistant to automatically save your changes, you must enable the preselection of values in the settings.")
|
||||||
</MudJustifiedText>
|
</MudJustifiedText>
|
||||||
}
|
}
|
||||||
|
|
||||||
<MudTextSwitch Label="Should we automatically save any input made?" Disabled="@this.AreServerPresetsBlocked" @bind-Value="@this.autoSave" LabelOn="Yes, please save my inputs" LabelOff="No, I will enter everything again or configure it manually in the settings" />
|
<MudTextSwitch Label="@T("Should we automatically save any input made?")" Disabled="@this.AreServerPresetsBlocked" @bind-Value="@this.autoSave" LabelOn="@T("Yes, please save my inputs")" LabelOff="@T("No, I will enter everything again or configure it manually in the settings")" />
|
||||||
|
|
||||||
<hr style="width: 100%; border-width: 0.25ch;" class="mt-6"/>
|
<hr style="width: 100%; border-width: 0.25ch;" class="mt-6"/>
|
||||||
|
|
||||||
<MudText Typo="Typo.h4" Class="mt-6 mb-1">
|
<MudText Typo="Typo.h4" Class="mt-6 mb-1">
|
||||||
Common ERI server settings
|
@T("Common ERI server settings")
|
||||||
</MudText>
|
</MudText>
|
||||||
<MudTextField T="string" Disabled="@this.IsNoneERIServerSelected" @bind-Text="@this.serverName" Validation="@this.ValidateServerName" Immediate="@true" Label="ERI server name" HelperText="Please give your ERI server a name that provides information about the data source and/or its intended purpose. The name will be displayed to users in AI Studio." Counter="60" MaxLength="60" Variant="Variant.Outlined" Margin="Margin.Normal" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3" OnKeyUp="() => this.ServerNameWasChanged()"/>
|
<MudTextField T="string" Disabled="@this.IsNoneERIServerSelected" @bind-Text="@this.serverName" Validation="@this.ValidateServerName" Immediate="@true" Label="@T("ERI server name")" HelperText="@T("Please give your ERI server a name that provides information about the data source and/or its intended purpose. The name will be displayed to users in AI Studio.")" Counter="60" MaxLength="60" Variant="Variant.Outlined" Margin="Margin.Normal" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3" OnKeyUp="() => this.ServerNameWasChanged()"/>
|
||||||
<MudTextField T="string" Disabled="@this.IsNoneERIServerSelected" @bind-Text="@this.serverDescription" Validation="@this.ValidateServerDescription" Immediate="@true" Label="ERI server description" HelperText="Please provide a brief description of your ERI server. Describe or explain what your ERI server does and what data it uses for this purpose. This description will be shown to users in AI Studio." Counter="512" MaxLength="512" Variant="Variant.Outlined" Margin="Margin.Normal" Lines="3" AutoGrow="@true" MaxLines="6" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
<MudTextField T="string" Disabled="@this.IsNoneERIServerSelected" @bind-Text="@this.serverDescription" Validation="@this.ValidateServerDescription" Immediate="@true" Label="@T("ERI server description")" HelperText="@T("Please provide a brief description of your ERI server. Describe or explain what your ERI server does and what data it uses for this purpose. This description will be shown to users in AI Studio.")" Counter="512" MaxLength="512" Variant="Variant.Outlined" Margin="Margin.Normal" Lines="3" AutoGrow="@true" MaxLines="6" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||||
|
|
||||||
<MudStack Row="@true" Class="mb-3">
|
<MudStack Row="@true" Class="mb-3">
|
||||||
<MudSelect Disabled="@this.IsNoneERIServerSelected" T="ProgrammingLanguages" @bind-Value="@this.selectedProgrammingLanguage" AdornmentIcon="@Icons.Material.Filled.Code" Adornment="Adornment.Start" Label="Programming language" Variant="Variant.Outlined" Margin="Margin.Dense" Validation="@this.ValidateProgrammingLanguage">
|
<MudSelect Disabled="@this.IsNoneERIServerSelected" T="ProgrammingLanguages" @bind-Value="@this.selectedProgrammingLanguage" AdornmentIcon="@Icons.Material.Filled.Code" Adornment="Adornment.Start" Label="@T("Programming language")" Variant="Variant.Outlined" Margin="Margin.Dense" Validation="@this.ValidateProgrammingLanguage">
|
||||||
@foreach (var language in Enum.GetValues<ProgrammingLanguages>())
|
@foreach (var language in Enum.GetValues<ProgrammingLanguages>())
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="@language">@language.Name()</MudSelectItem>
|
<MudSelectItem Value="@language">
|
||||||
|
@language.Name()
|
||||||
|
</MudSelectItem>
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
@if (this.selectedProgrammingLanguage is ProgrammingLanguages.OTHER)
|
@if (this.selectedProgrammingLanguage is ProgrammingLanguages.OTHER)
|
||||||
{
|
{
|
||||||
<MudTextField Disabled="@this.IsNoneERIServerSelected" T="string" @bind-Text="@this.otherProgrammingLanguage" Validation="@this.ValidateOtherLanguage" Label="Other language" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
<MudTextField Disabled="@this.IsNoneERIServerSelected" T="string" @bind-Text="@this.otherProgrammingLanguage" Validation="@this.ValidateOtherLanguage" Label="@T("Other language")" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||||
}
|
}
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
|
||||||
<MudStack Row="@true" AlignItems="AlignItems.Center" Class="mb-3">
|
<MudStack Row="@true" AlignItems="AlignItems.Center" Class="mb-3">
|
||||||
<MudSelect Disabled="@this.IsNoneERIServerSelected" T="ERIVersion" @bind-Value="@this.selectedERIVersion" Label="ERI specification version" Variant="Variant.Outlined" Margin="Margin.Dense" Validation="@this.ValidateERIVersion">
|
<MudSelect Disabled="@this.IsNoneERIServerSelected" T="ERIVersion" @bind-Value="@this.selectedERIVersion" Label="@T("ERI specification version")" Variant="Variant.Outlined" Margin="Margin.Dense" Validation="@this.ValidateERIVersion">
|
||||||
@foreach (var version in Enum.GetValues<ERIVersion>())
|
@foreach (var version in Enum.GetValues<ERIVersion>())
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="@version">@version</MudSelectItem>
|
<MudSelectItem Value="@version">
|
||||||
|
@version
|
||||||
|
</MudSelectItem>
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
<MudButton Variant="Variant.Outlined" Size="Size.Small" Disabled="@(!this.selectedERIVersion.WasSpecificationSelected() || this.IsNoneERIServerSelected)" Href="@this.selectedERIVersion.SpecificationURL()" Target="_blank">
|
<MudButton Variant="Variant.Outlined" Size="Size.Small" Disabled="@(!this.selectedERIVersion.WasSpecificationSelected() || this.IsNoneERIServerSelected)" Href="@this.selectedERIVersion.SpecificationURL()" Target="_blank">
|
||||||
<MudIcon Icon="@Icons.Material.Filled.Link" Class="mr-2"/> Download specification
|
<MudIcon Icon="@Icons.Material.Filled.Link" Class="mr-2"/> @T("Download specification")
|
||||||
</MudButton>
|
</MudButton>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
|
||||||
<MudText Typo="Typo.h4" Class="mt-9 mb-3">
|
<MudText Typo="Typo.h4" Class="mt-9 mb-3">
|
||||||
Data source settings
|
@T("Data source settings")
|
||||||
</MudText>
|
</MudText>
|
||||||
|
|
||||||
<MudStack Row="@false" Spacing="1" Class="mb-3">
|
<MudStack Row="@false" Spacing="1" Class="mb-3">
|
||||||
<MudSelect Disabled="@this.IsNoneERIServerSelected" T="DataSources" @bind-Value="@this.selectedDataSource" AdornmentIcon="@Icons.Material.Filled.Dataset" Adornment="Adornment.Start" Label="Data source" Variant="Variant.Outlined" Margin="Margin.Dense" Validation="@this.ValidateDataSource" SelectedValuesChanged="@this.DataSourceWasChanged">
|
<MudSelect Disabled="@this.IsNoneERIServerSelected" T="DataSources" @bind-Value="@this.selectedDataSource" AdornmentIcon="@Icons.Material.Filled.Dataset" Adornment="Adornment.Start" Label="@T("Data source")" Variant="Variant.Outlined" Margin="Margin.Dense" Validation="@this.ValidateDataSource" SelectedValuesChanged="@this.DataSourceWasChanged">
|
||||||
@foreach (var dataSource in Enum.GetValues<DataSources>())
|
@foreach (var dataSource in Enum.GetValues<DataSources>())
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="@dataSource">@dataSource.Name()</MudSelectItem>
|
<MudSelectItem Value="@dataSource">
|
||||||
|
@dataSource.Name()
|
||||||
|
</MudSelectItem>
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
@if (this.selectedDataSource is DataSources.CUSTOM)
|
@if (this.selectedDataSource is DataSources.CUSTOM)
|
||||||
{
|
{
|
||||||
<MudTextField Disabled="@this.IsNoneERIServerSelected" T="string" @bind-Text="@this.otherDataSource" Validation="@this.ValidateOtherDataSource" Label="Describe your data source" Variant="Variant.Outlined" Margin="Margin.Normal" Lines="3" AutoGrow="@true" MaxLines="6" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
<MudTextField Disabled="@this.IsNoneERIServerSelected" T="string" @bind-Text="@this.otherDataSource" Validation="@this.ValidateOtherDataSource" Label="@T("Describe your data source")" Variant="Variant.Outlined" Margin="Margin.Normal" Lines="3" AutoGrow="@true" MaxLines="6" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||||
}
|
}
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
|
||||||
@if(this.selectedDataSource > DataSources.FILE_SYSTEM)
|
@if(this.selectedDataSource > DataSources.FILE_SYSTEM)
|
||||||
{
|
{
|
||||||
<MudTextField Disabled="@this.IsNoneERIServerSelected" T="string" @bind-Text="@this.dataSourceProductName" Label="Data source: product name" Validation="@this.ValidateDataSourceProductName" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
<MudTextField Disabled="@this.IsNoneERIServerSelected" T="string" @bind-Text="@this.dataSourceProductName" Label="@T("Data source: product name")" Validation="@this.ValidateDataSourceProductName" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (this.NeedHostnamePort())
|
@if (this.NeedHostnamePort())
|
||||||
{
|
{
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<MudStack Row="@true">
|
<MudStack Row="@true">
|
||||||
<MudTextField Disabled="@this.IsNoneERIServerSelected" T="string" @bind-Text="@this.dataSourceHostname" Label="Data source: hostname" Validation="@this.ValidateHostname" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
<MudTextField Disabled="@this.IsNoneERIServerSelected" T="string" @bind-Text="@this.dataSourceHostname" Label="@T("Data source: hostname")" Validation="@this.ValidateHostname" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||||
<MudNumericField Disabled="@this.IsNoneERIServerSelected" Label="Data source: port" Immediate="@true" Min="1" Max="65535" Validation="@this.ValidatePort" @bind-Value="@this.dataSourcePort" Variant="Variant.Outlined" Margin="Margin.Dense" OnKeyUp="() => this.DataSourcePortWasTyped()"/>
|
<MudNumericField Disabled="@this.IsNoneERIServerSelected" Label="@T("Data source: port")" Immediate="@true" Min="1" Max="65535" Validation="@this.ValidatePort" @bind-Value="@this.dataSourcePort" Variant="Variant.Outlined" Margin="Margin.Dense" OnKeyUp="() => this.DataSourcePortWasTyped()"/>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
@if (this.dataSourcePort < 1024)
|
@if (this.dataSourcePort < 1024)
|
||||||
{
|
{
|
||||||
<MudText Typo="Typo.body2">
|
<MudText Typo="Typo.body2">
|
||||||
<b>Warning:</b> Ports below 1024 are reserved for system services. Your ERI server need to run with elevated permissions (root user).
|
<b>@T("Warning:")</b> @T("Ports below 1024 are reserved for system services. Your ERI server need to run with elevated permissions (root user).")
|
||||||
</MudText>
|
</MudText>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<MudText Typo="Typo.h4" Class="mt-9 mb-3">
|
<MudText Typo="Typo.h4" Class="mt-9 mb-3">
|
||||||
Authentication settings
|
@T("Authentication settings")
|
||||||
</MudText>
|
</MudText>
|
||||||
|
|
||||||
<MudStack Row="@false" Spacing="1" Class="mb-1">
|
<MudStack Row="@false" Spacing="1" Class="mb-1">
|
||||||
@ -168,12 +174,14 @@ else
|
|||||||
SelectedValues="@this.selectedAuthenticationMethods"
|
SelectedValues="@this.selectedAuthenticationMethods"
|
||||||
Validation="@this.ValidateAuthenticationMethods"
|
Validation="@this.ValidateAuthenticationMethods"
|
||||||
SelectedValuesChanged="@this.AuthenticationMethodWasChanged"
|
SelectedValuesChanged="@this.AuthenticationMethodWasChanged"
|
||||||
Label="Authentication method(s)"
|
Label="@T("Authentication method(s)")"
|
||||||
Variant="Variant.Outlined"
|
Variant="Variant.Outlined"
|
||||||
Margin="Margin.Dense">
|
Margin="Margin.Dense">
|
||||||
@foreach (var authMethod in Enum.GetValues<Auth>())
|
@foreach (var authMethod in Enum.GetValues<Auth>())
|
||||||
{
|
{
|
||||||
<MudSelectItemExtended Value="@authMethod">@authMethod.Name()</MudSelectItemExtended>
|
<MudSelectItemExtended Value="@authMethod">
|
||||||
|
@authMethod.Name()
|
||||||
|
</MudSelectItemExtended>
|
||||||
}
|
}
|
||||||
</MudSelectExtended>
|
</MudSelectExtended>
|
||||||
<MudTextField Disabled="@this.IsNoneERIServerSelected" T="string" @bind-Text="@this.authDescription" Label="@this.AuthDescriptionTitle()" Validation="@this.ValidateAuthDescription" Variant="Variant.Outlined" Margin="Margin.Normal" Lines="3" AutoGrow="@true" MaxLines="6" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
<MudTextField Disabled="@this.IsNoneERIServerSelected" T="string" @bind-Text="@this.authDescription" Label="@this.AuthDescriptionTitle()" Validation="@this.ValidateAuthDescription" Variant="Variant.Outlined" Margin="Margin.Normal" Lines="3" AutoGrow="@true" MaxLines="6" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||||
@ -181,40 +189,39 @@ else
|
|||||||
|
|
||||||
@if (this.selectedAuthenticationMethods.Contains(Auth.KERBEROS))
|
@if (this.selectedAuthenticationMethods.Contains(Auth.KERBEROS))
|
||||||
{
|
{
|
||||||
<MudSelect Disabled="@this.IsNoneERIServerSelected" T="OperatingSystem" @bind-Value="@this.selectedOperatingSystem" Label="Operating system on which your ERI will run" Variant="Variant.Outlined" Margin="Margin.Dense" Validation="@this.ValidateOperatingSystem" Class="mb-1">
|
<MudSelect Disabled="@this.IsNoneERIServerSelected" T="OperatingSystem" @bind-Value="@this.selectedOperatingSystem" Label="@T("Operating system on which your ERI will run")" Variant="Variant.Outlined" Margin="Margin.Dense" Validation="@this.ValidateOperatingSystem" Class="mb-1">
|
||||||
@foreach (var os in Enum.GetValues<OperatingSystem>())
|
@foreach (var os in Enum.GetValues<OperatingSystem>())
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="@os">@os.Name()</MudSelectItem>
|
<MudSelectItem Value="@os">
|
||||||
|
@os.Name()
|
||||||
|
</MudSelectItem>
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
}
|
}
|
||||||
|
|
||||||
<MudText Typo="Typo.h4" Class="mt-11 mb-3">
|
<MudText Typo="Typo.h4" Class="mt-11 mb-3">
|
||||||
Data protection settings
|
@T("Data protection settings")
|
||||||
</MudText>
|
</MudText>
|
||||||
|
|
||||||
<MudSelect Disabled="@this.IsNoneERIServerSelected" T="AllowedLLMProviders" @bind-Value="@this.allowedLLMProviders" Label="Allowed LLM providers for this data source" Variant="Variant.Outlined" Margin="Margin.Dense" Validation="@this.ValidateAllowedLLMProviders" Class="mb-1">
|
<MudSelect Disabled="@this.IsNoneERIServerSelected" T="AllowedLLMProviders" @bind-Value="@this.allowedLLMProviders" Label="@T("Allowed LLM providers for this data source")" Variant="Variant.Outlined" Margin="Margin.Dense" Validation="@this.ValidateAllowedLLMProviders" Class="mb-1">
|
||||||
@foreach (var option in Enum.GetValues<AllowedLLMProviders>())
|
@foreach (var option in Enum.GetValues<AllowedLLMProviders>())
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="@option">@option.Description()</MudSelectItem>
|
<MudSelectItem Value="@option">
|
||||||
|
@option.Description()
|
||||||
|
</MudSelectItem>
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
|
|
||||||
<MudText Typo="Typo.h4" Class="mt-11 mb-3">
|
<MudText Typo="Typo.h4" Class="mt-11 mb-3">
|
||||||
Embedding settings
|
@T("Embedding settings")
|
||||||
</MudText>
|
</MudText>
|
||||||
|
|
||||||
<MudJustifiedText Typo="Typo.body1" Class="mb-2">
|
<MudJustifiedText Typo="Typo.body1" Class="mb-2">
|
||||||
You will likely use one or more embedding methods to encode the meaning of your data into a typically high-dimensional vector
|
@T("You will likely use one or more embedding methods to encode the meaning of your data into a typically high-dimensional vector space. In this case, you will use a vector database to store and search these vectors (called embeddings). However, you don't have to use embedding methods. When your retrieval method works without any embedding, you can ignore this section. An example: You store files on a file server, and your retrieval method works exclusively with file names in the file system, so you don't need embeddings.")
|
||||||
space. In this case, you will use a vector database to store and search these vectors (called embeddings). However, you don't
|
|
||||||
have to use embedding methods. When your retrieval method works without any embedding, you can ignore this section. An example: You
|
|
||||||
store files on a file server, and your retrieval method works exclusively with file names in the file system, so you don't
|
|
||||||
need embeddings.
|
|
||||||
</MudJustifiedText>
|
</MudJustifiedText>
|
||||||
|
|
||||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||||
You can specify more than one embedding method. This can be useful when you want to use different embeddings for different queries
|
@T("You can specify more than one embedding method. This can be useful when you want to use different embeddings for different queries or data types. For example, one embedding for texts, another for images, and a third for videos, etc.")
|
||||||
or data types. For example, one embedding for texts, another for images, and a third for videos, etc.
|
|
||||||
</MudJustifiedText>
|
</MudJustifiedText>
|
||||||
|
|
||||||
@if (!this.IsNoneERIServerSelected)
|
@if (!this.IsNoneERIServerSelected)
|
||||||
@ -226,43 +233,44 @@ else
|
|||||||
<col style="width: 34em;"/>
|
<col style="width: 34em;"/>
|
||||||
</ColGroup>
|
</ColGroup>
|
||||||
<HeaderContent>
|
<HeaderContent>
|
||||||
<MudTh>Name</MudTh>
|
<MudTh>@T("Name")</MudTh>
|
||||||
<MudTh>Type</MudTh>
|
<MudTh>@T("Type")</MudTh>
|
||||||
<MudTh Style="text-align: left;">Actions</MudTh>
|
<MudTh>@T("Actions")</MudTh>
|
||||||
</HeaderContent>
|
</HeaderContent>
|
||||||
<RowTemplate>
|
<RowTemplate>
|
||||||
<MudTd>@context.EmbeddingName</MudTd>
|
<MudTd>@context.EmbeddingName</MudTd>
|
||||||
<MudTd>@context.EmbeddingType</MudTd>
|
<MudTd>@context.EmbeddingType</MudTd>
|
||||||
<MudTd Style="text-align: left;">
|
<MudTd>
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" Class="ma-2" OnClick="() => this.EditEmbedding(context)">
|
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
|
||||||
Edit
|
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" OnClick="() => this.EditEmbedding(context)">
|
||||||
|
@T("Edit")
|
||||||
</MudButton>
|
</MudButton>
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" Class="ma-2" OnClick="() => this.DeleteEmbedding(context)">
|
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteEmbedding(context)">
|
||||||
Delete
|
@T("Delete")
|
||||||
</MudButton>
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
</MudTd>
|
</MudTd>
|
||||||
</RowTemplate>
|
</RowTemplate>
|
||||||
</MudTable>
|
</MudTable>
|
||||||
|
|
||||||
@if (this.embeddings.Count == 0)
|
@if (this.embeddings.Count == 0)
|
||||||
{
|
{
|
||||||
<MudText Typo="Typo.h6" Class="mt-3">No embedding methods configured yet.</MudText>
|
<MudText Typo="Typo.h6" Class="mt-3">
|
||||||
|
@T("No embedding methods configured yet.")
|
||||||
|
</MudText>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<MudButton Disabled="@this.IsNoneERIServerSelected" Variant="Variant.Filled" Color="@Color.Primary" StartIcon="@Icons.Material.Filled.AddRoad" Class="mt-3 mb-6" OnClick="@this.AddEmbedding">
|
<MudButton Disabled="@this.IsNoneERIServerSelected" Variant="Variant.Filled" Color="@Color.Primary" StartIcon="@Icons.Material.Filled.AddRoad" Class="mt-3 mb-6" OnClick="@this.AddEmbedding">
|
||||||
Add Embedding Method
|
@T("Add Embedding Method")
|
||||||
</MudButton>
|
</MudButton>
|
||||||
|
|
||||||
<MudText Typo="Typo.h4" Class="mt-6 mb-1">
|
<MudText Typo="Typo.h4" Class="mt-6 mb-1">
|
||||||
Data retrieval settings
|
@T("Data retrieval settings")
|
||||||
</MudText>
|
</MudText>
|
||||||
|
|
||||||
<MudJustifiedText Typo="Typo.body1" Class="mb-2">
|
<MudJustifiedText Typo="Typo.body1" Class="mb-2">
|
||||||
For your ERI server, you need to retrieve data that matches a chat or prompt in some way. We call this the retrieval process.
|
@T("For your ERI server, you need to retrieve data that matches a chat or prompt in some way. We call this the retrieval process. You must describe at least one such process. You may offer several retrieval processes from which users can choose. This allows you to test with beta users which process works better. Or you might generally want to give users the choice so they can select the process that best suits their circumstances.")
|
||||||
You must describe at least one such process. You may offer several retrieval processes from which users can choose. This allows
|
|
||||||
you to test with beta users which process works better. Or you might generally want to give users the choice so they can select
|
|
||||||
the process that best suits their circumstances.
|
|
||||||
</MudJustifiedText>
|
</MudJustifiedText>
|
||||||
|
|
||||||
@if (!this.IsNoneERIServerSelected)
|
@if (!this.IsNoneERIServerSelected)
|
||||||
@ -273,77 +281,67 @@ else
|
|||||||
<col style="width: 34em;"/>
|
<col style="width: 34em;"/>
|
||||||
</ColGroup>
|
</ColGroup>
|
||||||
<HeaderContent>
|
<HeaderContent>
|
||||||
<MudTh>Name</MudTh>
|
<MudTh>@T("Name")</MudTh>
|
||||||
<MudTh Style="text-align: left;">Actions</MudTh>
|
<MudTh>@T("Actions")</MudTh>
|
||||||
</HeaderContent>
|
</HeaderContent>
|
||||||
<RowTemplate>
|
<RowTemplate>
|
||||||
<MudTd>@context.Name</MudTd>
|
<MudTd>@context.Name</MudTd>
|
||||||
<MudTd Style="text-align: left;">
|
<MudTd>
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" Class="ma-2" OnClick="() => this.EditRetrievalProcess(context)">
|
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
|
||||||
Edit
|
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" OnClick="() => this.EditRetrievalProcess(context)">
|
||||||
|
@T("Edit")
|
||||||
</MudButton>
|
</MudButton>
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" Class="ma-2" OnClick="() => this.DeleteRetrievalProcess(context)">
|
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteRetrievalProcess(context)">
|
||||||
Delete
|
@T("Delete")
|
||||||
</MudButton>
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
</MudTd>
|
</MudTd>
|
||||||
</RowTemplate>
|
</RowTemplate>
|
||||||
</MudTable>
|
</MudTable>
|
||||||
|
|
||||||
@if (this.retrievalProcesses.Count == 0)
|
@if (this.retrievalProcesses.Count == 0)
|
||||||
{
|
{
|
||||||
<MudText Typo="Typo.h6" Class="mt-3">No retrieval process configured yet.</MudText>
|
<MudText Typo="Typo.h6" Class="mt-3">
|
||||||
|
@T("No retrieval process configured yet.")
|
||||||
|
</MudText>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<MudButton Disabled="@this.IsNoneERIServerSelected" Variant="Variant.Filled" Color="@Color.Primary" StartIcon="@Icons.Material.Filled.AddRoad" Class="mt-3 mb-6" OnClick="@this.AddRetrievalProcess">
|
<MudButton Disabled="@this.IsNoneERIServerSelected" Variant="Variant.Filled" Color="@Color.Primary" StartIcon="@Icons.Material.Filled.AddRoad" Class="mt-3 mb-6" OnClick="@this.AddRetrievalProcess">
|
||||||
Add Retrieval Process
|
@T("Add Retrieval Process")
|
||||||
</MudButton>
|
</MudButton>
|
||||||
|
|
||||||
<MudJustifiedText Typo="Typo.body1" Class="mb-1">
|
<MudJustifiedText Typo="Typo.body1" Class="mb-1">
|
||||||
You can integrate additional libraries. Perhaps you want to evaluate the prompts in advance using a machine learning method or analyze them with a text
|
@T("You can integrate additional libraries. Perhaps you want to evaluate the prompts in advance using a machine learning method or analyze them with a text mining approach? Or maybe you want to preprocess images in the prompts? For such advanced scenarios, you can specify which libraries you want to use here. It's best to describe which library you want to integrate for which purpose. This way, the LLM that writes the ERI server for you can try to use these libraries effectively. This should result in less rework being necessary. If you don't know the necessary libraries, you can instead attempt to describe the intended use. The LLM can then attempt to choose suitable libraries. However, hallucinations can occur, and fictional libraries might be selected.")
|
||||||
mining approach? Or maybe you want to preprocess images in the prompts? For such advanced scenarios, you can specify which libraries you want to use here.
|
|
||||||
It's best to describe which library you want to integrate for which purpose. This way, the LLM that writes the ERI server for you can try to use these
|
|
||||||
libraries effectively. This should result in less rework being necessary. If you don't know the necessary libraries, you can instead attempt to describe
|
|
||||||
the intended use. The LLM can then attempt to choose suitable libraries. However, hallucinations can occur, and fictional libraries might be selected.
|
|
||||||
</MudJustifiedText>
|
</MudJustifiedText>
|
||||||
|
|
||||||
<MudTextField Disabled="@this.IsNoneERIServerSelected" T="string" @bind-Text="@this.additionalLibraries" Label="(Optional) Additional libraries" HelperText="Do you want to include additional libraries? Then name them and briefly describe what you want to achieve with them." Variant="Variant.Outlined" Margin="Margin.Normal" Lines="3" AutoGrow="@true" MaxLines="12" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
<MudTextField Disabled="@this.IsNoneERIServerSelected" T="string" @bind-Text="@this.additionalLibraries" Label="@T("(Optional) Additional libraries")" HelperText="@T("Do you want to include additional libraries? Then name them and briefly describe what you want to achieve with them.")" Variant="Variant.Outlined" Margin="Margin.Normal" Lines="3" AutoGrow="@true" MaxLines="12" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||||
|
|
||||||
<MudText Typo="Typo.h4" Class="mt-9 mb-1">
|
<MudText Typo="Typo.h4" Class="mt-9 mb-1">
|
||||||
Provider selection for generation
|
@T("Provider selection for generation")
|
||||||
</MudText>
|
</MudText>
|
||||||
|
|
||||||
<MudJustifiedText Typo="Typo.body1" Class="mb-2">
|
<MudJustifiedText Typo="Typo.body1" Class="mb-2">
|
||||||
The task of writing the ERI server for you is very complex. Therefore, a very powerful LLM is needed to successfully accomplish this task.
|
@T("The task of writing the ERI server for you is very complex. Therefore, a very powerful LLM is needed to successfully accomplish this task. Small local models will probably not be sufficient. Instead, try using a large cloud-based or a large self-hosted model.")
|
||||||
Small local models will probably not be sufficient. Instead, try using a large cloud-based or a large self-hosted model.
|
|
||||||
</MudJustifiedText>
|
</MudJustifiedText>
|
||||||
|
|
||||||
<MudJustifiedText Typo="Typo.body1" Class="mb-2">
|
<MudJustifiedText Typo="Typo.body1" Class="mb-2">
|
||||||
<b>Important:</b> The LLM may need to generate many files. This reaches the request limit of most providers. Typically, only a certain number
|
<b>@T("Important:")</b> @T("The LLM may need to generate many files. This reaches the request limit of most providers. Typically, only a certain number of requests can be made per minute, and only a maximum number of tokens can be generated per minute. AI Studio automatically considers this.") <b>@T("However, generating all the files takes a certain amount of time.")</b> @T("Local or self-hosted models may work without these limitations and can generate responses faster. AI Studio dynamically adapts its behavior and always tries to achieve the fastest possible data processing.")
|
||||||
of requests can be made per minute, and only a maximum number of tokens can be generated per minute. AI Studio automatically considers this.
|
|
||||||
<b>However, generating all the files takes a certain amount of time.</b> Local or self-hosted models may work without these limitations
|
|
||||||
and can generate responses faster. AI Studio dynamically adapts its behavior and always tries to achieve the fastest possible data processing.
|
|
||||||
</MudJustifiedText>
|
</MudJustifiedText>
|
||||||
|
|
||||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||||
|
|
||||||
<MudText Typo="Typo.h4" Class="mt-9 mb-1">
|
<MudText Typo="Typo.h4" Class="mt-9 mb-1">
|
||||||
Write code to file system
|
@T("Write code to file system")
|
||||||
</MudText>
|
</MudText>
|
||||||
|
|
||||||
<MudJustifiedText Typo="Typo.body1" Class="mb-2">
|
<MudJustifiedText Typo="Typo.body1" Class="mb-2">
|
||||||
AI Studio can save the generated code to the file system. You can select a base folder for this. AI Studio ensures that no files are created
|
@T("AI Studio can save the generated code to the file system. You can select a base folder for this. AI Studio ensures that no files are created outside of this base folder. Furthermore, we recommend that you create a Git repository in this folder. This way, you can see what changes the AI has made in which files.")
|
||||||
outside of this base folder. Furthermore, we recommend that you create a Git repository in this folder. This way, you can see what changes the
|
|
||||||
AI has made in which files.
|
|
||||||
</MudJustifiedText>
|
</MudJustifiedText>
|
||||||
|
|
||||||
<MudJustifiedText Typo="Typo.body1" Class="mb-2">
|
<MudJustifiedText Typo="Typo.body1" Class="mb-2">
|
||||||
When you rebuild / re-generate the ERI server code, AI Studio proceeds as follows: All files generated last time will be deleted. All
|
@T("When you rebuild / re-generate the ERI server code, AI Studio proceeds as follows: All files generated last time will be deleted. All other files you have created remain. Then, the AI generates the new files.") <b>@T("But beware:")</b> @T("It may happen that the AI generates a file this time that you manually created last time. In this case, your manually created file will then be overwritten. Therefore, you should always create a Git repository and commit or revert all changes before using this assistant. With a diff visualization, you can immediately see where the AI has made changes. It is best to use an IDE suitable for your selected language for this purpose.")
|
||||||
other files you have created remain. Then, the AI generates the new files. <b>But beware:</b> It may happen that the AI generates a
|
|
||||||
file this time that you manually created last time. In this case, your manually created file will then be overwritten. Therefore,
|
|
||||||
you should always create a Git repository and commit or revert all changes before using this assistant. With a diff visualization,
|
|
||||||
you can immediately see where the AI has made changes. It is best to use an IDE suitable for your selected language for this purpose.
|
|
||||||
</MudJustifiedText>
|
</MudJustifiedText>
|
||||||
|
|
||||||
<MudTextSwitch Label="Should we write the generated code to the file system?" Disabled="@this.IsNoneERIServerSelected" @bind-Value="@this.writeToFilesystem" LabelOn="Yes, please write or update all generated code to the file system" LabelOff="No, just show me the code" />
|
<MudTextSwitch Label="@T("Should we write the generated code to the file system?")" Disabled="@this.IsNoneERIServerSelected" @bind-Value="@this.writeToFilesystem" LabelOn="@T("Yes, please write or update all generated code to the file system")" LabelOff="@T("No, just show me the code")" />
|
||||||
<SelectDirectory Label="Base directory where to write the code" @bind-Directory="@this.baseDirectory" Disabled="@(this.IsNoneERIServerSelected || !this.writeToFilesystem)" DirectoryDialogTitle="Select the target directory for the ERI server" Validation="@this.ValidateDirectory" />
|
<SelectDirectory Label="@T("Base directory where to write the code")" @bind-Directory="@this.baseDirectory" Disabled="@(this.IsNoneERIServerSelected || !this.writeToFilesystem)" DirectoryDialogTitle="@T("Select the target directory for the ERI server")" Validation="@this.ValidateDirectory" />
|
||||||
|
@ -22,16 +22,9 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
|
|
||||||
public override Tools.Components Component => Tools.Components.ERI_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.ERI_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "ERI Server";
|
protected override string Title => T("ERI Server");
|
||||||
|
|
||||||
protected override string Description =>
|
protected override string Description => T("The ERI is the External Retrieval Interface for AI Studio and other tools. The ERI acts as a contract between decentralized data sources and, e.g., AI Studio. The ERI is implemented by the data sources, allowing them to be integrated into AI Studio later. This means that the data sources assume the server role and AI Studio (or any other LLM tool) assumes the client role of the API. This approach serves to realize a Retrieval-Augmented Generation (RAG) process with external data.");
|
||||||
"""
|
|
||||||
The ERI is the External Retrieval Interface for AI Studio and other tools. The ERI acts as a contract
|
|
||||||
between decentralized data sources and, e.g., AI Studio. The ERI is implemented by the data sources,
|
|
||||||
allowing them to be integrated into AI Studio later. This means that the data sources assume the server
|
|
||||||
role and AI Studio (or any other LLM tool) assumes the client role of the API. This approach serves to
|
|
||||||
realize a Retrieval-Augmented Generation (RAG) process with external data.
|
|
||||||
""";
|
|
||||||
|
|
||||||
protected override string SystemPrompt
|
protected override string SystemPrompt
|
||||||
{
|
{
|
||||||
@ -44,7 +37,7 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
// Introduction
|
// Introduction
|
||||||
// ---------------------------------
|
// ---------------------------------
|
||||||
//
|
//
|
||||||
var programmingLanguage = this.selectedProgrammingLanguage is ProgrammingLanguages.OTHER ? this.otherProgrammingLanguage : this.selectedProgrammingLanguage.Name();
|
var programmingLanguage = this.selectedProgrammingLanguage is ProgrammingLanguages.OTHER ? this.otherProgrammingLanguage : this.selectedProgrammingLanguage.ToPrompt();
|
||||||
sb.Append($"""
|
sb.Append($"""
|
||||||
# Introduction
|
# Introduction
|
||||||
You are an experienced {programmingLanguage} developer. Your task is to implement an API server in
|
You are an experienced {programmingLanguage} developer. Your task is to implement an API server in
|
||||||
@ -159,7 +152,7 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
{
|
{
|
||||||
sb.Append($"""
|
sb.Append($"""
|
||||||
|
|
||||||
The server will run on {this.selectedOperatingSystem.Name()} operating systems. Keep
|
The server will run on {this.selectedOperatingSystem.ToPrompt()} operating systems. Keep
|
||||||
this in mind when implementing the SSO with Kerberos.
|
this in mind when implementing the SSO with Kerberos.
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
@ -304,7 +297,7 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
|
|
||||||
protected override bool ShowSendTo => false;
|
protected override bool ShowSendTo => false;
|
||||||
|
|
||||||
protected override string SubmitText => "Create the ERI server";
|
protected override string SubmitText => T("Create the ERI server");
|
||||||
|
|
||||||
protected override Func<Task> SubmitAction => this.GenerateServer;
|
protected override Func<Task> SubmitAction => this.GenerateServer;
|
||||||
|
|
||||||
@ -469,7 +462,7 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
{
|
{
|
||||||
this.SettingsManager.ConfigurationData.ERI.ERIServers.Add(new ()
|
this.SettingsManager.ConfigurationData.ERI.ERIServers.Add(new ()
|
||||||
{
|
{
|
||||||
ServerName = $"ERI Server {DateTimeOffset.UtcNow}",
|
ServerName = string.Format(T("ERI Server {0}"), DateTimeOffset.UtcNow),
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.SettingsManager.StoreSettings();
|
await this.SettingsManager.StoreSettings();
|
||||||
@ -482,10 +475,10 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
|
|
||||||
var dialogParameters = new DialogParameters
|
var dialogParameters = new DialogParameters
|
||||||
{
|
{
|
||||||
{ "Message", $"Are you sure you want to delete the ERI server preset '{this.selectedERIServer.ServerName}'?" },
|
{ "Message", string.Format(T("Are you sure you want to delete the ERI server preset '{0}'?"), this.selectedERIServer.ServerName) },
|
||||||
};
|
};
|
||||||
|
|
||||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Delete ERI server preset", dialogParameters, DialogOptions.FULLSCREEN);
|
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete ERI server preset"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
var dialogResult = await dialogReference.Result;
|
var dialogResult = await dialogReference.Result;
|
||||||
if (dialogResult is null || dialogResult.Canceled)
|
if (dialogResult is null || dialogResult.Canceled)
|
||||||
return;
|
return;
|
||||||
@ -518,13 +511,13 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
private string? ValidateServerName(string name)
|
private string? ValidateServerName(string name)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(name))
|
if(string.IsNullOrWhiteSpace(name))
|
||||||
return "Please provide a name for your ERI server. This name will be used to identify the server in AI Studio.";
|
return T("Please provide a name for your ERI server. This name will be used to identify the server in AI Studio.");
|
||||||
|
|
||||||
if(name.Length is > 60 or < 6)
|
if(name.Length is > 60 or < 6)
|
||||||
return "The name of your ERI server must be between 6 and 60 characters long.";
|
return T("The name of your ERI server must be between 6 and 60 characters long.");
|
||||||
|
|
||||||
if(this.SettingsManager.ConfigurationData.ERI.ERIServers.Where(n => n != this.selectedERIServer).Any(n => n.ServerName == name))
|
if(this.SettingsManager.ConfigurationData.ERI.ERIServers.Where(n => n != this.selectedERIServer).Any(n => n.ServerName == name))
|
||||||
return "An ERI server preset with this name already exists. Please choose a different name.";
|
return T("An ERI server preset with this name already exists. Please choose a different name.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -532,10 +525,10 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
private string? ValidateServerDescription(string description)
|
private string? ValidateServerDescription(string description)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(description))
|
if(string.IsNullOrWhiteSpace(description))
|
||||||
return "Please provide a description for your ERI server. What data will the server retrieve? This description will be used to inform users about the purpose of your ERI server.";
|
return T("Please provide a description for your ERI server. What data will the server retrieve? This description will be used to inform users about the purpose of your ERI server.");
|
||||||
|
|
||||||
if(description.Length is < 32 or > 512)
|
if(description.Length is < 32 or > 512)
|
||||||
return "The description of your ERI server must be between 32 and 512 characters long.";
|
return T("The description of your ERI server must be between 32 and 512 characters long.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -543,7 +536,7 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
private string? ValidateERIVersion(ERIVersion version)
|
private string? ValidateERIVersion(ERIVersion version)
|
||||||
{
|
{
|
||||||
if (version == ERIVersion.NONE)
|
if (version == ERIVersion.NONE)
|
||||||
return "Please select an ERI specification version for the ERI server.";
|
return T("Please select an ERI specification version for the ERI server.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -554,7 +547,7 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (language == ProgrammingLanguages.NONE)
|
if (language == ProgrammingLanguages.NONE)
|
||||||
return "Please select a programming language for the ERI server.";
|
return T("Please select a programming language for the ERI server.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -565,7 +558,7 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(language))
|
if(string.IsNullOrWhiteSpace(language))
|
||||||
return "Please specify the custom programming language for the ERI server.";
|
return T("Please specify the custom programming language for the ERI server.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -576,7 +569,7 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (dataSource == DataSources.NONE)
|
if (dataSource == DataSources.NONE)
|
||||||
return "Please select a data source for the ERI server.";
|
return T("Please select a data source for the ERI server.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -587,7 +580,7 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(productName))
|
if(string.IsNullOrWhiteSpace(productName))
|
||||||
return "Please specify the product name of the data source, e.g., 'MongoDB', 'Redis', 'PostgreSQL', 'Neo4j', or 'MinIO', etc.";
|
return T("Please specify the product name of the data source, e.g., 'MongoDB', 'Redis', 'PostgreSQL', 'Neo4j', or 'MinIO', etc.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -598,7 +591,7 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(dataSource))
|
if(string.IsNullOrWhiteSpace(dataSource))
|
||||||
return "Please describe the data source of your ERI server.";
|
return T("Please describe the data source of your ERI server.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -613,10 +606,10 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(hostname))
|
if(string.IsNullOrWhiteSpace(hostname))
|
||||||
return "Please provide the hostname of the data source. Use 'localhost' if the data source is on the same machine as the ERI server.";
|
return T("Please provide the hostname of the data source. Use 'localhost' if the data source is on the same machine as the ERI server.");
|
||||||
|
|
||||||
if(hostname.Length > 255)
|
if(hostname.Length > 255)
|
||||||
return "The hostname of the data source must not exceed 255 characters.";
|
return T("The hostname of the data source must not exceed 255 characters.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -631,10 +624,10 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
if(port is null)
|
if(port is null)
|
||||||
return "Please provide the port of the data source.";
|
return T("Please provide the port of the data source.");
|
||||||
|
|
||||||
if(port is < 1 or > 65535)
|
if(port is < 1 or > 65535)
|
||||||
return "The port of the data source must be between 1 and 65535.";
|
return T("The port of the data source must be between 1 and 65535.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -679,7 +672,7 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
{
|
{
|
||||||
var authenticationMethods = (this.selectedAuthenticationMethods as HashSet<Auth>)!;
|
var authenticationMethods = (this.selectedAuthenticationMethods as HashSet<Auth>)!;
|
||||||
if(authenticationMethods.Count == 0)
|
if(authenticationMethods.Count == 0)
|
||||||
return "Please select at least one authentication method for the ERI server.";
|
return T("Please select at least one authentication method for the ERI server.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -709,7 +702,7 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
if(os is OperatingSystem.NONE)
|
if(os is OperatingSystem.NONE)
|
||||||
return "Please select the operating system on which the ERI server will run. This is necessary when using SSO with Kerberos.";
|
return T("Please select the operating system on which the ERI server will run. This is necessary when using SSO with Kerberos.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -717,16 +710,12 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
private string? ValidateAllowedLLMProviders(AllowedLLMProviders provider)
|
private string? ValidateAllowedLLMProviders(AllowedLLMProviders provider)
|
||||||
{
|
{
|
||||||
if(provider == AllowedLLMProviders.NONE)
|
if(provider == AllowedLLMProviders.NONE)
|
||||||
return "Please select which types of LLMs users are allowed to use with the data from this ERI server.";
|
return T("Please select which types of LLMs users are allowed to use with the data from this ERI server.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string AuthDescriptionTitle()
|
private string AuthDescriptionTitle() => this.IsAuthDescriptionOptional() ? T("(Optional) Describe how you planned the authentication process") : T("Describe how you planned the authentication process");
|
||||||
{
|
|
||||||
const string TITLE = "Describe how you planned the authentication process";
|
|
||||||
return this.IsAuthDescriptionOptional() ? $"(Optional) {TITLE}" : TITLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsAuthDescriptionOptional()
|
private bool IsAuthDescriptionOptional()
|
||||||
{
|
{
|
||||||
@ -746,10 +735,10 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
{
|
{
|
||||||
var authenticationMethods = (this.selectedAuthenticationMethods as HashSet<Auth>)!;
|
var authenticationMethods = (this.selectedAuthenticationMethods as HashSet<Auth>)!;
|
||||||
if(authenticationMethods.Any(n => n == Auth.NONE) && authenticationMethods.Count > 1 && string.IsNullOrWhiteSpace(this.authDescription))
|
if(authenticationMethods.Any(n => n == Auth.NONE) && authenticationMethods.Count > 1 && string.IsNullOrWhiteSpace(this.authDescription))
|
||||||
return "Please describe how the selected authentication methods should be used. Especially, explain for what data the NONE method (public access) is used.";
|
return T("Please describe how the selected authentication methods should be used. Especially, explain for what data the NONE method (public access) is used.");
|
||||||
|
|
||||||
if(authenticationMethods.Count > 1 && string.IsNullOrWhiteSpace(this.authDescription))
|
if(authenticationMethods.Count > 1 && string.IsNullOrWhiteSpace(this.authDescription))
|
||||||
return "Please describe how the selected authentication methods should be used.";
|
return T("Please describe how the selected authentication methods should be used.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -760,7 +749,7 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(path))
|
if(string.IsNullOrWhiteSpace(path))
|
||||||
return "Please provide a base directory for the ERI server to write files to.";
|
return T("Please provide a base directory for the ERI server to write files to.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -768,12 +757,12 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
private string GetMultiSelectionAuthText(List<Auth> selectedValues)
|
private string GetMultiSelectionAuthText(List<Auth> selectedValues)
|
||||||
{
|
{
|
||||||
if(selectedValues.Count == 0)
|
if(selectedValues.Count == 0)
|
||||||
return "Please select at least one authentication method";
|
return T("Please select at least one authentication method");
|
||||||
|
|
||||||
if(selectedValues.Count == 1)
|
if(selectedValues.Count == 1)
|
||||||
return $"You have selected 1 authentication method";
|
return T("You have selected 1 authentication method");
|
||||||
|
|
||||||
return $"You have selected {selectedValues.Count} authentication methods";
|
return string.Format(T("You have selected {0} authentication methods"), selectedValues.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool NeedHostnamePort()
|
private bool NeedHostnamePort()
|
||||||
@ -797,7 +786,7 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
{ x => x.UsedEmbeddingMethodNames, this.embeddings.Select(n => n.EmbeddingName).ToList() },
|
{ x => x.UsedEmbeddingMethodNames, this.embeddings.Select(n => n.EmbeddingName).ToList() },
|
||||||
};
|
};
|
||||||
|
|
||||||
var dialogReference = await this.DialogService.ShowAsync<EmbeddingMethodDialog>("Add Embedding Method", dialogParameters, DialogOptions.FULLSCREEN);
|
var dialogReference = await this.DialogService.ShowAsync<EmbeddingMethodDialog>(T("Add Embedding Method"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
var dialogResult = await dialogReference.Result;
|
var dialogResult = await dialogReference.Result;
|
||||||
if (dialogResult is null || dialogResult.Canceled)
|
if (dialogResult is null || dialogResult.Canceled)
|
||||||
return;
|
return;
|
||||||
@ -821,7 +810,7 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
{ x => x.IsEditing, true },
|
{ x => x.IsEditing, true },
|
||||||
};
|
};
|
||||||
|
|
||||||
var dialogReference = await this.DialogService.ShowAsync<EmbeddingMethodDialog>("Edit Embedding Method", dialogParameters, DialogOptions.FULLSCREEN);
|
var dialogReference = await this.DialogService.ShowAsync<EmbeddingMethodDialog>(T("Edit Embedding Method"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
var dialogResult = await dialogReference.Result;
|
var dialogResult = await dialogReference.Result;
|
||||||
if (dialogResult is null || dialogResult.Canceled)
|
if (dialogResult is null || dialogResult.Canceled)
|
||||||
return;
|
return;
|
||||||
@ -835,15 +824,15 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
private async Task DeleteEmbedding(EmbeddingInfo embeddingInfo)
|
private async Task DeleteEmbedding(EmbeddingInfo embeddingInfo)
|
||||||
{
|
{
|
||||||
var message = this.retrievalProcesses.Any(n => n.Embeddings?.Contains(embeddingInfo) is true)
|
var message = this.retrievalProcesses.Any(n => n.Embeddings?.Contains(embeddingInfo) is true)
|
||||||
? $"The embedding '{embeddingInfo.EmbeddingName}' is used in one or more retrieval processes. Are you sure you want to delete it?"
|
? string.Format(T("The embedding '{0}' is used in one or more retrieval processes. Are you sure you want to delete it?"), embeddingInfo.EmbeddingName)
|
||||||
: $"Are you sure you want to delete the embedding '{embeddingInfo.EmbeddingName}'?";
|
: string.Format(T("Are you sure you want to delete the embedding '{0}'?"), embeddingInfo.EmbeddingName);
|
||||||
|
|
||||||
var dialogParameters = new DialogParameters
|
var dialogParameters = new DialogParameters
|
||||||
{
|
{
|
||||||
{ "Message", message },
|
{ "Message", message },
|
||||||
};
|
};
|
||||||
|
|
||||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Delete Embedding", dialogParameters, DialogOptions.FULLSCREEN);
|
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Embedding"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
var dialogResult = await dialogReference.Result;
|
var dialogResult = await dialogReference.Result;
|
||||||
if (dialogResult is null || dialogResult.Canceled)
|
if (dialogResult is null || dialogResult.Canceled)
|
||||||
return;
|
return;
|
||||||
@ -863,7 +852,7 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
{ x => x.UsedRetrievalProcessNames, this.retrievalProcesses.Select(n => n.Name).ToList() },
|
{ x => x.UsedRetrievalProcessNames, this.retrievalProcesses.Select(n => n.Name).ToList() },
|
||||||
};
|
};
|
||||||
|
|
||||||
var dialogReference = await this.DialogService.ShowAsync<RetrievalProcessDialog>("Add Retrieval Process", dialogParameters, DialogOptions.FULLSCREEN);
|
var dialogReference = await this.DialogService.ShowAsync<RetrievalProcessDialog>(T("Add Retrieval Process"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
var dialogResult = await dialogReference.Result;
|
var dialogResult = await dialogReference.Result;
|
||||||
if (dialogResult is null || dialogResult.Canceled)
|
if (dialogResult is null || dialogResult.Canceled)
|
||||||
return;
|
return;
|
||||||
@ -888,7 +877,7 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
{ x => x.UsedRetrievalProcessNames, this.retrievalProcesses.Where(n => n != retrievalInfo).Select(n => n.Name).ToList() },
|
{ x => x.UsedRetrievalProcessNames, this.retrievalProcesses.Where(n => n != retrievalInfo).Select(n => n.Name).ToList() },
|
||||||
};
|
};
|
||||||
|
|
||||||
var dialogReference = await this.DialogService.ShowAsync<RetrievalProcessDialog>("Edit Retrieval Process", dialogParameters, DialogOptions.FULLSCREEN);
|
var dialogReference = await this.DialogService.ShowAsync<RetrievalProcessDialog>(T("Edit Retrieval Process"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
var dialogResult = await dialogReference.Result;
|
var dialogResult = await dialogReference.Result;
|
||||||
if (dialogResult is null || dialogResult.Canceled)
|
if (dialogResult is null || dialogResult.Canceled)
|
||||||
return;
|
return;
|
||||||
@ -903,10 +892,10 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
{
|
{
|
||||||
var dialogParameters = new DialogParameters
|
var dialogParameters = new DialogParameters
|
||||||
{
|
{
|
||||||
{ "Message", $"Are you sure you want to delete the retrieval process '{retrievalInfo.Name}'?" },
|
{ "Message", string.Format(T("Are you sure you want to delete the retrieval process '{0}'?"), retrievalInfo.Name) },
|
||||||
};
|
};
|
||||||
|
|
||||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Delete Retrieval Process", dialogParameters, DialogOptions.FULLSCREEN);
|
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Retrieval Process"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
var dialogResult = await dialogReference.Result;
|
var dialogResult = await dialogReference.Result;
|
||||||
if (dialogResult is null || dialogResult.Canceled)
|
if (dialogResult is null || dialogResult.Canceled)
|
||||||
return;
|
return;
|
||||||
@ -957,14 +946,14 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
|||||||
|
|
||||||
if(this.retrievalProcesses.Count == 0)
|
if(this.retrievalProcesses.Count == 0)
|
||||||
{
|
{
|
||||||
this.AddInputIssue("Please describe at least one retrieval process.");
|
this.AddInputIssue(T("Please describe at least one retrieval process."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.eriSpecification = await this.selectedERIVersion.ReadSpecification(this.HttpClient);
|
this.eriSpecification = await this.selectedERIVersion.ReadSpecification(this.HttpClient);
|
||||||
if (string.IsNullOrWhiteSpace(this.eriSpecification))
|
if (string.IsNullOrWhiteSpace(this.eriSpecification))
|
||||||
{
|
{
|
||||||
this.AddInputIssue("The ERI specification could not be loaded. Please try again later.");
|
this.AddInputIssue(T("The ERI specification could not be loaded. Please try again later."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,15 +2,17 @@ namespace AIStudio.Assistants.ERI;
|
|||||||
|
|
||||||
public static class AuthExtensions
|
public static class AuthExtensions
|
||||||
{
|
{
|
||||||
|
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(AuthExtensions).Namespace, nameof(AuthExtensions));
|
||||||
|
|
||||||
public static string Name(this Auth auth) => auth switch
|
public static string Name(this Auth auth) => auth switch
|
||||||
{
|
{
|
||||||
Auth.NONE => "No login necessary: useful for public data sources",
|
Auth.NONE => TB("No login necessary: useful for public data sources"),
|
||||||
|
|
||||||
Auth.KERBEROS => "Login by single-sign-on (SSO) using Kerberos: very complex to implement and to operate, useful for many users",
|
Auth.KERBEROS => TB("Login by single-sign-on (SSO) using Kerberos: very complex to implement and to operate, useful for many users"),
|
||||||
Auth.USERNAME_PASSWORD => "Login by username and password: simple to implement and to operate, useful for few users; easy to use for users",
|
Auth.USERNAME_PASSWORD => TB("Login by username and password: simple to implement and to operate, useful for few users; easy to use for users"),
|
||||||
Auth.TOKEN => "Login by token: simple to implement and to operate, useful for few users; unusual for many users",
|
Auth.TOKEN => TB("Login by token: simple to implement and to operate, useful for few users; unusual for many users"),
|
||||||
|
|
||||||
_ => "Unknown login method"
|
_ => TB("Unknown login method")
|
||||||
};
|
};
|
||||||
|
|
||||||
public static string ToPrompt(this Auth auth) => auth switch
|
public static string ToPrompt(this Auth auth) => auth switch
|
||||||
|
@ -2,18 +2,20 @@ namespace AIStudio.Assistants.ERI;
|
|||||||
|
|
||||||
public static class DataSourcesExtensions
|
public static class DataSourcesExtensions
|
||||||
{
|
{
|
||||||
|
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(DataSourcesExtensions).Namespace, nameof(DataSourcesExtensions));
|
||||||
|
|
||||||
public static string Name(this DataSources dataSource) => dataSource switch
|
public static string Name(this DataSources dataSource) => dataSource switch
|
||||||
{
|
{
|
||||||
DataSources.NONE => "No data source selected",
|
DataSources.NONE => TB("No data source selected"),
|
||||||
DataSources.CUSTOM => "Custom description",
|
DataSources.CUSTOM => TB("Custom description"),
|
||||||
|
|
||||||
DataSources.FILE_SYSTEM => "File system (local or network share)",
|
DataSources.FILE_SYSTEM => TB("File system (local or network share)"),
|
||||||
DataSources.OBJECT_STORAGE => "Object storage, like Amazon S3, MinIO, etc.",
|
DataSources.OBJECT_STORAGE => TB("Object storage, like Amazon S3, MinIO, etc."),
|
||||||
DataSources.KEY_VALUE_STORE => "Key-Value store, like Redis, etc.",
|
DataSources.KEY_VALUE_STORE => TB("Key-Value store, like Redis, etc."),
|
||||||
DataSources.DOCUMENT_STORE => "Document store, like MongoDB, etc.",
|
DataSources.DOCUMENT_STORE => TB("Document store, like MongoDB, etc."),
|
||||||
DataSources.RELATIONAL_DATABASE => "Relational database, like MySQL, PostgreSQL, etc.",
|
DataSources.RELATIONAL_DATABASE => TB("Relational database, like MySQL, PostgreSQL, etc."),
|
||||||
DataSources.GRAPH_DATABASE => "Graph database, like Neo4j, ArangoDB, etc.",
|
DataSources.GRAPH_DATABASE => TB("Graph database, like Neo4j, ArangoDB, etc."),
|
||||||
|
|
||||||
_ => "Unknown data source"
|
_ => TB("Unknown data source")
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -2,7 +2,19 @@ namespace AIStudio.Assistants.ERI;
|
|||||||
|
|
||||||
public static class OperatingSystemExtensions
|
public static class OperatingSystemExtensions
|
||||||
{
|
{
|
||||||
|
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(OperatingSystemExtensions).Namespace, nameof(OperatingSystemExtensions));
|
||||||
|
|
||||||
public static string Name(this OperatingSystem os) => os switch
|
public static string Name(this OperatingSystem os) => os switch
|
||||||
|
{
|
||||||
|
OperatingSystem.NONE => TB("No operating system specified"),
|
||||||
|
|
||||||
|
OperatingSystem.WINDOWS => TB("Windows"),
|
||||||
|
OperatingSystem.LINUX => TB("Linux"),
|
||||||
|
|
||||||
|
_ => TB("Unknown operating system")
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string ToPrompt(this OperatingSystem os) => os switch
|
||||||
{
|
{
|
||||||
OperatingSystem.NONE => "No operating system specified",
|
OperatingSystem.NONE => "No operating system specified",
|
||||||
|
|
||||||
|
@ -2,7 +2,29 @@ namespace AIStudio.Assistants.ERI;
|
|||||||
|
|
||||||
public static class ProgrammingLanguagesExtensions
|
public static class ProgrammingLanguagesExtensions
|
||||||
{
|
{
|
||||||
|
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(ProgrammingLanguagesExtensions).Namespace, nameof(ProgrammingLanguagesExtensions));
|
||||||
|
|
||||||
public static string Name(this ProgrammingLanguages language) => language switch
|
public static string Name(this ProgrammingLanguages language) => language switch
|
||||||
|
{
|
||||||
|
ProgrammingLanguages.NONE => TB("No programming language selected"),
|
||||||
|
|
||||||
|
ProgrammingLanguages.C => "C",
|
||||||
|
ProgrammingLanguages.CPP => "C++",
|
||||||
|
ProgrammingLanguages.CSHARP => "C#",
|
||||||
|
ProgrammingLanguages.GO => "Go",
|
||||||
|
ProgrammingLanguages.JAVA => "Java",
|
||||||
|
ProgrammingLanguages.JAVASCRIPT => "JavaScript",
|
||||||
|
ProgrammingLanguages.JULIA => "Julia",
|
||||||
|
ProgrammingLanguages.MATLAB => "MATLAB",
|
||||||
|
ProgrammingLanguages.PHP => "PHP",
|
||||||
|
ProgrammingLanguages.PYTHON => "Python",
|
||||||
|
ProgrammingLanguages.RUST => "Rust",
|
||||||
|
|
||||||
|
ProgrammingLanguages.OTHER => TB("Other"),
|
||||||
|
_ => TB("Unknown")
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string ToPrompt(this ProgrammingLanguages language) => language switch
|
||||||
{
|
{
|
||||||
ProgrammingLanguages.NONE => "No programming language selected",
|
ProgrammingLanguages.NONE => "No programming language selected",
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
@attribute [Route(Routes.ASSISTANT_GRAMMAR_SPELLING)]
|
@attribute [Route(Routes.ASSISTANT_GRAMMAR_SPELLING)]
|
||||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogGrammarSpelling>
|
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogGrammarSpelling>
|
||||||
|
|
||||||
<MudTextField T="string" @bind-Text="@this.inputText" Validation="@this.ValidateText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="Your input to check" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
<MudTextField T="string" @bind-Text="@this.inputText" Validation="@this.ValidateText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="@T("Your input to check")" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom language" />
|
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="@T("Language")" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="@T("Custom language")" />
|
||||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
@ -7,12 +7,9 @@ public partial class AssistantGrammarSpelling : AssistantBaseCore<SettingsDialog
|
|||||||
{
|
{
|
||||||
public override Tools.Components Component => Tools.Components.GRAMMAR_SPELLING_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.GRAMMAR_SPELLING_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Grammar & Spelling Checker";
|
protected override string Title => T("Grammar & Spelling Checker");
|
||||||
|
|
||||||
protected override string Description =>
|
protected override string Description => T("Check the grammar and spelling of a text.");
|
||||||
"""
|
|
||||||
Check the grammar and spelling of a text.
|
|
||||||
""";
|
|
||||||
|
|
||||||
protected override string SystemPrompt =>
|
protected override string SystemPrompt =>
|
||||||
$"""
|
$"""
|
||||||
@ -40,7 +37,7 @@ public partial class AssistantGrammarSpelling : AssistantBaseCore<SettingsDialog
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
protected override string SubmitText => "Proofread";
|
protected override string SubmitText => T("Proofread");
|
||||||
|
|
||||||
protected override Func<Task> SubmitAction => this.ProofreadText;
|
protected override Func<Task> SubmitAction => this.ProofreadText;
|
||||||
|
|
||||||
@ -93,7 +90,7 @@ public partial class AssistantGrammarSpelling : AssistantBaseCore<SettingsDialog
|
|||||||
private string? ValidateText(string text)
|
private string? ValidateText(string text)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(text))
|
if(string.IsNullOrWhiteSpace(text))
|
||||||
return "Please provide a text as input. You might copy the desired text from a document or a website.";
|
return T("Please provide a text as input. You might copy the desired text from a document or a website.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -101,7 +98,7 @@ public partial class AssistantGrammarSpelling : AssistantBaseCore<SettingsDialog
|
|||||||
private string? ValidateCustomLanguage(string language)
|
private string? ValidateCustomLanguage(string language)
|
||||||
{
|
{
|
||||||
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||||
return "Please provide a custom language.";
|
return T("Please provide a custom language.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
124
app/MindWork AI Studio/Assistants/I18N/AssistantI18N.razor
Normal file
124
app/MindWork AI Studio/Assistants/I18N/AssistantI18N.razor
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
@attribute [Route(Routes.ASSISTANT_AI_STUDIO_I18N)]
|
||||||
|
@using AIStudio.Settings
|
||||||
|
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogI18N>
|
||||||
|
|
||||||
|
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelecting())" @bind-Value="@this.selectedTargetLanguage" ValidateSelection="@this.ValidatingTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="@T("Target language")" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="@T("Custom target language")" SelectionUpdated="_ => this.OnChangedLanguage()" />
|
||||||
|
<ConfigurationSelect OptionDescription="@T("Language plugin used for comparision")" SelectedValue="@(() => this.selectedLanguagePluginId)" Data="@ConfigurationSelectDataFactory.GetLanguagesData()" SelectionUpdate="@(async void (id) => await this.OnLanguagePluginChanged(id))" OptionHelp="@T("Select the language plugin used for comparision.")"/>
|
||||||
|
@if (this.isLoading)
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.body1" Class="mb-6">
|
||||||
|
@T("The data is being loaded, please wait...")
|
||||||
|
</MudText>
|
||||||
|
} else if (!this.isLoading && !string.IsNullOrWhiteSpace(this.loadingIssue))
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.body1" Class="mb-6">
|
||||||
|
@T("While loading the I18N data, an issue occurred:") @this.loadingIssue
|
||||||
|
</MudText>
|
||||||
|
}
|
||||||
|
else if (!this.isLoading && string.IsNullOrWhiteSpace(this.loadingIssue))
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.h6">
|
||||||
|
@this.AddedContentText
|
||||||
|
</MudText>
|
||||||
|
<MudTable Items="@this.addedContent" Hover="@true" Filter="@this.FilterFunc" Class="border-dashed border rounded-lg mb-6">
|
||||||
|
<ToolBarContent>
|
||||||
|
<MudTextField @bind-Value="@this.searchString" Immediate="true" Placeholder="@T("Search")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"/>
|
||||||
|
</ToolBarContent>
|
||||||
|
<ColGroup>
|
||||||
|
<col/>
|
||||||
|
<col/>
|
||||||
|
</ColGroup>
|
||||||
|
<HeaderContent>
|
||||||
|
<MudTh>@T("Key")</MudTh>
|
||||||
|
<MudTh>@T("Text")</MudTh>
|
||||||
|
</HeaderContent>
|
||||||
|
<RowTemplate>
|
||||||
|
<MudTd>
|
||||||
|
<pre style="font-size: 0.8em;">
|
||||||
|
@context.Key
|
||||||
|
</pre>
|
||||||
|
</MudTd>
|
||||||
|
<MudTd>
|
||||||
|
@context.Value
|
||||||
|
</MudTd>
|
||||||
|
</RowTemplate>
|
||||||
|
<PagerContent>
|
||||||
|
<MudTablePager />
|
||||||
|
</PagerContent>
|
||||||
|
</MudTable>
|
||||||
|
|
||||||
|
<MudText Typo="Typo.h6">
|
||||||
|
@this.RemovedContentText
|
||||||
|
</MudText>
|
||||||
|
<MudTable Items="@this.removedContent" Hover="@true" Filter="@this.FilterFunc" Class="border-dashed border rounded-lg mb-6">
|
||||||
|
<ToolBarContent>
|
||||||
|
<MudTextField @bind-Value="@this.searchString" Immediate="true" Placeholder="@T("Search")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"/>
|
||||||
|
</ToolBarContent>
|
||||||
|
<ColGroup>
|
||||||
|
<col/>
|
||||||
|
<col/>
|
||||||
|
</ColGroup>
|
||||||
|
<HeaderContent>
|
||||||
|
<MudTh>@T("Key")</MudTh>
|
||||||
|
<MudTh>@T("Text")</MudTh>
|
||||||
|
</HeaderContent>
|
||||||
|
<RowTemplate>
|
||||||
|
<MudTd>
|
||||||
|
<pre style="font-size: 0.8em;">
|
||||||
|
@context.Key
|
||||||
|
</pre>
|
||||||
|
</MudTd>
|
||||||
|
<MudTd>
|
||||||
|
@context.Value
|
||||||
|
</MudTd>
|
||||||
|
</RowTemplate>
|
||||||
|
<PagerContent>
|
||||||
|
<MudTablePager />
|
||||||
|
</PagerContent>
|
||||||
|
</MudTable>
|
||||||
|
|
||||||
|
@if (this.selectedTargetLanguage is CommonLanguages.EN_US)
|
||||||
|
{
|
||||||
|
<MudJustifiedText Typo="Typo.body1" Class="mb-6">
|
||||||
|
@T("Please note: neither is a translation needed nor performed for English (USA). Anyway, you might want to generate the related Lua code.")
|
||||||
|
</MudJustifiedText>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (this.localizedContent.Count > 0)
|
||||||
|
{
|
||||||
|
<hr style="width: 100%; border-width: 0.25ch;" class="mt-6 mb-6"/>
|
||||||
|
<MudText Typo="Typo.h6">
|
||||||
|
@this.LocalizedContentText
|
||||||
|
</MudText>
|
||||||
|
<MudTable Items="@this.localizedContent" Hover="@true" Filter="@this.FilterFunc" Class="border-dashed border rounded-lg mb-6">
|
||||||
|
<ToolBarContent>
|
||||||
|
<MudTextField @bind-Value="@this.searchString" Immediate="true" Placeholder="@T("Search")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"/>
|
||||||
|
</ToolBarContent>
|
||||||
|
<ColGroup>
|
||||||
|
<col/>
|
||||||
|
<col/>
|
||||||
|
</ColGroup>
|
||||||
|
<HeaderContent>
|
||||||
|
<MudTh>@T("Key")</MudTh>
|
||||||
|
<MudTh>@T("Text")</MudTh>
|
||||||
|
</HeaderContent>
|
||||||
|
<RowTemplate>
|
||||||
|
<MudTd>
|
||||||
|
<pre style="font-size: 0.8em;">
|
||||||
|
@context.Key
|
||||||
|
</pre>
|
||||||
|
</MudTd>
|
||||||
|
<MudTd>
|
||||||
|
@context.Value
|
||||||
|
</MudTd>
|
||||||
|
</RowTemplate>
|
||||||
|
<PagerContent>
|
||||||
|
<MudTablePager />
|
||||||
|
</PagerContent>
|
||||||
|
</MudTable>
|
||||||
|
}
|
||||||
|
}
|
377
app/MindWork AI Studio/Assistants/I18N/AssistantI18N.razor.cs
Normal file
377
app/MindWork AI Studio/Assistants/I18N/AssistantI18N.razor.cs
Normal file
@ -0,0 +1,377 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using AIStudio.Dialogs.Settings;
|
||||||
|
using AIStudio.Tools.PluginSystem;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.FileProviders;
|
||||||
|
|
||||||
|
using SharedTools;
|
||||||
|
|
||||||
|
#if RELEASE
|
||||||
|
using System.Reflection;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace AIStudio.Assistants.I18N;
|
||||||
|
|
||||||
|
public partial class AssistantI18N : AssistantBaseCore<SettingsDialogI18N>
|
||||||
|
{
|
||||||
|
public override Tools.Components Component => Tools.Components.I18N_ASSISTANT;
|
||||||
|
|
||||||
|
protected override string Title => T("Localization");
|
||||||
|
|
||||||
|
protected override string Description => T("Translate MindWork AI Studio text content into another language.");
|
||||||
|
|
||||||
|
protected override string SystemPrompt =>
|
||||||
|
$"""
|
||||||
|
# Assignment
|
||||||
|
You are an expert in professional translations from English (US) to {this.SystemPromptLanguage()}.
|
||||||
|
You translate the texts without adding any new information. When necessary, you correct
|
||||||
|
spelling and grammar.
|
||||||
|
|
||||||
|
# Context
|
||||||
|
The texts to be translated come from the open source app "MindWork AI Studio". The goal
|
||||||
|
is to localize the app so that it can be offered in other languages. You will always
|
||||||
|
receive one text at a time. A text may be, for example, for a button, a label, or an
|
||||||
|
explanation within the app. The app "AI Studio" is a desktop app for macOS, Linux,
|
||||||
|
and Windows. Users can use Large Language Models (LLMs) in practical ways in their
|
||||||
|
daily lives with it. The app offers the regular chat mode for which LLMs have become
|
||||||
|
known. However, AI Studio also offers so-called assistants, where users no longer
|
||||||
|
have to prompt.
|
||||||
|
|
||||||
|
# Target Audience
|
||||||
|
The app is intended for everyone, not just IT specialists or scientists. When translating,
|
||||||
|
make sure the texts are easy for everyone to understand.
|
||||||
|
""";
|
||||||
|
|
||||||
|
protected override bool AllowProfiles => false;
|
||||||
|
|
||||||
|
protected override bool ShowResult => false;
|
||||||
|
|
||||||
|
protected override bool ShowCopyResult => false;
|
||||||
|
|
||||||
|
protected override bool ShowSendTo => false;
|
||||||
|
|
||||||
|
protected override IReadOnlyList<IButtonData> FooterButtons =>
|
||||||
|
[
|
||||||
|
new ButtonData
|
||||||
|
{
|
||||||
|
Text = T("Copy Lua code to clipboard"),
|
||||||
|
Icon = Icons.Material.Filled.Extension,
|
||||||
|
Color = Color.Default,
|
||||||
|
AsyncAction = async () => await this.RustService.CopyText2Clipboard(this.Snackbar, this.finalLuaCode.ToString()),
|
||||||
|
DisabledActionParam = () => this.finalLuaCode.Length == 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
protected override string SubmitText => T("Localize AI Studio & generate the Lua code");
|
||||||
|
|
||||||
|
protected override Func<Task> SubmitAction => this.LocalizeTextContent;
|
||||||
|
|
||||||
|
protected override bool SubmitDisabled => !this.localizationPossible;
|
||||||
|
|
||||||
|
protected override bool ShowDedicatedProgress => true;
|
||||||
|
|
||||||
|
protected override void ResetForm()
|
||||||
|
{
|
||||||
|
if (!this.MightPreselectValues())
|
||||||
|
{
|
||||||
|
this.selectedLanguagePluginId = InternalPlugin.LANGUAGE_EN_US.MetaData().Id;
|
||||||
|
this.selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||||
|
this.customTargetLanguage = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = this.OnChangedLanguage();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool MightPreselectValues()
|
||||||
|
{
|
||||||
|
if (this.SettingsManager.ConfigurationData.I18N.PreselectOptions)
|
||||||
|
{
|
||||||
|
this.selectedLanguagePluginId = this.SettingsManager.ConfigurationData.I18N.PreselectedLanguagePluginId;
|
||||||
|
this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.I18N.PreselectedTargetLanguage;
|
||||||
|
this.customTargetLanguage = this.SettingsManager.ConfigurationData.I18N.PreselectOtherLanguage;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommonLanguages selectedTargetLanguage;
|
||||||
|
private string customTargetLanguage = string.Empty;
|
||||||
|
private bool isLoading = true;
|
||||||
|
private string loadingIssue = string.Empty;
|
||||||
|
private bool localizationPossible;
|
||||||
|
private string searchString = string.Empty;
|
||||||
|
private Guid selectedLanguagePluginId;
|
||||||
|
private ILanguagePlugin? selectedLanguagePlugin;
|
||||||
|
private Dictionary<string, string> addedContent = [];
|
||||||
|
private Dictionary<string, string> removedContent = [];
|
||||||
|
private Dictionary<string, string> localizedContent = [];
|
||||||
|
private StringBuilder finalLuaCode = new();
|
||||||
|
|
||||||
|
#region Overrides of AssistantBase<SettingsDialogI18N>
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
await base.OnInitializedAsync();
|
||||||
|
await this.OnLanguagePluginChanged(this.selectedLanguagePluginId);
|
||||||
|
await this.LoadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private string SystemPromptLanguage() => this.selectedTargetLanguage switch
|
||||||
|
{
|
||||||
|
CommonLanguages.OTHER => this.customTargetLanguage,
|
||||||
|
_ => $"{this.selectedTargetLanguage.Name()}",
|
||||||
|
};
|
||||||
|
|
||||||
|
private async Task OnLanguagePluginChanged(Guid pluginId)
|
||||||
|
{
|
||||||
|
this.selectedLanguagePluginId = pluginId;
|
||||||
|
await this.OnChangedLanguage();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnChangedLanguage()
|
||||||
|
{
|
||||||
|
this.finalLuaCode.Clear();
|
||||||
|
this.localizedContent.Clear();
|
||||||
|
this.localizationPossible = false;
|
||||||
|
if (PluginFactory.RunningPlugins.FirstOrDefault(n => n is PluginLanguage && n.Id == this.selectedLanguagePluginId) is not PluginLanguage comparisonPlugin)
|
||||||
|
{
|
||||||
|
this.loadingIssue = string.Format(T("Was not able to load the language plugin for comparison ({0}). Please select a valid, loaded & running language plugin."), this.selectedLanguagePluginId);
|
||||||
|
this.selectedLanguagePlugin = null;
|
||||||
|
}
|
||||||
|
else if (comparisonPlugin.IETFTag != this.selectedTargetLanguage.ToIETFTag())
|
||||||
|
{
|
||||||
|
this.loadingIssue = string.Format(T("The selected language plugin for comparison uses the IETF tag '{0}' which does not match the selected target language '{1}'. Please select a valid, loaded & running language plugin which matches the target language."), comparisonPlugin.IETFTag, this.selectedTargetLanguage.ToIETFTag());
|
||||||
|
this.selectedLanguagePlugin = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.selectedLanguagePlugin = comparisonPlugin;
|
||||||
|
this.loadingIssue = string.Empty;
|
||||||
|
await this.LoadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadData()
|
||||||
|
{
|
||||||
|
if (this.selectedLanguagePlugin is null)
|
||||||
|
{
|
||||||
|
this.loadingIssue = T("Please select a language plugin for comparison.");
|
||||||
|
this.localizationPossible = false;
|
||||||
|
this.isLoading = false;
|
||||||
|
this.StateHasChanged();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isLoading = true;
|
||||||
|
this.StateHasChanged();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Read the file `Assistants\I18N\allTexts.lua`:
|
||||||
|
//
|
||||||
|
#if DEBUG
|
||||||
|
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");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
var file = resourceFileProvider.GetFileInfo("allTexts.lua");
|
||||||
|
await using var fileStream = file.CreateReadStream();
|
||||||
|
using var reader = new StreamReader(fileStream);
|
||||||
|
var newI18NDataLuaCode = await reader.ReadToEndAsync();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Next, we try to load the text as a language plugin -- without
|
||||||
|
// actually starting the plugin:
|
||||||
|
//
|
||||||
|
var newI18NPlugin = await PluginFactory.Load(null, newI18NDataLuaCode);
|
||||||
|
switch (newI18NPlugin)
|
||||||
|
{
|
||||||
|
case NoPlugin noPlugin when noPlugin.Issues.Any():
|
||||||
|
this.loadingIssue = noPlugin.Issues.First();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NoPlugin:
|
||||||
|
this.loadingIssue = T("Was not able to load the I18N plugin. Please check the plugin code.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case { IsValid: false } plugin when plugin.Issues.Any():
|
||||||
|
this.loadingIssue = plugin.Issues.First();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PluginLanguage pluginLanguage:
|
||||||
|
this.loadingIssue = string.Empty;
|
||||||
|
var newI18NContent = pluginLanguage.Content;
|
||||||
|
|
||||||
|
var currentI18NContent = this.selectedLanguagePlugin.Content;
|
||||||
|
this.addedContent = newI18NContent.ExceptBy(currentI18NContent.Keys, n => n.Key).ToDictionary();
|
||||||
|
this.removedContent = currentI18NContent.ExceptBy(newI18NContent.Keys, n => n.Key).ToDictionary();
|
||||||
|
this.localizationPossible = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isLoading = false;
|
||||||
|
this.StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool FilterFunc(KeyValuePair<string, string> element)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(this.searchString))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (element.Key.Contains(this.searchString, StringComparison.OrdinalIgnoreCase))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (element.Value.Contains(this.searchString, StringComparison.OrdinalIgnoreCase))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? ValidatingTargetLanguage(CommonLanguages language)
|
||||||
|
{
|
||||||
|
if(language == CommonLanguages.AS_IS)
|
||||||
|
return T("Please select a target language.");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? ValidateCustomLanguage(string language)
|
||||||
|
{
|
||||||
|
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||||
|
return T("Please provide a custom language.");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int NumTotalItems => (this.selectedLanguagePlugin?.Content.Count ?? 0) + this.addedContent.Count - this.removedContent.Count;
|
||||||
|
|
||||||
|
private string AddedContentText => string.Format(T("Added Content ({0} entries)"), this.addedContent.Count);
|
||||||
|
|
||||||
|
private string RemovedContentText => string.Format(T("Removed Content ({0} entries)"), this.removedContent.Count);
|
||||||
|
|
||||||
|
private string LocalizedContentText => string.Format(T("Localized Content ({0} entries of {1})"), this.localizedContent.Count, this.NumTotalItems);
|
||||||
|
|
||||||
|
private async Task LocalizeTextContent()
|
||||||
|
{
|
||||||
|
await this.form!.Validate();
|
||||||
|
if (!this.inputIsValid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(this.selectedLanguagePlugin is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this.selectedLanguagePlugin.IETFTag != this.selectedTargetLanguage.ToIETFTag())
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.localizedContent.Clear();
|
||||||
|
if (this.selectedTargetLanguage is not CommonLanguages.EN_US)
|
||||||
|
{
|
||||||
|
// Phase 1: Translate added content
|
||||||
|
await this.Phase1TranslateAddedContent();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Case: no translation needed
|
||||||
|
this.localizedContent = this.addedContent.ToDictionary();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.cancellationTokenSource!.IsCancellationRequested)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Now, we have localized the added content. Next, we must merge
|
||||||
|
// the localized content with the existing content. However, we
|
||||||
|
// must skip the removed content. We use the localizedContent
|
||||||
|
// dictionary for the final result:
|
||||||
|
//
|
||||||
|
foreach (var keyValuePair in this.selectedLanguagePlugin.Content)
|
||||||
|
{
|
||||||
|
if (this.cancellationTokenSource!.IsCancellationRequested)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (this.localizedContent.ContainsKey(keyValuePair.Key))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (this.removedContent.ContainsKey(keyValuePair.Key))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
this.localizedContent.Add(keyValuePair.Key, keyValuePair.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.cancellationTokenSource!.IsCancellationRequested)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Phase 2: Create the Lua code. We want to use the base language
|
||||||
|
// for the comments, though:
|
||||||
|
//
|
||||||
|
var commentContent = new Dictionary<string, string>(this.addedContent);
|
||||||
|
foreach (var keyValuePair in PluginFactory.BaseLanguage.Content)
|
||||||
|
{
|
||||||
|
if (this.cancellationTokenSource!.IsCancellationRequested)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (this.removedContent.ContainsKey(keyValuePair.Key))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
commentContent.TryAdd(keyValuePair.Key, keyValuePair.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Phase2CreateLuaCode(commentContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Phase1TranslateAddedContent()
|
||||||
|
{
|
||||||
|
var stopwatch = new Stopwatch();
|
||||||
|
var minimumTime = TimeSpan.FromMilliseconds(500);
|
||||||
|
foreach (var keyValuePair in this.addedContent)
|
||||||
|
{
|
||||||
|
if(this.cancellationTokenSource!.IsCancellationRequested)
|
||||||
|
break;
|
||||||
|
|
||||||
|
//
|
||||||
|
// We measure the time for each translation.
|
||||||
|
// We do not want to make more than 120 requests
|
||||||
|
// per minute, i.e., 2 requests per second.
|
||||||
|
//
|
||||||
|
stopwatch.Reset();
|
||||||
|
stopwatch.Start();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Translate one text at a time:
|
||||||
|
//
|
||||||
|
this.CreateChatThread();
|
||||||
|
var time = this.AddUserRequest(keyValuePair.Value);
|
||||||
|
this.localizedContent.Add(keyValuePair.Key, await this.AddAIResponseAsync(time));
|
||||||
|
|
||||||
|
if (this.cancellationTokenSource!.IsCancellationRequested)
|
||||||
|
break;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Ensure that we do not exceed the rate limit of 2 requests per second:
|
||||||
|
//
|
||||||
|
stopwatch.Stop();
|
||||||
|
if (stopwatch.Elapsed < minimumTime)
|
||||||
|
await Task.Delay(minimumTime - stopwatch.Elapsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Phase2CreateLuaCode(IReadOnlyDictionary<string, string> commentContent)
|
||||||
|
{
|
||||||
|
this.finalLuaCode.Clear();
|
||||||
|
LuaTable.Create(ref this.finalLuaCode, "UI_TEXT_CONTENT", this.localizedContent, commentContent, this.cancellationTokenSource!.Token);
|
||||||
|
|
||||||
|
// Next, we must remove the `root::` prefix from the keys:
|
||||||
|
this.finalLuaCode.Replace("""UI_TEXT_CONTENT["root::""", """
|
||||||
|
UI_TEXT_CONTENT["
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
}
|
5630
app/MindWork AI Studio/Assistants/I18N/allTexts.lua
Normal file
5630
app/MindWork AI Studio/Assistants/I18N/allTexts.lua
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,18 +1,22 @@
|
|||||||
@attribute [Route(Routes.ASSISTANT_ICON_FINDER)]
|
@attribute [Route(Routes.ASSISTANT_ICON_FINDER)]
|
||||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogIconFinder>
|
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogIconFinder>
|
||||||
|
|
||||||
<MudTextField T="string" @bind-Text="@this.inputContext" Validation="@this.ValidatingContext" AdornmentIcon="@Icons.Material.Filled.Description" Adornment="Adornment.Start" Label="Your context" Variant="Variant.Outlined" Lines="3" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
<MudTextField T="string" @bind-Text="@this.inputContext" Validation="@this.ValidatingContext" AdornmentIcon="@Icons.Material.Filled.Description" Adornment="Adornment.Start" Label="@T("Your context")" Variant="Variant.Outlined" Lines="3" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||||
|
|
||||||
<MudStack Row="@true" AlignItems="AlignItems.Center" Class="mb-3">
|
<MudStack Row="@true" AlignItems="AlignItems.Center" Class="mb-3">
|
||||||
<MudSelect T="IconSources" @bind-Value="@this.selectedIconSource" AdornmentIcon="@Icons.Material.Filled.Source" Adornment="Adornment.Start" Label="Your icon source" Variant="Variant.Outlined" Margin="Margin.Dense">
|
<MudSelect T="IconSources" @bind-Value="@this.selectedIconSource" AdornmentIcon="@Icons.Material.Filled.Source" Adornment="Adornment.Start" Label="@T("Your icon source")" Variant="Variant.Outlined" Margin="Margin.Dense">
|
||||||
@foreach (var source in Enum.GetValues<IconSources>())
|
@foreach (var source in Enum.GetValues<IconSources>())
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="@source">@source.Name()</MudSelectItem>
|
<MudSelectItem Value="@source">
|
||||||
|
@source.Name()
|
||||||
|
</MudSelectItem>
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
@if (this.selectedIconSource is not IconSources.GENERIC)
|
@if (this.selectedIconSource is not IconSources.GENERIC)
|
||||||
{
|
{
|
||||||
<MudButton Href="@this.selectedIconSource.URL()" Target="_blank" Variant="Variant.Filled" Size="Size.Medium">Open website</MudButton>
|
<MudButton Href="@this.selectedIconSource.URL()" Target="_blank" Variant="Variant.Filled" Size="Size.Medium">
|
||||||
|
@T("Open website")
|
||||||
|
</MudButton>
|
||||||
}
|
}
|
||||||
</MudStack>
|
</MudStack>
|
||||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
@ -6,17 +6,9 @@ public partial class AssistantIconFinder : AssistantBaseCore<SettingsDialogIconF
|
|||||||
{
|
{
|
||||||
public override Tools.Components Component => Tools.Components.ICON_FINDER_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.ICON_FINDER_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Icon Finder";
|
protected override string Title => T("Icon Finder");
|
||||||
|
|
||||||
protected override string Description =>
|
protected override string Description => T("""Finding the right icon for a context, such as for a piece of text, is not easy. The first challenge: You need to extract a concept from your context, such as from a text. Let's take an example where your text contains statements about multiple departments. The sought-after concept could be "departments." The next challenge is that we need to anticipate the bias of the icon designers: under the search term "departments," there may be no relevant icons or only unsuitable ones. Depending on the icon source, it might be more effective to search for "buildings," for instance. LLMs assist you with both steps.""");
|
||||||
"""
|
|
||||||
Finding the right icon for a context, such as for a piece of text, is not easy. The first challenge:
|
|
||||||
You need to extract a concept from your context, such as from a text. Let's take an example where
|
|
||||||
your text contains statements about multiple departments. The sought-after concept could be "departments."
|
|
||||||
The next challenge is that we need to anticipate the bias of the icon designers: under the search term
|
|
||||||
"departments," there may be no relevant icons or only unsuitable ones. Depending on the icon source,
|
|
||||||
it might be more effective to search for "buildings," for instance. LLMs assist you with both steps.
|
|
||||||
""";
|
|
||||||
|
|
||||||
protected override string SystemPrompt =>
|
protected override string SystemPrompt =>
|
||||||
"""
|
"""
|
||||||
@ -31,7 +23,7 @@ public partial class AssistantIconFinder : AssistantBaseCore<SettingsDialogIconF
|
|||||||
|
|
||||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||||
|
|
||||||
protected override string SubmitText => "Find Icon";
|
protected override string SubmitText => T("Find Icon");
|
||||||
|
|
||||||
protected override Func<Task> SubmitAction => this.FindIcon;
|
protected override Func<Task> SubmitAction => this.FindIcon;
|
||||||
|
|
||||||
@ -74,7 +66,7 @@ public partial class AssistantIconFinder : AssistantBaseCore<SettingsDialogIconF
|
|||||||
private string? ValidatingContext(string context)
|
private string? ValidatingContext(string context)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(context))
|
if(string.IsNullOrWhiteSpace(context))
|
||||||
return "Please provide a context. This will help the AI to find the right icon. You might type just a keyword or copy a sentence from your text, e.g., from a slide where you want to use the icon.";
|
return T("Please provide a context. This will help the AI to find the right icon. You might type just a keyword or copy a sentence from your text, e.g., from a slide where you want to use the icon.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
@attribute [Route(Routes.ASSISTANT_JOB_POSTING)]
|
@attribute [Route(Routes.ASSISTANT_JOB_POSTING)]
|
||||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogJobPostings>
|
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogJobPostings>
|
||||||
|
|
||||||
<MudTextField T="string" @bind-Text="@this.inputCompanyName" Label="(Optional) The company name" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Warehouse" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
<MudTextField T="string" @bind-Text="@this.inputCompanyName" Label="@T("(Optional) The company name")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Warehouse" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||||
<MudTextField T="string" @bind-Text="@this.inputCountryLegalFramework" Label="Provide the country, where the company is located" Validation="@this.ValidateCountryLegalFramework" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Flag" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3" HelperText="This is important to consider the legal framework of the country."/>
|
<MudTextField T="string" @bind-Text="@this.inputCountryLegalFramework" Label="@T("Provide the country, where the company is located")" Validation="@this.ValidateCountryLegalFramework" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Flag" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3" HelperText="@T("This is important to consider the legal framework of the country.")"/>
|
||||||
<MudTextField T="string" @bind-Text="@this.inputMandatoryInformation" Label="(Optional) Provide mandatory information" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.TextSnippet" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Mandatory information that your company requires for all job postings. This can include the company description, etc." />
|
<MudTextField T="string" @bind-Text="@this.inputMandatoryInformation" Label="@T("(Optional) Provide mandatory information")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.TextSnippet" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="@T("Mandatory information that your company requires for all job postings. This can include the company description, etc.")" />
|
||||||
<MudTextField T="string" @bind-Text="@this.inputJobDescription" Label="Job description" Validation="@this.ValidateJobDescription" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Settings" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Describe what the person is supposed to do in the company. This might be just short bullet points." />
|
<MudTextField T="string" @bind-Text="@this.inputJobDescription" Label="@T("Job description")" Validation="@this.ValidateJobDescription" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Settings" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="@T("Describe what the person is supposed to do in the company. This might be just short bullet points.")" />
|
||||||
<MudTextField T="string" @bind-Text="@this.inputQualifications" Label="(Optional) Provide necessary job qualifications" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Settings" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Describe what the person should bring to the table. This might be just short bullet points." />
|
<MudTextField T="string" @bind-Text="@this.inputQualifications" Label="@T("(Optional) Provide necessary job qualifications")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Settings" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="@T("Describe what the person should bring to the table. This might be just short bullet points.")" />
|
||||||
<MudTextField T="string" @bind-Text="@this.inputResponsibilities" Label="(Optional) Provide job responsibilities" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Settings" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Describe the responsibilities the person should take on in the company." />
|
<MudTextField T="string" @bind-Text="@this.inputResponsibilities" Label="@T("(Optional) Provide job responsibilities")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Settings" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="@T("Describe the responsibilities the person should take on in the company.")" />
|
||||||
<MudTextField T="string" @bind-Text="@this.inputWorkLocation" Label="(Optional) Provide the work location" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.MyLocation" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
<MudTextField T="string" @bind-Text="@this.inputWorkLocation" Label="@T("(Optional) Provide the work location")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.MyLocation" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||||
<MudTextField T="string" @bind-Text="@this.inputEntryDate" Label="(Optional) Provide the entry date" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.DateRange" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
<MudTextField T="string" @bind-Text="@this.inputEntryDate" Label="@T("(Optional) Provide the entry date")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.DateRange" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||||
<MudTextField T="string" @bind-Text="@this.inputValidUntil" Label="(Optional) Provide the date until the job posting is valid" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.DateRange" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
<MudTextField T="string" @bind-Text="@this.inputValidUntil" Label="@T("(Optional) Provide the date until the job posting is valid")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.DateRange" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||||
|
|
||||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Target language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom target language" />
|
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="@T("Target language")" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="@T("Custom target language")" />
|
||||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
@ -7,13 +7,9 @@ public partial class AssistantJobPostings : AssistantBaseCore<SettingsDialogJobP
|
|||||||
{
|
{
|
||||||
public override Tools.Components Component => Tools.Components.JOB_POSTING_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.JOB_POSTING_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Job Posting";
|
protected override string Title => T("Job Posting");
|
||||||
|
|
||||||
protected override string Description =>
|
protected override string Description => T("Provide some key points about the job you want to post. The AI will then formulate a suggestion that you can finalize.");
|
||||||
"""
|
|
||||||
Provide some key points about the job you want to post. The AI will then
|
|
||||||
formulate a suggestion that you can finalize.
|
|
||||||
""";
|
|
||||||
|
|
||||||
protected override string SystemPrompt =>
|
protected override string SystemPrompt =>
|
||||||
$"""
|
$"""
|
||||||
@ -47,7 +43,7 @@ public partial class AssistantJobPostings : AssistantBaseCore<SettingsDialogJobP
|
|||||||
|
|
||||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||||
|
|
||||||
protected override string SubmitText => "Create the job posting";
|
protected override string SubmitText => T("Create the job posting");
|
||||||
|
|
||||||
protected override Func<Task> SubmitAction => this.CreateJobPosting;
|
protected override Func<Task> SubmitAction => this.CreateJobPosting;
|
||||||
|
|
||||||
@ -126,7 +122,7 @@ public partial class AssistantJobPostings : AssistantBaseCore<SettingsDialogJobP
|
|||||||
private string? ValidateCustomLanguage(string language)
|
private string? ValidateCustomLanguage(string language)
|
||||||
{
|
{
|
||||||
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||||
return "Please provide a custom target language.";
|
return T("Please provide a custom target language.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -134,7 +130,7 @@ public partial class AssistantJobPostings : AssistantBaseCore<SettingsDialogJobP
|
|||||||
private string? ValidateJobDescription(string jobDescription)
|
private string? ValidateJobDescription(string jobDescription)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(jobDescription))
|
if(string.IsNullOrWhiteSpace(jobDescription))
|
||||||
return "Please provide a job description.";
|
return T("Please provide a job description.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -142,7 +138,7 @@ public partial class AssistantJobPostings : AssistantBaseCore<SettingsDialogJobP
|
|||||||
private string? ValidateCountryLegalFramework(string countryLegalFramework)
|
private string? ValidateCountryLegalFramework(string countryLegalFramework)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(countryLegalFramework))
|
if(string.IsNullOrWhiteSpace(countryLegalFramework))
|
||||||
return "Please provide the country where the job is posted (legal framework).";
|
return T("Please provide the country where the job is posted (legal framework).");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
<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)"/>
|
<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)"/>
|
||||||
}
|
}
|
||||||
|
|
||||||
<MudTextField T="string" Disabled="@this.isAgentRunning" @bind-Text="@this.inputLegalDocument" Validation="@this.ValidatingLegalDocument" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="Legal document" Variant="Variant.Outlined" Lines="12" AutoGrow="@true" MaxLines="24" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
<ReadFileContent @bind-FileContent="@this.inputLegalDocument"/>
|
||||||
<MudTextField T="string" Disabled="@this.isAgentRunning" @bind-Text="@this.inputQuestions" Validation="@this.ValidatingQuestions" AdornmentIcon="@Icons.Material.Filled.QuestionAnswer" Adornment="Adornment.Start" Label="Your questions" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
<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"/>
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
@ -7,14 +7,9 @@ public partial class AssistantLegalCheck : AssistantBaseCore<SettingsDialogLegal
|
|||||||
{
|
{
|
||||||
public override Tools.Components Component => Tools.Components.LEGAL_CHECK_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.LEGAL_CHECK_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Legal Check";
|
protected override string Title => T("Legal Check");
|
||||||
|
|
||||||
protected override string Description =>
|
protected override string Description => T("Provide a legal document and ask a question about it. This assistant does not replace legal advice. Consult a lawyer to get professional advice. Remember that LLMs can invent answers and facts. Please do not rely on this answers.");
|
||||||
"""
|
|
||||||
Provide a legal document and ask a question about it. This assistant does not
|
|
||||||
replace legal advice. Consult a lawyer to get professional advice. Remember
|
|
||||||
that LLMs can invent answers and facts. Please do not rely on this answers.
|
|
||||||
""";
|
|
||||||
|
|
||||||
protected override string SystemPrompt =>
|
protected override string SystemPrompt =>
|
||||||
"""
|
"""
|
||||||
@ -27,7 +22,7 @@ public partial class AssistantLegalCheck : AssistantBaseCore<SettingsDialogLegal
|
|||||||
|
|
||||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||||
|
|
||||||
protected override string SubmitText => "Ask your questions";
|
protected override string SubmitText => T("Ask your questions");
|
||||||
|
|
||||||
protected override Func<Task> SubmitAction => this.AksQuestions;
|
protected override Func<Task> SubmitAction => this.AksQuestions;
|
||||||
|
|
||||||
@ -67,7 +62,7 @@ public partial class AssistantLegalCheck : AssistantBaseCore<SettingsDialogLegal
|
|||||||
private string? ValidatingLegalDocument(string text)
|
private string? ValidatingLegalDocument(string text)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(text))
|
if(string.IsNullOrWhiteSpace(text))
|
||||||
return "Please provide a legal document as input. You might copy the desired text from a document or a website.";
|
return T("Please provide a legal document as input. You might copy the desired text from a document or a website.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -75,7 +70,7 @@ public partial class AssistantLegalCheck : AssistantBaseCore<SettingsDialogLegal
|
|||||||
private string? ValidatingQuestions(string text)
|
private string? ValidatingQuestions(string text)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(text))
|
if(string.IsNullOrWhiteSpace(text))
|
||||||
return "Please provide your questions as input.";
|
return T("Please provide your questions as input.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,6 @@
|
|||||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogMyTasks>
|
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogMyTasks>
|
||||||
|
|
||||||
<ProfileFormSelection Validation="@this.ValidateProfile" @bind-Profile="@this.currentProfile"/>
|
<ProfileFormSelection Validation="@this.ValidateProfile" @bind-Profile="@this.currentProfile"/>
|
||||||
<MudTextField T="string" @bind-Text="@this.inputText" Validation="@this.ValidatingText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="Text or email" Variant="Variant.Outlined" Lines="12" AutoGrow="@true" MaxLines="24" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
<MudTextField T="string" @bind-Text="@this.inputText" Validation="@this.ValidatingText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="@T("Text or email")" Variant="Variant.Outlined" Lines="12" AutoGrow="@true" MaxLines="24" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Target language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom target language" />
|
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="@T("Target language")" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="@T("Custom target language")" />
|
||||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
@ -8,15 +8,9 @@ public partial class AssistantMyTasks : AssistantBaseCore<SettingsDialogMyTasks>
|
|||||||
{
|
{
|
||||||
public override Tools.Components Component => Tools.Components.MY_TASKS_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.MY_TASKS_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "My Tasks";
|
protected override string Title => T("My Tasks");
|
||||||
|
|
||||||
protected override string Description =>
|
protected override string Description => T("You received a cryptic email that was sent to many recipients and you are now wondering if you need to do something? Copy the email into the input field. You also need to select a personal profile. In this profile, you should describe your role in the organization. The AI will then try to give you hints on what your tasks might be.");
|
||||||
"""
|
|
||||||
You received a cryptic email that was sent to many recipients and you are now wondering
|
|
||||||
if you need to do something? Copy the email into the input field. You also need to select
|
|
||||||
a personal profile. In this profile, you should describe your role in the organization.
|
|
||||||
The AI will then try to give you hints on what your tasks might be.
|
|
||||||
""";
|
|
||||||
|
|
||||||
protected override string SystemPrompt =>
|
protected override string SystemPrompt =>
|
||||||
$"""
|
$"""
|
||||||
@ -31,7 +25,7 @@ public partial class AssistantMyTasks : AssistantBaseCore<SettingsDialogMyTasks>
|
|||||||
|
|
||||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||||
|
|
||||||
protected override string SubmitText => "Analyze text";
|
protected override string SubmitText => T("Analyze text");
|
||||||
|
|
||||||
protected override Func<Task> SubmitAction => this.AnalyzeText;
|
protected override Func<Task> SubmitAction => this.AnalyzeText;
|
||||||
|
|
||||||
@ -84,7 +78,7 @@ public partial class AssistantMyTasks : AssistantBaseCore<SettingsDialogMyTasks>
|
|||||||
private string? ValidatingText(string text)
|
private string? ValidatingText(string text)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(text))
|
if(string.IsNullOrWhiteSpace(text))
|
||||||
return "Please provide some text as input. For example, an email.";
|
return T("Please provide some text as input. For example, an email.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -92,7 +86,7 @@ public partial class AssistantMyTasks : AssistantBaseCore<SettingsDialogMyTasks>
|
|||||||
private string? ValidateProfile(Profile profile)
|
private string? ValidateProfile(Profile profile)
|
||||||
{
|
{
|
||||||
if(profile == default || profile == Profile.NO_PROFILE)
|
if(profile == default || profile == Profile.NO_PROFILE)
|
||||||
return "Please select one of your profiles.";
|
return T("Please select one of your profiles.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -100,7 +94,7 @@ public partial class AssistantMyTasks : AssistantBaseCore<SettingsDialogMyTasks>
|
|||||||
private string? ValidateCustomLanguage(string language)
|
private string? ValidateCustomLanguage(string language)
|
||||||
{
|
{
|
||||||
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||||
return "Please provide a custom language.";
|
return T("Please provide a custom language.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
@attribute [Route(Routes.ASSISTANT_REWRITE)]
|
@attribute [Route(Routes.ASSISTANT_REWRITE)]
|
||||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogRewrite>
|
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogRewrite>
|
||||||
|
|
||||||
<MudTextField T="string" @bind-Text="@this.inputText" Validation="@this.ValidateText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="Your input to improve" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
<MudTextField T="string" @bind-Text="@this.inputText" Validation="@this.ValidateText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="@T("Your input to improve")" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom language" />
|
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="@T("Language")" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="@T("Custom language")" />
|
||||||
<EnumSelection T="WritingStyles" NameFunc="@(style => style.Name())" @bind-Value="@this.selectedWritingStyle" Icon="@Icons.Material.Filled.Edit" Label="Writing style" AllowOther="@false" />
|
<EnumSelection T="WritingStyles" NameFunc="@(style => style.Name())" @bind-Value="@this.selectedWritingStyle" Icon="@Icons.Material.Filled.Edit" Label="@T("Writing style")" AllowOther="@false" />
|
||||||
<EnumSelection T="SentenceStructure" NameFunc="@(voice => voice.Name())" @bind-Value="@this.selectedSentenceStructure" Icon="@Icons.Material.Filled.Person4" Label="Sentence structure" />
|
<EnumSelection T="SentenceStructure" NameFunc="@(voice => voice.Name())" @bind-Value="@this.selectedSentenceStructure" Icon="@Icons.Material.Filled.Person4" Label="@T("Sentence structure")" />
|
||||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
@ -7,12 +7,9 @@ public partial class AssistantRewriteImprove : AssistantBaseCore<SettingsDialogR
|
|||||||
{
|
{
|
||||||
public override Tools.Components Component => Tools.Components.REWRITE_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.REWRITE_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Rewrite & Improve Text";
|
protected override string Title => T("Rewrite & Improve Text");
|
||||||
|
|
||||||
protected override string Description =>
|
protected override string Description => T("Rewrite and improve your text. Please note, that the capabilities of the different LLM providers will vary.");
|
||||||
"""
|
|
||||||
Rewrite and improve your text. Please note, that the capabilities of the different LLM providers will vary.
|
|
||||||
""";
|
|
||||||
|
|
||||||
protected override string SystemPrompt =>
|
protected override string SystemPrompt =>
|
||||||
$"""
|
$"""
|
||||||
@ -41,7 +38,7 @@ public partial class AssistantRewriteImprove : AssistantBaseCore<SettingsDialogR
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
protected override string SubmitText => "Improve";
|
protected override string SubmitText => T("Improve your text");
|
||||||
|
|
||||||
protected override Func<Task> SubmitAction => this.RewriteText;
|
protected override Func<Task> SubmitAction => this.RewriteText;
|
||||||
|
|
||||||
@ -100,7 +97,7 @@ public partial class AssistantRewriteImprove : AssistantBaseCore<SettingsDialogR
|
|||||||
private string? ValidateText(string text)
|
private string? ValidateText(string text)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(text))
|
if(string.IsNullOrWhiteSpace(text))
|
||||||
return "Please provide a text as input. You might copy the desired text from a document or a website.";
|
return T("Please provide a text as input. You might copy the desired text from a document or a website.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -108,7 +105,7 @@ public partial class AssistantRewriteImprove : AssistantBaseCore<SettingsDialogR
|
|||||||
private string? ValidateCustomLanguage(string language)
|
private string? ValidateCustomLanguage(string language)
|
||||||
{
|
{
|
||||||
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||||
return "Please provide a custom language.";
|
return T("Please provide a custom language.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,14 @@ namespace AIStudio.Assistants.RewriteImprove;
|
|||||||
|
|
||||||
public static class SentenceStructureExtensions
|
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
|
public static string Name(this SentenceStructure sentenceStructure) => sentenceStructure switch
|
||||||
{
|
{
|
||||||
SentenceStructure.ACTIVE => "Active voice",
|
SentenceStructure.ACTIVE => TB("Active voice"),
|
||||||
SentenceStructure.PASSIVE => "Passive voice",
|
SentenceStructure.PASSIVE => TB("Passive voice"),
|
||||||
|
|
||||||
_ => "Not Specified",
|
_ => TB("Not Specified"),
|
||||||
};
|
};
|
||||||
|
|
||||||
public static string Prompt(this SentenceStructure sentenceStructure) => sentenceStructure switch
|
public static string Prompt(this SentenceStructure sentenceStructure) => sentenceStructure switch
|
||||||
|
@ -13,4 +13,5 @@ public enum WritingStyles
|
|||||||
MARKETING,
|
MARKETING,
|
||||||
ACADEMIC,
|
ACADEMIC,
|
||||||
LEGAL,
|
LEGAL,
|
||||||
|
CHANGELOG,
|
||||||
}
|
}
|
@ -2,19 +2,22 @@ namespace AIStudio.Assistants.RewriteImprove;
|
|||||||
|
|
||||||
public static class WritingStylesExtensions
|
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
|
public static string Name(this WritingStyles style) => style switch
|
||||||
{
|
{
|
||||||
WritingStyles.EVERYDAY => "Everyday (personal texts, social media)",
|
WritingStyles.EVERYDAY => TB("Everyday (personal texts, social media)"),
|
||||||
WritingStyles.BUSINESS => "Business (business emails, reports, presentations)",
|
WritingStyles.BUSINESS => TB("Business (business emails, reports, presentations)"),
|
||||||
WritingStyles.SCIENTIFIC => "Scientific (scientific papers, research reports)",
|
WritingStyles.SCIENTIFIC => TB("Scientific (scientific papers, research reports)"),
|
||||||
WritingStyles.JOURNALISTIC => "Journalistic (magazines, newspapers, news)",
|
WritingStyles.JOURNALISTIC => TB("Journalistic (magazines, newspapers, news)"),
|
||||||
WritingStyles.LITERARY => "Literary (fiction, poetry)",
|
WritingStyles.LITERARY => TB("Literary (fiction, poetry)"),
|
||||||
WritingStyles.TECHNICAL => "Technical (manuals, documentation)",
|
WritingStyles.TECHNICAL => TB("Technical (manuals, documentation)"),
|
||||||
WritingStyles.MARKETING => "Marketing (advertisements, sales texts)",
|
WritingStyles.MARKETING => TB("Marketing (advertisements, sales texts)"),
|
||||||
WritingStyles.ACADEMIC => "Academic (essays, seminar papers)",
|
WritingStyles.ACADEMIC => TB("Academic (essays, seminar papers)"),
|
||||||
WritingStyles.LEGAL => "Legal (legal texts, contracts)",
|
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
|
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.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.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.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.",
|
_ => "Keep the style of the text as it is.",
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
@attribute [Route(Routes.ASSISTANT_SYNONYMS)]
|
@attribute [Route(Routes.ASSISTANT_SYNONYMS)]
|
||||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogSynonyms>
|
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogSynonyms>
|
||||||
|
|
||||||
<MudTextField T="string" @bind-Text="@this.inputText" Validation="@this.ValidatingText" AdornmentIcon="@Icons.Material.Filled.Spellcheck" Adornment="Adornment.Start" Label="Your word or phrase" Variant="Variant.Outlined" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
<MudTextField T="string" @bind-Text="@this.inputText" Validation="@this.ValidatingText" AdornmentIcon="@Icons.Material.Filled.Spellcheck" Adornment="Adornment.Start" Label="@T("Your word or phrase")" Variant="Variant.Outlined" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||||
<MudTextField T="string" @bind-Text="@this.inputContext" AdornmentIcon="@Icons.Material.Filled.Description" Adornment="Adornment.Start" Lines="2" AutoGrow="@false" Label="(Optional) The context for the given word or phrase" Variant="Variant.Outlined" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
<MudTextField T="string" @bind-Text="@this.inputContext" AdornmentIcon="@Icons.Material.Filled.Description" Adornment="Adornment.Start" Lines="2" AutoGrow="@false" Label="@T("(Optional) The context for the given word or phrase")" Variant="Variant.Outlined" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||||
|
|
||||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedLanguage" Icon="@Icons.Material.Filled.Translate" Label="Language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom target language" />
|
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedLanguage" Icon="@Icons.Material.Filled.Translate" Label="@T("Language")" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="@T("Custom target language")" />
|
||||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||||
|
@ -7,12 +7,9 @@ public partial class AssistantSynonyms : AssistantBaseCore<SettingsDialogSynonym
|
|||||||
{
|
{
|
||||||
public override Tools.Components Component => Tools.Components.SYNONYMS_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.SYNONYMS_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Synonyms";
|
protected override string Title => T("Synonyms");
|
||||||
|
|
||||||
protected override string Description =>
|
protected override string Description => T("Find synonyms for words or phrases.");
|
||||||
"""
|
|
||||||
Find synonyms for words or phrases.
|
|
||||||
""";
|
|
||||||
|
|
||||||
protected override string SystemPrompt =>
|
protected override string SystemPrompt =>
|
||||||
$"""
|
$"""
|
||||||
@ -52,7 +49,7 @@ public partial class AssistantSynonyms : AssistantBaseCore<SettingsDialogSynonym
|
|||||||
|
|
||||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||||
|
|
||||||
protected override string SubmitText => "Find synonyms";
|
protected override string SubmitText => T("Find synonyms");
|
||||||
|
|
||||||
protected override Func<Task> SubmitAction => this.FindSynonyms;
|
protected override Func<Task> SubmitAction => this.FindSynonyms;
|
||||||
|
|
||||||
@ -105,7 +102,7 @@ public partial class AssistantSynonyms : AssistantBaseCore<SettingsDialogSynonym
|
|||||||
private string? ValidatingText(string text)
|
private string? ValidatingText(string text)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(text))
|
if(string.IsNullOrWhiteSpace(text))
|
||||||
return "Please provide a word or phrase as input.";
|
return T("Please provide a word or phrase as input.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -113,7 +110,7 @@ public partial class AssistantSynonyms : AssistantBaseCore<SettingsDialogSynonym
|
|||||||
private string? ValidateCustomLanguage(string language)
|
private string? ValidateCustomLanguage(string language)
|
||||||
{
|
{
|
||||||
if(this.selectedLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
if(this.selectedLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||||
return "Please provide a custom language.";
|
return T("Please provide a custom language.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
<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)"/>
|
<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)"/>
|
||||||
}
|
}
|
||||||
|
|
||||||
<MudTextField T="string" Disabled="@this.isAgentRunning" @bind-Text="@this.inputText" Validation="@this.ValidatingText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="Your input" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
<ReadFileContent @bind-FileContent="@this.inputText"/>
|
||||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.Name())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Target language" AllowOther="@true" @bind-OtherInput="@this.customTargetLanguage" OtherValue="CommonLanguages.OTHER" LabelOther="Custom target language" ValidateOther="@this.ValidateCustomLanguage" />
|
<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="Complexity" NameFunc="@(complexity => complexity.Name())" @bind-Value="@this.selectedComplexity" Icon="@Icons.Material.Filled.Layers" Label="Target complexity" AllowOther="@true" @bind-OtherInput="@this.expertInField" OtherValue="Complexity.SCIENTIFIC_LANGUAGE_OTHER_EXPERTS" LabelOther="Your expertise" ValidateOther="@this.ValidateExpertInField" />
|
<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" />
|
||||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
@ -7,15 +7,9 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
|
|||||||
{
|
{
|
||||||
public override Tools.Components Component => Tools.Components.TEXT_SUMMARIZER_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.TEXT_SUMMARIZER_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Text Summarizer";
|
protected override string Title => T("Text Summarizer");
|
||||||
|
|
||||||
protected override string Description =>
|
protected override string Description => T("Summarize long text into a shorter version while retaining the main points. You might want to change the language of the summary to make it more readable. It is also possible to change the complexity of the summary to make it easy to understand.");
|
||||||
"""
|
|
||||||
Summarize long text into a shorter version while retaining the main points.
|
|
||||||
You might want to change the language of the summary to make it more readable.
|
|
||||||
It is also possible to change the complexity of the summary to make it
|
|
||||||
easy to understand.
|
|
||||||
""";
|
|
||||||
|
|
||||||
protected override string SystemPrompt =>
|
protected override string SystemPrompt =>
|
||||||
"""
|
"""
|
||||||
@ -30,7 +24,7 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
|
|||||||
|
|
||||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||||
|
|
||||||
protected override string SubmitText => "Summarize";
|
protected override string SubmitText => T("Summarize");
|
||||||
|
|
||||||
protected override Func<Task> SubmitAction => this.SummarizeText;
|
protected override Func<Task> SubmitAction => this.SummarizeText;
|
||||||
|
|
||||||
@ -90,7 +84,7 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
|
|||||||
private string? ValidatingText(string text)
|
private string? ValidatingText(string text)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(text))
|
if(string.IsNullOrWhiteSpace(text))
|
||||||
return "Please provide a text as input. You might copy the desired text from a document or a website.";
|
return T("Please provide a text as input. You might copy the desired text from a document or a website.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -98,7 +92,7 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
|
|||||||
private string? ValidateCustomLanguage(string language)
|
private string? ValidateCustomLanguage(string language)
|
||||||
{
|
{
|
||||||
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||||
return "Please provide a custom language.";
|
return T("Please provide a custom language.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -106,7 +100,7 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
|
|||||||
private string? ValidateExpertInField(string field)
|
private string? ValidateExpertInField(string field)
|
||||||
{
|
{
|
||||||
if(this.selectedComplexity == Complexity.SCIENTIFIC_LANGUAGE_OTHER_EXPERTS && string.IsNullOrWhiteSpace(field))
|
if(this.selectedComplexity == Complexity.SCIENTIFIC_LANGUAGE_OTHER_EXPERTS && string.IsNullOrWhiteSpace(field))
|
||||||
return "Please provide your field of expertise.";
|
return T("Please provide your field of expertise.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -2,18 +2,20 @@ namespace AIStudio.Assistants.TextSummarizer;
|
|||||||
|
|
||||||
public static class ComplexityExtensions
|
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
|
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.SIMPLE_LANGUAGE => TB("Simple language, e.g., for children"),
|
||||||
Complexity.TEEN_LANGUAGE => "Teen language, e.g., for teenagers",
|
Complexity.TEEN_LANGUAGE => TB("Teen language, e.g., for teenagers"),
|
||||||
Complexity.EVERYDAY_LANGUAGE => "Everyday language, e.g., for adults",
|
Complexity.EVERYDAY_LANGUAGE => TB("Everyday language, e.g., for adults"),
|
||||||
Complexity.POPULAR_SCIENCE_LANGUAGE => "Popular science language, e.g., for people interested in science",
|
Complexity.POPULAR_SCIENCE_LANGUAGE => TB("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_FIELD_EXPERTS => TB("Scientific language for experts in this field"),
|
||||||
Complexity.SCIENTIFIC_LANGUAGE_OTHER_EXPERTS => "Scientific language for experts from other fields (interdisciplinary)",
|
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
|
public static string Prompt(this Complexity complexity, string expertInField) => complexity switch
|
||||||
|
@ -6,15 +6,17 @@
|
|||||||
<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)"/>
|
<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)"/>
|
||||||
}
|
}
|
||||||
|
|
||||||
<MudTextSwitch Label="Live translation" @bind-Value="@this.liveTranslation" LabelOn="Live translation" LabelOff="No live translation"/>
|
<ReadFileContent @bind-FileContent="@this.inputText"/>
|
||||||
|
|
||||||
|
<MudTextSwitch Label="@T("Live translation")" @bind-Value="@this.liveTranslation" LabelOn="@T("Live translation")" LabelOff="@T("No live translation")"/>
|
||||||
@if (this.liveTranslation)
|
@if (this.liveTranslation)
|
||||||
{
|
{
|
||||||
<MudTextField T="string" Disabled="@this.isAgentRunning" @bind-Text="@this.inputText" Validation="@this.ValidatingText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="Your input" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" Immediate="@true" DebounceInterval="@this.SettingsManager.ConfigurationData.Translation.DebounceIntervalMilliseconds" OnDebounceIntervalElapsed="() => this.TranslateText(force: false)" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
<DebouncedTextField Disabled="@this.isAgentRunning" @bind-Text="@this.inputText" ValidationFunc="@this.ValidatingText" Icon="@Icons.Material.Filled.DocumentScanner" Label="@T("Your input")" Lines="6" MaxLines="12" DebounceTime="@TimeSpan.FromMilliseconds(this.SettingsManager.ConfigurationData.Translation.DebounceIntervalMilliseconds)" WhenTextChangedAsync="async _ => await this.TranslateText(force: false)" Attributes="@USER_INPUT_ATTRIBUTES"/>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<MudTextField T="string" Disabled="@this.isAgentRunning" @bind-Text="@this.inputText" Validation="@this.ValidatingText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="Your input" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
<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.NameSelecting())" @bind-Value="@this.selectedTargetLanguage" ValidateSelection="@this.ValidatingTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Target language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom target language" />
|
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelecting())" @bind-Value="@this.selectedTargetLanguage" ValidateSelection="@this.ValidatingTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="@T("Target language")" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="@T("Custom target language")" />
|
||||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
@ -7,12 +7,9 @@ public partial class AssistantTranslation : AssistantBaseCore<SettingsDialogTran
|
|||||||
{
|
{
|
||||||
public override Tools.Components Component => Tools.Components.TRANSLATION_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.TRANSLATION_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Translation";
|
protected override string Title => T("Translation");
|
||||||
|
|
||||||
protected override string Description =>
|
protected override string Description => T("Translate text from one language to another.");
|
||||||
"""
|
|
||||||
Translate text from one language to another.
|
|
||||||
""";
|
|
||||||
|
|
||||||
protected override string SystemPrompt =>
|
protected override string SystemPrompt =>
|
||||||
"""
|
"""
|
||||||
@ -26,7 +23,7 @@ public partial class AssistantTranslation : AssistantBaseCore<SettingsDialogTran
|
|||||||
|
|
||||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||||
|
|
||||||
protected override string SubmitText => "Translate";
|
protected override string SubmitText => T("Translate");
|
||||||
|
|
||||||
protected override Func<Task> SubmitAction => () => this.TranslateText(true);
|
protected override Func<Task> SubmitAction => () => this.TranslateText(true);
|
||||||
|
|
||||||
@ -85,7 +82,7 @@ public partial class AssistantTranslation : AssistantBaseCore<SettingsDialogTran
|
|||||||
private string? ValidatingText(string text)
|
private string? ValidatingText(string text)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(text))
|
if(string.IsNullOrWhiteSpace(text))
|
||||||
return "Please provide a text as input. You might copy the desired text from a document or a website.";
|
return T("Please provide a text as input. You might copy the desired text from a document or a website.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -93,7 +90,7 @@ public partial class AssistantTranslation : AssistantBaseCore<SettingsDialogTran
|
|||||||
private string? ValidatingTargetLanguage(CommonLanguages language)
|
private string? ValidatingTargetLanguage(CommonLanguages language)
|
||||||
{
|
{
|
||||||
if(language == CommonLanguages.AS_IS)
|
if(language == CommonLanguages.AS_IS)
|
||||||
return "Please select a target language.";
|
return T("Please select a target language.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -101,7 +98,7 @@ public partial class AssistantTranslation : AssistantBaseCore<SettingsDialogTran
|
|||||||
private string? ValidateCustomLanguage(string language)
|
private string? ValidateCustomLanguage(string language)
|
||||||
{
|
{
|
||||||
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||||
return "Please provide a custom language.";
|
return T("Please provide a custom language.");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -13,51 +13,3 @@ public enum ChatRole
|
|||||||
AI,
|
AI,
|
||||||
AGENT,
|
AGENT,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Extensions for the ChatRole enum.
|
|
||||||
/// </summary>
|
|
||||||
public static class ExtensionsChatRole
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the name of the role.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="role">The role.</param>
|
|
||||||
/// <returns>The name of the role.</returns>
|
|
||||||
public static string ToName(this ChatRole role) => role switch
|
|
||||||
{
|
|
||||||
ChatRole.SYSTEM => "System",
|
|
||||||
ChatRole.USER => "You",
|
|
||||||
ChatRole.AI => "AI",
|
|
||||||
|
|
||||||
_ => "Unknown",
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the color of the role.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="role">The role.</param>
|
|
||||||
/// <returns>The color of the role.</returns>
|
|
||||||
public static Color ToColor(this ChatRole role) => role switch
|
|
||||||
{
|
|
||||||
ChatRole.SYSTEM => Color.Info,
|
|
||||||
ChatRole.USER => Color.Primary,
|
|
||||||
ChatRole.AI => Color.Tertiary,
|
|
||||||
|
|
||||||
_ => Color.Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the icon of the role.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="role">The role.</param>
|
|
||||||
/// <returns>The icon of the role.</returns>
|
|
||||||
public static string ToIcon(this ChatRole role) => role switch
|
|
||||||
{
|
|
||||||
ChatRole.SYSTEM => Icons.Material.Filled.Settings,
|
|
||||||
ChatRole.USER => Icons.Material.Filled.Person,
|
|
||||||
ChatRole.AI => Icons.Material.Filled.AutoAwesome,
|
|
||||||
|
|
||||||
_ => Icons.Material.Filled.Help,
|
|
||||||
};
|
|
||||||
}
|
|
75
app/MindWork AI Studio/Chat/ChatRoleExtensions.cs
Normal file
75
app/MindWork AI Studio/Chat/ChatRoleExtensions.cs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
using AIStudio.Tools.PluginSystem;
|
||||||
|
|
||||||
|
namespace AIStudio.Chat;
|
||||||
|
|
||||||
|
public static class ChatRoleExtensions
|
||||||
|
{
|
||||||
|
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ChatRoleExtensions).Namespace, nameof(ChatRoleExtensions));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the name of the role.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="role">The role.</param>
|
||||||
|
/// <returns>The name of the role.</returns>
|
||||||
|
public static string ToName(this ChatRole role) => role switch
|
||||||
|
{
|
||||||
|
ChatRole.SYSTEM => TB("System"),
|
||||||
|
ChatRole.USER => TB("You"),
|
||||||
|
ChatRole.AI => TB("AI"),
|
||||||
|
|
||||||
|
_ => TB("Unknown"),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the color of the role.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="role">The role.</param>
|
||||||
|
/// <returns>The color of the role.</returns>
|
||||||
|
public static Color ToColor(this ChatRole role) => role switch
|
||||||
|
{
|
||||||
|
ChatRole.SYSTEM => Color.Info,
|
||||||
|
ChatRole.USER => Color.Primary,
|
||||||
|
ChatRole.AI => Color.Tertiary,
|
||||||
|
|
||||||
|
_ => Color.Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the icon of the role.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="role">The role.</param>
|
||||||
|
/// <returns>The icon of the role.</returns>
|
||||||
|
public static string ToIcon(this ChatRole role) => role switch
|
||||||
|
{
|
||||||
|
ChatRole.SYSTEM => Icons.Material.Filled.Settings,
|
||||||
|
ChatRole.USER => Icons.Material.Filled.Person,
|
||||||
|
ChatRole.AI => Icons.Material.Filled.AutoAwesome,
|
||||||
|
|
||||||
|
_ => Icons.Material.Filled.Help,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the specific name of the role for the chat template.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="role">The role.</param>
|
||||||
|
/// <returns>The name of the role.</returns>
|
||||||
|
public static string ToChatTemplateName(this ChatRole role) => role switch
|
||||||
|
{
|
||||||
|
ChatRole.SYSTEM => TB("System"),
|
||||||
|
ChatRole.USER => TB("User"),
|
||||||
|
ChatRole.AI => TB("Assistant"),
|
||||||
|
|
||||||
|
_ => TB("Unknown"),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Selects the next role for a chat template.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="currentRole">The current role.</param>
|
||||||
|
/// <returns>The next role for the chat template.</returns>
|
||||||
|
public static ChatRole SelectNextRoleForTemplate(this ChatRole currentRole) => currentRole switch
|
||||||
|
{
|
||||||
|
ChatRole.USER => ChatRole.AI,
|
||||||
|
_ => ChatRole.USER,
|
||||||
|
};
|
||||||
|
}
|
10
app/MindWork AI Studio/Chat/ChatRoles.cs
Normal file
10
app/MindWork AI Studio/Chat/ChatRoles.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace AIStudio.Chat;
|
||||||
|
|
||||||
|
public static class ChatRoles
|
||||||
|
{
|
||||||
|
public static IEnumerable<ChatRole> ChatTemplateRoles()
|
||||||
|
{
|
||||||
|
yield return ChatRole.USER;
|
||||||
|
yield return ChatRole.AI;
|
||||||
|
}
|
||||||
|
}
|
@ -30,6 +30,11 @@ public sealed record ChatThread
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string SelectedProfile { get; set; } = string.Empty;
|
public string SelectedProfile { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the profile selected for the chat thread.
|
||||||
|
/// </summary>
|
||||||
|
public string SelectedChatTemplate { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The data source options for this chat thread.
|
/// The data source options for this chat thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -70,6 +75,8 @@ public sealed record ChatThread
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public List<ContentBlock> Blocks { get; init; } = [];
|
public List<ContentBlock> Blocks { get; init; } = [];
|
||||||
|
|
||||||
|
private bool allowProfile = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prepares the system prompt for the chat thread.
|
/// Prepares the system prompt for the chat thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -84,16 +91,51 @@ public sealed record ChatThread
|
|||||||
/// <returns>The prepared system prompt.</returns>
|
/// <returns>The prepared system prompt.</returns>
|
||||||
public string PrepareSystemPrompt(SettingsManager settingsManager, ChatThread chatThread, ILogger logger)
|
public string PrepareSystemPrompt(SettingsManager settingsManager, ChatThread chatThread, ILogger logger)
|
||||||
{
|
{
|
||||||
|
//
|
||||||
|
// Use the information from the chat template, if provided. Otherwise, use the default system prompt
|
||||||
|
//
|
||||||
|
string systemPromptTextWithChatTemplate;
|
||||||
|
var logMessage = $"Using no chat template for chat thread '{chatThread.Name}'.";
|
||||||
|
if (string.IsNullOrWhiteSpace(chatThread.SelectedChatTemplate))
|
||||||
|
systemPromptTextWithChatTemplate = chatThread.SystemPrompt;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(!Guid.TryParse(chatThread.SelectedChatTemplate, out var chatTeamplateId))
|
||||||
|
systemPromptTextWithChatTemplate = chatThread.SystemPrompt;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(chatThread.SelectedChatTemplate == ChatTemplate.NO_CHAT_TEMPLATE.Id || chatTeamplateId == Guid.Empty)
|
||||||
|
systemPromptTextWithChatTemplate = chatThread.SystemPrompt;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var chatTemplate = settingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatThread.SelectedChatTemplate);
|
||||||
|
if(chatTemplate == default)
|
||||||
|
systemPromptTextWithChatTemplate = chatThread.SystemPrompt;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logMessage = $"Using chat template '{chatTemplate.Name}' for chat thread '{chatThread.Name}'.";
|
||||||
|
this.allowProfile = chatTemplate.AllowProfileUsage;
|
||||||
|
systemPromptTextWithChatTemplate = chatTemplate.ToSystemPrompt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogInformation(logMessage);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Add augmented data, if available:
|
||||||
|
//
|
||||||
var isAugmentedDataAvailable = !string.IsNullOrWhiteSpace(chatThread.AugmentedData);
|
var isAugmentedDataAvailable = !string.IsNullOrWhiteSpace(chatThread.AugmentedData);
|
||||||
var systemPromptWithAugmentedData = isAugmentedDataAvailable switch
|
var systemPromptWithAugmentedData = isAugmentedDataAvailable switch
|
||||||
{
|
{
|
||||||
true => $"""
|
true => $"""
|
||||||
{chatThread.SystemPrompt}
|
{systemPromptTextWithChatTemplate}
|
||||||
|
|
||||||
{chatThread.AugmentedData}
|
{chatThread.AugmentedData}
|
||||||
""",
|
""",
|
||||||
|
|
||||||
false => chatThread.SystemPrompt,
|
false => systemPromptTextWithChatTemplate,
|
||||||
};
|
};
|
||||||
|
|
||||||
if(isAugmentedDataAvailable)
|
if(isAugmentedDataAvailable)
|
||||||
@ -101,12 +143,13 @@ public sealed record ChatThread
|
|||||||
else
|
else
|
||||||
logger.LogInformation("No augmented data is available for the chat thread.");
|
logger.LogInformation("No augmented data is available for the chat thread.");
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Prepare the system prompt:
|
// Add information from the profile if available and allowed:
|
||||||
//
|
//
|
||||||
string systemPromptText;
|
string systemPromptText;
|
||||||
var logMessage = $"Using no profile for chat thread '{chatThread.Name}'.";
|
logMessage = $"Using no profile for chat thread '{chatThread.Name}'.";
|
||||||
if (string.IsNullOrWhiteSpace(chatThread.SelectedProfile))
|
if (string.IsNullOrWhiteSpace(chatThread.SelectedProfile) || this.allowProfile is false)
|
||||||
systemPromptText = systemPromptWithAugmentedData;
|
systemPromptText = systemPromptWithAugmentedData;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -210,7 +253,7 @@ public sealed record ChatThread
|
|||||||
ChatRole.SYSTEM => Role.SYSTEM,
|
ChatRole.SYSTEM => Role.SYSTEM,
|
||||||
ChatRole.NONE => Role.NONE,
|
ChatRole.NONE => Role.NONE,
|
||||||
|
|
||||||
_ => Role.UNKNOW,
|
_ => Role.UNKNOWN,
|
||||||
},
|
},
|
||||||
|
|
||||||
Content = contentData,
|
Content = contentData,
|
||||||
|
@ -18,15 +18,24 @@ public class ContentBlock
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The content of the block.
|
/// The content of the block.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IContent? Content { get; init; }
|
public IContent? Content { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The role of the content block in the chat thread, e.g., user, AI, etc.
|
/// The role of the content block in the chat thread, e.g., user, AI, etc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ChatRole Role { get; init; } = ChatRole.NONE;
|
public ChatRole Role { get; set; } = ChatRole.NONE;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Should the content block be hidden from the user?
|
/// Should the content block be hidden from the user?
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HideFromUser { get; set; }
|
public bool HideFromUser { get; init; }
|
||||||
|
|
||||||
|
public ContentBlock DeepClone() => new()
|
||||||
|
{
|
||||||
|
Time = this.Time,
|
||||||
|
ContentType = this.ContentType,
|
||||||
|
Content = this.Content?.DeepClone(),
|
||||||
|
Role = this.Role,
|
||||||
|
HideFromUser = this.HideFromUser,
|
||||||
|
};
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
@using AIStudio.Tools
|
@using AIStudio.Tools
|
||||||
@using MudBlazor
|
@using MudBlazor
|
||||||
|
@inherits AIStudio.Components.MSGComponentBase
|
||||||
<MudCard Class="@this.CardClasses" Outlined="@true">
|
<MudCard Class="@this.CardClasses" Outlined="@true">
|
||||||
<MudCardHeader>
|
<MudCardHeader>
|
||||||
<CardHeaderAvatar>
|
<CardHeaderAvatar>
|
||||||
@ -9,34 +9,36 @@
|
|||||||
</MudAvatar>
|
</MudAvatar>
|
||||||
</CardHeaderAvatar>
|
</CardHeaderAvatar>
|
||||||
<CardHeaderContent>
|
<CardHeaderContent>
|
||||||
<MudText Typo="Typo.body1">@this.Role.ToName() (@this.Time)</MudText>
|
<MudText Typo="Typo.body1">
|
||||||
|
@this.Role.ToName() (@this.Time.LocalDateTime)
|
||||||
|
</MudText>
|
||||||
</CardHeaderContent>
|
</CardHeaderContent>
|
||||||
<CardHeaderActions>
|
<CardHeaderActions>
|
||||||
@if (this.IsSecondToLastBlock && this.Role is ChatRole.USER && this.EditLastUserBlockFunc is not null)
|
@if (this.IsSecondToLastBlock && this.Role is ChatRole.USER && this.EditLastUserBlockFunc is not null)
|
||||||
{
|
{
|
||||||
<MudTooltip Text="Edit" Placement="Placement.Bottom">
|
<MudTooltip Text="@T("Edit")" Placement="Placement.Bottom">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Edit" Color="Color.Default" OnClick="@this.EditLastUserBlock"/>
|
<MudIconButton Icon="@Icons.Material.Filled.Edit" Color="Color.Default" OnClick="@this.EditLastUserBlock"/>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
@if (this.IsLastContentBlock && this.Role is ChatRole.USER && this.EditLastBlockFunc is not null)
|
@if (this.IsLastContentBlock && this.Role is ChatRole.USER && this.EditLastBlockFunc is not null)
|
||||||
{
|
{
|
||||||
<MudTooltip Text="Edit" Placement="Placement.Bottom">
|
<MudTooltip Text="@T("Edit")" Placement="Placement.Bottom">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Edit" Color="Color.Default" OnClick="@this.EditLastBlock"/>
|
<MudIconButton Icon="@Icons.Material.Filled.Edit" Color="Color.Default" OnClick="@this.EditLastBlock"/>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
@if (this.IsLastContentBlock && this.Role is ChatRole.AI && this.RegenerateFunc is not null)
|
@if (this.IsLastContentBlock && this.Role is ChatRole.AI && this.RegenerateFunc is not null)
|
||||||
{
|
{
|
||||||
<MudTooltip Text="Regenerate" Placement="Placement.Bottom">
|
<MudTooltip Text="@T("Regenerate")" Placement="Placement.Bottom">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Recycling" Color="Color.Default" Disabled="@(!this.RegenerateEnabled())" OnClick="@this.RegenerateBlock"/>
|
<MudIconButton Icon="@Icons.Material.Filled.Recycling" Color="Color.Default" Disabled="@(!this.RegenerateEnabled())" OnClick="@this.RegenerateBlock"/>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
@if (this.RemoveBlockFunc is not null)
|
@if (this.RemoveBlockFunc is not null)
|
||||||
{
|
{
|
||||||
<MudTooltip Text="Removes this block" Placement="Placement.Bottom">
|
<MudTooltip Text="@T("Removes this block")" Placement="Placement.Bottom">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error" OnClick="@this.RemoveBlock"/>
|
<MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error" OnClick="@this.RemoveBlock"/>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
<MudTooltip Text="Copies the content to the clipboard" Placement="Placement.Bottom">
|
<MudTooltip Text="@T("Copies the content to the clipboard")" Placement="Placement.Bottom">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.ContentCopy" Color="Color.Default" OnClick="@this.CopyToClipboard"/>
|
<MudIconButton Icon="@Icons.Material.Filled.ContentCopy" Color="Color.Default" OnClick="@this.CopyToClipboard"/>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
</CardHeaderActions>
|
</CardHeaderActions>
|
||||||
@ -65,12 +67,12 @@
|
|||||||
@if (this.Content.IsStreaming)
|
@if (this.Content.IsStreaming)
|
||||||
{
|
{
|
||||||
<MudText Typo="Typo.body1" Style="white-space: pre-wrap;">
|
<MudText Typo="Typo.body1" Style="white-space: pre-wrap;">
|
||||||
@textContent.Text
|
@textContent.Text.RemoveThinkTags()
|
||||||
</MudText>
|
</MudText>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<MudMarkdown Value="@textContent.Text" OverrideHeaderTypo="@Markdown.OverrideHeaderTypo" CodeBlockTheme="@this.CodeColorPalette"/>
|
<MudMarkdown Value="@textContent.Text.RemoveThinkTags().Trim()" OverrideHeaderTypo="@Markdown.OverrideHeaderTypo" Styling="@this.MarkdownStyling" />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,7 +89,7 @@
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
<MudText Typo="Typo.body2">
|
<MudText Typo="Typo.body2">
|
||||||
Cannot render content of type @this.Type yet.
|
@string.Format(T("Cannot render content of type {0} yet."), this.Type)
|
||||||
</MudText>
|
</MudText>
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using AIStudio.Settings;
|
using AIStudio.Components;
|
||||||
using AIStudio.Tools.Services;
|
using AIStudio.Tools.Services;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
@ -8,7 +8,7 @@ namespace AIStudio.Chat;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The UI component for a chat content block, i.e., for any IContent.
|
/// The UI component for a chat content block, i.e., for any IContent.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ContentBlockComponent : ComponentBase
|
public partial class ContentBlockComponent : MSGComponentBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The role of the chat content block.
|
/// The role of the chat content block.
|
||||||
@ -41,10 +41,10 @@ public partial class ContentBlockComponent : ComponentBase
|
|||||||
public string Class { get; set; } = string.Empty;
|
public string Class { get; set; } = string.Empty;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool IsLastContentBlock { get; set; } = false;
|
public bool IsLastContentBlock { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool IsSecondToLastBlock { get; set; } = false;
|
public bool IsSecondToLastBlock { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Func<IContent, Task>? RemoveBlockFunc { get; set; }
|
public Func<IContent, Task>? RemoveBlockFunc { get; set; }
|
||||||
@ -67,9 +67,6 @@ public partial class ContentBlockComponent : ComponentBase
|
|||||||
[Inject]
|
[Inject]
|
||||||
private ISnackbar Snackbar { get; init; } = null!;
|
private ISnackbar Snackbar { get; init; } = null!;
|
||||||
|
|
||||||
[Inject]
|
|
||||||
private SettingsManager SettingsManager { get; init; } = null!;
|
|
||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
private IDialogService DialogService { get; init; } = null!;
|
private IDialogService DialogService { get; init; } = null!;
|
||||||
|
|
||||||
@ -132,7 +129,7 @@ public partial class ContentBlockComponent : ComponentBase
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
this.Snackbar.Add("Cannot copy this content type to clipboard!", Severity.Error, config =>
|
this.Snackbar.Add(T("Cannot copy this content type to clipboard!"), Severity.Error, config =>
|
||||||
{
|
{
|
||||||
config.Icon = Icons.Material.Filled.ContentCopy;
|
config.Icon = Icons.Material.Filled.ContentCopy;
|
||||||
config.IconSize = Size.Large;
|
config.IconSize = Size.Large;
|
||||||
@ -146,16 +143,21 @@ public partial class ContentBlockComponent : ComponentBase
|
|||||||
|
|
||||||
private CodeBlockTheme CodeColorPalette => this.SettingsManager.IsDarkMode ? CodeBlockTheme.Dark : CodeBlockTheme.Default;
|
private CodeBlockTheme CodeColorPalette => this.SettingsManager.IsDarkMode ? CodeBlockTheme.Dark : CodeBlockTheme.Default;
|
||||||
|
|
||||||
|
private MudMarkdownStyling MarkdownStyling => new()
|
||||||
|
{
|
||||||
|
CodeBlock = { Theme = this.CodeColorPalette },
|
||||||
|
};
|
||||||
|
|
||||||
private async Task RemoveBlock()
|
private async Task RemoveBlock()
|
||||||
{
|
{
|
||||||
if (this.RemoveBlockFunc is null)
|
if (this.RemoveBlockFunc is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var remove = await this.DialogService.ShowMessageBox(
|
var remove = await this.DialogService.ShowMessageBox(
|
||||||
"Remove Message",
|
T("Remove Message"),
|
||||||
"Do you really want to remove this message?",
|
T("Do you really want to remove this message?"),
|
||||||
"Yes, remove it",
|
T("Yes, remove it"),
|
||||||
"No, keep it");
|
T("No, keep it"));
|
||||||
|
|
||||||
if (remove.HasValue && remove.Value)
|
if (remove.HasValue && remove.Value)
|
||||||
await this.RemoveBlockFunc(this.Content);
|
await this.RemoveBlockFunc(this.Content);
|
||||||
@ -170,10 +172,10 @@ public partial class ContentBlockComponent : ComponentBase
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var regenerate = await this.DialogService.ShowMessageBox(
|
var regenerate = await this.DialogService.ShowMessageBox(
|
||||||
"Regenerate Message",
|
T("Regenerate Message"),
|
||||||
"Do you really want to regenerate this message?",
|
T("Do you really want to regenerate this message?"),
|
||||||
"Yes, regenerate it",
|
T("Yes, regenerate it"),
|
||||||
"No, keep it");
|
T("No, keep it"));
|
||||||
|
|
||||||
if (regenerate.HasValue && regenerate.Value)
|
if (regenerate.HasValue && regenerate.Value)
|
||||||
await this.RegenerateFunc(this.Content);
|
await this.RegenerateFunc(this.Content);
|
||||||
@ -199,10 +201,10 @@ public partial class ContentBlockComponent : ComponentBase
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var edit = await this.DialogService.ShowMessageBox(
|
var edit = await this.DialogService.ShowMessageBox(
|
||||||
"Edit Message",
|
T("Edit Message"),
|
||||||
"Do you really want to edit this message? In order to edit this message, the AI response will be deleted.",
|
T("Do you really want to edit this message? In order to edit this message, the AI response will be deleted."),
|
||||||
"Yes, remove the AI response and edit it",
|
T("Yes, remove the AI response and edit it"),
|
||||||
"No, keep it");
|
T("No, keep it"));
|
||||||
|
|
||||||
if (edit.HasValue && edit.Value)
|
if (edit.HasValue && edit.Value)
|
||||||
await this.EditLastUserBlockFunc(this.Content);
|
await this.EditLastUserBlockFunc(this.Content);
|
||||||
|
@ -13,11 +13,11 @@ public sealed class ContentImage : IContent, IImageSource
|
|||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public bool InitialRemoteWait { get; set; } = false;
|
public bool InitialRemoteWait { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public bool IsStreaming { get; set; } = false;
|
public bool IsStreaming { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
@ -33,6 +33,15 @@ public sealed class ContentImage : IContent, IImageSource
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IContent DeepClone() => new ContentImage
|
||||||
|
{
|
||||||
|
Source = this.Source,
|
||||||
|
InitialRemoteWait = this.InitialRemoteWait,
|
||||||
|
IsStreaming = this.IsStreaming,
|
||||||
|
SourceType = this.SourceType,
|
||||||
|
};
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -120,11 +120,21 @@ public sealed class ContentText : IContent
|
|||||||
this.IsStreaming = false;
|
this.IsStreaming = false;
|
||||||
}, token);
|
}, token);
|
||||||
|
|
||||||
|
this.Text = this.Text.RemoveThinkTags().Trim();
|
||||||
|
|
||||||
// Inform the UI that the streaming is done:
|
// Inform the UI that the streaming is done:
|
||||||
await this.StreamingDone();
|
await this.StreamingDone();
|
||||||
return chatThread;
|
return chatThread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IContent DeepClone() => new ContentText
|
||||||
|
{
|
||||||
|
Text = this.Text,
|
||||||
|
InitialRemoteWait = this.InitialRemoteWait,
|
||||||
|
IsStreaming = this.IsStreaming,
|
||||||
|
};
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -43,6 +43,12 @@ public interface IContent
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastPrompt, ChatThread? chatChatThread, CancellationToken token = default);
|
public Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastPrompt, ChatThread? chatChatThread, CancellationToken token = default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a deep copy
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The copy</returns>
|
||||||
|
public IContent DeepClone();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the corresponding ERI content type.
|
/// Returns the corresponding ERI content type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
18
app/MindWork AI Studio/Chat/StringExtension.cs
Normal file
18
app/MindWork AI Studio/Chat/StringExtension.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
namespace AIStudio.Chat;
|
||||||
|
|
||||||
|
public static class StringExtensions
|
||||||
|
{
|
||||||
|
public static string RemoveThinkTags(this string input)
|
||||||
|
{
|
||||||
|
const string OPEN_TAG = "<think>";
|
||||||
|
const string CLOSE_TAG = "</think>";
|
||||||
|
if (string.IsNullOrWhiteSpace(input) || !input.StartsWith(OPEN_TAG, StringComparison.Ordinal))
|
||||||
|
return input;
|
||||||
|
|
||||||
|
var endIndex = input.IndexOf(CLOSE_TAG, StringComparison.Ordinal);
|
||||||
|
if (endIndex == -1)
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
return input[(endIndex + CLOSE_TAG.Length)..];
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +0,0 @@
|
|||||||
namespace AIStudio.Chat;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data about a workspace.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">The name of the workspace.</param>
|
|
||||||
public sealed class Workspace(string name)
|
|
||||||
{
|
|
||||||
public string Name { get; set; } = name;
|
|
||||||
|
|
||||||
public List<ChatThread> Threads { get; set; } = new();
|
|
||||||
}
|
|
@ -1,3 +1,4 @@
|
|||||||
|
@inherits MSGComponentBase
|
||||||
@typeparam TSettings
|
@typeparam TSettings
|
||||||
|
|
||||||
<MudCard Outlined="@true" Style="@this.BlockStyle">
|
<MudCard Outlined="@true" Style="@this.BlockStyle">
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
using AIStudio.Settings;
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||||
|
|
||||||
namespace AIStudio.Components;
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
public partial class AssistantBlock<TSettings> : ComponentBase, IMessageBusReceiver, IDisposable where TSettings : IComponent
|
public partial class AssistantBlock<TSettings> : MSGComponentBase where TSettings : IComponent
|
||||||
{
|
{
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
@ -26,12 +24,6 @@ public partial class AssistantBlock<TSettings> : ComponentBase, IMessageBusRecei
|
|||||||
[Inject]
|
[Inject]
|
||||||
private MudTheme ColorTheme { get; init; } = null!;
|
private MudTheme ColorTheme { get; init; } = null!;
|
||||||
|
|
||||||
[Inject]
|
|
||||||
private SettingsManager SettingsManager { get; init; } = null!;
|
|
||||||
|
|
||||||
[Inject]
|
|
||||||
private MessageBus MessageBus { get; init; } = null!;
|
|
||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
private IDialogService DialogService { get; init; } = null!;
|
private IDialogService DialogService { get; init; } = null!;
|
||||||
|
|
||||||
@ -39,44 +31,9 @@ public partial class AssistantBlock<TSettings> : ComponentBase, IMessageBusRecei
|
|||||||
{
|
{
|
||||||
var dialogParameters = new DialogParameters();
|
var dialogParameters = new DialogParameters();
|
||||||
|
|
||||||
await this.DialogService.ShowAsync<TSettings>("Open Settings", dialogParameters, DialogOptions.FULLSCREEN);
|
await this.DialogService.ShowAsync<TSettings>(T("Open Settings"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Overrides of ComponentBase
|
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
|
||||||
{
|
|
||||||
this.MessageBus.RegisterComponent(this);
|
|
||||||
this.MessageBus.ApplyFilters(this, [], [ Event.COLOR_THEME_CHANGED ]);
|
|
||||||
|
|
||||||
await base.OnInitializedAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Implementation of IMessageBusReceiver
|
|
||||||
|
|
||||||
public string ComponentName => nameof(AssistantBlock<TSettings>);
|
|
||||||
|
|
||||||
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
|
||||||
{
|
|
||||||
switch (triggeredEvent)
|
|
||||||
{
|
|
||||||
case Event.COLOR_THEME_CHANGED:
|
|
||||||
this.StateHasChanged();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
|
|
||||||
{
|
|
||||||
return Task.FromResult<TResult?>(default);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private string BorderColor => this.SettingsManager.IsDarkMode switch
|
private string BorderColor => this.SettingsManager.IsDarkMode switch
|
||||||
{
|
{
|
||||||
true => this.ColorTheme.GetCurrentPalette(this.SettingsManager).GrayLight,
|
true => this.ColorTheme.GetCurrentPalette(this.SettingsManager).GrayLight,
|
||||||
@ -84,13 +41,4 @@ public partial class AssistantBlock<TSettings> : ComponentBase, IMessageBusRecei
|
|||||||
};
|
};
|
||||||
|
|
||||||
private string BlockStyle => $"border-width: 2px; border-color: {this.BorderColor}; border-radius: 12px; border-style: solid; max-width: 20em;";
|
private string BlockStyle => $"border-width: 2px; border-color: {this.BorderColor}; border-radius: 12px; border-style: solid; max-width: 20em;";
|
||||||
|
|
||||||
#region Implementation of IDisposable
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
this.MessageBus.Unregister(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
@ -13,6 +13,19 @@ public partial class Changelog
|
|||||||
|
|
||||||
public static readonly Log[] LOGS =
|
public static readonly Log[] LOGS =
|
||||||
[
|
[
|
||||||
|
new (224, "v0.9.49, build 224 (2025-07-02 12:12 UTC)", "v0.9.49.md"),
|
||||||
|
new (223, "v0.9.48, build 223 (2025-06-10 13:15 UTC)", "v0.9.48.md"),
|
||||||
|
new (222, "v0.9.47, build 222 (2025-06-02 18:25 UTC)", "v0.9.47.md"),
|
||||||
|
new (221, "v0.9.46, build 221 (2025-06-01 19:19 UTC)", "v0.9.46.md"),
|
||||||
|
new (220, "v0.9.45, build 220 (2025-05-25 13:56 UTC)", "v0.9.45.md"),
|
||||||
|
new (219, "v0.9.44, build 219 (2025-05-18 19:33 UTC)", "v0.9.44.md"),
|
||||||
|
new (218, "v0.9.43, build 218 (2025-05-11 17:22 UTC)", "v0.9.43.md"),
|
||||||
|
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"),
|
||||||
|
new (213, "v0.9.38, build 213 (2025-03-17 18:18 UTC)", "v0.9.38.md"),
|
||||||
|
new (212, "v0.9.37, build 212 (2025-03-16 20:32 UTC)", "v0.9.37.md"),
|
||||||
new (211, "v0.9.36, build 211 (2025-03-15 10:42 UTC)", "v0.9.36.md"),
|
new (211, "v0.9.36, build 211 (2025-03-15 10:42 UTC)", "v0.9.36.md"),
|
||||||
new (210, "v0.9.35, build 210 (2025-03-13 08:44 UTC)", "v0.9.35.md"),
|
new (210, "v0.9.35, build 210 (2025-03-13 08:44 UTC)", "v0.9.35.md"),
|
||||||
new (209, "v0.9.34, build 209 (2025-03-11 13:02 UTC)", "v0.9.34.md"),
|
new (209, "v0.9.34, build 209 (2025-03-11 13:02 UTC)", "v0.9.34.md"),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<MudSelect T="Log" @bind-Value="@this.SelectedLog" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Schedule" Margin="Margin.Dense" Label="Changelog" Class="mb-2 rounded-lg" Variant="Variant.Outlined" SelectedValuesChanged="() => this.ReadLogAsync()" OnKeyUp="() => this.ReadLogAsync()">
|
@inherits MSGComponentBase
|
||||||
|
<MudSelect T="Log" @bind-Value="@this.SelectedLog" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Schedule" Margin="Margin.Dense" Label="@T("Changelog")" Class="mb-2 rounded-lg" Variant="Variant.Outlined" SelectedValuesChanged="() => this.ReadLogAsync()" OnKeyUp="() => this.ReadLogAsync()">
|
||||||
@foreach (var log in LOGS)
|
@foreach (var log in LOGS)
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="@log"/>
|
<MudSelectItem Value="@log"/>
|
||||||
|
@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Components;
|
|||||||
|
|
||||||
namespace AIStudio.Components;
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
public partial class Changelog : ComponentBase
|
public partial class Changelog : MSGComponentBase
|
||||||
{
|
{
|
||||||
[Inject]
|
[Inject]
|
||||||
private HttpClient HttpClient { get; set; } = null!;
|
private HttpClient HttpClient { get; set; } = null!;
|
||||||
|
@ -58,19 +58,19 @@
|
|||||||
this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is not WorkspaceStorageBehavior.DISABLE_WORKSPACES
|
this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is not WorkspaceStorageBehavior.DISABLE_WORKSPACES
|
||||||
&& this.SettingsManager.ConfigurationData.Workspace.DisplayBehavior is WorkspaceDisplayBehavior.TOGGLE_OVERLAY)
|
&& this.SettingsManager.ConfigurationData.Workspace.DisplayBehavior is WorkspaceDisplayBehavior.TOGGLE_OVERLAY)
|
||||||
{
|
{
|
||||||
<MudTooltip Text="Show your workspaces" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
<MudTooltip Text="@T("Show your workspaces")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.SnippetFolder" OnClick="() => this.ToggleWorkspaceOverlay()"/>
|
<MudIconButton Icon="@Icons.Material.Filled.SnippetFolder" OnClick="() => this.ToggleWorkspaceOverlay()"/>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_MANUALLY)
|
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_MANUALLY)
|
||||||
{
|
{
|
||||||
<MudTooltip Text="Save chat" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
<MudTooltip Text="@T("Save chat")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Save" OnClick="() => this.SaveThread()" Disabled="@(!this.CanThreadBeSaved)"/>
|
<MudIconButton Icon="@Icons.Material.Filled.Save" OnClick="() => this.SaveThread()" Disabled="@(!this.CanThreadBeSaved)"/>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
|
|
||||||
<MudTooltip Text="Start temporary chat" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
<MudTooltip Text="@T("Start temporary chat")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.AddComment" OnClick="() => this.StartNewChat(useSameWorkspace: false)"/>
|
<MudIconButton Icon="@Icons.Material.Filled.AddComment" OnClick="() => this.StartNewChat(useSameWorkspace: false)"/>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
|
|
||||||
@ -81,16 +81,17 @@
|
|||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<ChatTemplateSelection CurrentChatTemplate="@this.currentChatTemplate" CurrentChatTemplateChanged="@this.ChatTemplateWasChanged"/>
|
||||||
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY)
|
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY)
|
||||||
{
|
{
|
||||||
<MudTooltip Text="Delete this chat & start a new one" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
<MudTooltip Text="@T("Delete this chat & start a new one.")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Refresh" OnClick="() => this.StartNewChat(useSameWorkspace: true, deletePreviousChat: true)" Disabled="@(!this.CanThreadBeSaved)"/>
|
<MudIconButton Icon="@Icons.Material.Filled.Refresh" OnClick="() => this.StartNewChat(useSameWorkspace: true, deletePreviousChat: true)" Disabled="@(!this.CanThreadBeSaved)"/>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is not WorkspaceStorageBehavior.DISABLE_WORKSPACES)
|
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is not WorkspaceStorageBehavior.DISABLE_WORKSPACES)
|
||||||
{
|
{
|
||||||
<MudTooltip Text="Move the chat to a workspace, or to another if it is already in one." Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
<MudTooltip Text="@T("Move the chat to a workspace, or to another if it is already in one.")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.MoveToInbox" Disabled="@(!this.CanThreadBeSaved)" OnClick="() => this.MoveChatToWorkspace()"/>
|
<MudIconButton Icon="@Icons.Material.Filled.MoveToInbox" Disabled="@(!this.CanThreadBeSaved)" OnClick="() => this.MoveChatToWorkspace()"/>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
@ -102,12 +103,12 @@
|
|||||||
|
|
||||||
@if (this.isStreaming && this.cancellationTokenSource is not null)
|
@if (this.isStreaming && this.cancellationTokenSource is not null)
|
||||||
{
|
{
|
||||||
<MudTooltip Text="Stop generation" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
<MudTooltip Text="@T("Stop generation")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Stop" Color="Color.Error" OnClick="() => this.CancelStreaming()"/>
|
<MudIconButton Icon="@Icons.Material.Filled.Stop" Color="Color.Error" OnClick="() => this.CancelStreaming()"/>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
|
|
||||||
<ProfileSelection CurrentProfile="@this.currentProfile" CurrentProfileChanged="@this.ProfileWasChanged"/>
|
<ProfileSelection CurrentProfile="@this.currentProfile" CurrentProfileChanged="@this.ProfileWasChanged" Disabled="@(!this.currentChatTemplate.AllowProfileUsage)" DisabledText="@T("Profile usage is disabled according to your chat template settings.")"/>
|
||||||
|
|
||||||
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
|
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
|
||||||
{
|
{
|
||||||
@ -116,7 +117,7 @@
|
|||||||
|
|
||||||
@if (!this.ChatThread.IsLLMProviderAllowed(this.Provider))
|
@if (!this.ChatThread.IsLLMProviderAllowed(this.Provider))
|
||||||
{
|
{
|
||||||
<MudTooltip Text="The selected provider is not allowed in this chat due to data security reasons." Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
<MudTooltip Text="@T("The selected provider is not allowed in this chat due to data security reasons.")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Error" Color="Color.Error"/>
|
<MudIconButton Icon="@Icons.Material.Filled.Error" Color="Color.Error"/>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ using AIStudio.Dialogs;
|
|||||||
using AIStudio.Provider;
|
using AIStudio.Provider;
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
using AIStudio.Settings.DataModel;
|
using AIStudio.Settings.DataModel;
|
||||||
using AIStudio.Tools.Services;
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.AspNetCore.Components.Web;
|
using Microsoft.AspNetCore.Components.Web;
|
||||||
@ -41,15 +40,13 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
[Inject]
|
[Inject]
|
||||||
private IDialogService DialogService { get; init; } = null!;
|
private IDialogService DialogService { get; init; } = null!;
|
||||||
|
|
||||||
[Inject]
|
|
||||||
private DataSourceService DataSourceService { get; init; } = null!;
|
|
||||||
|
|
||||||
private const Placement TOOLBAR_TOOLTIP_PLACEMENT = Placement.Top;
|
private const Placement TOOLBAR_TOOLTIP_PLACEMENT = Placement.Top;
|
||||||
private static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();
|
private static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();
|
||||||
|
|
||||||
private DataSourceSelection? dataSourceSelectionComponent;
|
private DataSourceSelection? dataSourceSelectionComponent;
|
||||||
private DataSourceOptions earlyDataSourceOptions = new();
|
private DataSourceOptions earlyDataSourceOptions = new();
|
||||||
private Profile currentProfile = Profile.NO_PROFILE;
|
private Profile currentProfile = Profile.NO_PROFILE;
|
||||||
|
private ChatTemplate currentChatTemplate = ChatTemplate.NO_CHAT_TEMPLATE;
|
||||||
private bool hasUnsavedChanges;
|
private bool hasUnsavedChanges;
|
||||||
private bool mustScrollToBottomAfterRender;
|
private bool mustScrollToBottomAfterRender;
|
||||||
private InnerScrolling scrollingArea = null!;
|
private InnerScrolling scrollingArea = null!;
|
||||||
@ -81,6 +78,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
// Get the preselected profile:
|
// Get the preselected profile:
|
||||||
this.currentProfile = this.SettingsManager.GetPreselectedProfile(Tools.Components.CHAT);
|
this.currentProfile = this.SettingsManager.GetPreselectedProfile(Tools.Components.CHAT);
|
||||||
|
|
||||||
|
// Get the preselected chat template:
|
||||||
|
this.currentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(Tools.Components.CHAT);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Check for deferred messages of the kind 'SEND_TO_CHAT',
|
// Check for deferred messages of the kind 'SEND_TO_CHAT',
|
||||||
// aka the user sends an assistant result to the chat:
|
// aka the user sends an assistant result to the chat:
|
||||||
@ -259,13 +259,22 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
|
|
||||||
private bool IsProviderSelected => this.Provider.UsedLLMProvider != LLMProviders.NONE;
|
private bool IsProviderSelected => this.Provider.UsedLLMProvider != LLMProviders.NONE;
|
||||||
|
|
||||||
private string ProviderPlaceholder => this.IsProviderSelected ? "Type your input here..." : "Select a provider first";
|
private string ProviderPlaceholder => this.IsProviderSelected ? T("Type your input here...") : T("Select a provider first");
|
||||||
|
|
||||||
private string InputLabel => this.IsProviderSelected ? $"Your Prompt (use selected instance '{this.Provider.InstanceName}', provider '{this.Provider.UsedLLMProvider.ToName()}')" : "Select a provider first";
|
private string InputLabel
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (this.IsProviderSelected)
|
||||||
|
return string.Format(T("Your Prompt (use selected instance '{0}', provider '{1}')"), this.Provider.InstanceName, this.Provider.UsedLLMProvider.ToName());
|
||||||
|
|
||||||
|
return this.T("Select a provider first");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool CanThreadBeSaved => this.ChatThread is not null && this.ChatThread.Blocks.Count > 0;
|
private bool CanThreadBeSaved => this.ChatThread is not null && this.ChatThread.Blocks.Count > 0;
|
||||||
|
|
||||||
private string TooltipAddChatToWorkspace => $"Start new chat in workspace \"{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;
|
private string UserInputStyle => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence ? this.Provider.UsedLLMProvider.GetConfidence(this.SettingsManager).SetColorStyle(this.SettingsManager) : string.Empty;
|
||||||
|
|
||||||
@ -286,6 +295,12 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
// We select the first 10 words of the user input:
|
// We select the first 10 words of the user input:
|
||||||
var words = firstUserInput.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
var words = firstUserInput.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||||
var threadName = string.Join(' ', words.Take(10));
|
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 the thread name is empty, we use a default name:
|
||||||
if (string.IsNullOrWhiteSpace(threadName))
|
if (string.IsNullOrWhiteSpace(threadName))
|
||||||
@ -308,6 +323,15 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
await this.ChatThreadChanged.InvokeAsync(this.ChatThread);
|
await this.ChatThreadChanged.InvokeAsync(this.ChatThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ChatTemplateWasChanged(ChatTemplate chatTemplate)
|
||||||
|
{
|
||||||
|
this.currentChatTemplate = chatTemplate;
|
||||||
|
if(this.ChatThread is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await this.StartNewChat(true);
|
||||||
|
}
|
||||||
|
|
||||||
private IReadOnlyList<DataSourceAgentSelected> GetAgentSelectedDataSources()
|
private IReadOnlyList<DataSourceAgentSelected> GetAgentSelectedDataSources()
|
||||||
{
|
{
|
||||||
if (this.ChatThread is null)
|
if (this.ChatThread is null)
|
||||||
@ -402,13 +426,14 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
{
|
{
|
||||||
SelectedProvider = this.Provider.Id,
|
SelectedProvider = this.Provider.Id,
|
||||||
SelectedProfile = this.currentProfile.Id,
|
SelectedProfile = this.currentProfile.Id,
|
||||||
|
SelectedChatTemplate = this.currentChatTemplate.Id,
|
||||||
SystemPrompt = SystemPrompts.DEFAULT,
|
SystemPrompt = SystemPrompts.DEFAULT,
|
||||||
WorkspaceId = this.currentWorkspaceId,
|
WorkspaceId = this.currentWorkspaceId,
|
||||||
ChatId = Guid.NewGuid(),
|
ChatId = Guid.NewGuid(),
|
||||||
DataSourceOptions = this.earlyDataSourceOptions,
|
DataSourceOptions = this.earlyDataSourceOptions,
|
||||||
Name = this.ExtractThreadName(this.userInput),
|
Name = this.ExtractThreadName(this.userInput),
|
||||||
Seed = this.RNG.Next(),
|
Seed = this.RNG.Next(),
|
||||||
Blocks = [],
|
Blocks = this.currentChatTemplate == default ? [] : this.currentChatTemplate.ExampleConversation.Select(x => x.DeepClone()).ToList(),
|
||||||
};
|
};
|
||||||
|
|
||||||
await this.ChatThreadChanged.InvokeAsync(this.ChatThread);
|
await this.ChatThreadChanged.InvokeAsync(this.ChatThread);
|
||||||
@ -419,9 +444,15 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
if (string.IsNullOrWhiteSpace(this.ChatThread.Name))
|
if (string.IsNullOrWhiteSpace(this.ChatThread.Name))
|
||||||
this.ChatThread.Name = this.ExtractThreadName(this.userInput);
|
this.ChatThread.Name = this.ExtractThreadName(this.userInput);
|
||||||
|
|
||||||
// Update provider and profile:
|
// Update provider, profile and chat template:
|
||||||
this.ChatThread.SelectedProvider = this.Provider.Id;
|
this.ChatThread.SelectedProvider = this.Provider.Id;
|
||||||
this.ChatThread.SelectedProfile = this.currentProfile.Id;
|
this.ChatThread.SelectedProfile = this.currentProfile.Id;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Remark: We do not update the chat template here
|
||||||
|
// because the chat template is only used when starting a new chat.
|
||||||
|
// Updating the chat template afterward is not supported.
|
||||||
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
var time = DateTimeOffset.Now;
|
var time = DateTimeOffset.Now;
|
||||||
@ -474,7 +505,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Clear the input field:
|
// Clear the input field:
|
||||||
|
await this.inputField.FocusAsync();
|
||||||
this.userInput = string.Empty;
|
this.userInput = string.Empty;
|
||||||
|
await this.inputField.BlurAsync();
|
||||||
|
|
||||||
// Enable the stream state for the chat component:
|
// Enable the stream state for the chat component:
|
||||||
this.isStreaming = true;
|
this.isStreaming = true;
|
||||||
@ -632,12 +665,13 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
{
|
{
|
||||||
SelectedProvider = this.Provider.Id,
|
SelectedProvider = this.Provider.Id,
|
||||||
SelectedProfile = this.currentProfile.Id,
|
SelectedProfile = this.currentProfile.Id,
|
||||||
|
SelectedChatTemplate = this.currentChatTemplate.Id,
|
||||||
SystemPrompt = SystemPrompts.DEFAULT,
|
SystemPrompt = SystemPrompts.DEFAULT,
|
||||||
WorkspaceId = this.currentWorkspaceId,
|
WorkspaceId = this.currentWorkspaceId,
|
||||||
ChatId = Guid.NewGuid(),
|
ChatId = Guid.NewGuid(),
|
||||||
Name = string.Empty,
|
Name = string.Empty,
|
||||||
Seed = this.RNG.Next(),
|
Seed = this.RNG.Next(),
|
||||||
Blocks = [],
|
Blocks = this.currentChatTemplate == default ? [] : this.currentChatTemplate.ExampleConversation.Select(x => x.DeepClone()).ToList(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -657,7 +691,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
{
|
{
|
||||||
var confirmationDialogParameters = new DialogParameters
|
var confirmationDialogParameters = new DialogParameters
|
||||||
{
|
{
|
||||||
{ "Message", "Are you sure you want to move this chat? All unsaved changes will be lost." },
|
{ "Message", T("Are you sure you want to move this chat? All unsaved changes will be lost.") },
|
||||||
};
|
};
|
||||||
|
|
||||||
var confirmationDialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Unsaved Changes", confirmationDialogParameters, DialogOptions.FULLSCREEN);
|
var confirmationDialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Unsaved Changes", confirmationDialogParameters, DialogOptions.FULLSCREEN);
|
||||||
@ -668,12 +702,12 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
|
|
||||||
var dialogParameters = new DialogParameters
|
var dialogParameters = new DialogParameters
|
||||||
{
|
{
|
||||||
{ "Message", "Please select the workspace where you want to move the chat to." },
|
{ "Message", T("Please select the workspace where you want to move the chat to.") },
|
||||||
{ "SelectedWorkspace", this.ChatThread?.WorkspaceId },
|
{ "SelectedWorkspace", this.ChatThread?.WorkspaceId },
|
||||||
{ "ConfirmText", "Move chat" },
|
{ "ConfirmText", T("Move chat") },
|
||||||
};
|
};
|
||||||
|
|
||||||
var dialogReference = await this.DialogService.ShowAsync<WorkspaceSelectionDialog>("Move Chat to Workspace", dialogParameters, DialogOptions.FULLSCREEN);
|
var dialogReference = await this.DialogService.ShowAsync<WorkspaceSelectionDialog>(T("Move Chat to Workspace"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
var dialogResult = await dialogReference.Result;
|
var dialogResult = await dialogReference.Result;
|
||||||
if (dialogResult is null || dialogResult.Canceled)
|
if (dialogResult is null || dialogResult.Canceled)
|
||||||
return;
|
return;
|
||||||
@ -743,6 +777,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
{
|
{
|
||||||
var chatProvider = this.ChatThread?.SelectedProvider;
|
var chatProvider = this.ChatThread?.SelectedProvider;
|
||||||
var chatProfile = this.ChatThread?.SelectedProfile;
|
var chatProfile = this.ChatThread?.SelectedProfile;
|
||||||
|
var chatChatTemplate = this.ChatThread?.SelectedChatTemplate;
|
||||||
|
|
||||||
switch (this.SettingsManager.ConfigurationData.Chat.LoadingProviderBehavior)
|
switch (this.SettingsManager.ConfigurationData.Chat.LoadingProviderBehavior)
|
||||||
{
|
{
|
||||||
@ -770,6 +805,14 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
if(this.currentProfile == default)
|
if(this.currentProfile == default)
|
||||||
this.currentProfile = Profile.NO_PROFILE;
|
this.currentProfile = Profile.NO_PROFILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to select the chat template:
|
||||||
|
if (!string.IsNullOrWhiteSpace(chatChatTemplate))
|
||||||
|
{
|
||||||
|
this.currentChatTemplate = this.SettingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatChatTemplate);
|
||||||
|
if(this.currentChatTemplate == default)
|
||||||
|
this.currentChatTemplate = ChatTemplate.NO_CHAT_TEMPLATE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ToggleWorkspaceOverlay()
|
private async Task ToggleWorkspaceOverlay()
|
||||||
@ -843,9 +886,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
|
|
||||||
#region Overrides of MSGComponentBase
|
#region Overrides of MSGComponentBase
|
||||||
|
|
||||||
public override string ComponentName => nameof(ChatComponent);
|
protected override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||||
|
|
||||||
public override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
|
||||||
{
|
{
|
||||||
switch (triggeredEvent)
|
switch (triggeredEvent)
|
||||||
{
|
{
|
||||||
@ -864,7 +905,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data) where TResult : default where TPayload : default
|
protected override Task<TResult?> ProcessIncomingMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data) where TResult : default where TPayload : default
|
||||||
{
|
{
|
||||||
switch (triggeredEvent)
|
switch (triggeredEvent)
|
||||||
{
|
{
|
||||||
@ -884,7 +925,6 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
|
|
||||||
public async ValueTask DisposeAsync()
|
public async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
this.MessageBus.Unregister(this);
|
|
||||||
if(this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY)
|
if(this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY)
|
||||||
{
|
{
|
||||||
await this.SaveThread();
|
await this.SaveThread();
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
@using AIStudio.Settings
|
||||||
|
@inherits MSGComponentBase
|
||||||
|
|
||||||
|
<MudTooltip Placement="Placement.Top" Text="@T("Start a new chat with a chat template")">
|
||||||
|
<MudMenu AnchorOrigin="Origin.TopLeft" TransformOrigin="@Origin.BottomLeft" Class="@this.MarginClass">
|
||||||
|
<ActivatorContent>
|
||||||
|
@if (this.CurrentChatTemplate != ChatTemplate.NO_CHAT_TEMPLATE)
|
||||||
|
{
|
||||||
|
<MudButton IconSize="Size.Large" StartIcon="@Icons.Material.Filled.RateReview" IconColor="Color.Default">
|
||||||
|
@this.CurrentChatTemplate.Name
|
||||||
|
</MudButton>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.RateReview" />
|
||||||
|
}
|
||||||
|
</ActivatorContent>
|
||||||
|
<ChildContent>
|
||||||
|
<MudMenuItem Icon="@Icons.Material.Filled.Settings" Label="@T("Manage your templates")" OnClick="async () => await this.OpenSettingsDialog()" />
|
||||||
|
<MudDivider/>
|
||||||
|
@foreach (var chatTemplate in this.SettingsManager.ConfigurationData.ChatTemplates.GetAllChatTemplates())
|
||||||
|
{
|
||||||
|
<MudMenuItem Icon="@Icons.Material.Filled.RateReview" OnClick="async () => await this.SelectionChanged(chatTemplate)">
|
||||||
|
@chatTemplate.Name
|
||||||
|
</MudMenuItem>
|
||||||
|
}
|
||||||
|
</ChildContent>
|
||||||
|
</MudMenu>
|
||||||
|
</MudTooltip>
|
@ -0,0 +1,39 @@
|
|||||||
|
using AIStudio.Dialogs.Settings;
|
||||||
|
using AIStudio.Settings;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||||
|
|
||||||
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
|
public partial class ChatTemplateSelection : MSGComponentBase
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public ChatTemplate CurrentChatTemplate { get; set; } = ChatTemplate.NO_CHAT_TEMPLATE;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback<ChatTemplate> CurrentChatTemplateChanged { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string MarginLeft { get; set; } = "ml-1";
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string MarginRight { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private IDialogService DialogService { get; init; } = null!;
|
||||||
|
|
||||||
|
private string MarginClass => $"{this.MarginLeft} {this.MarginRight}";
|
||||||
|
|
||||||
|
private async Task SelectionChanged(ChatTemplate chatTemplate)
|
||||||
|
{
|
||||||
|
this.CurrentChatTemplate = chatTemplate;
|
||||||
|
await this.CurrentChatTemplateChanged.InvokeAsync(chatTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenSettingsDialog()
|
||||||
|
{
|
||||||
|
var dialogParameters = new DialogParameters();
|
||||||
|
await this.DialogService.ShowAsync<SettingsDialogChatTemplate>(T("Open Chat Template Options"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
|
}
|
||||||
|
}
|
22
app/MindWork AI Studio/Components/CodeBlock.razor
Normal file
22
app/MindWork AI Studio/Components/CodeBlock.razor
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
@if (!this.IsInline)
|
||||||
|
{
|
||||||
|
@if (this.ParentTabs is null)
|
||||||
|
{
|
||||||
|
<MudPaper Class="code-block no-elevation" Style="@this.BlockPadding">
|
||||||
|
<pre>
|
||||||
|
<code>
|
||||||
|
@this.ChildContent
|
||||||
|
</code>
|
||||||
|
</pre>
|
||||||
|
</MudPaper>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="inline-code-block">
|
||||||
|
<kbd>
|
||||||
|
@this.ChildContent
|
||||||
|
</kbd>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
|
37
app/MindWork AI Studio/Components/CodeBlock.razor.cs
Normal file
37
app/MindWork AI Studio/Components/CodeBlock.razor.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.AspNetCore.Components.Rendering;
|
||||||
|
|
||||||
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
|
public partial class CodeBlock : ComponentBase
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment? ChildContent { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string? Title { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool IsInline { get; set; }
|
||||||
|
|
||||||
|
[CascadingParameter]
|
||||||
|
public CodeTabs? ParentTabs { get; set; }
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
if (this.ParentTabs is not null && this.Title is not null)
|
||||||
|
{
|
||||||
|
void BlockSelf(RenderTreeBuilder builder)
|
||||||
|
{
|
||||||
|
builder.OpenComponent<CodeBlock>(0);
|
||||||
|
builder.AddAttribute(1, "Title", this.Title);
|
||||||
|
builder.AddAttribute(2, "ChildContent", this.ChildContent);
|
||||||
|
builder.CloseComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ParentTabs.RegisterBlock(this.Title, BlockSelf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BlockPadding => this.ParentTabs is null ? "padding: 16px !important;" : "padding: 8px !important";
|
||||||
|
}
|
11
app/MindWork AI Studio/Components/CodeTabs.razor
Normal file
11
app/MindWork AI Studio/Components/CodeTabs.razor
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<MudTabs ActivePanelIndex="this.SelectedIndex" ActivePanelIndexChanged="async idx => await this.TabChanged(idx)" PanelClass="code-block mt-1" MinimumTabWidth="30px" Class="mt-2">
|
||||||
|
@foreach (var block in this.blocks)
|
||||||
|
{
|
||||||
|
<MudTabPanel Text="@block.Title">
|
||||||
|
@block.Fragment
|
||||||
|
</MudTabPanel>
|
||||||
|
}
|
||||||
|
</MudTabs>
|
||||||
|
<CascadingValue Value="this">
|
||||||
|
@this.ChildContent
|
||||||
|
</CascadingValue>
|
42
app/MindWork AI Studio/Components/CodeTabs.razor.cs
Normal file
42
app/MindWork AI Studio/Components/CodeTabs.razor.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
|
public partial class CodeTabs : ComponentBase
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment? ChildContent { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public int SelectedIndex { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback<int> SelectedIndexChanged { get; set; }
|
||||||
|
|
||||||
|
private readonly List<CodeTabItem> blocks = new();
|
||||||
|
|
||||||
|
internal void RegisterBlock(string title, RenderFragment fragment)
|
||||||
|
{
|
||||||
|
this.blocks.Add(new CodeTabItem
|
||||||
|
{
|
||||||
|
Title = title,
|
||||||
|
Fragment = fragment,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CodeTabItem
|
||||||
|
{
|
||||||
|
public string Title { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
public RenderFragment Fragment { get; init; } = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task TabChanged(int index)
|
||||||
|
{
|
||||||
|
this.SelectedIndex = index;
|
||||||
|
await this.SelectedIndexChanged.InvokeAsync(index);
|
||||||
|
await this.InvokeAsync(this.StateHasChanged);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
@using AIStudio.Provider
|
@using AIStudio.Provider
|
||||||
|
@inherits MSGComponentBase
|
||||||
|
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<MudTooltip Text="Shows and hides the confidence card with information about the selected LLM provider." Placement="Placement.Top">
|
<MudTooltip Text="@T("Shows and hides the confidence card with information about the selected LLM provider.")" Placement="Placement.Top">
|
||||||
@if (this.Mode is PopoverTriggerMode.ICON)
|
@if (this.Mode is PopoverTriggerMode.ICON)
|
||||||
{
|
{
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Security" Class="confidence-icon" Style="@this.LLMProvider.GetConfidence(this.SettingsManager).SetColorStyle(this.SettingsManager)" OnClick="@(() => this.ToggleConfidence())"/>
|
<MudIconButton Icon="@Icons.Material.Filled.Security" Class="confidence-icon" Style="@this.LLMProvider.GetConfidence(this.SettingsManager).SetColorStyle(this.SettingsManager)" OnClick="@(() => this.ToggleConfidence())"/>
|
||||||
@ -8,7 +10,7 @@
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.Security" IconClass="confidence-icon" Style="@this.LLMProvider.GetConfidence(this.SettingsManager).SetColorStyle(this.SettingsManager)" OnClick="@(() => this.ToggleConfidence())">
|
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.Security" IconClass="confidence-icon" Style="@this.LLMProvider.GetConfidence(this.SettingsManager).SetColorStyle(this.SettingsManager)" OnClick="@(() => this.ToggleConfidence())">
|
||||||
Confidence
|
@T("Confidence")
|
||||||
</MudButton>
|
</MudButton>
|
||||||
}
|
}
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
@ -17,16 +19,22 @@
|
|||||||
<MudCard>
|
<MudCard>
|
||||||
<MudCardHeader>
|
<MudCardHeader>
|
||||||
<CardHeaderContent>
|
<CardHeaderContent>
|
||||||
<MudText Typo="Typo.h5">Confidence Card</MudText>
|
<MudText Typo="Typo.h5">
|
||||||
|
@T("Confidence Card")
|
||||||
|
</MudText>
|
||||||
</CardHeaderContent>
|
</CardHeaderContent>
|
||||||
</MudCardHeader>
|
</MudCardHeader>
|
||||||
<MudCardContent Style="max-height: 50vh; max-width: 35vw; overflow: auto;">
|
<MudCardContent Style="max-height: 50vh; max-width: 35vw; overflow: auto;">
|
||||||
<MudText Typo="Typo.h6">Description</MudText>
|
<MudText Typo="Typo.h6">
|
||||||
|
@T("Description")
|
||||||
|
</MudText>
|
||||||
<MudMarkdown Value="@this.currentConfidence.Description"/>
|
<MudMarkdown Value="@this.currentConfidence.Description"/>
|
||||||
|
|
||||||
@if (this.currentConfidence.Sources.Count > 0)
|
@if (this.currentConfidence.Sources.Count > 0)
|
||||||
{
|
{
|
||||||
<MudText Typo="Typo.h6">Sources</MudText>
|
<MudText Typo="Typo.h6">
|
||||||
|
@T("Sources")
|
||||||
|
</MudText>
|
||||||
<MudList T="@string">
|
<MudList T="@string">
|
||||||
@foreach (var sourceTuple in this.GetConfidenceSources())
|
@foreach (var sourceTuple in this.GetConfidenceSources())
|
||||||
{
|
{
|
||||||
@ -37,13 +45,17 @@
|
|||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(this.currentConfidence.Region))
|
@if (!string.IsNullOrWhiteSpace(this.currentConfidence.Region))
|
||||||
{
|
{
|
||||||
<MudText Typo="Typo.h6">Region</MudText>
|
<MudText Typo="Typo.h6">
|
||||||
|
@T("Region")
|
||||||
|
</MudText>
|
||||||
<MudText Typo="Typo.body1" Class="mb-3">
|
<MudText Typo="Typo.body1" Class="mb-3">
|
||||||
<b>@this.currentConfidence.Region</b>
|
<b>@this.currentConfidence.Region</b>
|
||||||
</MudText>
|
</MudText>
|
||||||
}
|
}
|
||||||
|
|
||||||
<MudText Typo="Typo.h6">Confidence Level</MudText>
|
<MudText Typo="Typo.h6">
|
||||||
|
@T("Confidence Level")
|
||||||
|
</MudText>
|
||||||
<MudText Typo="Typo.body1" Style="@this.GetCurrentConfidenceColor()">
|
<MudText Typo="Typo.body1" Style="@this.GetCurrentConfidenceColor()">
|
||||||
<b>@this.currentConfidence.Level.GetName()</b>
|
<b>@this.currentConfidence.Level.GetName()</b>
|
||||||
</MudText>
|
</MudText>
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
using AIStudio.Provider;
|
using AIStudio.Provider;
|
||||||
using AIStudio.Settings;
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
namespace AIStudio.Components;
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
public partial class ConfidenceInfo : ComponentBase, IMessageBusReceiver, IDisposable
|
public partial class ConfidenceInfo : MSGComponentBase
|
||||||
{
|
{
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public PopoverTriggerMode Mode { get; set; } = PopoverTriggerMode.BUTTON;
|
public PopoverTriggerMode Mode { get; set; } = PopoverTriggerMode.BUTTON;
|
||||||
@ -13,12 +12,6 @@ public partial class ConfidenceInfo : ComponentBase, IMessageBusReceiver, IDispo
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public LLMProviders LLMProvider { get; set; }
|
public LLMProviders LLMProvider { get; set; }
|
||||||
|
|
||||||
[Inject]
|
|
||||||
private SettingsManager SettingsManager { get; init; } = null!;
|
|
||||||
|
|
||||||
[Inject]
|
|
||||||
private MessageBus MessageBus { get; init; } = null!;
|
|
||||||
|
|
||||||
private Confidence currentConfidence;
|
private Confidence currentConfidence;
|
||||||
private bool showConfidence;
|
private bool showConfidence;
|
||||||
|
|
||||||
@ -31,9 +24,6 @@ public partial class ConfidenceInfo : ComponentBase, IMessageBusReceiver, IDispo
|
|||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
this.MessageBus.RegisterComponent(this);
|
|
||||||
this.MessageBus.ApplyFilters(this, [], [ Event.COLOR_THEME_CHANGED ]);
|
|
||||||
|
|
||||||
this.currentConfidence = this.LLMProvider.GetConfidence(this.SettingsManager);
|
this.currentConfidence = this.LLMProvider.GetConfidence(this.SettingsManager);
|
||||||
await base.OnParametersSetAsync();
|
await base.OnParametersSetAsync();
|
||||||
}
|
}
|
||||||
@ -54,43 +44,10 @@ public partial class ConfidenceInfo : ComponentBase, IMessageBusReceiver, IDispo
|
|||||||
{
|
{
|
||||||
var index = 0;
|
var index = 0;
|
||||||
foreach (var source in this.currentConfidence.Sources)
|
foreach (var source in this.currentConfidence.Sources)
|
||||||
yield return ($"Source {++index}", source);
|
yield return (string.Format(T("Source {0}"), ++index), source);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetCurrentConfidenceColor() => $"color: {this.currentConfidence.Level.GetColor(this.SettingsManager)};";
|
private string GetCurrentConfidenceColor() => $"color: {this.currentConfidence.Level.GetColor(this.SettingsManager)};";
|
||||||
|
|
||||||
private string GetPopoverStyle() => $"border-color: {this.currentConfidence.Level.GetColor(this.SettingsManager)};";
|
private string GetPopoverStyle() => $"border-color: {this.currentConfidence.Level.GetColor(this.SettingsManager)};";
|
||||||
|
|
||||||
#region Implementation of IMessageBusReceiver
|
|
||||||
|
|
||||||
public string ComponentName => nameof(ConfidenceInfo);
|
|
||||||
|
|
||||||
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
|
||||||
{
|
|
||||||
switch (triggeredEvent)
|
|
||||||
{
|
|
||||||
case Event.COLOR_THEME_CHANGED:
|
|
||||||
this.showConfidence = false;
|
|
||||||
this.StateHasChanged();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
|
|
||||||
{
|
|
||||||
return Task.FromResult<TResult?>(default);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Implementation of IDisposable
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
this.MessageBus.Unregister(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
@ -0,0 +1 @@
|
|||||||
|
@inherits MSGComponentBase
|
@ -1,5 +1,3 @@
|
|||||||
using AIStudio.Settings;
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
namespace AIStudio.Components;
|
namespace AIStudio.Components;
|
||||||
@ -7,7 +5,7 @@ namespace AIStudio.Components;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A base class for configuration options.
|
/// A base class for configuration options.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ConfigurationBase : ComponentBase, IMessageBusReceiver, IDisposable
|
public partial class ConfigurationBase : MSGComponentBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The description of the option, i.e., the name. Should be
|
/// The description of the option, i.e., the name. Should be
|
||||||
@ -28,12 +26,6 @@ public partial class ConfigurationBase : ComponentBase, IMessageBusReceiver, IDi
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public Func<bool> Disabled { get; set; } = () => false;
|
public Func<bool> Disabled { get; set; } = () => false;
|
||||||
|
|
||||||
[Inject]
|
|
||||||
protected SettingsManager SettingsManager { get; init; } = null!;
|
|
||||||
|
|
||||||
[Inject]
|
|
||||||
protected MessageBus MessageBus { get; init; } = null!;
|
|
||||||
|
|
||||||
protected const string MARGIN_CLASS = "mb-6";
|
protected const string MARGIN_CLASS = "mb-6";
|
||||||
protected static readonly Dictionary<string, object?> SPELLCHECK_ATTRIBUTES = new();
|
protected static readonly Dictionary<string, object?> SPELLCHECK_ATTRIBUTES = new();
|
||||||
|
|
||||||
@ -41,13 +33,8 @@ public partial class ConfigurationBase : ComponentBase, IMessageBusReceiver, IDi
|
|||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
// Configure the spellchecking for the instance name input:
|
|
||||||
this.SettingsManager.InjectSpellchecking(SPELLCHECK_ATTRIBUTES);
|
this.SettingsManager.InjectSpellchecking(SPELLCHECK_ATTRIBUTES);
|
||||||
|
this.ApplyFilters([], [ Event.CONFIGURATION_CHANGED ]);
|
||||||
// Register this component with the message bus:
|
|
||||||
this.MessageBus.RegisterComponent(this);
|
|
||||||
this.MessageBus.ApplyFilters(this, [], [ Event.CONFIGURATION_CHANGED ]);
|
|
||||||
|
|
||||||
await base.OnInitializedAsync();
|
await base.OnInitializedAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,11 +42,9 @@ public partial class ConfigurationBase : ComponentBase, IMessageBusReceiver, IDi
|
|||||||
|
|
||||||
protected async Task InformAboutChange() => await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
protected async Task InformAboutChange() => await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||||
|
|
||||||
#region Implementation of IMessageBusReceiver
|
#region Overrides of MSGComponentBase
|
||||||
|
|
||||||
public string ComponentName => nameof(ConfigurationBase);
|
protected override Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||||
|
|
||||||
public Task ProcessMessage<TMsg>(ComponentBase? sendingComponent, Event triggeredEvent, TMsg? data)
|
|
||||||
{
|
{
|
||||||
switch (triggeredEvent)
|
switch (triggeredEvent)
|
||||||
{
|
{
|
||||||
@ -71,19 +56,5 @@ public partial class ConfigurationBase : ComponentBase, IMessageBusReceiver, IDi
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
|
|
||||||
{
|
|
||||||
return Task.FromResult<TResult?>(default);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Implementation of IDisposable
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
this.MessageBus.Unregister(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
@ -1,2 +1,3 @@
|
|||||||
@using AIStudio.Settings
|
@using AIStudio.Settings
|
||||||
<ConfigurationSelect Disabled="@this.Disabled" OptionDescription="Select a minimum confidence level" SelectedValue="@this.FilteredSelectedValue" Data="@ConfigurationSelectDataFactory.GetConfidenceLevelsData(this.SettingsManager, this.RestrictToGlobalMinimumConfidence)" SelectionUpdate="@this.SelectionUpdate" OptionHelp="Choose the minimum confidence level that all LLM providers must meet. This way, you can ensure that only trustworthy providers are used. You cannot use any provider that falls below this level."/>
|
@inherits MSGComponentBase
|
||||||
|
<ConfigurationSelect Disabled="@this.Disabled" OptionDescription="@T("Select a minimum confidence level")" SelectedValue="@this.FilteredSelectedValue" Data="@ConfigurationSelectDataFactory.GetConfidenceLevelsData(this.SettingsManager, this.RestrictToGlobalMinimumConfidence)" SelectionUpdate="@this.SelectionUpdate" OptionHelp="@T("Choose the minimum confidence level that all LLM providers must meet. This way, you can ensure that only trustworthy providers are used. You cannot use any provider that falls below this level.")"/>
|
@ -1,11 +1,10 @@
|
|||||||
using AIStudio.Provider;
|
using AIStudio.Provider;
|
||||||
using AIStudio.Settings;
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
namespace AIStudio.Components;
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
public partial class ConfigurationMinConfidenceSelection : ComponentBase
|
public partial class ConfigurationMinConfidenceSelection : MSGComponentBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The selected value.
|
/// The selected value.
|
||||||
@ -31,9 +30,6 @@ public partial class ConfigurationMinConfidenceSelection : ComponentBase
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public bool RestrictToGlobalMinimumConfidence { get; set; }
|
public bool RestrictToGlobalMinimumConfidence { get; set; }
|
||||||
|
|
||||||
[Inject]
|
|
||||||
private SettingsManager SettingsManager { get; init; } = null!;
|
|
||||||
|
|
||||||
private ConfidenceLevel FilteredSelectedValue()
|
private ConfidenceLevel FilteredSelectedValue()
|
||||||
{
|
{
|
||||||
if (this.SelectedValue() is ConfidenceLevel.NONE)
|
if (this.SelectedValue() is ConfidenceLevel.NONE)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user