mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-20 13:58:00 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
No files matched your search
@@ -1,2 +1 @@
|
|||||||
github: [MindWorkAI]
|
github: [MindWorkAI]
|
||||||
open_collective: mindwork-ai
|
|
||||||
@@ -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: 'deb'
|
||||||
|
|
||||||
- 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,13 @@ 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
|
||||||
|
|
||||||
# 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 +177,7 @@ 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
|
||||||
|
|
||||||
# Log the metadata:
|
# Log the metadata:
|
||||||
echo "App version: '${formatted_app_version}'"
|
echo "App version: '${formatted_app_version}'"
|
||||||
@@ -174,6 +188,7 @@ 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 }}'"
|
||||||
|
|
||||||
- name: Read and format metadata (Windows)
|
- name: Read and format metadata (Windows)
|
||||||
if: matrix.platform == 'windows-latest'
|
if: matrix.platform == 'windows-latest'
|
||||||
@@ -206,6 +221,12 @@ 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
|
||||||
|
|
||||||
# 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 +236,7 @@ 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
|
||||||
|
|
||||||
# Log the metadata:
|
# Log the metadata:
|
||||||
Write-Output "App version: '${formatted_app_version}'"
|
Write-Output "App version: '${formatted_app_version}'"
|
||||||
@@ -225,6 +247,7 @@ 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 }}'"
|
||||||
|
|
||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v4
|
uses: actions/setup-dotnet@v4
|
||||||
@@ -277,6 +300,12 @@ jobs:
|
|||||||
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
|
||||||
|
|
||||||
|
- 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
|
||||||
|
|
||||||
- name: Setup Tauri (Unix)
|
- name: Setup Tauri (Unix)
|
||||||
if: matrix.platform != 'windows-latest'
|
if: matrix.platform != 'windows-latest'
|
||||||
run: |
|
run: |
|
||||||
@@ -304,14 +333,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 +429,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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
- [ ] App: Implement dialog for checking & handling [pandoc](https://pandoc.org/) installation ([PR #393](https://github.com/MindWorkAI/AI-Studio/pull/393))
|
||||||
- [ ] 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,37 @@ 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))~~
|
||||||
|
- [ ] 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))
|
||||||
|
- [ ] Add an I18N assistant to translate all AI Studio texts to a certain language & culture
|
||||||
|
- [ ] Provide MindWork AI Studio in German ([#31](https://github.com/MindWorkAI/Planning/issues/31))
|
||||||
|
- [ ] Add configuration plugins, which allow pre-defining some LLM providers in organizations
|
||||||
|
- [ ] 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.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.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 +68,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.
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// 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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
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
|
||||||
|
{
|
||||||
|
[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 sb = new StringBuilder();
|
||||||
|
|
||||||
|
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);
|
||||||
|
var langNamespace = $"{ns}::{name}".ToUpperInvariant().Replace(".", "::");
|
||||||
|
foreach (var match in matches)
|
||||||
|
{
|
||||||
|
var key = $"root::{langNamespace}::T{match.ToFNV32()}";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($" {counter:###,###} files processed.");
|
||||||
|
Console.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> FindAllTextTags(ReadOnlySpan<char> fileContent)
|
||||||
|
{
|
||||||
|
const string START_TAG = """
|
||||||
|
T("
|
||||||
|
""";
|
||||||
|
|
||||||
|
const string END_TAG = """
|
||||||
|
")
|
||||||
|
""";
|
||||||
|
|
||||||
|
var matches = new List<string>();
|
||||||
|
var startIdx = fileContent.IndexOf(START_TAG);
|
||||||
|
var content = fileContent;
|
||||||
|
while (startIdx > -1)
|
||||||
|
{
|
||||||
|
content = content[(startIdx + START_TAG.Length)..];
|
||||||
|
var endIdx = content.IndexOf(END_TAG);
|
||||||
|
if (endIdx == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var match = content[..endIdx];
|
||||||
|
matches.Add(match.ToString());
|
||||||
|
|
||||||
|
startIdx = content.IndexOf(START_TAG);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Build.Commands;
|
||||||
|
|
||||||
|
public enum PrepareAction
|
||||||
|
{
|
||||||
|
NONE,
|
||||||
|
|
||||||
|
PATCH,
|
||||||
|
MINOR,
|
||||||
|
MAJOR,
|
||||||
|
}
|
||||||
@@ -0,0 +1,587 @@
|
|||||||
|
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.Prepare(action);
|
||||||
|
|
||||||
|
// 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.WriteLine("- Prepare the metadata for the next release ...");
|
||||||
|
|
||||||
|
var appVersion = await this.UpdateAppVersion(action);
|
||||||
|
if (!string.IsNullOrWhiteSpace(appVersion))
|
||||||
|
{
|
||||||
|
var buildNumber = await this.IncreaseBuildNumber();
|
||||||
|
var buildTime = await this.UpdateBuildTime();
|
||||||
|
await this.UpdateChangelog(buildNumber, appVersion, buildTime);
|
||||||
|
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 rids = Environment.GetRidsForCurrentOS();
|
||||||
|
foreach (var rid in rids)
|
||||||
|
{
|
||||||
|
Console.WriteLine("==============================");
|
||||||
|
await this.UpdateArchitecture(rid);
|
||||||
|
|
||||||
|
Console.Write($"- Start .NET build for '{rid.AsMicrosoftRid()}' ...");
|
||||||
|
await this.ReadCommandOutput(pathApp, "dotnet", $"clean --configuration release --runtime {rid.AsMicrosoftRid()}");
|
||||||
|
var dotnetBuildOutput = await this.ReadCommandOutput(pathApp, "dotnet", $"publish --configuration release --runtime {rid.AsMicrosoftRid()} --disable-build-servers --force");
|
||||||
|
var dotnetBuildOutputLines = dotnetBuildOutput.Split([global::System.Environment.NewLine], StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
var foundIssue = false;
|
||||||
|
foreach (var buildOutputLine in dotnetBuildOutputLines)
|
||||||
|
{
|
||||||
|
if(buildOutputLine.Contains(" error ") || buildOutputLine.Contains("#warning"))
|
||||||
|
{
|
||||||
|
if(!foundIssue)
|
||||||
|
{
|
||||||
|
foundIssue = true;
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine("- Build has issues:");
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.Write(" - ");
|
||||||
|
Console.WriteLine(buildOutputLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(foundIssue)
|
||||||
|
Console.WriteLine();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine(" completed successfully.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Prepare the .NET artifact to be used by Tauri as sidecar:
|
||||||
|
//
|
||||||
|
var os = Environment.GetOS();
|
||||||
|
var tauriSidecarArtifactName = rid switch
|
||||||
|
{
|
||||||
|
RID.WIN_X64 => "mindworkAIStudioServer-x86_64-pc-windows-msvc.exe",
|
||||||
|
RID.WIN_ARM64 => "mindworkAIStudioServer-aarch64-pc-windows-msvc.exe",
|
||||||
|
|
||||||
|
RID.LINUX_X64 => "mindworkAIStudioServer-x86_64-unknown-linux-gnu",
|
||||||
|
RID.LINUX_ARM64 => "mindworkAIStudioServer-aarch64-unknown-linux-gnu",
|
||||||
|
|
||||||
|
RID.OSX_ARM64 => "mindworkAIStudioServer-aarch64-apple-darwin",
|
||||||
|
RID.OSX_X64 => "mindworkAIStudioServer-x86_64-apple-darwin",
|
||||||
|
|
||||||
|
_ => string.Empty,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(tauriSidecarArtifactName))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"- Error: Unsupported rid '{rid.AsMicrosoftRid()}'.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var 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}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Build the Rust project / runtime:
|
||||||
|
//
|
||||||
|
|
||||||
|
Console.WriteLine("==============================");
|
||||||
|
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 UpdateChangelog(int buildNumber, string appVersion, string buildTime)
|
||||||
|
{
|
||||||
|
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($"- 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ...");
|
||||||
|
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<string> UpdateAppVersion(PrepareAction action)
|
||||||
|
{
|
||||||
|
const int APP_VERSION_INDEX = 0;
|
||||||
|
|
||||||
|
if (action == PrepareAction.NONE)
|
||||||
|
{
|
||||||
|
Console.WriteLine("- No action specified. Skipping app version update.");
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 updatedAppVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// ReSharper disable ClassNeverInstantiated.Global
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
|
||||||
|
using Build.Tools;
|
||||||
|
|
||||||
|
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.GetRidsForCurrentOS().First();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
Directory.CreateDirectory(Path.Join(cwd, "wwwroot", "system"));
|
||||||
|
var sourcePaths = Directory.EnumerateFiles(contentPath, "*", SearchOption.AllDirectories);
|
||||||
|
var counter = 0;
|
||||||
|
foreach(var sourcePath in sourcePaths)
|
||||||
|
{
|
||||||
|
counter++;
|
||||||
|
var relativePath = Path.GetRelativePath(cwd, sourcePath);
|
||||||
|
var targetPath = Path.Join(cwd, "wwwroot", 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// Global using directives
|
||||||
|
|
||||||
|
global using System.Text;
|
||||||
|
|
||||||
|
global using Cocona;
|
||||||
|
|
||||||
|
global using Environment = Build.Tools.Environment;
|
||||||
@@ -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();
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
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 [];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,21 @@
|
|||||||
<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/=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/=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: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>
|
||||||
@@ -28,10 +28,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="() => this.SubmitAction()" Style="@this.SubmitButtonStyle">
|
||||||
@this.SubmitText
|
@this.SubmitText
|
||||||
</MudButton>
|
</MudButton>
|
||||||
|
@if (this.isProcessing && this.cancellationTokenSource is not null)
|
||||||
|
{
|
||||||
|
<MudTooltip Text="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 +80,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="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())">
|
||||||
@@ -103,7 +110,7 @@
|
|||||||
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="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)">
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase, IMe
|
|||||||
|
|
||||||
private readonly Timer formChangeTimer = new(TimeSpan.FromSeconds(1.6));
|
private readonly Timer formChangeTimer = new(TimeSpan.FromSeconds(1.6));
|
||||||
|
|
||||||
|
private CancellationTokenSource? cancellationTokenSource;
|
||||||
private ContentBlock? resultingContentBlock;
|
private ContentBlock? resultingContentBlock;
|
||||||
private string[] inputIssues = [];
|
private string[] inputIssues = [];
|
||||||
private bool isProcessing;
|
private bool isProcessing;
|
||||||
@@ -286,11 +287,15 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase, IMe
|
|||||||
this.isProcessing = true;
|
this.isProcessing = true;
|
||||||
this.StateHasChanged();
|
this.StateHasChanged();
|
||||||
|
|
||||||
|
using (this.cancellationTokenSource = new())
|
||||||
|
{
|
||||||
// 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.cancellationTokenSource = null;
|
||||||
this.isProcessing = false;
|
this.isProcessing = false;
|
||||||
this.StateHasChanged();
|
this.StateHasChanged();
|
||||||
|
|
||||||
@@ -298,6 +303,13 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase, IMe
|
|||||||
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 +326,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)
|
||||||
|
|||||||
@@ -228,18 +228,20 @@ else
|
|||||||
<HeaderContent>
|
<HeaderContent>
|
||||||
<MudTh>Name</MudTh>
|
<MudTh>Name</MudTh>
|
||||||
<MudTh>Type</MudTh>
|
<MudTh>Type</MudTh>
|
||||||
<MudTh Style="text-align: left;">Actions</MudTh>
|
<MudTh>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">
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" OnClick="() => this.EditEmbedding(context)">
|
||||||
Edit
|
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
|
Delete
|
||||||
</MudButton>
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
</MudTd>
|
</MudTd>
|
||||||
</RowTemplate>
|
</RowTemplate>
|
||||||
</MudTable>
|
</MudTable>
|
||||||
@@ -274,17 +276,19 @@ else
|
|||||||
</ColGroup>
|
</ColGroup>
|
||||||
<HeaderContent>
|
<HeaderContent>
|
||||||
<MudTh>Name</MudTh>
|
<MudTh>Name</MudTh>
|
||||||
<MudTh Style="text-align: left;">Actions</MudTh>
|
<MudTh>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">
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" OnClick="() => this.EditRetrievalProcess(context)">
|
||||||
Edit
|
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
|
Delete
|
||||||
</MudButton>
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
</MudTd>
|
</MudTd>
|
||||||
</RowTemplate>
|
</RowTemplate>
|
||||||
</MudTable>
|
</MudTable>
|
||||||
|
|||||||
@@ -210,7 +210,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,
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
@@ -13,6 +13,10 @@ public partial class Changelog
|
|||||||
|
|
||||||
public static readonly Log[] LOGS =
|
public static readonly Log[] LOGS =
|
||||||
[
|
[
|
||||||
|
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"),
|
||||||
|
|||||||
@@ -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,9 +40,6 @@ 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();
|
||||||
|
|
||||||
@@ -845,7 +841,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
|
|
||||||
public override string ComponentName => nameof(ChatComponent);
|
public override string ComponentName => nameof(ChatComponent);
|
||||||
|
|
||||||
public override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
protected override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||||
{
|
{
|
||||||
switch (triggeredEvent)
|
switch (triggeredEvent)
|
||||||
{
|
{
|
||||||
@@ -864,7 +860,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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
@if (this.SelectionMode is DataSourceSelectionMode.SELECTION_MODE)
|
@if (this.SelectionMode is DataSourceSelectionMode.SELECTION_MODE)
|
||||||
{
|
{
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<MudTooltip Text="Select the data sources you want to use here." Placement="Placement.Top">
|
<MudTooltip Text="Select the data you want to use here." Placement="Placement.Top">
|
||||||
@if (this.PopoverTriggerMode is PopoverTriggerMode.ICON)
|
@if (this.PopoverTriggerMode is PopoverTriggerMode.ICON)
|
||||||
{
|
{
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Source" Class="@this.PopoverButtonClasses" OnClick="@(() => this.ToggleDataSourceSelection())"/>
|
<MudIconButton Icon="@Icons.Material.Filled.Source" Class="@this.PopoverButtonClasses" OnClick="@(() => this.ToggleDataSourceSelection())"/>
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.Source" Class="@this.PopoverButtonClasses" OnClick="@(() => this.ToggleDataSourceSelection())">
|
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.Source" Class="@this.PopoverButtonClasses" OnClick="@(() => this.ToggleDataSourceSelection())">
|
||||||
Select data sources
|
Select data
|
||||||
</MudButton>
|
</MudButton>
|
||||||
}
|
}
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
@@ -21,7 +21,13 @@
|
|||||||
<MudCardHeader>
|
<MudCardHeader>
|
||||||
<CardHeaderContent>
|
<CardHeaderContent>
|
||||||
<PreviewPrototype/>
|
<PreviewPrototype/>
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center">
|
||||||
<MudText Typo="Typo.h5">Data Source Selection</MudText>
|
<MudText Typo="Typo.h5">Data Source Selection</MudText>
|
||||||
|
<MudSpacer/>
|
||||||
|
<MudTooltip Text="Manage your data sources" Placement="Placement.Top">
|
||||||
|
<MudIconButton Variant="Variant.Filled" Icon="@Icons.Material.Filled.Settings" OnClick="@this.OpenSettingsDialog"/>
|
||||||
|
</MudTooltip>
|
||||||
|
</MudStack>
|
||||||
</CardHeaderContent>
|
</CardHeaderContent>
|
||||||
</MudCardHeader>
|
</MudCardHeader>
|
||||||
<MudCardContent Style="min-width: 24em; max-height: 60vh; max-width: 45vw; overflow: auto;">
|
<MudCardContent Style="min-width: 24em; max-height: 60vh; max-width: 45vw; overflow: auto;">
|
||||||
@@ -31,6 +37,26 @@
|
|||||||
<MudSkeleton Width="80%"/>
|
<MudSkeleton Width="80%"/>
|
||||||
<MudSkeleton Width="100%"/>
|
<MudSkeleton Width="100%"/>
|
||||||
}
|
}
|
||||||
|
else if (this.SettingsManager.ConfigurationData.DataSources.Count == 0)
|
||||||
|
{
|
||||||
|
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||||
|
You haven't configured any data sources. To grant the AI access to your data, you need to
|
||||||
|
add such a source. However, if you wish to use data from your device, you first have to set up
|
||||||
|
a so-called embedding. This embedding is necessary so the AI can effectively search your data,
|
||||||
|
find and retrieve the correct information required for each task. In addition to local data,
|
||||||
|
you can also incorporate your company's data. To do so, your company must provide the data through
|
||||||
|
an ERI (External Retrieval Interface).
|
||||||
|
</MudJustifiedText>
|
||||||
|
|
||||||
|
<MudStack StretchItems="StretchItems.None" AlignItems="AlignItems.Start">
|
||||||
|
<MudButton Variant="Variant.Filled" OnClick="this.OpenSettingsDialog" StartIcon="@Icons.Material.Filled.Settings">
|
||||||
|
Manage Data Sources
|
||||||
|
</MudButton>
|
||||||
|
<MudButton Variant="Variant.Filled" Href="https://mindworkai.org/#eri---external-retrieval-interface" Target="_blank" StartIcon="@Icons.Material.Filled.Settings">
|
||||||
|
Read more about ERI
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
}
|
||||||
else if (this.showDataSourceSelection)
|
else if (this.showDataSourceSelection)
|
||||||
{
|
{
|
||||||
<MudTextSwitch Label="Are data sources enabled?" Value="@this.areDataSourcesEnabled" LabelOn="Yes, I want to use data sources." LabelOff="No, I don't want to use data sources." ValueChanged="@this.EnabledChanged"/>
|
<MudTextSwitch Label="Are data sources enabled?" Value="@this.areDataSourcesEnabled" LabelOn="Yes, I want to use data sources." LabelOff="No, I don't want to use data sources." ValueChanged="@this.EnabledChanged"/>
|
||||||
@@ -43,8 +69,27 @@
|
|||||||
<MudTextSwitch Label="AI-based data validation" Value="@this.aiBasedValidation" LabelOn="Yes, let the AI validate & filter the retrieved data." LabelOff="No, use all data retrieved from the data sources." ValueChanged="@this.ValidationModeChanged"/>
|
<MudTextSwitch Label="AI-based data validation" Value="@this.aiBasedValidation" LabelOn="Yes, let the AI validate & filter the retrieved data." LabelOff="No, use all data retrieved from the data sources." ValueChanged="@this.ValidationModeChanged"/>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (this.aiBasedSourceSelection is false || this.DataSourcesAISelected.Count == 0)
|
@switch (this.aiBasedSourceSelection)
|
||||||
{
|
{
|
||||||
|
case true when this.availableDataSources.Count == 0:
|
||||||
|
<MudText Typo="Typo.body1" Class="mb-3">
|
||||||
|
Your data sources cannot be used with the LLM provider you selected due to data privacy, or they are currently unavailable.
|
||||||
|
</MudText>
|
||||||
|
break;
|
||||||
|
|
||||||
|
case true when this.DataSourcesAISelected.Count == 0:
|
||||||
|
<MudText Typo="Typo.body1" Class="mb-3">
|
||||||
|
The AI evaluates each of your inputs to determine whether and which data sources are necessary. Currently, the AI has not selected any source.
|
||||||
|
</MudText>
|
||||||
|
break;
|
||||||
|
|
||||||
|
case false when this.availableDataSources.Count == 0:
|
||||||
|
<MudText Typo="Typo.body1" Class="mb-3">
|
||||||
|
Your data sources cannot be used with the LLM provider you selected due to data privacy, or they are currently unavailable.
|
||||||
|
</MudText>
|
||||||
|
break;
|
||||||
|
|
||||||
|
case false:
|
||||||
<MudField Label="Available Data Sources" Variant="Variant.Outlined" Class="mb-3" Disabled="@this.aiBasedSourceSelection">
|
<MudField Label="Available Data Sources" Variant="Variant.Outlined" Class="mb-3" Disabled="@this.aiBasedSourceSelection">
|
||||||
<MudList T="IDataSource" SelectionMode="@this.GetListSelectionMode()" @bind-SelectedValues:get="@this.selectedDataSources" @bind-SelectedValues:set="@(x => this.SelectionChanged(x))" Style="max-height: 14em;">
|
<MudList T="IDataSource" SelectionMode="@this.GetListSelectionMode()" @bind-SelectedValues:get="@this.selectedDataSources" @bind-SelectedValues:set="@(x => this.SelectionChanged(x))" Style="max-height: 14em;">
|
||||||
@foreach (var source in this.availableDataSources)
|
@foreach (var source in this.availableDataSources)
|
||||||
@@ -55,9 +100,9 @@
|
|||||||
}
|
}
|
||||||
</MudList>
|
</MudList>
|
||||||
</MudField>
|
</MudField>
|
||||||
}
|
break;
|
||||||
else
|
|
||||||
{
|
case true:
|
||||||
<MudExpansionPanels MultiExpansion="@false" Class="mt-3" Style="max-height: 14em;">
|
<MudExpansionPanels MultiExpansion="@false" Class="mt-3" Style="max-height: 14em;">
|
||||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.TouchApp" HeaderText="Available Data Sources">
|
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.TouchApp" HeaderText="Available Data Sources">
|
||||||
<MudList T="IDataSource" SelectionMode="MudBlazor.SelectionMode.SingleSelection" SelectedValues="@this.selectedDataSources" Style="max-height: 14em;">
|
<MudList T="IDataSource" SelectionMode="MudBlazor.SelectionMode.SingleSelection" SelectedValues="@this.selectedDataSources" Style="max-height: 14em;">
|
||||||
@@ -81,7 +126,7 @@
|
|||||||
|
|
||||||
<MudProgressLinear Color="Color.Info" Min="0" Max="1" Value="@source.AIDecision.Confidence"/>
|
<MudProgressLinear Color="Color.Info" Min="0" Max="1" Value="@source.AIDecision.Confidence"/>
|
||||||
<MudJustifiedText Typo="Typo.body2">
|
<MudJustifiedText Typo="Typo.body2">
|
||||||
@this.GetAIReasoning(source)
|
@(this.GetAIReasoning(source))
|
||||||
</MudJustifiedText>
|
</MudJustifiedText>
|
||||||
</ChildContent>
|
</ChildContent>
|
||||||
</MudListItem>
|
</MudListItem>
|
||||||
@@ -89,6 +134,7 @@
|
|||||||
</MudList>
|
</MudList>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
</MudExpansionPanels>
|
</MudExpansionPanels>
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
|
using AIStudio.Dialogs.Settings;
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
using AIStudio.Settings.DataModel;
|
using AIStudio.Settings.DataModel;
|
||||||
using AIStudio.Tools.Services;
|
using AIStudio.Tools.Services;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||||
|
|
||||||
namespace AIStudio.Components;
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
public partial class DataSourceSelection : ComponentBase, IMessageBusReceiver, IDisposable
|
public partial class DataSourceSelection : ComponentBase, IMessageBusReceiver, IDisposable
|
||||||
@@ -45,7 +48,7 @@ public partial class DataSourceSelection : ComponentBase, IMessageBusReceiver, I
|
|||||||
private DataSourceService DataSourceService { get; init; } = null!;
|
private DataSourceService DataSourceService { get; init; } = null!;
|
||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
private ILogger<DataSourceSelection> Logger { get; init; } = null!;
|
private IDialogService DialogService { get; init; } = null!;
|
||||||
|
|
||||||
private bool internalChange;
|
private bool internalChange;
|
||||||
private bool showDataSourceSelection;
|
private bool showDataSourceSelection;
|
||||||
@@ -132,6 +135,20 @@ public partial class DataSourceSelection : ComponentBase, IMessageBusReceiver, I
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
private async Task OpenSettingsDialog()
|
||||||
|
{
|
||||||
|
this.showDataSourceSelection = false;
|
||||||
|
this.StateHasChanged();
|
||||||
|
|
||||||
|
var dialogParameters = new DialogParameters();
|
||||||
|
var dialogReference = await this.DialogService.ShowAsync<SettingsDialogDataSources>(null, dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
|
await dialogReference.Result;
|
||||||
|
await this.LoadAndApplyFilters();
|
||||||
|
|
||||||
|
this.showDataSourceSelection = true;
|
||||||
|
this.StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
private SelectionMode GetListSelectionMode() => this.aiBasedSourceSelection ? MudBlazor.SelectionMode.SingleSelection : MudBlazor.SelectionMode.MultiSelection;
|
private SelectionMode GetListSelectionMode() => this.aiBasedSourceSelection ? MudBlazor.SelectionMode.SingleSelection : MudBlazor.SelectionMode.MultiSelection;
|
||||||
|
|
||||||
private IReadOnlyCollection<DataSourceAgentSelected> GetSelectedDataSourcesWithAI() => this.DataSourcesAISelected.Where(n => n.Selected).ToList();
|
private IReadOnlyCollection<DataSourceAgentSelected> GetSelectedDataSourcesWithAI() => this.DataSourcesAISelected.Where(n => n.Selected).ToList();
|
||||||
|
|||||||
@@ -12,6 +12,6 @@
|
|||||||
</MudSelect>
|
</MudSelect>
|
||||||
@if (this.AllowOther && this.Value.Equals(this.OtherValue))
|
@if (this.AllowOther && this.Value.Equals(this.OtherValue))
|
||||||
{
|
{
|
||||||
<MudTextField T="string" Text="@this.OtherInput" TextChanged="this.OtherInputChanged" Validation="@this.ValidateOther" Label="@this.LabelOther" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Immediate="@true"/>
|
<MudTextField T="string" Text="@this.OtherInput" TextChanged="this.OtherValueChanged" Validation="@this.ValidateOther" Label="@this.LabelOther" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Immediate="@true"/>
|
||||||
}
|
}
|
||||||
</MudStack>
|
</MudStack>
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
using AIStudio.Layout;
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
namespace AIStudio.Components;
|
namespace AIStudio.Components;
|
||||||
@@ -30,9 +28,6 @@ public partial class InnerScrolling : MSGComponentBase
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public string Style { get; set; } = string.Empty;
|
public string Style { get; set; } = string.Empty;
|
||||||
|
|
||||||
[CascadingParameter]
|
|
||||||
private MainLayout MainLayout { get; set; } = null!;
|
|
||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
private IJSRuntime JsRuntime { get; init; } = null!;
|
private IJSRuntime JsRuntime { get; init; } = null!;
|
||||||
|
|
||||||
@@ -52,7 +47,7 @@ public partial class InnerScrolling : MSGComponentBase
|
|||||||
|
|
||||||
public override string ComponentName => nameof(InnerScrolling);
|
public override string ComponentName => nameof(InnerScrolling);
|
||||||
|
|
||||||
public override Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
protected override Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||||
{
|
{
|
||||||
switch (triggeredEvent)
|
switch (triggeredEvent)
|
||||||
{
|
{
|
||||||
@@ -64,11 +59,6 @@ public partial class InnerScrolling : MSGComponentBase
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data) where TResult : default where TPayload : default
|
|
||||||
{
|
|
||||||
return Task.FromResult(default(TResult));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private string MinWidthStyle => string.IsNullOrWhiteSpace(this.MinWidth) ? string.Empty : $"min-width: {this.MinWidth}; ";
|
private string MinWidthStyle => string.IsNullOrWhiteSpace(this.MinWidth) ? string.Empty : $"min-width: {this.MinWidth}; ";
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
|
using AIStudio.Tools.PluginSystem;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
using SharedTools;
|
||||||
|
|
||||||
namespace AIStudio.Components;
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBusReceiver
|
public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBusReceiver, ILang
|
||||||
{
|
{
|
||||||
[Inject]
|
[Inject]
|
||||||
protected SettingsManager SettingsManager { get; init; } = null!;
|
protected SettingsManager SettingsManager { get; init; } = null!;
|
||||||
@@ -12,12 +15,37 @@ public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBus
|
|||||||
[Inject]
|
[Inject]
|
||||||
protected MessageBus MessageBus { get; init; } = null!;
|
protected MessageBus MessageBus { get; init; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private ILogger<PluginLanguage> Logger { get; init; } = null!;
|
||||||
|
|
||||||
|
private ILanguagePlugin Lang { get; set; } = PluginFactory.BaseLanguage;
|
||||||
|
|
||||||
#region Overrides of ComponentBase
|
#region Overrides of ComponentBase
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
this.Lang = await this.SettingsManager.GetActiveLanguagePlugin();
|
||||||
|
|
||||||
this.MessageBus.RegisterComponent(this);
|
this.MessageBus.RegisterComponent(this);
|
||||||
base.OnInitialized();
|
await base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation of ILang
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string T(string fallbackEN)
|
||||||
|
{
|
||||||
|
var type = this.GetType();
|
||||||
|
var ns = $"{type.Namespace!}::{type.Name}".ToUpperInvariant().Replace(".", "::");
|
||||||
|
var key = $"root::{ns}::T{fallbackEN.ToFNV32()}";
|
||||||
|
|
||||||
|
if(this.Lang.TryGetText(key, out var text, logWarning: false))
|
||||||
|
return text;
|
||||||
|
|
||||||
|
this.Logger.LogWarning($"Missing translation key '{key}' for content '{fallbackEN}'.");
|
||||||
|
return fallbackEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -26,7 +54,7 @@ public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBus
|
|||||||
|
|
||||||
public abstract string ComponentName { get; }
|
public abstract string ComponentName { get; }
|
||||||
|
|
||||||
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
public async Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
||||||
{
|
{
|
||||||
switch (triggeredEvent)
|
switch (triggeredEvent)
|
||||||
{
|
{
|
||||||
@@ -34,18 +62,33 @@ public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBus
|
|||||||
this.StateHasChanged();
|
this.StateHasChanged();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Event.PLUGINS_RELOADED:
|
||||||
|
this.Lang = await this.SettingsManager.GetActiveLanguagePlugin();
|
||||||
|
await this.InvokeAsync(this.StateHasChanged);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return this.ProcessIncomingMessage(sendingComponent, triggeredEvent, data);
|
await this.ProcessIncomingMessage(sendingComponent, triggeredEvent, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
|
||||||
|
{
|
||||||
|
return await this.ProcessIncomingMessageWithResult<TPayload, TResult>(sendingComponent, triggeredEvent, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
protected virtual Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
||||||
|
{
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data);
|
protected virtual Task<TResult?> ProcessIncomingMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
|
||||||
|
{
|
||||||
public abstract Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data);
|
return Task.FromResult<TResult?>(default);
|
||||||
|
}
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Implementation of IDisposable
|
#region Implementation of IDisposable
|
||||||
|
|
||||||
@@ -71,7 +114,8 @@ public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBus
|
|||||||
// Append the color theme changed event to the list of events:
|
// Append the color theme changed event to the list of events:
|
||||||
var eventsList = new List<Event>(events)
|
var eventsList = new List<Event>(events)
|
||||||
{
|
{
|
||||||
Event.COLOR_THEME_CHANGED
|
Event.COLOR_THEME_CHANGED,
|
||||||
|
Event.PLUGINS_RELOADED,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.MessageBus.ApplyFilters(this, components, eventsList.ToArray());
|
this.MessageBus.ApplyFilters(this, components, eventsList.ToArray());
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<MudTooltip Placement="Placement.Bottom" Arrow="@true">
|
<MudTooltip Placement="Placement.Bottom" Arrow="@true" Class="@this.Classes">
|
||||||
<ChildContent>
|
<ChildContent>
|
||||||
<MudChip T="string" Icon="@Icons.Material.Filled.FirstPage" Color="Color.Error" Class="mb-3">
|
<MudChip T="string" Icon="@Icons.Material.Filled.FirstPage" Color="Color.Error" Class="mb-3">
|
||||||
Alpha
|
Alpha
|
||||||
|
|||||||
@@ -2,4 +2,10 @@ using Microsoft.AspNetCore.Components;
|
|||||||
|
|
||||||
namespace AIStudio.Components;
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
public partial class PreviewAlpha : ComponentBase;
|
public partial class PreviewAlpha : ComponentBase
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public bool ApplyInnerScrollingFix { get; set; }
|
||||||
|
|
||||||
|
private string Classes => this.ApplyInnerScrollingFix ? "InnerScrollingFix" : string.Empty;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<MudTooltip Placement="Placement.Bottom" Arrow="@true">
|
<MudTooltip Placement="Placement.Bottom" Arrow="@true" Class="@this.Classes">
|
||||||
<ChildContent>
|
<ChildContent>
|
||||||
<MudChip T="string" Icon="@Icons.Material.Filled.HourglassTop" Color="Color.Info" Class="mb-3">
|
<MudChip T="string" Icon="@Icons.Material.Filled.HourglassTop" Color="Color.Info" Class="mb-3">
|
||||||
Beta
|
Beta
|
||||||
|
|||||||
@@ -2,4 +2,10 @@ using Microsoft.AspNetCore.Components;
|
|||||||
|
|
||||||
namespace AIStudio.Components;
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
public partial class PreviewBeta : ComponentBase;
|
public partial class PreviewBeta : ComponentBase
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public bool ApplyInnerScrollingFix { get; set; }
|
||||||
|
|
||||||
|
private string Classes => this.ApplyInnerScrollingFix ? "InnerScrollingFix" : string.Empty;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<MudTooltip Placement="Placement.Bottom" Arrow="@true">
|
<MudTooltip Placement="Placement.Bottom" Arrow="@true" Class="@this.Classes">
|
||||||
<ChildContent>
|
<ChildContent>
|
||||||
<MudChip T="string" Icon="@Icons.Material.Filled.Science" Color="Color.Error" Class="mb-3">
|
<MudChip T="string" Icon="@Icons.Material.Filled.Science" Color="Color.Error" Class="mb-3">
|
||||||
Experimental
|
Experimental
|
||||||
|
|||||||
@@ -2,4 +2,10 @@ using Microsoft.AspNetCore.Components;
|
|||||||
|
|
||||||
namespace AIStudio.Components;
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
public partial class PreviewExperimental : ComponentBase;
|
public partial class PreviewExperimental : ComponentBase
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public bool ApplyInnerScrollingFix { get; set; }
|
||||||
|
|
||||||
|
private string Classes => this.ApplyInnerScrollingFix ? "InnerScrollingFix" : string.Empty;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<MudTooltip Placement="Placement.Bottom" Arrow="@true">
|
<MudTooltip Placement="Placement.Bottom" Arrow="@true" Class="@this.Classes">
|
||||||
<ChildContent>
|
<ChildContent>
|
||||||
<MudChip T="string" Icon="@Icons.Material.Filled.HourglassBottom" Color="Color.Error" Class="mb-3">
|
<MudChip T="string" Icon="@Icons.Material.Filled.HourglassBottom" Color="Color.Error" Class="mb-3">
|
||||||
Prototype
|
Prototype
|
||||||
|
|||||||
@@ -2,4 +2,10 @@ using Microsoft.AspNetCore.Components;
|
|||||||
|
|
||||||
namespace AIStudio.Components;
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
public partial class PreviewPrototype : ComponentBase;
|
public partial class PreviewPrototype : ComponentBase
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public bool ApplyInnerScrollingFix { get; set; }
|
||||||
|
|
||||||
|
private string Classes => this.ApplyInnerScrollingFix ? "InnerScrollingFix" : string.Empty;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<MudTooltip Placement="Placement.Bottom" Arrow="@true">
|
<MudTooltip Placement="Placement.Bottom" Arrow="@true" Class="@this.Classes">
|
||||||
<ChildContent>
|
<ChildContent>
|
||||||
<MudChip T="string" Icon="@Icons.Material.Filled.VerifiedUser" Color="Color.Success" Class="mb-3">
|
<MudChip T="string" Icon="@Icons.Material.Filled.VerifiedUser" Color="Color.Success" Class="mb-3">
|
||||||
Release Candidate
|
Release Candidate
|
||||||
|
|||||||
@@ -2,4 +2,10 @@ using Microsoft.AspNetCore.Components;
|
|||||||
|
|
||||||
namespace AIStudio.Components;
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
public partial class PreviewReleaseCandidate : ComponentBase;
|
public partial class PreviewReleaseCandidate : ComponentBase
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public bool ApplyInnerScrollingFix { get; set; }
|
||||||
|
|
||||||
|
private string Classes => this.ApplyInnerScrollingFix ? "InnerScrollingFix" : string.Empty;
|
||||||
|
}
|
||||||
@@ -3,14 +3,25 @@
|
|||||||
@inherits SettingsPanelBase
|
@inherits SettingsPanelBase
|
||||||
|
|
||||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Apps" HeaderText="App Options">
|
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Apps" HeaderText="App Options">
|
||||||
|
|
||||||
|
@if (PreviewFeatures.PRE_PLUGINS_2025.IsEnabled(this.SettingsManager))
|
||||||
|
{
|
||||||
|
<ConfigurationSelect OptionDescription="Language behavior" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.LanguageBehavior)" Data="@ConfigurationSelectDataFactory.GetLangBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.LanguageBehavior = selectedValue)" OptionHelp="Select the language behavior for the app. The default is to use the system language. You might want to choose a language manually?"/>
|
||||||
|
|
||||||
|
@if (this.SettingsManager.ConfigurationData.App.LanguageBehavior is LangBehavior.MANUAL)
|
||||||
|
{
|
||||||
|
<ConfigurationSelect OptionDescription="Language" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.LanguagePluginId)" Data="@ConfigurationSelectDataFactory.GetLanguagesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.LanguagePluginId = selectedValue)" OptionHelp="Select the language for the app."/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<ConfigurationSelect OptionDescription="Color theme" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreferredTheme)" Data="@ConfigurationSelectDataFactory.GetThemesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreferredTheme = selectedValue)" OptionHelp="Choose the color theme that best suits for you."/>
|
<ConfigurationSelect OptionDescription="Color theme" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreferredTheme)" Data="@ConfigurationSelectDataFactory.GetThemesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreferredTheme = selectedValue)" OptionHelp="Choose the color theme that best suits for you."/>
|
||||||
<ConfigurationOption OptionDescription="Save energy?" LabelOn="Energy saving is enabled" LabelOff="Energy saving is disabled" State="@(() => this.SettingsManager.ConfigurationData.App.IsSavingEnergy)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.IsSavingEnergy = updatedState)" OptionHelp="When enabled, streamed content from the AI is updated once every third second. When disabled, streamed content will be updated as soon as it is available."/>
|
<ConfigurationOption OptionDescription="Save energy?" LabelOn="Energy saving is enabled" LabelOff="Energy saving is disabled" State="@(() => this.SettingsManager.ConfigurationData.App.IsSavingEnergy)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.IsSavingEnergy = updatedState)" OptionHelp="When enabled, streamed content from the AI is updated once every third second. When disabled, streamed content will be updated as soon as it is available."/>
|
||||||
<ConfigurationOption OptionDescription="Enable spellchecking?" LabelOn="Spellchecking is enabled" LabelOff="Spellchecking is disabled" State="@(() => this.SettingsManager.ConfigurationData.App.EnableSpellchecking)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.EnableSpellchecking = updatedState)" OptionHelp="When enabled, spellchecking will be active in all input fields. Depending on your operating system, errors may not be visually highlighted, but right-clicking may still offer possible corrections." />
|
<ConfigurationOption OptionDescription="Enable spellchecking?" LabelOn="Spellchecking is enabled" LabelOff="Spellchecking is disabled" State="@(() => this.SettingsManager.ConfigurationData.App.EnableSpellchecking)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.EnableSpellchecking = updatedState)" OptionHelp="When enabled, spellchecking will be active in all input fields. Depending on your operating system, errors may not be visually highlighted, but right-clicking may still offer possible corrections."/>
|
||||||
<ConfigurationSelect OptionDescription="Check for updates" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.UpdateBehavior)" Data="@ConfigurationSelectDataFactory.GetUpdateBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UpdateBehavior = selectedValue)" OptionHelp="How often should we check for app updates?"/>
|
<ConfigurationSelect OptionDescription="Check for updates" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.UpdateBehavior)" Data="@ConfigurationSelectDataFactory.GetUpdateBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UpdateBehavior = selectedValue)" OptionHelp="How often should we check for app updates?"/>
|
||||||
<ConfigurationSelect OptionDescription="Navigation bar behavior" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.NavigationBehavior)" Data="@ConfigurationSelectDataFactory.GetNavBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.NavigationBehavior = selectedValue)" OptionHelp="Select the desired behavior for the navigation bar."/>
|
<ConfigurationSelect OptionDescription="Navigation bar behavior" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.NavigationBehavior)" Data="@ConfigurationSelectDataFactory.GetNavBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.NavigationBehavior = selectedValue)" OptionHelp="Select the desired behavior for the navigation bar."/>
|
||||||
<ConfigurationSelect OptionDescription="Preview feature visibility" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreviewVisibility)" Data="@ConfigurationSelectDataFactory.GetPreviewVisibility()" SelectionUpdate="@this.UpdatePreviewFeatures" OptionHelp="Do you want to show preview features in the app?"/>
|
<ConfigurationSelect OptionDescription="Preview feature visibility" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreviewVisibility)" Data="@ConfigurationSelectDataFactory.GetPreviewVisibility()" SelectionUpdate="@this.UpdatePreviewFeatures" OptionHelp="Do you want to show preview features in the app?"/>
|
||||||
|
|
||||||
@if(this.SettingsManager.ConfigurationData.App.PreviewVisibility > PreviewVisibility.NONE)
|
@if (this.SettingsManager.ConfigurationData.App.PreviewVisibility > PreviewVisibility.NONE)
|
||||||
{
|
{
|
||||||
var availablePreviewFeatures = ConfigurationSelectDataFactory.GetPreviewFeaturesData(this.SettingsManager).ToList();
|
var availablePreviewFeatures = ConfigurationSelectDataFactory.GetPreviewFeaturesData(this.SettingsManager).ToList();
|
||||||
if (availablePreviewFeatures.Count > 0)
|
if (availablePreviewFeatures.Count > 0)
|
||||||
|
|||||||
@@ -36,24 +36,26 @@
|
|||||||
<MudTh>Name</MudTh>
|
<MudTh>Name</MudTh>
|
||||||
<MudTh>Provider</MudTh>
|
<MudTh>Provider</MudTh>
|
||||||
<MudTh>Model</MudTh>
|
<MudTh>Model</MudTh>
|
||||||
<MudTh Style="text-align: left;">Actions</MudTh>
|
<MudTh>Actions</MudTh>
|
||||||
</HeaderContent>
|
</HeaderContent>
|
||||||
<RowTemplate>
|
<RowTemplate>
|
||||||
<MudTd>@context.Num</MudTd>
|
<MudTd>@context.Num</MudTd>
|
||||||
<MudTd>@context.Name</MudTd>
|
<MudTd>@context.Name</MudTd>
|
||||||
<MudTd>@context.UsedLLMProvider</MudTd>
|
<MudTd>@context.UsedLLMProvider.ToName()</MudTd>
|
||||||
<MudTd>@this.GetEmbeddingProviderModelName(context)</MudTd>
|
<MudTd>@this.GetEmbeddingProviderModelName(context)</MudTd>
|
||||||
|
|
||||||
<MudTd Style="text-align: left;">
|
<MudTd>
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.OpenInBrowser" Class="ma-2" Href="@context.UsedLLMProvider.GetDashboardURL()" Target="_blank" Disabled="@(!context.UsedLLMProvider.HasDashboard())">
|
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.OpenInBrowser" Href="@context.UsedLLMProvider.GetDashboardURL()" Target="_blank" Disabled="@(!context.UsedLLMProvider.HasDashboard())">
|
||||||
Open Dashboard
|
Open Dashboard
|
||||||
</MudButton>
|
</MudButton>
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" Class="ma-2" OnClick="() => this.EditEmbeddingProvider(context)">
|
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" OnClick="() => this.EditEmbeddingProvider(context)">
|
||||||
Edit
|
Edit
|
||||||
</MudButton>
|
</MudButton>
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" Class="ma-2" OnClick="() => this.DeleteEmbeddingProvider(context)">
|
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteEmbeddingProvider(context)">
|
||||||
Delete
|
Delete
|
||||||
</MudButton>
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
</MudTd>
|
</MudTd>
|
||||||
</RowTemplate>
|
</RowTemplate>
|
||||||
</MudTable>
|
</MudTable>
|
||||||
|
|||||||
@@ -23,18 +23,20 @@
|
|||||||
<HeaderContent>
|
<HeaderContent>
|
||||||
<MudTh>#</MudTh>
|
<MudTh>#</MudTh>
|
||||||
<MudTh>Profile Name</MudTh>
|
<MudTh>Profile Name</MudTh>
|
||||||
<MudTh Style="text-align: left;">Actions</MudTh>
|
<MudTh>Actions</MudTh>
|
||||||
</HeaderContent>
|
</HeaderContent>
|
||||||
<RowTemplate>
|
<RowTemplate>
|
||||||
<MudTd>@context.Num</MudTd>
|
<MudTd>@context.Num</MudTd>
|
||||||
<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.EditProfile(context)">
|
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" OnClick="() => this.EditProfile(context)">
|
||||||
Edit
|
Edit
|
||||||
</MudButton>
|
</MudButton>
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" Class="ma-2" OnClick="() => this.DeleteProfile(context)">
|
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteProfile(context)">
|
||||||
Delete
|
Delete
|
||||||
</MudButton>
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
</MudTd>
|
</MudTd>
|
||||||
</RowTemplate>
|
</RowTemplate>
|
||||||
</MudTable>
|
</MudTable>
|
||||||
|
|||||||
@@ -24,12 +24,12 @@
|
|||||||
<MudTh>Instance Name</MudTh>
|
<MudTh>Instance Name</MudTh>
|
||||||
<MudTh>Provider</MudTh>
|
<MudTh>Provider</MudTh>
|
||||||
<MudTh>Model</MudTh>
|
<MudTh>Model</MudTh>
|
||||||
<MudTh Style="text-align: left;">Actions</MudTh>
|
<MudTh>Actions</MudTh>
|
||||||
</HeaderContent>
|
</HeaderContent>
|
||||||
<RowTemplate>
|
<RowTemplate>
|
||||||
<MudTd>@context.Num</MudTd>
|
<MudTd>@context.Num</MudTd>
|
||||||
<MudTd>@context.InstanceName</MudTd>
|
<MudTd>@context.InstanceName</MudTd>
|
||||||
<MudTd>@context.UsedLLMProvider</MudTd>
|
<MudTd>@context.UsedLLMProvider.ToName()</MudTd>
|
||||||
<MudTd>
|
<MudTd>
|
||||||
@if (context.UsedLLMProvider is not LLMProviders.SELF_HOSTED)
|
@if (context.UsedLLMProvider is not LLMProviders.SELF_HOSTED)
|
||||||
{
|
{
|
||||||
@@ -44,16 +44,18 @@
|
|||||||
@("as selected by provider")
|
@("as selected by provider")
|
||||||
}
|
}
|
||||||
</MudTd>
|
</MudTd>
|
||||||
<MudTd Style="text-align: left;">
|
<MudTd>
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.OpenInBrowser" Class="ma-2" Href="@context.UsedLLMProvider.GetDashboardURL()" Target="_blank" Disabled="@(!context.UsedLLMProvider.HasDashboard())">
|
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.OpenInBrowser" Href="@context.UsedLLMProvider.GetDashboardURL()" Target="_blank" Disabled="@(!context.UsedLLMProvider.HasDashboard())">
|
||||||
Open Dashboard
|
Open Dashboard
|
||||||
</MudButton>
|
</MudButton>
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" Class="ma-2" OnClick="() => this.EditLLMProvider(context)">
|
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" OnClick="() => this.EditLLMProvider(context)">
|
||||||
Edit
|
Edit
|
||||||
</MudButton>
|
</MudButton>
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" Class="ma-2" OnClick="() => this.DeleteLLMProvider(context)">
|
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteLLMProvider(context)">
|
||||||
Delete
|
Delete
|
||||||
</MudButton>
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
</MudTd>
|
</MudTd>
|
||||||
</RowTemplate>
|
</RowTemplate>
|
||||||
</MudTable>
|
</MudTable>
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ public partial class SettingsPanelProviders : SettingsPanelBase
|
|||||||
{ x => x.IsSelfHosted, provider.IsSelfHosted },
|
{ x => x.IsSelfHosted, provider.IsSelfHosted },
|
||||||
{ x => x.IsEditing, true },
|
{ x => x.IsEditing, true },
|
||||||
{ x => x.DataHost, provider.Host },
|
{ x => x.DataHost, provider.Host },
|
||||||
|
{ x => x.HFInferenceProviderId, provider.HFInferenceProvider },
|
||||||
};
|
};
|
||||||
|
|
||||||
var dialogReference = await this.DialogService.ShowAsync<ProviderDialog>("Edit LLM Provider", dialogParameters, DialogOptions.FULLSCREEN);
|
var dialogReference = await this.DialogService.ShowAsync<ProviderDialog>("Edit LLM Provider", dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
@@ -105,6 +106,22 @@ public partial class SettingsPanelProviders : SettingsPanelBase
|
|||||||
this.SettingsManager.ConfigurationData.Providers.Remove(provider);
|
this.SettingsManager.ConfigurationData.Providers.Remove(provider);
|
||||||
await this.SettingsManager.StoreSettings();
|
await this.SettingsManager.StoreSettings();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var issueDialogParameters = new DialogParameters
|
||||||
|
{
|
||||||
|
{ "Message", $"Couldn't delete the provider '{provider.InstanceName}'. The issue: {deleteSecretResponse.Issue}. We can ignore this issue and delete the provider anyway. Do you want to ignore it and delete this provider?" },
|
||||||
|
};
|
||||||
|
|
||||||
|
var issueDialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Delete LLM Provider", issueDialogParameters, DialogOptions.FULLSCREEN);
|
||||||
|
var issueDialogResult = await issueDialogReference.Result;
|
||||||
|
if (issueDialogResult is null || issueDialogResult.Canceled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Case: The user wants to ignore the issue and delete the provider anyway:
|
||||||
|
this.SettingsManager.ConfigurationData.Providers.Remove(provider);
|
||||||
|
await this.SettingsManager.StoreSettings();
|
||||||
|
}
|
||||||
|
|
||||||
await this.UpdateProviders();
|
await this.UpdateProviders();
|
||||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||||
|
|||||||
@@ -13,9 +13,6 @@ namespace AIStudio.Components;
|
|||||||
|
|
||||||
public partial class Workspaces : ComponentBase
|
public partial class Workspaces : ComponentBase
|
||||||
{
|
{
|
||||||
[Inject]
|
|
||||||
private SettingsManager SettingsManager { get; init; } = null!;
|
|
||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
private IDialogService DialogService { get; init; } = null!;
|
private IDialogService DialogService { get; init; } = null!;
|
||||||
|
|
||||||
|
|||||||
@@ -11,49 +11,58 @@
|
|||||||
{
|
{
|
||||||
if (provider.ProvideEmbeddings())
|
if (provider.ProvideEmbeddings())
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="@provider">@provider</MudSelectItem>
|
<MudSelectItem Value="@provider">
|
||||||
|
@provider.ToName()
|
||||||
|
</MudSelectItem>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
<MudButton Disabled="@(!this.DataLLMProvider.ShowRegisterButton())" Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.OpenInBrowser" Href="@this.DataLLMProvider.GetCreationURL()" Target="_blank">Create account</MudButton>
|
<MudButton Disabled="@(!this.DataLLMProvider.ShowRegisterButton())" Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.OpenInBrowser" Href="@this.DataLLMProvider.GetCreationURL()" Target="_blank">Create account</MudButton>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
|
||||||
|
@if (this.DataLLMProvider.IsAPIKeyNeeded(this.DataHost))
|
||||||
|
{
|
||||||
@* ReSharper disable once CSharpWarnings::CS8974 *@
|
@* ReSharper disable once CSharpWarnings::CS8974 *@
|
||||||
<MudTextField
|
<MudTextField
|
||||||
T="string"
|
T="string"
|
||||||
@bind-Text="@this.dataAPIKey"
|
@bind-Text="@this.dataAPIKey"
|
||||||
Label="@this.APIKeyText"
|
Label="@this.APIKeyText"
|
||||||
Disabled="@(!this.DataLLMProvider.IsAPIKeyNeeded(this.DataHost))"
|
|
||||||
Class="mb-3"
|
Class="mb-3"
|
||||||
Adornment="Adornment.Start"
|
Adornment="Adornment.Start"
|
||||||
AdornmentIcon="@Icons.Material.Filled.VpnKey"
|
AdornmentIcon="@Icons.Material.Filled.VpnKey"
|
||||||
AdornmentColor="Color.Info"
|
AdornmentColor="Color.Info"
|
||||||
InputType="InputType.Password"
|
InputType="InputType.Password"
|
||||||
Validation="@this.providerValidation.ValidatingAPIKey"
|
Validation="@this.providerValidation.ValidatingAPIKey"/>
|
||||||
/>
|
}
|
||||||
|
|
||||||
|
@if (this.DataLLMProvider.IsHostnameNeeded())
|
||||||
|
{
|
||||||
<MudTextField
|
<MudTextField
|
||||||
T="string"
|
T="string"
|
||||||
@bind-Text="@this.DataHostname"
|
@bind-Text="@this.DataHostname"
|
||||||
Label="Hostname"
|
Label="Hostname"
|
||||||
Disabled="@(!this.DataLLMProvider.IsHostnameNeeded())"
|
|
||||||
Class="mb-3"
|
Class="mb-3"
|
||||||
Adornment="Adornment.Start"
|
Adornment="Adornment.Start"
|
||||||
AdornmentIcon="@Icons.Material.Filled.Dns"
|
AdornmentIcon="@Icons.Material.Filled.Dns"
|
||||||
AdornmentColor="Color.Info"
|
AdornmentColor="Color.Info"
|
||||||
Validation="@this.providerValidation.ValidatingHostname"
|
Validation="@this.providerValidation.ValidatingHostname"
|
||||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
UserAttributes="@SPELLCHECK_ATTRIBUTES"/>
|
||||||
/>
|
}
|
||||||
|
|
||||||
<MudSelect Disabled="@(!this.DataLLMProvider.IsHostNeeded())" @bind-Value="@this.DataHost" Label="Host" Class="mb-3" OpenIcon="@Icons.Material.Filled.ExpandMore" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.providerValidation.ValidatingHost">
|
@if (this.DataLLMProvider.IsHostNeeded())
|
||||||
|
{
|
||||||
|
<MudSelect @bind-Value="@this.DataHost" Label="Host" Class="mb-3" OpenIcon="@Icons.Material.Filled.ExpandMore" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.providerValidation.ValidatingHost">
|
||||||
@foreach (Host host in Enum.GetValues(typeof(Host)))
|
@foreach (Host host in Enum.GetValues(typeof(Host)))
|
||||||
{
|
{
|
||||||
if (host.AreEmbeddingsSupported())
|
if (host.AreEmbeddingsSupported())
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="@host">@host.Name()</MudSelectItem>
|
<MudSelectItem Value="@host">
|
||||||
|
@host.Name()
|
||||||
|
</MudSelectItem>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
|
}
|
||||||
|
|
||||||
<MudStack Row="@true" AlignItems="AlignItems.Center">
|
<MudStack Row="@true" AlignItems="AlignItems.Center">
|
||||||
@if (this.DataLLMProvider.IsEmbeddingModelProvidedManually(this.DataHost))
|
@if (this.DataLLMProvider.IsEmbeddingModelProvidedManually(this.DataHost))
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
@using AIStudio.Provider
|
@using AIStudio.Provider
|
||||||
|
@using AIStudio.Provider.HuggingFace
|
||||||
@using AIStudio.Provider.SelfHosted
|
@using AIStudio.Provider.SelfHosted
|
||||||
|
|
||||||
<MudDialog>
|
<MudDialog>
|
||||||
@@ -9,57 +10,80 @@
|
|||||||
<MudSelect @bind-Value="@this.DataLLMProvider" Label="Provider" Class="mb-3" OpenIcon="@Icons.Material.Filled.AccountBalance" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.providerValidation.ValidatingProvider">
|
<MudSelect @bind-Value="@this.DataLLMProvider" Label="Provider" Class="mb-3" OpenIcon="@Icons.Material.Filled.AccountBalance" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.providerValidation.ValidatingProvider">
|
||||||
@foreach (LLMProviders provider in Enum.GetValues(typeof(LLMProviders)))
|
@foreach (LLMProviders provider in Enum.GetValues(typeof(LLMProviders)))
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="@provider">@provider</MudSelectItem>
|
<MudSelectItem Value="@provider">
|
||||||
|
@provider.ToName()
|
||||||
|
</MudSelectItem>
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
<MudButton Disabled="@(!this.DataLLMProvider.ShowRegisterButton())" Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.OpenInBrowser" Href="@this.DataLLMProvider.GetCreationURL()" Target="_blank">Create account</MudButton>
|
<MudButton Disabled="@(!this.DataLLMProvider.ShowRegisterButton())" Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.OpenInBrowser" Href="@this.DataLLMProvider.GetCreationURL()" Target="_blank">Create account</MudButton>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
|
||||||
|
@if (this.DataLLMProvider.IsAPIKeyNeeded(this.DataHost))
|
||||||
|
{
|
||||||
@* ReSharper disable once CSharpWarnings::CS8974 *@
|
@* ReSharper disable once CSharpWarnings::CS8974 *@
|
||||||
<MudTextField
|
<MudTextField
|
||||||
T="string"
|
T="string"
|
||||||
@bind-Text="@this.dataAPIKey"
|
@bind-Text="@this.dataAPIKey"
|
||||||
Label="@this.APIKeyText"
|
Label="@this.APIKeyText"
|
||||||
Disabled="@(!this.DataLLMProvider.IsAPIKeyNeeded(this.DataHost))"
|
|
||||||
Class="mb-3"
|
Class="mb-3"
|
||||||
Adornment="Adornment.Start"
|
Adornment="Adornment.Start"
|
||||||
AdornmentIcon="@Icons.Material.Filled.VpnKey"
|
AdornmentIcon="@Icons.Material.Filled.VpnKey"
|
||||||
AdornmentColor="Color.Info"
|
AdornmentColor="Color.Info"
|
||||||
InputType="InputType.Password"
|
InputType="InputType.Password"
|
||||||
Validation="@this.providerValidation.ValidatingAPIKey"
|
Validation="@this.providerValidation.ValidatingAPIKey"/>
|
||||||
/>
|
}
|
||||||
|
|
||||||
|
@if (this.DataLLMProvider.IsHostnameNeeded())
|
||||||
|
{
|
||||||
<MudTextField
|
<MudTextField
|
||||||
T="string"
|
T="string"
|
||||||
@bind-Text="@this.DataHostname"
|
@bind-Text="@this.DataHostname"
|
||||||
Label="Hostname"
|
Label="Hostname"
|
||||||
Disabled="@(!this.DataLLMProvider.IsHostnameNeeded())"
|
|
||||||
Class="mb-3"
|
Class="mb-3"
|
||||||
Adornment="Adornment.Start"
|
Adornment="Adornment.Start"
|
||||||
AdornmentIcon="@Icons.Material.Filled.Dns"
|
AdornmentIcon="@Icons.Material.Filled.Dns"
|
||||||
AdornmentColor="Color.Info"
|
AdornmentColor="Color.Info"
|
||||||
Validation="@this.providerValidation.ValidatingHostname"
|
Validation="@this.providerValidation.ValidatingHostname"
|
||||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
UserAttributes="@SPELLCHECK_ATTRIBUTES"/>
|
||||||
/>
|
}
|
||||||
|
|
||||||
<MudSelect Disabled="@(!this.DataLLMProvider.IsHostNeeded())" @bind-Value="@this.DataHost" Label="Host" Class="mb-3" OpenIcon="@Icons.Material.Filled.ExpandMore" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.providerValidation.ValidatingHost">
|
@if (this.DataLLMProvider.IsHostNeeded())
|
||||||
|
{
|
||||||
|
<MudSelect @bind-Value="@this.DataHost" Label="Host" Class="mb-3" OpenIcon="@Icons.Material.Filled.ExpandMore" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.providerValidation.ValidatingHost">
|
||||||
@foreach (Host host in Enum.GetValues(typeof(Host)))
|
@foreach (Host host in Enum.GetValues(typeof(Host)))
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="@host">@host.Name()</MudSelectItem>
|
<MudSelectItem Value="@host">
|
||||||
|
@host.Name()
|
||||||
|
</MudSelectItem>
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (this.DataLLMProvider.IsHFInstanceProviderNeeded())
|
||||||
|
{
|
||||||
|
<MudSelect Disabled="@(!this.DataLLMProvider.IsHFInstanceProviderNeeded())" @bind-Value="@this.HFInferenceProviderId" Label="HF Inference Provider" Class="mb-3" OpenIcon="@Icons.Material.Filled.Dns" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.providerValidation.ValidatingHFInstanceProvider">
|
||||||
|
@foreach (HFInferenceProvider inferenceProvider in Enum.GetValues(typeof(HFInferenceProvider)))
|
||||||
|
{
|
||||||
|
<MudSelectItem Value="@inferenceProvider">
|
||||||
|
@inferenceProvider.ToName()
|
||||||
|
</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
|
||||||
|
<MudJustifiedText Class="mb-3"> Please double-check if your model name matches the curl specifications provided by the inference provider. If it doesn't, you might get a <b>Not Found</b> error when trying to use the model. Here's a <MudLink Href="https://huggingface.co/meta-llama/Llama-3.1-8B-Instruct?inference_api=true&inference_provider=novita&language=sh" Target="_blank">curl example</MudLink>.</MudJustifiedText>
|
||||||
|
}
|
||||||
|
|
||||||
<MudStack Row="@true" AlignItems="AlignItems.Center">
|
<MudStack Row="@true" AlignItems="AlignItems.Center">
|
||||||
@if (this.DataLLMProvider.IsLLMModelProvidedManually())
|
@if (this.DataLLMProvider.IsLLMModelProvidedManually())
|
||||||
{
|
{
|
||||||
<MudButton Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.OpenInBrowser" Href="@this.DataLLMProvider.GetModelsOverviewURL()" Target="_blank">Show available models</MudButton>
|
<MudButton Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.OpenInBrowser" Href="@this.DataLLMProvider.GetModelsOverviewURL(this.HFInferenceProviderId)" Target="_blank">Show available models</MudButton>
|
||||||
<MudTextField
|
<MudTextField
|
||||||
T="string"
|
T="string"
|
||||||
@bind-Text="@this.dataManuallyModel"
|
@bind-Text="@this.dataManuallyModel"
|
||||||
Label="Model"
|
Label="Model"
|
||||||
Class="mb-3"
|
Class="mb-3"
|
||||||
Adornment="Adornment.Start"
|
Adornment="Adornment.Start"
|
||||||
AdornmentIcon="@Icons.Material.Filled.Dns"
|
AdornmentIcon="@Icons.Material.Filled.FaceRetouchingNatural"
|
||||||
AdornmentColor="Color.Info"
|
AdornmentColor="Color.Info"
|
||||||
Validation="@this.ValidateManuallyModel"
|
Validation="@this.ValidateManuallyModel"
|
||||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using AIStudio.Provider;
|
using AIStudio.Provider;
|
||||||
|
using AIStudio.Provider.HuggingFace;
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
using AIStudio.Tools.Services;
|
using AIStudio.Tools.Services;
|
||||||
using AIStudio.Tools.Validation;
|
using AIStudio.Tools.Validation;
|
||||||
@@ -47,6 +48,12 @@ public partial class ProviderDialog : ComponentBase, ISecretId
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public Host DataHost { get; set; } = Host.NONE;
|
public Host DataHost { get; set; } = Host.NONE;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The HFInstanceProvider to use, e.g., CEREBRAS.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public HFInferenceProvider HFInferenceProviderId { get; set; } = HFInferenceProvider.NONE;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Is this provider self-hosted?
|
/// Is this provider self-hosted?
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -122,10 +129,16 @@ public partial class ProviderDialog : ComponentBase, ISecretId
|
|||||||
Id = this.DataId,
|
Id = this.DataId,
|
||||||
InstanceName = this.DataInstanceName,
|
InstanceName = this.DataInstanceName,
|
||||||
UsedLLMProvider = this.DataLLMProvider,
|
UsedLLMProvider = this.DataLLMProvider,
|
||||||
Model = this.DataLLMProvider is LLMProviders.FIREWORKS ? new Model(this.dataManuallyModel, null) : this.DataModel,
|
Model = this.DataLLMProvider switch
|
||||||
|
{
|
||||||
|
LLMProviders.FIREWORKS => new Model(this.dataManuallyModel, null),
|
||||||
|
LLMProviders.HUGGINGFACE => new Model(this.dataManuallyModel, null),
|
||||||
|
_ => this.DataModel
|
||||||
|
},
|
||||||
IsSelfHosted = this.DataLLMProvider is LLMProviders.SELF_HOSTED,
|
IsSelfHosted = this.DataLLMProvider is LLMProviders.SELF_HOSTED,
|
||||||
Hostname = cleanedHostname.EndsWith('/') ? cleanedHostname[..^1] : cleanedHostname,
|
Hostname = cleanedHostname.EndsWith('/') ? cleanedHostname[..^1] : cleanedHostname,
|
||||||
Host = this.DataHost,
|
Host = this.DataHost,
|
||||||
|
HFInferenceProvider = this.HFInferenceProviderId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,8 +159,8 @@ public partial class ProviderDialog : ComponentBase, ISecretId
|
|||||||
{
|
{
|
||||||
this.dataEditingPreviousInstanceName = this.DataInstanceName.ToLowerInvariant();
|
this.dataEditingPreviousInstanceName = this.DataInstanceName.ToLowerInvariant();
|
||||||
|
|
||||||
// When using Fireworks, we must copy the model name:
|
// When using Fireworks or Hugging Face, we must copy the model name:
|
||||||
if (this.DataLLMProvider is LLMProviders.FIREWORKS)
|
if (this.DataLLMProvider is LLMProviders.FIREWORKS or LLMProviders.HUGGINGFACE)
|
||||||
this.dataManuallyModel = this.DataModel.Id;
|
this.dataManuallyModel = this.DataModel.Id;
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -230,7 +243,7 @@ public partial class ProviderDialog : ComponentBase, ISecretId
|
|||||||
|
|
||||||
private string? ValidateManuallyModel(string manuallyModel)
|
private string? ValidateManuallyModel(string manuallyModel)
|
||||||
{
|
{
|
||||||
if (this.DataLLMProvider is LLMProviders.FIREWORKS && string.IsNullOrWhiteSpace(manuallyModel))
|
if ((this.DataLLMProvider is LLMProviders.FIREWORKS or LLMProviders.HUGGINGFACE) && string.IsNullOrWhiteSpace(manuallyModel))
|
||||||
return "Please enter a model name.";
|
return "Please enter a model name.";
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
<ConfigurationText OptionDescription="Preselect another agenda language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectedOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Agenda.PreselectedOtherLanguage = updatedText)"/>
|
<ConfigurationText OptionDescription="Preselect another agenda language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectedOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Agenda.PreselectedOtherLanguage = updatedText)"/>
|
||||||
}
|
}
|
||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Agenda.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Agenda.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.AGENDA_ASSISTANT" Data="@this.AvailableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.AGENDA_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectedProvider = selectedValue)"/>
|
||||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
}
|
}
|
||||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.BiasOfTheDay.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.BiasOfTheDay.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.BIAS_DAY_ASSISTANT" Data="@this.AvailableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.BIAS_DAY_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</MudField>
|
</MudField>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
|
using AIStudio.Tools.Services;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
namespace AIStudio.Dialogs.Settings;
|
namespace AIStudio.Dialogs.Settings;
|
||||||
|
|
||||||
public abstract class SettingsDialogBase : ComponentBase
|
public abstract class SettingsDialogBase : ComponentBase, IMessageBusReceiver, IDisposable
|
||||||
{
|
{
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
protected IMudDialogInstance MudDialog { get; set; } = null!;
|
protected IMudDialogInstance MudDialog { get; set; } = null!;
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public List<ConfigurationSelectData<string>> AvailableLLMProviders { get; set; } = new();
|
|
||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
protected SettingsManager SettingsManager { get; init; } = null!;
|
protected SettingsManager SettingsManager { get; init; } = null!;
|
||||||
|
|
||||||
@@ -23,14 +21,24 @@ public abstract class SettingsDialogBase : ComponentBase
|
|||||||
[Inject]
|
[Inject]
|
||||||
protected MessageBus MessageBus { get; init; } = null!;
|
protected MessageBus MessageBus { get; init; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
protected RustService RustService { get; init; } = null!;
|
||||||
|
|
||||||
|
protected readonly List<ConfigurationSelectData<string>> availableLLMProviders = new();
|
||||||
|
protected readonly List<ConfigurationSelectData<string>> availableEmbeddingProviders = new();
|
||||||
|
|
||||||
#region Overrides of ComponentBase
|
#region Overrides of ComponentBase
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void OnInitialized()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
// Register this component with the message bus:
|
||||||
|
this.MessageBus.RegisterComponent(this);
|
||||||
|
this.MessageBus.ApplyFilters(this, [], [ Event.CONFIGURATION_CHANGED ]);
|
||||||
|
|
||||||
this.UpdateProviders();
|
this.UpdateProviders();
|
||||||
base.OnInitialized();
|
this.UpdateEmbeddingProviders();
|
||||||
|
await base.OnInitializedAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -40,8 +48,47 @@ public abstract class SettingsDialogBase : ComponentBase
|
|||||||
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
|
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
|
||||||
private void UpdateProviders()
|
private void UpdateProviders()
|
||||||
{
|
{
|
||||||
this.AvailableLLMProviders.Clear();
|
this.availableLLMProviders.Clear();
|
||||||
foreach (var provider in this.SettingsManager.ConfigurationData.Providers)
|
foreach (var provider in this.SettingsManager.ConfigurationData.Providers)
|
||||||
this.AvailableLLMProviders.Add(new (provider.InstanceName, provider.Id));
|
this.availableLLMProviders.Add(new (provider.InstanceName, provider.Id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateEmbeddingProviders()
|
||||||
|
{
|
||||||
|
this.availableEmbeddingProviders.Clear();
|
||||||
|
foreach (var provider in this.SettingsManager.ConfigurationData.EmbeddingProviders)
|
||||||
|
this.availableEmbeddingProviders.Add(new (provider.Name, provider.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Implementation of IMessageBusReceiver
|
||||||
|
|
||||||
|
public string ComponentName => nameof(Settings);
|
||||||
|
|
||||||
|
public Task ProcessMessage<TMsg>(ComponentBase? sendingComponent, Event triggeredEvent, TMsg? data)
|
||||||
|
{
|
||||||
|
switch (triggeredEvent)
|
||||||
|
{
|
||||||
|
case Event.CONFIGURATION_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
|
||||||
|
|
||||||
|
#region Implementation of IDisposable
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
this.MessageBus.Unregister(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
<ConfigurationText OptionDescription="Preselect another programming language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" Icon="@Icons.Material.Filled.Code" Text="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectedOtherProgrammingLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Coding.PreselectedOtherProgrammingLanguage = updatedText)"/>
|
<ConfigurationText OptionDescription="Preselect another programming language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" Icon="@Icons.Material.Filled.Code" Text="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectedOtherProgrammingLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Coding.PreselectedOtherProgrammingLanguage = updatedText)"/>
|
||||||
}
|
}
|
||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Coding.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Coding.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Coding.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Coding.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.CODING_ASSISTANT" Data="@this.AvailableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Coding.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.CODING_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Coding.PreselectedProvider = selectedValue)"/>
|
||||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Coding.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Coding.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
+19
-12
@@ -1,13 +1,15 @@
|
|||||||
@using AIStudio.Settings.DataModel
|
@using AIStudio.Settings.DataModel
|
||||||
@inherits SettingsPanelBase
|
@inherits SettingsDialogBase
|
||||||
|
|
||||||
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
|
<MudDialog>
|
||||||
{
|
<TitleContent>
|
||||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.IntegrationInstructions" HeaderText="Configure Data Sources">
|
|
||||||
<PreviewPrototype/>
|
<PreviewPrototype/>
|
||||||
<MudText Typo="Typo.h4" Class="mb-3">
|
<MudText Typo="Typo.h6" Class="d-flex align-center">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.IntegrationInstructions" Class="mr-2"/>
|
||||||
Configured Data Sources
|
Configured Data Sources
|
||||||
</MudText>
|
</MudText>
|
||||||
|
</TitleContent>
|
||||||
|
<DialogContent>
|
||||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||||
You might configure different data sources. A data source can include one file, all files
|
You might configure different data sources. A data source can include one file, all files
|
||||||
in a directory, or data from your company. Later, you can incorporate these data sources
|
in a directory, or data from your company. Later, you can incorporate these data sources
|
||||||
@@ -27,7 +29,7 @@
|
|||||||
<MudTh>Name</MudTh>
|
<MudTh>Name</MudTh>
|
||||||
<MudTh>Type</MudTh>
|
<MudTh>Type</MudTh>
|
||||||
<MudTh>Embedding</MudTh>
|
<MudTh>Embedding</MudTh>
|
||||||
<MudTh Style="text-align: left;">Actions</MudTh>
|
<MudTh>Actions</MudTh>
|
||||||
</HeaderContent>
|
</HeaderContent>
|
||||||
<RowTemplate>
|
<RowTemplate>
|
||||||
<MudTd>@context.Num</MudTd>
|
<MudTd>@context.Num</MudTd>
|
||||||
@@ -35,14 +37,16 @@
|
|||||||
<MudTd>@context.Type.GetDisplayName()</MudTd>
|
<MudTd>@context.Type.GetDisplayName()</MudTd>
|
||||||
<MudTd>@this.GetEmbeddingName(context)</MudTd>
|
<MudTd>@this.GetEmbeddingName(context)</MudTd>
|
||||||
|
|
||||||
<MudTd Style="text-align: left;">
|
<MudTd>
|
||||||
<MudIconButton Variant="Variant.Filled" Color="Color.Info" Icon="@Icons.Material.Filled.Info" Class="ma-2" OnClick="() => this.ShowInformation(context)"/>
|
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" Class="ma-2" OnClick="() => this.EditDataSource(context)">
|
<MudIconButton Variant="Variant.Filled" Color="Color.Info" Icon="@Icons.Material.Filled.Info" OnClick="() => this.ShowInformation(context)"/>
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" OnClick="() => this.EditDataSource(context)">
|
||||||
Edit
|
Edit
|
||||||
</MudButton>
|
</MudButton>
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" Class="ma-2" OnClick="() => this.DeleteDataSource(context)">
|
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteDataSource(context)">
|
||||||
Delete
|
Delete
|
||||||
</MudButton>
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
</MudTd>
|
</MudTd>
|
||||||
</RowTemplate>
|
</RowTemplate>
|
||||||
</MudTable>
|
</MudTable>
|
||||||
@@ -57,5 +61,8 @@
|
|||||||
<MudMenuItem OnClick="() => this.AddDataSource(DataSourceType.LOCAL_DIRECTORY)">Local Directory</MudMenuItem>
|
<MudMenuItem OnClick="() => this.AddDataSource(DataSourceType.LOCAL_DIRECTORY)">Local Directory</MudMenuItem>
|
||||||
<MudMenuItem OnClick="() => this.AddDataSource(DataSourceType.LOCAL_FILE)">Local File</MudMenuItem>
|
<MudMenuItem OnClick="() => this.AddDataSource(DataSourceType.LOCAL_FILE)">Local File</MudMenuItem>
|
||||||
</MudMenu>
|
</MudMenu>
|
||||||
</ExpansionPanel>
|
</DialogContent>
|
||||||
}
|
<DialogActions>
|
||||||
|
<MudButton OnClick="@this.Close" Variant="Variant.Filled">Close</MudButton>
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
+6
-42
@@ -1,35 +1,11 @@
|
|||||||
using AIStudio.Dialogs;
|
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
using AIStudio.Settings.DataModel;
|
using AIStudio.Settings.DataModel;
|
||||||
using AIStudio.Tools.ERIClient.DataModel;
|
using AIStudio.Tools.ERIClient.DataModel;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
namespace AIStudio.Dialogs.Settings;
|
||||||
|
|
||||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
public partial class SettingsDialogDataSources : SettingsDialogBase
|
||||||
|
|
||||||
namespace AIStudio.Components.Settings;
|
|
||||||
|
|
||||||
public partial class SettingsPanelDataSources : SettingsPanelBase
|
|
||||||
{
|
{
|
||||||
[Parameter]
|
|
||||||
public List<ConfigurationSelectData<string>> AvailableDataSources { get; set; } = new();
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public EventCallback<List<ConfigurationSelectData<string>>> AvailableDataSourcesChanged { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public Func<IReadOnlyList<ConfigurationSelectData<string>>> AvailableEmbeddingsFunc { get; set; } = () => [];
|
|
||||||
|
|
||||||
#region Overrides of ComponentBase
|
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
|
||||||
{
|
|
||||||
await this.UpdateDataSources();
|
|
||||||
await base.OnInitializedAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private string GetEmbeddingName(IDataSource dataSource)
|
private string GetEmbeddingName(IDataSource dataSource)
|
||||||
{
|
{
|
||||||
if(dataSource is IInternalDataSource internalDataSource)
|
if(dataSource is IInternalDataSource internalDataSource)
|
||||||
@@ -56,7 +32,7 @@ public partial class SettingsPanelDataSources : SettingsPanelBase
|
|||||||
var localFileDialogParameters = new DialogParameters<DataSourceLocalFileDialog>
|
var localFileDialogParameters = new DialogParameters<DataSourceLocalFileDialog>
|
||||||
{
|
{
|
||||||
{ x => x.IsEditing, false },
|
{ x => x.IsEditing, false },
|
||||||
{ x => x.AvailableEmbeddings, this.AvailableEmbeddingsFunc() }
|
{ x => x.AvailableEmbeddings, this.availableEmbeddingProviders }
|
||||||
};
|
};
|
||||||
|
|
||||||
var localFileDialogReference = await this.DialogService.ShowAsync<DataSourceLocalFileDialog>("Add Local File as Data Source", localFileDialogParameters, DialogOptions.FULLSCREEN);
|
var localFileDialogReference = await this.DialogService.ShowAsync<DataSourceLocalFileDialog>("Add Local File as Data Source", localFileDialogParameters, DialogOptions.FULLSCREEN);
|
||||||
@@ -73,7 +49,7 @@ public partial class SettingsPanelDataSources : SettingsPanelBase
|
|||||||
var localDirectoryDialogParameters = new DialogParameters<DataSourceLocalDirectoryDialog>
|
var localDirectoryDialogParameters = new DialogParameters<DataSourceLocalDirectoryDialog>
|
||||||
{
|
{
|
||||||
{ x => x.IsEditing, false },
|
{ x => x.IsEditing, false },
|
||||||
{ x => x.AvailableEmbeddings, this.AvailableEmbeddingsFunc() }
|
{ x => x.AvailableEmbeddings, this.availableEmbeddingProviders }
|
||||||
};
|
};
|
||||||
|
|
||||||
var localDirectoryDialogReference = await this.DialogService.ShowAsync<DataSourceLocalDirectoryDialog>("Add Local Directory as Data Source", localDirectoryDialogParameters, DialogOptions.FULLSCREEN);
|
var localDirectoryDialogReference = await this.DialogService.ShowAsync<DataSourceLocalDirectoryDialog>("Add Local Directory as Data Source", localDirectoryDialogParameters, DialogOptions.FULLSCREEN);
|
||||||
@@ -107,7 +83,6 @@ public partial class SettingsPanelDataSources : SettingsPanelBase
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this.SettingsManager.ConfigurationData.DataSources.Add(addedDataSource);
|
this.SettingsManager.ConfigurationData.DataSources.Add(addedDataSource);
|
||||||
await this.UpdateDataSources();
|
|
||||||
await this.SettingsManager.StoreSettings();
|
await this.SettingsManager.StoreSettings();
|
||||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||||
}
|
}
|
||||||
@@ -122,7 +97,7 @@ public partial class SettingsPanelDataSources : SettingsPanelBase
|
|||||||
{
|
{
|
||||||
{ x => x.IsEditing, true },
|
{ x => x.IsEditing, true },
|
||||||
{ x => x.DataSource, localFile },
|
{ x => x.DataSource, localFile },
|
||||||
{ x => x.AvailableEmbeddings, this.AvailableEmbeddingsFunc() }
|
{ x => x.AvailableEmbeddings, this.availableEmbeddingProviders }
|
||||||
};
|
};
|
||||||
|
|
||||||
var localFileDialogReference = await this.DialogService.ShowAsync<DataSourceLocalFileDialog>("Edit Local File Data Source", localFileDialogParameters, DialogOptions.FULLSCREEN);
|
var localFileDialogReference = await this.DialogService.ShowAsync<DataSourceLocalFileDialog>("Edit Local File Data Source", localFileDialogParameters, DialogOptions.FULLSCREEN);
|
||||||
@@ -138,7 +113,7 @@ public partial class SettingsPanelDataSources : SettingsPanelBase
|
|||||||
{
|
{
|
||||||
{ x => x.IsEditing, true },
|
{ x => x.IsEditing, true },
|
||||||
{ x => x.DataSource, localDirectory },
|
{ x => x.DataSource, localDirectory },
|
||||||
{ x => x.AvailableEmbeddings, this.AvailableEmbeddingsFunc() }
|
{ x => x.AvailableEmbeddings, this.availableEmbeddingProviders }
|
||||||
};
|
};
|
||||||
|
|
||||||
var localDirectoryDialogReference = await this.DialogService.ShowAsync<DataSourceLocalDirectoryDialog>("Edit Local Directory Data Source", localDirectoryDialogParameters, DialogOptions.FULLSCREEN);
|
var localDirectoryDialogReference = await this.DialogService.ShowAsync<DataSourceLocalDirectoryDialog>("Edit Local Directory Data Source", localDirectoryDialogParameters, DialogOptions.FULLSCREEN);
|
||||||
@@ -170,7 +145,6 @@ public partial class SettingsPanelDataSources : SettingsPanelBase
|
|||||||
|
|
||||||
this.SettingsManager.ConfigurationData.DataSources[this.SettingsManager.ConfigurationData.DataSources.IndexOf(dataSource)] = editedDataSource;
|
this.SettingsManager.ConfigurationData.DataSources[this.SettingsManager.ConfigurationData.DataSources.IndexOf(dataSource)] = editedDataSource;
|
||||||
|
|
||||||
await this.UpdateDataSources();
|
|
||||||
await this.SettingsManager.StoreSettings();
|
await this.SettingsManager.StoreSettings();
|
||||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||||
}
|
}
|
||||||
@@ -210,7 +184,6 @@ public partial class SettingsPanelDataSources : SettingsPanelBase
|
|||||||
{
|
{
|
||||||
this.SettingsManager.ConfigurationData.DataSources.Remove(dataSource);
|
this.SettingsManager.ConfigurationData.DataSources.Remove(dataSource);
|
||||||
await this.SettingsManager.StoreSettings();
|
await this.SettingsManager.StoreSettings();
|
||||||
await this.UpdateDataSources();
|
|
||||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,13 +220,4 @@ public partial class SettingsPanelDataSources : SettingsPanelBase
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateDataSources()
|
|
||||||
{
|
|
||||||
this.AvailableDataSources.Clear();
|
|
||||||
foreach (var dataSource in this.SettingsManager.ConfigurationData.DataSources)
|
|
||||||
this.AvailableDataSources.Add(new (dataSource.Name, dataSource.Id));
|
|
||||||
|
|
||||||
await this.AvailableDataSourcesChanged.InvokeAsync(this.AvailableDataSources);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -3,8 +3,9 @@
|
|||||||
|
|
||||||
<MudDialog>
|
<MudDialog>
|
||||||
<TitleContent>
|
<TitleContent>
|
||||||
|
<PreviewPrototype/>
|
||||||
<MudText Typo="Typo.h6" Class="d-flex align-center">
|
<MudText Typo="Typo.h6" Class="d-flex align-center">
|
||||||
<MudIcon Icon="@Icons.Material.Filled.PrivateConnectivity" Class="mr-2" />
|
<MudIcon Icon="@Icons.Material.Filled.PrivateConnectivity" Class="mr-2"/>
|
||||||
Assistant: ERI Server Options
|
Assistant: ERI Server Options
|
||||||
</MudText>
|
</MudText>
|
||||||
</TitleContent>
|
</TitleContent>
|
||||||
@@ -18,10 +19,6 @@
|
|||||||
Most ERI server options can be customized and saved directly in the ERI server assistant.
|
Most ERI server options can be customized and saved directly in the ERI server assistant.
|
||||||
For this, the ERI server assistant has an auto-save function.
|
For this, the ERI server assistant has an auto-save function.
|
||||||
</MudText>
|
</MudText>
|
||||||
|
|
||||||
<MudButton Size="Size.Large" Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.PrivateConnectivity" Color="Color.Default" Href="@Routes.ASSISTANT_ERI">
|
|
||||||
Switch to ERI server assistant
|
|
||||||
</MudButton>
|
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<ConfigurationText OptionDescription="Preselect another target language" Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedOtherLanguage = updatedText)"/>
|
<ConfigurationText OptionDescription="Preselect another target language" Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedOtherLanguage = updatedText)"/>
|
||||||
}
|
}
|
||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.GrammarSpelling.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.GrammarSpelling.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.GRAMMAR_SPELLING_ASSISTANT" Data="@this.AvailableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.GRAMMAR_SPELLING_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<ConfigurationOption OptionDescription="Preselect icon options?" LabelOn="Icon options are preselected" LabelOff="No icon options are preselected" State="@(() => this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect the icon options. This is might be useful when you prefer a specific icon source or LLM model."/>
|
<ConfigurationOption OptionDescription="Preselect icon options?" LabelOn="Icon options are preselected" LabelOff="No icon options are preselected" State="@(() => this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect the icon options. This is might be useful when you prefer a specific icon source or LLM model."/>
|
||||||
<ConfigurationSelect OptionDescription="Preselect the icon source" Disabled="@(() => !this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.IconFinder.PreselectedSource)" Data="@ConfigurationSelectDataFactory.GetIconSourcesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.IconFinder.PreselectedSource = selectedValue)" OptionHelp="Which icon source should be preselected?"/>
|
<ConfigurationSelect OptionDescription="Preselect the icon source" Disabled="@(() => !this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.IconFinder.PreselectedSource)" Data="@ConfigurationSelectDataFactory.GetIconSourcesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.IconFinder.PreselectedSource = selectedValue)" OptionHelp="Which icon source should be preselected?"/>
|
||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.IconFinder.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.IconFinder.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.IconFinder.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.IconFinder.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.ICON_FINDER_ASSISTANT" Data="@this.AvailableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.IconFinder.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.IconFinder.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.ICON_FINDER_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.IconFinder.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.IconFinder.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
<ConfigurationText OptionDescription="Preselect another target language" Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.JobPostings.PreselectOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.JobPostings.PreselectOtherLanguage = updatedText)"/>
|
<ConfigurationText OptionDescription="Preselect another target language" Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.JobPostings.PreselectOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.JobPostings.PreselectOtherLanguage = updatedText)"/>
|
||||||
}
|
}
|
||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.JobPostings.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.JobPostings.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.JobPostings.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.JobPostings.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.JOB_POSTING_ASSISTANT" Data="@this.AvailableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.JobPostings.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.JobPostings.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.JOB_POSTING_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.JobPostings.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.JobPostings.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<ConfigurationOption OptionDescription="Preselect the web content reader?" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions || this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader)" LabelOn="Web content reader is preselected" LabelOff="Web content reader is not preselected" State="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectWebContentReader)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.LegalCheck.PreselectWebContentReader = updatedState)" OptionHelp="When enabled, the web content reader is preselected. This is might be useful when you prefer to load legal content from the web very often."/>
|
<ConfigurationOption OptionDescription="Preselect the web content reader?" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions || this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader)" LabelOn="Web content reader is preselected" LabelOff="Web content reader is not preselected" State="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectWebContentReader)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.LegalCheck.PreselectWebContentReader = updatedState)" OptionHelp="When enabled, the web content reader is preselected. This is might be useful when you prefer to load legal content from the web very often."/>
|
||||||
<ConfigurationOption OptionDescription="Preselect the content cleaner agent?" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions || this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader)" LabelOn="Content cleaner agent is preselected" LabelOff="Content cleaner agent is not preselected" State="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectContentCleanerAgent)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.LegalCheck.PreselectContentCleanerAgent = updatedState)" OptionHelp="When enabled, the content cleaner agent is preselected. This is might be useful when you prefer to clean up the legal content before translating it."/>
|
<ConfigurationOption OptionDescription="Preselect the content cleaner agent?" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions || this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader)" LabelOn="Content cleaner agent is preselected" LabelOff="Content cleaner agent is not preselected" State="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectContentCleanerAgent)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.LegalCheck.PreselectContentCleanerAgent = updatedState)" OptionHelp="When enabled, the content cleaner agent is preselected. This is might be useful when you prefer to clean up the legal content before translating it."/>
|
||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.LegalCheck.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LegalCheck.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.LegalCheck.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LegalCheck.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.LEGAL_CHECK_ASSISTANT" Data="@this.AvailableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.LEGAL_CHECK_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProvider = selectedValue)"/>
|
||||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
}
|
}
|
||||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.MyTasks.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.MyTasks.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.MyTasks.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.MyTasks.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.MY_TASKS_ASSISTANT" Data="@this.AvailableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.MY_TASKS_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<ConfigurationSelect OptionDescription="Preselect a writing style" Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedWritingStyle)" Data="@ConfigurationSelectDataFactory.GetWritingStyles4RewriteData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedWritingStyle = selectedValue)" OptionHelp="Which writing style should be preselected?"/>
|
<ConfigurationSelect OptionDescription="Preselect a writing style" Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedWritingStyle)" Data="@ConfigurationSelectDataFactory.GetWritingStyles4RewriteData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedWritingStyle = selectedValue)" OptionHelp="Which writing style should be preselected?"/>
|
||||||
<ConfigurationSelect OptionDescription="Preselect a sentence structure" Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedSentenceStructure)" Data="@ConfigurationSelectDataFactory.GetSentenceStructureData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedSentenceStructure = selectedValue)" OptionHelp="Which voice should be preselected for the sentence structure?"/>
|
<ConfigurationSelect OptionDescription="Preselect a sentence structure" Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedSentenceStructure)" Data="@ConfigurationSelectDataFactory.GetSentenceStructureData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedSentenceStructure = selectedValue)" OptionHelp="Which voice should be preselected for the sentence structure?"/>
|
||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.REWRITE_ASSISTANT" Data="@this.AvailableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.REWRITE_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<ConfigurationText OptionDescription="Preselect another language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Synonyms.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.Synonyms.PreselectedOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Synonyms.PreselectedOtherLanguage = updatedText)"/>
|
<ConfigurationText OptionDescription="Preselect another language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Synonyms.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.Synonyms.PreselectedOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Synonyms.PreselectedOtherLanguage = updatedText)"/>
|
||||||
}
|
}
|
||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.Synonyms.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Synonyms.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Synonyms.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.Synonyms.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Synonyms.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Synonyms.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.SYNONYMS_ASSISTANT" Data="@this.AvailableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Synonyms.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Synonyms.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Synonyms.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.SYNONYMS_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Synonyms.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Synonyms.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Synonyms.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<ConfigurationText OptionDescription="Preselect your expertise" Disabled="@(() => !this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions)" Icon="@Icons.Material.Filled.Person" Text="@(() => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedExpertInField)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedExpertInField = updatedText)"/>
|
<ConfigurationText OptionDescription="Preselect your expertise" Disabled="@(() => !this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions)" Icon="@Icons.Material.Filled.Person" Text="@(() => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedExpertInField)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedExpertInField = updatedText)"/>
|
||||||
}
|
}
|
||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.TextSummarizer.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.TextSummarizer.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.TextSummarizer.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.TextSummarizer.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.TEXT_SUMMARIZER_ASSISTANT" Data="@this.AvailableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.TEXT_SUMMARIZER_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<ConfigurationText OptionDescription="Preselect another target language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Translation.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.Translation.PreselectOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Translation.PreselectOtherLanguage = updatedText)"/>
|
<ConfigurationText OptionDescription="Preselect another target language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Translation.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.Translation.PreselectOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Translation.PreselectOtherLanguage = updatedText)"/>
|
||||||
}
|
}
|
||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.Translation.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Translation.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Translation.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.Translation.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Translation.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Translation.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.TRANSLATION_ASSISTANT" Data="@this.AvailableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Translation.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Translation.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Translation.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.TRANSLATION_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Translation.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Translation.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Translation.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
}
|
}
|
||||||
<ConfigurationSelect OptionDescription="Preselect a writing style" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.PreselectedWritingStyle)" Data="@ConfigurationSelectDataFactory.GetWritingStyles4EMailData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.PreselectedWritingStyle = selectedValue)" OptionHelp="Which writing style should be preselected?"/>
|
<ConfigurationSelect OptionDescription="Preselect a writing style" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.PreselectedWritingStyle)" Data="@ConfigurationSelectDataFactory.GetWritingStyles4EMailData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.PreselectedWritingStyle = selectedValue)" OptionHelp="Which writing style should be preselected?"/>
|
||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.EMAIL_ASSISTANT" Data="@this.AvailableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.EMAIL_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.PreselectedProvider = selectedValue)"/>
|
||||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
|
using AIStudio.Tools.Metadata;
|
||||||
using AIStudio.Tools.Rust;
|
using AIStudio.Tools.Rust;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|||||||
@@ -45,24 +45,9 @@
|
|||||||
|
|
||||||
<MudMainContent Class="mud-height-full pt-1" Style="@this.PaddingLeft">
|
<MudMainContent Class="mud-height-full pt-1" Style="@this.PaddingLeft">
|
||||||
<MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="mud-height-full" Style="margin-left: 5em; width: calc(100% - 5em);">
|
<MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="mud-height-full" Style="margin-left: 5em; width: calc(100% - 5em);">
|
||||||
@if (!this.performingUpdate && this.IsUpdateAlertVisible)
|
|
||||||
{
|
|
||||||
<MudAlert NoIcon="@true" Severity="Severity.Info" Variant="Variant.Filled" ShowCloseIcon="@true" Dense="@true" CloseIconClicked="() => this.DismissUpdate()" Class="mt-2 mb-2">
|
|
||||||
<div class="d-inline-flex align-center">
|
|
||||||
<MudIcon Icon="@Icons.Material.Filled.Update" Size="Size.Medium" Class="mr-3"/>
|
|
||||||
An update to version @this.updateToVersion is available.
|
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Dark" Size="Size.Small" Class="ml-3" OnClick="() => this.ShowUpdateDialog()">
|
|
||||||
Show details
|
|
||||||
</MudButton>
|
|
||||||
</div>
|
|
||||||
</MudAlert>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (!this.performingUpdate)
|
@if (!this.performingUpdate)
|
||||||
{
|
{
|
||||||
<CascadingValue Value="@this" IsFixed="true">
|
|
||||||
@this.Body
|
@this.Body
|
||||||
</CascadingValue>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<MudOverlay Visible="@this.performingUpdate" DarkBackground="@true" LockScroll="@true">
|
<MudOverlay Visible="@this.performingUpdate" DarkBackground="@true" LockScroll="@true">
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using AIStudio.Dialogs;
|
using AIStudio.Dialogs;
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
using AIStudio.Settings.DataModel;
|
using AIStudio.Settings.DataModel;
|
||||||
|
using AIStudio.Tools.PluginSystem;
|
||||||
using AIStudio.Tools.Rust;
|
using AIStudio.Tools.Rust;
|
||||||
using AIStudio.Tools.Services;
|
using AIStudio.Tools.Services;
|
||||||
|
|
||||||
@@ -37,8 +38,6 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
|
|||||||
[Inject]
|
[Inject]
|
||||||
private MudTheme ColorTheme { get; init; } = null!;
|
private MudTheme ColorTheme { get; init; } = null!;
|
||||||
|
|
||||||
public string AdditionalHeight { get; private set; } = "0em";
|
|
||||||
|
|
||||||
private string PaddingLeft => this.navBarOpen ? $"padding-left: {NAVBAR_EXPANDED_WIDTH_INT - NAVBAR_COLLAPSED_WIDTH_INT}em;" : "padding-left: 0em;";
|
private string PaddingLeft => this.navBarOpen ? $"padding-left: {NAVBAR_EXPANDED_WIDTH_INT - NAVBAR_COLLAPSED_WIDTH_INT}em;" : "padding-left: 0em;";
|
||||||
|
|
||||||
private const int NAVBAR_COLLAPSED_WIDTH_INT = 4;
|
private const int NAVBAR_COLLAPSED_WIDTH_INT = 4;
|
||||||
@@ -47,10 +46,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
|
|||||||
private static readonly string NAVBAR_EXPANDED_WIDTH = $"{NAVBAR_EXPANDED_WIDTH_INT}em";
|
private static readonly string NAVBAR_EXPANDED_WIDTH = $"{NAVBAR_EXPANDED_WIDTH_INT}em";
|
||||||
|
|
||||||
private bool navBarOpen;
|
private bool navBarOpen;
|
||||||
private bool isUpdateAvailable;
|
|
||||||
private bool performingUpdate;
|
private bool performingUpdate;
|
||||||
private bool userDismissedUpdate;
|
|
||||||
private string updateToVersion = string.Empty;
|
|
||||||
private UpdateResponse? currentUpdateResponse;
|
private UpdateResponse? currentUpdateResponse;
|
||||||
private MudThemeProvider themeProvider = null!;
|
private MudThemeProvider themeProvider = null!;
|
||||||
private bool useDarkMode;
|
private bool useDarkMode;
|
||||||
@@ -78,12 +74,22 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
|
|||||||
SettingsManager.DataDirectory = dataDir;
|
SettingsManager.DataDirectory = dataDir;
|
||||||
Directory.CreateDirectory(SettingsManager.DataDirectory);
|
Directory.CreateDirectory(SettingsManager.DataDirectory);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Read the user language from Rust:
|
||||||
|
//
|
||||||
|
var userLanguage = await this.RustService.ReadUserLanguage();
|
||||||
|
this.Logger.LogInformation($"The user language is: '{userLanguage}'");
|
||||||
|
|
||||||
// Ensure that all settings are loaded:
|
// Ensure that all settings are loaded:
|
||||||
await this.SettingsManager.LoadSettings();
|
await this.SettingsManager.LoadSettings();
|
||||||
|
|
||||||
// Register this component with the message bus:
|
// Register this component with the message bus:
|
||||||
this.MessageBus.RegisterComponent(this);
|
this.MessageBus.RegisterComponent(this);
|
||||||
this.MessageBus.ApplyFilters(this, [], [ Event.UPDATE_AVAILABLE, Event.USER_SEARCH_FOR_UPDATE, Event.CONFIGURATION_CHANGED, Event.COLOR_THEME_CHANGED ]);
|
this.MessageBus.ApplyFilters(this, [],
|
||||||
|
[
|
||||||
|
Event.UPDATE_AVAILABLE, Event.CONFIGURATION_CHANGED, Event.COLOR_THEME_CHANGED, Event.SHOW_ERROR,
|
||||||
|
Event.STARTUP_PLUGIN_SYSTEM, Event.PLUGINS_RELOADED
|
||||||
|
]);
|
||||||
|
|
||||||
// Set the snackbar for the update service:
|
// Set the snackbar for the update service:
|
||||||
UpdateService.SetBlazorDependencies(this.Snackbar);
|
UpdateService.SetBlazorDependencies(this.Snackbar);
|
||||||
@@ -93,6 +99,12 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
|
|||||||
if(this.SettingsManager.ConfigurationData.App.NavigationBehavior is NavBehavior.ALWAYS_EXPAND)
|
if(this.SettingsManager.ConfigurationData.App.NavigationBehavior is NavBehavior.ALWAYS_EXPAND)
|
||||||
this.navBarOpen = true;
|
this.navBarOpen = true;
|
||||||
|
|
||||||
|
// Solve issue https://github.com/MudBlazor/MudBlazor/issues/11133:
|
||||||
|
MudGlobal.TooltipDefaults.Duration = TimeSpan.Zero;
|
||||||
|
|
||||||
|
// Send a message to start the plugin system:
|
||||||
|
await this.MessageBus.SendMessage<bool>(this, Event.STARTUP_PLUGIN_SYSTEM);
|
||||||
|
|
||||||
await this.themeProvider.WatchSystemPreference(this.SystemeThemeChanged);
|
await this.themeProvider.WatchSystemPreference(this.SystemeThemeChanged);
|
||||||
await this.UpdateThemeConfiguration();
|
await this.UpdateThemeConfiguration();
|
||||||
this.LoadNavItems();
|
this.LoadNavItems();
|
||||||
@@ -102,33 +114,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
|
|||||||
|
|
||||||
private void LoadNavItems()
|
private void LoadNavItems()
|
||||||
{
|
{
|
||||||
var palette = this.ColorTheme.GetCurrentPalette(this.SettingsManager);
|
this.navItems = new List<NavBarItem>(this.GetNavItems());
|
||||||
var isWriterModePreviewEnabled = PreviewFeatures.PRE_WRITER_MODE_2024.IsEnabled(this.SettingsManager);
|
|
||||||
if (!isWriterModePreviewEnabled)
|
|
||||||
{
|
|
||||||
this.navItems = new List<NavBarItem>
|
|
||||||
{
|
|
||||||
new("Home", Icons.Material.Filled.Home, palette.DarkLighten, palette.GrayLight, Routes.HOME, true),
|
|
||||||
new("Chat", Icons.Material.Filled.Chat, palette.DarkLighten, palette.GrayLight, Routes.CHAT, false),
|
|
||||||
new("Assistants", Icons.Material.Filled.Apps, palette.DarkLighten, palette.GrayLight, Routes.ASSISTANTS, false),
|
|
||||||
new("Supporters", Icons.Material.Filled.Favorite, palette.Error.Value, "#801a00", Routes.SUPPORTERS, false),
|
|
||||||
new("About", Icons.Material.Filled.Info, palette.DarkLighten, palette.GrayLight, Routes.ABOUT, false),
|
|
||||||
new("Settings", Icons.Material.Filled.Settings, palette.DarkLighten, palette.GrayLight, Routes.SETTINGS, false),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.navItems = new List<NavBarItem>
|
|
||||||
{
|
|
||||||
new("Home", Icons.Material.Filled.Home, palette.DarkLighten, palette.GrayLight, Routes.HOME, true),
|
|
||||||
new("Chat", Icons.Material.Filled.Chat, palette.DarkLighten, palette.GrayLight, Routes.CHAT, false),
|
|
||||||
new("Assistants", Icons.Material.Filled.Apps, palette.DarkLighten, palette.GrayLight, Routes.ASSISTANTS, false),
|
|
||||||
new("Writer", Icons.Material.Filled.Create, palette.DarkLighten, palette.GrayLight, Routes.WRITER, false),
|
|
||||||
new("Supporters", Icons.Material.Filled.Favorite, palette.Error.Value, "#801a00", Routes.SUPPORTERS, false),
|
|
||||||
new("About", Icons.Material.Filled.Info, palette.DarkLighten, palette.GrayLight, Routes.ABOUT, false),
|
|
||||||
new("Settings", Icons.Material.Filled.Settings, palette.DarkLighten, palette.GrayLight, Routes.SETTINGS, false),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -141,19 +127,24 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
|
|||||||
{
|
{
|
||||||
switch (triggeredEvent)
|
switch (triggeredEvent)
|
||||||
{
|
{
|
||||||
case Event.USER_SEARCH_FOR_UPDATE:
|
|
||||||
this.userDismissedUpdate = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Event.UPDATE_AVAILABLE:
|
case Event.UPDATE_AVAILABLE:
|
||||||
if (data is UpdateResponse updateResponse)
|
if (data is UpdateResponse updateResponse)
|
||||||
{
|
{
|
||||||
this.currentUpdateResponse = updateResponse;
|
this.currentUpdateResponse = updateResponse;
|
||||||
this.isUpdateAvailable = updateResponse.UpdateIsAvailable;
|
this.Snackbar.Add($"An update to version {updateResponse.NewVersion} is available.", Severity.Info, config =>
|
||||||
this.updateToVersion = updateResponse.NewVersion;
|
{
|
||||||
|
config.Icon = Icons.Material.Filled.Update;
|
||||||
await this.InvokeAsync(this.StateHasChanged);
|
config.IconSize = Size.Large;
|
||||||
await this.SendMessage<bool>(Event.STATE_HAS_CHANGED);
|
config.HideTransitionDuration = 600;
|
||||||
|
config.VisibleStateDuration = 32_000;
|
||||||
|
config.OnClick = async _ =>
|
||||||
|
{
|
||||||
|
await this.ShowUpdateDialog();
|
||||||
|
};
|
||||||
|
config.Action = "Show details";
|
||||||
|
config.ActionVariant = Variant.Filled;
|
||||||
|
config.ActionColor = Color.Dark;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -172,6 +163,38 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
|
|||||||
case Event.COLOR_THEME_CHANGED:
|
case Event.COLOR_THEME_CHANGED:
|
||||||
this.StateHasChanged();
|
this.StateHasChanged();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Event.SHOW_ERROR:
|
||||||
|
if (data is Error error)
|
||||||
|
error.Show(this.Snackbar);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Event.STARTUP_PLUGIN_SYSTEM:
|
||||||
|
if(PreviewFeatures.PRE_PLUGINS_2025.IsEnabled(this.SettingsManager))
|
||||||
|
{
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
// Set up the plugin system:
|
||||||
|
PluginFactory.Setup();
|
||||||
|
|
||||||
|
// Ensure that all internal plugins are present:
|
||||||
|
await PluginFactory.EnsureInternalPlugins();
|
||||||
|
|
||||||
|
// Load (but not start) all plugins, without waiting for them:
|
||||||
|
var pluginLoadingTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||||
|
await PluginFactory.LoadAll(pluginLoadingTimeout.Token);
|
||||||
|
|
||||||
|
// Set up hot reloading for plugins:
|
||||||
|
PluginFactory.SetUpHotReloading();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Event.PLUGINS_RELOADED:
|
||||||
|
await this.InvokeAsync(this.StateHasChanged);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,23 +205,23 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private async Task DismissUpdate()
|
private IEnumerable<NavBarItem> GetNavItems()
|
||||||
{
|
{
|
||||||
this.userDismissedUpdate = true;
|
var palette = this.ColorTheme.GetCurrentPalette(this.SettingsManager);
|
||||||
this.AdditionalHeight = "0em";
|
|
||||||
|
|
||||||
await this.SendMessage<bool>(Event.STATE_HAS_CHANGED);
|
yield return new("Home", Icons.Material.Filled.Home, palette.DarkLighten, palette.GrayLight, Routes.HOME, true);
|
||||||
}
|
yield return new("Chat", Icons.Material.Filled.Chat, palette.DarkLighten, palette.GrayLight, Routes.CHAT, false);
|
||||||
|
yield return new("Assistants", Icons.Material.Filled.Apps, palette.DarkLighten, palette.GrayLight, Routes.ASSISTANTS, false);
|
||||||
|
|
||||||
private bool IsUpdateAlertVisible
|
if (PreviewFeatures.PRE_WRITER_MODE_2024.IsEnabled(this.SettingsManager))
|
||||||
{
|
yield return new("Writer", Icons.Material.Filled.Create, palette.DarkLighten, palette.GrayLight, Routes.WRITER, false);
|
||||||
get
|
|
||||||
{
|
|
||||||
var state = this.isUpdateAvailable && !this.userDismissedUpdate;
|
|
||||||
this.AdditionalHeight = state ? "3em" : "0em";
|
|
||||||
|
|
||||||
return state;
|
if (PreviewFeatures.PRE_PLUGINS_2025.IsEnabled(this.SettingsManager))
|
||||||
}
|
yield return new("Plugins", Icons.Material.TwoTone.Extension, palette.DarkLighten, palette.GrayLight, Routes.PLUGINS, false);
|
||||||
|
|
||||||
|
yield return new("Supporters", Icons.Material.Filled.Favorite, palette.Error.Value, "#801a00", Routes.SUPPORTERS, false);
|
||||||
|
yield return new("About", Icons.Material.Filled.Info, palette.DarkLighten, palette.GrayLight, Routes.ABOUT, false);
|
||||||
|
yield return new("Settings", Icons.Material.Filled.Settings, palette.DarkLighten, palette.GrayLight, Routes.SETTINGS, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ShowUpdateDialog()
|
private async Task ShowUpdateDialog()
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
namespace AIStudio;
|
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Assembly)]
|
|
||||||
public class MetaDataAttribute(
|
|
||||||
string version,
|
|
||||||
string buildTime,
|
|
||||||
uint buildNum,
|
|
||||||
string dotnetSdkVersion,
|
|
||||||
string dotnetVersion,
|
|
||||||
string rustVersion,
|
|
||||||
string mudBlazorVersion,
|
|
||||||
string tauriVersion,
|
|
||||||
string appCommitHash
|
|
||||||
) : Attribute
|
|
||||||
{
|
|
||||||
public string BuildTime { get; } = buildTime;
|
|
||||||
|
|
||||||
public string Version { get; } = version;
|
|
||||||
|
|
||||||
public uint BuildNum { get; } = buildNum;
|
|
||||||
|
|
||||||
public string DotnetVersion { get; } = dotnetVersion;
|
|
||||||
|
|
||||||
public string DotnetSdkVersion { get; } = dotnetSdkVersion;
|
|
||||||
|
|
||||||
public string RustVersion { get; } = rustVersion;
|
|
||||||
|
|
||||||
public string MudBlazorVersion { get; } = mudBlazorVersion;
|
|
||||||
|
|
||||||
public string TauriVersion { get; } = tauriVersion;
|
|
||||||
|
|
||||||
public string AppCommitHash { get; } = appCommitHash;
|
|
||||||
}
|
|
||||||
@@ -41,20 +41,22 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- Embed all files in wwwroot folder -->
|
|
||||||
<EmbeddedResource Include="wwwroot\**" CopyToOutputDirectory="PreserveNewest" />
|
<EmbeddedResource Include="wwwroot\**" CopyToOutputDirectory="PreserveNewest" />
|
||||||
|
<EmbeddedResource Include="Plugins\**" CopyToOutputDirectory="PreserveNewest" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CodeBeam.MudBlazor.Extensions" Version="8.0.0" />
|
<PackageReference Include="CodeBeam.MudBlazor.Extensions" Version="8.0.0" />
|
||||||
<PackageReference Include="HtmlAgilityPack" Version="1.12.0" />
|
<PackageReference Include="HtmlAgilityPack" Version="1.12.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.3" />
|
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.4" />
|
||||||
<PackageReference Include="MudBlazor" Version="8.3.0" />
|
<PackageReference Include="MudBlazor" Version="8.5.1" />
|
||||||
<PackageReference Include="MudBlazor.Markdown" Version="8.0.0" />
|
<PackageReference Include="MudBlazor.Markdown" Version="8.5.1" />
|
||||||
<PackageReference Include="ReverseMarkdown" Version="4.6.0" />
|
<PackageReference Include="ReverseMarkdown" Version="4.6.0" />
|
||||||
|
<PackageReference Include="LuaCSharp" Version="0.4.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\SharedTools\SharedTools.csproj" />
|
||||||
<ProjectReference Include="..\SourceCodeRules\SourceCodeRules\SourceCodeRules.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
<ProjectReference Include="..\SourceCodeRules\SourceCodeRules\SourceCodeRules.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@@ -76,6 +78,7 @@
|
|||||||
<MetaMudBlazorVersion>$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 6 ])</MetaMudBlazorVersion>
|
<MetaMudBlazorVersion>$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 6 ])</MetaMudBlazorVersion>
|
||||||
<MetaTauriVersion>$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 7 ])</MetaTauriVersion>
|
<MetaTauriVersion>$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 7 ])</MetaTauriVersion>
|
||||||
<MetaAppCommitHash>$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 8 ])</MetaAppCommitHash>
|
<MetaAppCommitHash>$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 8 ])</MetaAppCommitHash>
|
||||||
|
<MetaArchitecture>$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 9 ])</MetaArchitecture>
|
||||||
|
|
||||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||||
|
|
||||||
@@ -85,7 +88,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AssemblyAttribute Include="AIStudio.MetaDataAttribute">
|
<AssemblyAttribute Include="AIStudio.Tools.Metadata.MetaData">
|
||||||
<_Parameter1>$(MetaVersion)</_Parameter1>
|
<_Parameter1>$(MetaVersion)</_Parameter1>
|
||||||
<_Parameter2>$(MetaBuildTime)</_Parameter2>
|
<_Parameter2>$(MetaBuildTime)</_Parameter2>
|
||||||
<_Parameter3>$(MetaBuild)</_Parameter3>
|
<_Parameter3>$(MetaBuild)</_Parameter3>
|
||||||
@@ -97,6 +100,9 @@
|
|||||||
<_Parameter8>$(MetaTauriVersion)</_Parameter8>
|
<_Parameter8>$(MetaTauriVersion)</_Parameter8>
|
||||||
<_Parameter9>$(MetaAppCommitHash)</_Parameter9>
|
<_Parameter9>$(MetaAppCommitHash)</_Parameter9>
|
||||||
</AssemblyAttribute>
|
</AssemblyAttribute>
|
||||||
|
<AssemblyAttribute Include="AIStudio.Tools.Metadata.MetaDataArchitecture">
|
||||||
|
<_Parameter1>$(MetaArchitecture)</_Parameter1>
|
||||||
|
</AssemblyAttribute>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Target>
|
</Target>
|
||||||
|
|||||||
@@ -102,12 +102,19 @@
|
|||||||
<ThirdPartyComponent Name="serde" Developer="Erick Tryzelaar, David Tolnay & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/serde-rs/serde/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/serde-rs/serde" UseCase="Now we have multiple systems, some developed in .NET and others in Rust. The data format JSON is responsible for translating data between both worlds (called data serialization and deserialization). Serde takes on this task in the Rust world. The counterpart in the .NET world is an integral part of .NET and is located in System.Text.Json."/>
|
<ThirdPartyComponent Name="serde" Developer="Erick Tryzelaar, David Tolnay & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/serde-rs/serde/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/serde-rs/serde" UseCase="Now we have multiple systems, some developed in .NET and others in Rust. The data format JSON is responsible for translating data between both worlds (called data serialization and deserialization). Serde takes on this task in the Rust world. The counterpart in the .NET world is an integral part of .NET and is located in System.Text.Json."/>
|
||||||
<ThirdPartyComponent Name="keyring" Developer="Walther Chen, Daniel Brotsky & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/hwchen/keyring-rs/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/hwchen/keyring-rs" UseCase="In order to use any LLM, each user must store their so-called token for each LLM provider. This token must be kept secure, similar to a password. The safest way to do this is offered by operating systems like macOS, Windows, and Linux: They have mechanisms to store such data, if available, on special security hardware. Since this is currently not possible in .NET, we use this Rust library."/>
|
<ThirdPartyComponent Name="keyring" Developer="Walther Chen, Daniel Brotsky & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/hwchen/keyring-rs/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/hwchen/keyring-rs" UseCase="In order to use any LLM, each user must store their so-called token for each LLM provider. This token must be kept secure, similar to a password. The safest way to do this is offered by operating systems like macOS, Windows, and Linux: They have mechanisms to store such data, if available, on special security hardware. Since this is currently not possible in .NET, we use this Rust library."/>
|
||||||
<ThirdPartyComponent Name="arboard" Developer="Artur Kovacs, Avi Weinstock, 1Password & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/1Password/arboard/blob/master/LICENSE-MIT.txt" RepositoryUrl="https://github.com/1Password/arboard" UseCase="To be able to use the responses of the LLM in other apps, we often use the clipboard of the respective operating system. Unfortunately, in .NET there is no solution that works with all operating systems. Therefore, I have opted for this library in Rust. This way, data transfer to other apps works on every system."/>
|
<ThirdPartyComponent Name="arboard" Developer="Artur Kovacs, Avi Weinstock, 1Password & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/1Password/arboard/blob/master/LICENSE-MIT.txt" RepositoryUrl="https://github.com/1Password/arboard" UseCase="To be able to use the responses of the LLM in other apps, we often use the clipboard of the respective operating system. Unfortunately, in .NET there is no solution that works with all operating systems. Therefore, I have opted for this library in Rust. This way, data transfer to other apps works on every system."/>
|
||||||
<ThirdPartyComponent Name="tokio" Developer="Alex Crichton & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/tokio-rs/tokio/blob/master/LICENSE" RepositoryUrl="https://github.com/tokio-rs/tokio" UseCase="Code in the Rust language can be specified as synchronous or asynchronous. Unlike .NET and the C# language, Rust cannot execute asynchronous code by itself. Rust requires support in the form of an executor for this. Tokio is one such executor."/>
|
<ThirdPartyComponent Name="tokio" Developer="Alex Crichton, Carl Lerche, Alice Ryhl, Taiki Endo, Ivan Petkov, Eliza Weisman, Lucio Franco & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/tokio-rs/tokio/blob/master/LICENSE" RepositoryUrl="https://github.com/tokio-rs/tokio" UseCase="Code in the Rust language can be specified as synchronous or asynchronous. Unlike .NET and the C# language, Rust cannot execute asynchronous code by itself. Rust requires support in the form of an executor for this. Tokio is one such executor."/>
|
||||||
|
<ThirdPartyComponent Name="futures" Developer="Alex Crichton, Taiki Endo, Taylor Cramer, Nemo157, Josef Brandl, Aaron Turon & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rust-lang/futures-rs/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/rust-lang/futures-rs" UseCase="This is a library providing the foundations for asynchronous programming in Rust. It includes key trait definitions like Stream, as well as utilities like join!, select!, and various futures combinator methods which enable expressive asynchronous control flow."/>
|
||||||
|
<ThirdPartyComponent Name="async-stream" Developer="Carl Lerche, Taiki Endo & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/tokio-rs/async-stream/blob/master/LICENSE" RepositoryUrl="https://github.com/tokio-rs/async-stream" UseCase="This library is used to create asynchronous streams in Rust. It allows us to work with streams of data that can be produced asynchronously, making it easier to handle events or data that arrive over time. We use this, e.g., to stream arbitrary data from the file system to the embedding system."/>
|
||||||
<ThirdPartyComponent Name="flexi_logger" Developer="emabee & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/emabee/flexi_logger/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/emabee/flexi_logger" UseCase="This Rust library is used to output the app's messages to the terminal. This is helpful during development and troubleshooting. This feature is initially invisible; when the app is started via the terminal, the messages become visible."/>
|
<ThirdPartyComponent Name="flexi_logger" Developer="emabee & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/emabee/flexi_logger/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/emabee/flexi_logger" UseCase="This Rust library is used to output the app's messages to the terminal. This is helpful during development and troubleshooting. This feature is initially invisible; when the app is started via the terminal, the messages become visible."/>
|
||||||
<ThirdPartyComponent Name="rand" Developer="Rust developers & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rust-random/rand/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/rust-random/rand" UseCase="We must generate random numbers, e.g., for securing the interprocess communication between the user interface and the runtime. The rand library is great for this purpose."/>
|
<ThirdPartyComponent Name="rand" Developer="Rust developers & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rust-random/rand/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/rust-random/rand" UseCase="We must generate random numbers, e.g., for securing the interprocess communication between the user interface and the runtime. The rand library is great for this purpose."/>
|
||||||
<ThirdPartyComponent Name="base64" Developer="Marshall Pierce, Alice Maz & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/marshallpierce/rust-base64/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/marshallpierce/rust-base64" UseCase="For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose."/>
|
<ThirdPartyComponent Name="base64" Developer="Marshall Pierce, Alice Maz & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/marshallpierce/rust-base64/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/marshallpierce/rust-base64" UseCase="For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose."/>
|
||||||
<ThirdPartyComponent Name="Rust Crypto" Developer="Artyom Pavlov, Tony Arcieri, Brian Warner, Arthur Gautier, Vlad Filippov, Friedel Ziegelmayer, Nicolas Stalder & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/RustCrypto/traits/blob/master/cipher/LICENSE-MIT" RepositoryUrl="https://github.com/RustCrypto" UseCase="When transferring sensitive data between Rust runtime and .NET app, we encrypt the data. We use some libraries from the Rust Crypto project for this purpose: cipher, aes, cbc, pbkdf2, hmac, and sha2. We are thankful for the great work of the Rust Crypto project."/>
|
<ThirdPartyComponent Name="Rust Crypto" Developer="Artyom Pavlov, Tony Arcieri, Brian Warner, Arthur Gautier, Vlad Filippov, Friedel Ziegelmayer, Nicolas Stalder & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/RustCrypto/traits/blob/master/cipher/LICENSE-MIT" RepositoryUrl="https://github.com/RustCrypto" UseCase="When transferring sensitive data between Rust runtime and .NET app, we encrypt the data. We use some libraries from the Rust Crypto project for this purpose: cipher, aes, cbc, pbkdf2, hmac, and sha2. We are thankful for the great work of the Rust Crypto project."/>
|
||||||
<ThirdPartyComponent Name="rcgen" Developer="RustTLS developers, est31 & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rustls/rcgen/blob/main/LICENSE" RepositoryUrl="https://github.com/rustls/rcgen" UseCase="For the secure communication between the user interface and the runtime, we need to create certificates. This Rust library is great for this purpose."/>
|
<ThirdPartyComponent Name="rcgen" Developer="RustTLS developers, est31 & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rustls/rcgen/blob/main/LICENSE" RepositoryUrl="https://github.com/rustls/rcgen" UseCase="For the secure communication between the user interface and the runtime, we need to create certificates. This Rust library is great for this purpose."/>
|
||||||
|
<ThirdPartyComponent Name="file-format" Developer="Mickaël Malécot & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/mmalecot/file-format/blob/main/LICENSE-MIT" RepositoryUrl="https://github.com/mmalecot/file-format" UseCase="This library is used to determine the file type of a file. This is necessary, e.g., when we want to stream a file."/>
|
||||||
|
<ThirdPartyComponent Name="calamine" Developer="Johann Tuffe, Joel Natividad, Eric Jolibois, Dmitriy & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/tafia/calamine/blob/master/LICENSE-MIT.md" RepositoryUrl="https://github.com/tafia/calamine" UseCase="This library is used to read Excel and OpenDocument spreadsheet files. This is necessary, e.g., for using spreadsheets as a data source for a chat."/>
|
||||||
|
<ThirdPartyComponent Name="pdfium-render" Developer="Alastair Carey, Dorian Rudolph & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/ajrcarey/pdfium-render/blob/master/LICENSE.md" RepositoryUrl="https://github.com/ajrcarey/pdfium-render" UseCase="This library is used to read PDF files. This is necessary, e.g., for using PDFs as a data source for a chat."/>
|
||||||
|
<ThirdPartyComponent Name="sys-locale" Developer="1Password Team, ComplexSpaces & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/1Password/sys-locale/blob/main/LICENSE-MIT" RepositoryUrl="https://github.com/1Password/sys-locale" UseCase="This library is used to determine the language of the operating system. This is necessary to set the language of the user interface."/>
|
||||||
|
<ThirdPartyComponent Name="Lua-CSharp" Developer="Yusuke Nakada & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/nuskey8/Lua-CSharp/blob/main/LICENSE" RepositoryUrl="https://github.com/nuskey8/Lua-CSharp" UseCase="We use Lua as the language for plugins. Lua-CSharp lets Lua scripts communicate with AI Studio and vice versa. Thank you, Yusuke Nakada, for this great library." />
|
||||||
<ThirdPartyComponent Name="HtmlAgilityPack" Developer="ZZZ Projects & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/zzzprojects/html-agility-pack/blob/master/LICENSE" RepositoryUrl="https://github.com/zzzprojects/html-agility-pack" UseCase="We use the HtmlAgilityPack to extract content from the web. This is necessary, e.g., when you provide a URL as input for an assistant."/>
|
<ThirdPartyComponent Name="HtmlAgilityPack" Developer="ZZZ Projects & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/zzzprojects/html-agility-pack/blob/master/LICENSE" RepositoryUrl="https://github.com/zzzprojects/html-agility-pack" UseCase="We use the HtmlAgilityPack to extract content from the web. This is necessary, e.g., when you provide a URL as input for an assistant."/>
|
||||||
<ThirdPartyComponent Name="ReverseMarkdown" Developer="Babu Annamalai & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/mysticmind/reversemarkdown-net/blob/master/LICENSE" RepositoryUrl="https://github.com/mysticmind/reversemarkdown-net" UseCase="This library is used to convert HTML to Markdown. This is necessary, e.g., when you provide a URL as input for an assistant."/>
|
<ThirdPartyComponent Name="ReverseMarkdown" Developer="Babu Annamalai & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/mysticmind/reversemarkdown-net/blob/master/LICENSE" RepositoryUrl="https://github.com/mysticmind/reversemarkdown-net" UseCase="This library is used to convert HTML to Markdown. This is necessary, e.g., when you provide a URL as input for an assistant."/>
|
||||||
<ThirdPartyComponent Name="wikEd diff" Developer="Cacycle & Open Source Community" LicenseName="None (public domain)" LicenseUrl="https://en.wikipedia.org/wiki/User:Cacycle/diff#License" RepositoryUrl="https://en.wikipedia.org/wiki/User:Cacycle/diff" UseCase="This library is used to display the differences between two texts. This is necessary, e.g., for the grammar and spelling assistant."/>
|
<ThirdPartyComponent Name="wikEd diff" Developer="Cacycle & Open Source Community" LicenseName="None (public domain)" LicenseUrl="https://en.wikipedia.org/wiki/User:Cacycle/diff#License" RepositoryUrl="https://en.wikipedia.org/wiki/User:Cacycle/diff" UseCase="This library is used to display the differences between two texts. This is necessary, e.g., for the grammar and spelling assistant."/>
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
|
using AIStudio.Tools.Metadata;
|
||||||
using AIStudio.Tools.Rust;
|
using AIStudio.Tools.Rust;
|
||||||
using AIStudio.Tools.Services;
|
using AIStudio.Tools.Services;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
using SharedTools;
|
||||||
|
|
||||||
namespace AIStudio.Pages;
|
namespace AIStudio.Pages;
|
||||||
|
|
||||||
public partial class About : ComponentBase
|
public partial class About : ComponentBase
|
||||||
@@ -20,6 +23,7 @@ public partial class About : ComponentBase
|
|||||||
|
|
||||||
private static readonly Assembly ASSEMBLY = Assembly.GetExecutingAssembly();
|
private static readonly Assembly ASSEMBLY = Assembly.GetExecutingAssembly();
|
||||||
private static readonly MetaDataAttribute META_DATA = ASSEMBLY.GetCustomAttribute<MetaDataAttribute>()!;
|
private static readonly MetaDataAttribute META_DATA = ASSEMBLY.GetCustomAttribute<MetaDataAttribute>()!;
|
||||||
|
private static readonly MetaDataArchitecture META_DATA_ARCH = ASSEMBLY.GetCustomAttribute<MetaDataArchitecture>()!;
|
||||||
|
|
||||||
private static string VersionDotnetRuntime => $"Used .NET runtime: v{META_DATA.DotnetVersion}";
|
private static string VersionDotnetRuntime => $"Used .NET runtime: v{META_DATA.DotnetVersion}";
|
||||||
|
|
||||||
@@ -27,7 +31,7 @@ public partial class About : ComponentBase
|
|||||||
|
|
||||||
private static string VersionRust => $"Used Rust compiler: v{META_DATA.RustVersion}";
|
private static string VersionRust => $"Used Rust compiler: v{META_DATA.RustVersion}";
|
||||||
|
|
||||||
private static string VersionApp => $"MindWork AI Studio: v{META_DATA.Version} (commit {META_DATA.AppCommitHash}, build {META_DATA.BuildNum})";
|
private static string VersionApp => $"MindWork AI Studio: v{META_DATA.Version} (commit {META_DATA.AppCommitHash}, build {META_DATA.BuildNum}, {META_DATA_ARCH.Architecture.ToRID().ToUserFriendlyName()})";
|
||||||
|
|
||||||
private static string BuildTime => $"Build time: {META_DATA.BuildTime}";
|
private static string BuildTime => $"Build time: {META_DATA.BuildTime}";
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,11 @@
|
|||||||
<MudText Typo="Typo.h3" Class="mb-2 mr-3">
|
<MudText Typo="Typo.h3" Class="mb-2 mr-3">
|
||||||
@if (this.chatThread is not null && this.chatThread.WorkspaceId != Guid.Empty)
|
@if (this.chatThread is not null && this.chatThread.WorkspaceId != Guid.Empty)
|
||||||
{
|
{
|
||||||
@($"Chat in Workspace \"{this.currentWorkspaceName}\"")
|
@(T("Chat in Workspace") + $" \"{this.currentWorkspaceName}\"")
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@("Temporary Chat")
|
@(T("Short-Term Chat"))
|
||||||
}
|
}
|
||||||
</MudText>
|
</MudText>
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ public partial class Chat : MSGComponentBase
|
|||||||
|
|
||||||
public override string ComponentName => nameof(Chat);
|
public override string ComponentName => nameof(Chat);
|
||||||
|
|
||||||
public override Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
protected override Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||||
{
|
{
|
||||||
switch (triggeredEvent)
|
switch (triggeredEvent)
|
||||||
{
|
{
|
||||||
@@ -96,10 +96,5 @@ public partial class Chat : MSGComponentBase
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data) where TResult : default where TPayload : default
|
|
||||||
{
|
|
||||||
return Task.FromResult(default(TResult));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
@attribute [Route(Routes.HOME)]
|
@attribute [Route(Routes.HOME)]
|
||||||
|
@inherits MSGComponentBase
|
||||||
|
|
||||||
<div class="inner-scrolling-context">
|
<div class="inner-scrolling-context">
|
||||||
<MudImage Src="svg/banner.svg" Style="max-height: 16em; width: 100%; object-fit: cover;" />
|
<MudImage Src="svg/banner.svg" Style="max-height: 16em; width: 100%; object-fit: cover;" />
|
||||||
<MudText Typo="Typo.h3" Class="mt-2 mb-2">Let's get started</MudText>
|
<MudText Typo="Typo.h3" Class="mt-2 mb-2">
|
||||||
|
@T("Let's get started")
|
||||||
|
</MudText>
|
||||||
|
|
||||||
<InnerScrolling>
|
<InnerScrolling>
|
||||||
<MudExpansionPanels Class="mb-3" MultiExpansion="@false">
|
<MudExpansionPanels Class="mb-3" MultiExpansion="@false">
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ using Changelog = AIStudio.Components.Changelog;
|
|||||||
|
|
||||||
namespace AIStudio.Pages;
|
namespace AIStudio.Pages;
|
||||||
|
|
||||||
public partial class Home : ComponentBase
|
public partial class Home : MSGComponentBase
|
||||||
{
|
{
|
||||||
[Inject]
|
[Inject]
|
||||||
private HttpClient HttpClient { get; set; } = null!;
|
private HttpClient HttpClient { get; init; } = null!;
|
||||||
|
|
||||||
private string LastChangeContent { get; set; } = string.Empty;
|
private string LastChangeContent { get; set; } = string.Empty;
|
||||||
|
|
||||||
@@ -23,6 +23,12 @@ public partial class Home : ComponentBase
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Overrides of MSGComponentBase
|
||||||
|
|
||||||
|
public override string ComponentName => nameof(Home);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
private async Task ReadLastChangeAsync()
|
private async Task ReadLastChangeAsync()
|
||||||
{
|
{
|
||||||
var latest = Changelog.LOGS.MaxBy(n => n.Build);
|
var latest = Changelog.LOGS.MaxBy(n => n.Build);
|
||||||
@@ -33,7 +39,7 @@ public partial class Home : ComponentBase
|
|||||||
private static readonly TextItem[] ITEMS_ADVANTAGES =
|
private static readonly TextItem[] ITEMS_ADVANTAGES =
|
||||||
[
|
[
|
||||||
new("Free of charge", "The app is free to use, both for personal and commercial purposes."),
|
new("Free of charge", "The app is free to use, both for personal and commercial purposes."),
|
||||||
new("Independence", "You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT4o, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, and self-hosted models using llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG 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."),
|
new("Independence", "You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT4o, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG 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."),
|
||||||
new("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."),
|
new("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."),
|
||||||
new("Unrestricted usage", "Unlike services like ChatGPT, which impose limits after intensive use, MindWork AI Studio offers unlimited usage through the providers API."),
|
new("Unrestricted usage", "Unlike services like ChatGPT, which impose limits after intensive use, MindWork AI Studio offers unlimited usage through the providers API."),
|
||||||
new("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."),
|
new("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."),
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
@using AIStudio.Tools.PluginSystem
|
||||||
|
@inherits MSGComponentBase
|
||||||
|
@attribute [Route(Routes.PLUGINS)]
|
||||||
|
|
||||||
|
<div class="inner-scrolling-context">
|
||||||
|
<MudText Typo="Typo.h3" Class="mb-2">
|
||||||
|
Plugins
|
||||||
|
</MudText>
|
||||||
|
<PreviewExperimental ApplyInnerScrollingFix="true"/>
|
||||||
|
|
||||||
|
<InnerScrolling>
|
||||||
|
|
||||||
|
<MudTable Items="@PluginFactory.AvailablePlugins" Hover="@true" GroupBy="@this.groupConfig" Class="border-dashed border rounded-lg">
|
||||||
|
<ColGroup>
|
||||||
|
<col style="width: 2em;" />
|
||||||
|
<col style="width: 2.1em; "/>
|
||||||
|
<col/>
|
||||||
|
<col style="width: 12em;"/>
|
||||||
|
</ColGroup>
|
||||||
|
<HeaderContent>
|
||||||
|
<MudTh colspan="2">Plugins</MudTh>
|
||||||
|
<MudTh>Actions</MudTh>
|
||||||
|
</HeaderContent>
|
||||||
|
<GroupHeaderTemplate>
|
||||||
|
<MudTh Class="mud-table-cell-custom-group" colspan="3">
|
||||||
|
@switch (context.Key)
|
||||||
|
{
|
||||||
|
case GROUP_ENABLED:
|
||||||
|
<MudText Typo="Typo.h6" Class="mb-2">
|
||||||
|
Enabled Plugins
|
||||||
|
</MudText>
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GROUP_DISABLED:
|
||||||
|
<MudText Typo="Typo.h6" Class="mb-2">
|
||||||
|
Disabled Plugins
|
||||||
|
</MudText>
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GROUP_INTERNAL:
|
||||||
|
<MudText Typo="Typo.h6" Class="mb-2">
|
||||||
|
Internal Plugins
|
||||||
|
</MudText>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
</MudTh>
|
||||||
|
</GroupHeaderTemplate>
|
||||||
|
<RowTemplate>
|
||||||
|
<MudTd>
|
||||||
|
<MudAvatar Size="Size.Medium" Style="width: 2em; height: 2em;" Class="align-content-stretch">
|
||||||
|
<div class="plugin-icon-container">
|
||||||
|
@((MarkupString)context.IconSVG)
|
||||||
|
</div>
|
||||||
|
</MudAvatar>
|
||||||
|
</MudTd>
|
||||||
|
<MudTd>
|
||||||
|
<MudStack Spacing="0">
|
||||||
|
<MudText Typo="Typo.h6" Class="mb-0 pb-0 gap-0">
|
||||||
|
@context.Name
|
||||||
|
</MudText>
|
||||||
|
<MudText Typo="Typo.body1">
|
||||||
|
@context.Description
|
||||||
|
</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudTd>
|
||||||
|
<MudTd>
|
||||||
|
@if (!context.IsInternal)
|
||||||
|
{
|
||||||
|
var isEnabled = this.SettingsManager.IsPluginEnabled(context);
|
||||||
|
<MudTooltip Text="@(isEnabled ? "Disable plugin" : "Enable plugin")">
|
||||||
|
<MudSwitch T="bool" Value="@isEnabled" ValueChanged="@(_ => this.PluginActivationStateChanged(context))"/>
|
||||||
|
</MudTooltip>
|
||||||
|
}
|
||||||
|
</MudTd>
|
||||||
|
</RowTemplate>
|
||||||
|
</MudTable>
|
||||||
|
</InnerScrolling>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
using AIStudio.Components;
|
||||||
|
using AIStudio.Tools.PluginSystem;
|
||||||
|
|
||||||
|
namespace AIStudio.Pages;
|
||||||
|
|
||||||
|
public partial class Plugins : MSGComponentBase
|
||||||
|
{
|
||||||
|
private const string GROUP_ENABLED = "Enabled";
|
||||||
|
private const string GROUP_DISABLED = "Disabled";
|
||||||
|
private const string GROUP_INTERNAL = "Internal";
|
||||||
|
|
||||||
|
private TableGroupDefinition<IPluginMetadata> groupConfig = null!;
|
||||||
|
|
||||||
|
#region Overrides of ComponentBase
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
this.MessageBus.RegisterComponent(this);
|
||||||
|
this.MessageBus.ApplyFilters(this, [], [ Event.PLUGINS_RELOADED ]);
|
||||||
|
|
||||||
|
this.groupConfig = new TableGroupDefinition<IPluginMetadata>
|
||||||
|
{
|
||||||
|
Expandable = true,
|
||||||
|
IsInitiallyExpanded = true,
|
||||||
|
Selector = pluginMeta =>
|
||||||
|
{
|
||||||
|
if (pluginMeta.IsInternal)
|
||||||
|
return GROUP_INTERNAL;
|
||||||
|
|
||||||
|
return this.SettingsManager.IsPluginEnabled(pluginMeta)
|
||||||
|
? GROUP_ENABLED
|
||||||
|
: GROUP_DISABLED;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Overrides of MSGComponentBase
|
||||||
|
|
||||||
|
public override string ComponentName => nameof(Plugins);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private async Task PluginActivationStateChanged(IPluginMetadata pluginMeta)
|
||||||
|
{
|
||||||
|
if (this.SettingsManager.IsPluginEnabled(pluginMeta))
|
||||||
|
this.SettingsManager.ConfigurationData.EnabledPlugins.Remove(pluginMeta.Id);
|
||||||
|
else
|
||||||
|
this.SettingsManager.ConfigurationData.EnabledPlugins.Add(pluginMeta.Id);
|
||||||
|
|
||||||
|
await this.SettingsManager.StoreSettings();
|
||||||
|
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,8 +12,8 @@
|
|||||||
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
|
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
|
||||||
{
|
{
|
||||||
<SettingsPanelEmbeddings AvailableLLMProvidersFunc="() => this.availableLLMProviders" @bind-AvailableEmbeddingProviders="@this.availableEmbeddingProviders"/>
|
<SettingsPanelEmbeddings AvailableLLMProvidersFunc="() => this.availableLLMProviders" @bind-AvailableEmbeddingProviders="@this.availableEmbeddingProviders"/>
|
||||||
<SettingsPanelDataSources AvailableLLMProvidersFunc="() => this.availableLLMProviders" AvailableEmbeddingsFunc="() => this.availableEmbeddingProviders" @bind-AvailableDataSources="@this.availableDataSources"/>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<SettingsPanelProfiles AvailableLLMProvidersFunc="() => this.availableLLMProviders"/>
|
<SettingsPanelProfiles AvailableLLMProvidersFunc="() => this.availableLLMProviders"/>
|
||||||
<SettingsPanelApp AvailableLLMProvidersFunc="() => this.availableLLMProviders"/>
|
<SettingsPanelApp AvailableLLMProvidersFunc="() => this.availableLLMProviders"/>
|
||||||
<SettingsPanelChat AvailableLLMProvidersFunc="() => this.availableLLMProviders"/>
|
<SettingsPanelChat AvailableLLMProvidersFunc="() => this.availableLLMProviders"/>
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable
|
|||||||
|
|
||||||
private List<ConfigurationSelectData<string>> availableLLMProviders = new();
|
private List<ConfigurationSelectData<string>> availableLLMProviders = new();
|
||||||
private List<ConfigurationSelectData<string>> availableEmbeddingProviders = new();
|
private List<ConfigurationSelectData<string>> availableEmbeddingProviders = new();
|
||||||
private List<ConfigurationSelectData<string>> availableDataSources = new();
|
|
||||||
|
|
||||||
#region Overrides of ComponentBase
|
#region Overrides of ComponentBase
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,8 @@
|
|||||||
<MudList T="string">
|
<MudList T="string">
|
||||||
<Supporter Name="DevNullx64" Type="SupporterType.INDIVIDUAL" URL="https://github.com/DevNullx64" Acknowledgment="Thanks Luc for your build script contribution."/>
|
<Supporter Name="DevNullx64" Type="SupporterType.INDIVIDUAL" URL="https://github.com/DevNullx64" Acknowledgment="Thanks Luc for your build script contribution."/>
|
||||||
<Supporter Name="SolsticeSpectrum" Type="SupporterType.INDIVIDUAL" URL="https://github.com/SolsticeSpectrum" Acknowledgment="Thanks for your build script contribution."/>
|
<Supporter Name="SolsticeSpectrum" Type="SupporterType.INDIVIDUAL" URL="https://github.com/SolsticeSpectrum" Acknowledgment="Thanks for your build script contribution."/>
|
||||||
<Supporter Name="peerschuett" Type="SupporterType.INDIVIDUAL" URL="https://github.com/peerschuett" Acknowledgment="Thanks for your contributions to the project."/>
|
<Supporter Name="peerschuett" Type="SupporterType.INDIVIDUAL" URL="https://github.com/peerschuett" Acknowledgment="Thank you, Peer, for familiarizing yourself with C#, providing excellent contributions like the Alibaba and Hugging Face providers, and revising the settings management."/>
|
||||||
|
<Supporter Name="nilskruthoff" Type="SupporterType.INDIVIDUAL" URL="https://github.com/nilskruthoff" Acknowledgment="Thanks, Nils, for taking the time to learn Rust and build the foundation for local retrieval."/>
|
||||||
</MudList>
|
</MudList>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</MudItem>
|
</MudItem>
|
||||||
|
|||||||
@@ -2,11 +2,10 @@
|
|||||||
@inherits MSGComponentBase
|
@inherits MSGComponentBase
|
||||||
|
|
||||||
<div class="inner-scrolling-context">
|
<div class="inner-scrolling-context">
|
||||||
<MudText Typo="Typo.h3" Class="mb-2 mr-3">
|
<MudText Typo="Typo.h3" Class="mb-2">
|
||||||
Writer
|
Writer
|
||||||
</MudText>
|
</MudText>
|
||||||
|
<PreviewExperimental ApplyInnerScrollingFix="true"/>
|
||||||
<PreviewExperimental/>
|
|
||||||
|
|
||||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings"/>
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings"/>
|
||||||
<InnerScrolling>
|
<InnerScrolling>
|
||||||
|
|||||||
@@ -43,16 +43,6 @@ public partial class Writer : MSGComponentBase, IAsyncDisposable
|
|||||||
|
|
||||||
public override string ComponentName => nameof(Writer);
|
public override string ComponentName => nameof(Writer);
|
||||||
|
|
||||||
public override Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
|
||||||
{
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data) where TResult : default where TPayload : default
|
|
||||||
{
|
|
||||||
return Task.FromResult(default(TResult));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private bool IsProviderSelected => this.providerSettings.UsedLLMProvider != LLMProviders.NONE;
|
private bool IsProviderSelected => this.providerSettings.UsedLLMProvider != LLMProviders.NONE;
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
CONTENT_HOME = {
|
||||||
|
LetsGetStarted = "Lass uns anfangen",
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
SVG = [[<svg viewBox="0 0 600 600" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2">
|
||||||
|
<path style="fill:none" d="M0 0h600v600H0z"/>
|
||||||
|
<clipPath id="a">
|
||||||
|
<path d="M0 0h600v600H0z"/>
|
||||||
|
</clipPath>
|
||||||
|
<g clip-path="url(#a)">
|
||||||
|
<path d="M17.467 200C58.688 83.529 169.851 0 300.369 0S542.05 83.529 583.271 200c11.072 31.284 17.098 64.944 17.098 100s-6.026 68.716-17.098 100C542.05 516.471 430.887 600 300.369 600S58.688 516.471 17.467 400C6.395 368.716.369 335.056.369 300s6.026-68.716 17.098-100Z"/>
|
||||||
|
<path d="M583.271 200c11.072 31.284 17.098 64.944 17.098 100s-6.026 68.716-17.098 100C542.05 516.471 430.887 600 300.369 600S58.688 516.471 17.467 400C6.395 368.716.369 335.056.369 300s6.026-68.716 17.098-100h565.804Z" style="fill:#d00"/>
|
||||||
|
<path d="M583.271 400C542.05 516.471 430.887 600 300.369 600S58.688 516.471 17.467 400h565.804Z" style="fill:#ffce00"/>
|
||||||
|
</g>
|
||||||
|
</svg>]]
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
require("contentHome")
|
||||||
|
require("icon")
|
||||||
|
|
||||||
|
-- The ID for this plugin:
|
||||||
|
ID = "43065dbc-78d0-45b7-92be-f14c2926e2dc"
|
||||||
|
|
||||||
|
-- The icon for the plugin:
|
||||||
|
ICON_SVG = SVG
|
||||||
|
|
||||||
|
-- The name of the plugin:
|
||||||
|
NAME = "MindWork AI Studio - German / Deutsch"
|
||||||
|
|
||||||
|
-- The description of the plugin:
|
||||||
|
DESCRIPTION = "Dieses Plugin bietet deutsche Sprachunterstützung für MindWork AI Studio."
|
||||||
|
|
||||||
|
-- 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 = "de-DE"
|
||||||
|
|
||||||
|
-- The language name in the user's language:
|
||||||
|
LANG_NAME = "Deutsch (Deutschland)"
|
||||||
|
|
||||||
|
UI_TEXT_CONTENT = {
|
||||||
|
HOME = CONTENT_HOME,
|
||||||
|
AISTUDIO = {
|
||||||
|
PAGES = {
|
||||||
|
HOME = {
|
||||||
|
T2331588413 = "Lass uns anfangen",
|
||||||
|
},
|
||||||
|
|
||||||
|
CHAT = {
|
||||||
|
T3718856736 = "Vorläufiger Chat",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
CONTENT_HOME = {
|
||||||
|
LetsGetStarted = "Let's get started",
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
SVG = [[<svg viewBox="0 0 650 650" style="fill-rule:evenodd;clip-rule:evenodd">
|
||||||
|
<path style="fill:none" d="M0 0h650v650H0z"/>
|
||||||
|
<clipPath id="a">
|
||||||
|
<path d="M0 0h650v650H0z"/>
|
||||||
|
</clipPath>
|
||||||
|
<g clip-path="url(#a)">
|
||||||
|
<path d="M208.018 21.723C244.323 7.694 283.77 0 325 0c44.15 0 86.256 8.823 124.649 24.8a319.796 319.796 0 0 1 9.251 4.02A324.269 324.269 0 0 1 494 47.402 326.269 326.269 0 0 1 532.582 75c33.111 27.531 60.675 61.509 80.757 100a322.631 322.631 0 0 1 32.831 100 326.842 326.842 0 0 1 3.83 50c0 17.002-1.308 33.7-3.83 50a322.631 322.631 0 0 1-32.831 100c-20.082 38.491-47.646 72.469-80.757 100-56.319 46.827-128.686 75-207.582 75-78.896 0-151.263-28.173-207.582-75-33.111-27.531-60.675-61.509-80.757-100A322.631 322.631 0 0 1 3.83 375a325.032 325.032 0 0 1-2.881-25A328.869 328.869 0 0 1 0 325c0-17.002 1.308-33.7 3.83-50a322.493 322.493 0 0 1 31.196-96.822 323.44 323.44 0 0 1 8.553-15.752C62.893 129.089 87.96 99.493 117.418 75A325.476 325.476 0 0 1 191.1 28.82a324.203 324.203 0 0 1 16.918-7.097Z" style="fill:#b31942;fill-rule:nonzero"/>
|
||||||
|
<path d="M0 75h1235m0 100H0m0 100h1235m0 100H0m0 100h1235m0 100H0" style="fill-rule:nonzero;stroke:#fff;stroke-width:50px"/>
|
||||||
|
<path d="M208.018 21.723C244.323 7.694 283.77 0 325 0c44.15 0 86.256 8.823 124.649 24.8a319.796 319.796 0 0 1 9.251 4.02A324.269 324.269 0 0 1 494 47.402V350H.949A328.869 328.869 0 0 1 0 325c0-17.002 1.308-33.7 3.83-50a322.493 322.493 0 0 1 31.196-96.822 323.44 323.44 0 0 1 8.553-15.752C62.893 129.089 87.96 99.493 117.418 75A325.476 325.476 0 0 1 191.1 28.82a324.203 324.203 0 0 1 16.918-7.097Z" style="fill:#0a3161;fill-rule:nonzero"/>
|
||||||
|
<path d="M30.895 186.613A317.843 317.843 0 0 1 36.661 175a323.44 323.44 0 0 1 6.918-12.574l2.078 6.394h14.531l-11.756 8.541 4.49 13.819-11.755-8.541-11.756 8.541 1.484-4.567ZM41.167 225l11.755 36.18-30.776-22.36h38.042l-30.777 22.36L41.167 225ZM41.167 295l11.755 36.18-30.776-22.36h38.042l-30.777 22.36L41.167 295ZM82.333 120l11.756 36.18-30.777-22.36h38.042l-30.776 22.36L82.333 120ZM82.333 190l11.756 36.18-30.777-22.36h38.042l-30.776 22.36L82.333 190ZM82.333 260l11.756 36.18-30.777-22.36h38.042l-30.776 22.36L82.333 260ZM123.5 85l11.756 36.18-30.777-22.36h38.042l-30.777 22.36L123.5 85ZM123.5 155l11.756 36.18-30.777-22.36h38.042l-30.777 22.36L123.5 155ZM123.5 225l11.756 36.18-30.777-22.36h38.042l-30.777 22.36L123.5 225ZM123.5 295l11.756 36.18-30.777-22.36h38.042l-30.777 22.36L123.5 295ZM164.667 50l11.755 36.18-30.776-22.36h38.042l-30.777 22.36L164.667 50ZM164.667 120l11.755 36.18-30.776-22.36h38.042l-30.777 22.36L164.667 120ZM164.667 190l11.755 36.18-30.776-22.36h38.042l-30.777 22.36L164.667 190ZM164.667 260l11.755 36.18-30.776-22.36h38.042l-30.777 22.36L164.667 260ZM198.568 37.361l-10.096-7.336c.874-.405 1.75-.807 2.628-1.205h10.243l1.66-5.111a334.886 334.886 0 0 1 5.015-1.986l2.306 7.097h14.53l-11.755 8.541 4.49 13.819-11.756-8.541-11.755 8.541 4.49-13.819ZM205.833 85l11.756 36.18-30.777-22.36h38.042l-30.776 22.36L205.833 85ZM205.833 155l11.756 36.18-30.777-22.36h38.042l-30.776 22.36L205.833 155ZM205.833 225l11.756 36.18-30.777-22.36h38.042l-30.776 22.36L205.833 225ZM205.833 295l11.756 36.18-30.777-22.36h38.042l-30.776 22.36L205.833 295ZM247 50l11.756 36.18-30.777-22.36h38.042l-30.777 22.36L247 50ZM247 120l11.756 36.18-30.777-22.36h38.042l-30.777 22.36L247 120ZM247 190l11.756 36.18-30.777-22.36h38.042l-30.777 22.36L247 190ZM247 260l11.756 36.18-30.777-22.36h38.042l-30.777 22.36L247 260ZM288.167 15l11.755 36.18-30.776-22.36h38.042l-30.777 22.36L288.167 15ZM288.167 85l11.755 36.18-30.776-22.36h38.042l-30.777 22.36L288.167 85ZM288.167 155l11.755 36.18-30.776-22.36h38.042l-30.777 22.36L288.167 155ZM288.167 225l11.755 36.18-30.776-22.36h38.042l-30.777 22.36L288.167 225ZM288.167 295l11.755 36.18-30.776-22.36h38.042l-30.777 22.36L288.167 295ZM329.333 50l11.756 36.18-30.777-22.36h38.042l-30.776 22.36L329.333 50ZM329.333 120l11.756 36.18-30.777-22.36h38.042l-30.776 22.36L329.333 120ZM329.333 190l11.756 36.18-30.777-22.36h38.042l-30.776 22.36L329.333 190ZM329.333 260l11.756 36.18-30.777-22.36h38.042l-30.776 22.36L329.333 260ZM370.5 15l11.756 36.18-30.777-22.36h38.042l-30.777 22.36L370.5 15ZM370.5 85l11.756 36.18-30.777-22.36h38.042l-30.777 22.36L370.5 85ZM370.5 155l11.756 36.18-30.777-22.36h38.042l-30.777 22.36L370.5 155ZM370.5 225l11.756 36.18-30.777-22.36h38.042l-30.777 22.36L370.5 225ZM370.5 295l11.756 36.18-30.777-22.36h38.042l-30.777 22.36L370.5 295ZM411.667 50l11.755 36.18-30.776-22.36h38.042l-30.777 22.36L411.667 50ZM411.667 120l11.755 36.18-30.776-22.36h38.042l-30.777 22.36L411.667 120ZM411.667 190l11.755 36.18-30.776-22.36h38.042l-30.777 22.36L411.667 190ZM411.667 260l11.755 36.18-30.776-22.36h38.042l-30.777 22.36L411.667 260ZM448.343 28.82l1.306-4.02a319.796 319.796 0 0 1 7.405 3.19l.27.83h1.576a320.481 320.481 0 0 1 7.869 3.695l-6.67 4.846 4.49 13.819-11.756-8.541-11.755 8.541 4.49-13.819-11.756-8.541h14.531ZM452.833 85l11.756 36.18-30.777-22.36h38.042l-30.776 22.36L452.833 85ZM452.833 155l11.756 36.18-30.777-22.36h38.042l-30.776 22.36L452.833 155ZM452.833 225l11.756 36.18-30.777-22.36h38.042l-30.776 22.36L452.833 225ZM452.833 295l11.756 36.18-30.777-22.36h38.042l-30.776 22.36L452.833 295Z" style="fill:#fff;fill-rule:nonzero"/>
|
||||||
|
</g>
|
||||||
|
</svg>]]
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
require("contentHome")
|
||||||
|
require("icon")
|
||||||
|
|
||||||
|
-- The ID for this plugin:
|
||||||
|
ID = "97dfb1ba-50c4-4440-8dfa-6575daf543c8"
|
||||||
|
|
||||||
|
-- The icon for the plugin:
|
||||||
|
ICON_SVG = SVG
|
||||||
|
|
||||||
|
-- The name of the plugin:
|
||||||
|
NAME = "MindWork AI Studio - US English"
|
||||||
|
|
||||||
|
-- The description of the plugin:
|
||||||
|
DESCRIPTION = "This plugin provides US English language support for MindWork AI Studio."
|
||||||
|
|
||||||
|
-- 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)"
|
||||||
|
|
||||||
|
UI_TEXT_CONTENT = {
|
||||||
|
HOME = CONTENT_HOME,
|
||||||
|
AISTUDIO = {
|
||||||
|
PAGES = {
|
||||||
|
HOME = {
|
||||||
|
T2331588413 = "Let's get started",
|
||||||
|
},
|
||||||
|
|
||||||
|
CHAT = {
|
||||||
|
T3718856736 = "Short-Term Chat",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using AIStudio.Agents;
|
using AIStudio.Agents;
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
|
using AIStudio.Tools.PluginSystem;
|
||||||
using AIStudio.Tools.Services;
|
using AIStudio.Tools.Services;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||||
@@ -22,6 +23,7 @@ internal sealed class Program
|
|||||||
public static Encryption ENCRYPTION = null!;
|
public static Encryption ENCRYPTION = null!;
|
||||||
public static string API_TOKEN = null!;
|
public static string API_TOKEN = null!;
|
||||||
public static IServiceProvider SERVICE_PROVIDER = null!;
|
public static IServiceProvider SERVICE_PROVIDER = null!;
|
||||||
|
public static ILoggerFactory LOGGER_FACTORY = null!;
|
||||||
|
|
||||||
public static async Task Main(string[] args)
|
public static async Task Main(string[] args)
|
||||||
{
|
{
|
||||||
@@ -147,6 +149,9 @@ internal sealed class Program
|
|||||||
// Execute the builder to get the app:
|
// Execute the builder to get the app:
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
|
// Get the logging factory for e.g., static classes:
|
||||||
|
LOGGER_FACTORY = app.Services.GetRequiredService<ILoggerFactory>();
|
||||||
|
|
||||||
// Get a program logger:
|
// Get a program logger:
|
||||||
var programLogger = app.Services.GetRequiredService<ILogger<Program>>();
|
var programLogger = app.Services.GetRequiredService<ILogger<Program>>();
|
||||||
programLogger.LogInformation("Starting the AI Studio server.");
|
programLogger.LogInformation("Starting the AI Studio server.");
|
||||||
@@ -205,6 +210,7 @@ internal sealed class Program
|
|||||||
await serverTask;
|
await serverTask;
|
||||||
|
|
||||||
RUST_SERVICE.Dispose();
|
RUST_SERVICE.Dispose();
|
||||||
|
PluginFactory.Dispose();
|
||||||
programLogger.LogInformation("The AI Studio server was stopped.");
|
programLogger.LogInformation("The AI Studio server was stopped.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
using AIStudio.Chat;
|
||||||
|
using AIStudio.Provider.OpenAI;
|
||||||
|
using AIStudio.Settings;
|
||||||
|
|
||||||
|
namespace AIStudio.Provider.AlibabaCloud;
|
||||||
|
|
||||||
|
public sealed class ProviderAlibabaCloud(ILogger logger) : BaseProvider("https://dashscope-intl.aliyuncs.com/compatible-mode/v1/", logger)
|
||||||
|
{
|
||||||
|
|
||||||
|
#region Implementation of IProvider
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string Id => LLMProviders.ALIBABA_CLOUD.ToName();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string InstanceName { get; set; } = "AlibabaCloud";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
|
{
|
||||||
|
// Get the API key:
|
||||||
|
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||||
|
if(!requestedSecret.Success)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
// Prepare the system prompt:
|
||||||
|
var systemPrompt = new Message
|
||||||
|
{
|
||||||
|
Role = "system",
|
||||||
|
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread, this.logger),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prepare the AlibabaCloud HTTP chat request:
|
||||||
|
var alibabaCloudChatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||||
|
{
|
||||||
|
Model = chatModel.Id,
|
||||||
|
|
||||||
|
// Build the messages:
|
||||||
|
// - First of all the system prompt
|
||||||
|
// - Then none-empty user and AI messages
|
||||||
|
Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message
|
||||||
|
{
|
||||||
|
Role = n.Role switch
|
||||||
|
{
|
||||||
|
ChatRole.USER => "user",
|
||||||
|
ChatRole.AI => "assistant",
|
||||||
|
ChatRole.AGENT => "assistant",
|
||||||
|
ChatRole.SYSTEM => "system",
|
||||||
|
|
||||||
|
_ => "user",
|
||||||
|
},
|
||||||
|
|
||||||
|
Content = n.Content switch
|
||||||
|
{
|
||||||
|
ContentText text => text.Text,
|
||||||
|
_ => string.Empty,
|
||||||
|
}
|
||||||
|
}).ToList()],
|
||||||
|
Stream = true,
|
||||||
|
}, JSON_SERIALIZER_OPTIONS);
|
||||||
|
|
||||||
|
async Task<HttpRequestMessage> RequestBuilder()
|
||||||
|
{
|
||||||
|
// Build the HTTP post request:
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
|
||||||
|
|
||||||
|
// Set the authorization header:
|
||||||
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
||||||
|
|
||||||
|
// Set the content:
|
||||||
|
request.Content = new StringContent(alibabaCloudChatRequest, Encoding.UTF8, "application/json");
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine>("AlibabaCloud", RequestBuilder, token))
|
||||||
|
yield return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async IAsyncEnumerable<ImageURL> StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var additionalModels = new[]
|
||||||
|
{
|
||||||
|
new Model("qwq-plus", "QwQ plus"), // reasoning model
|
||||||
|
new Model("qwen-max-latest", "Qwen-Max (Latest)"),
|
||||||
|
new Model("qwen-plus-latest", "Qwen-Plus (Latest)"),
|
||||||
|
new Model("qwen-turbo-latest", "Qwen-Turbo (Latest)"),
|
||||||
|
new Model("qvq-max", "QVQ Max"), // visual reasoning model
|
||||||
|
new Model("qvq-max-latest", "QVQ Max (Latest)"), // visual reasoning model
|
||||||
|
new Model("qwen-vl-max", "Qwen-VL Max"), // text generation model that can understand and process images
|
||||||
|
new Model("qwen-vl-plus", "Qwen-VL Plus"), // text generation model that can understand and process images
|
||||||
|
new Model("qwen-mt-plus", "Qwen-MT Plus"), // machine translation
|
||||||
|
new Model("qwen-mt-turbo", "Qwen-MT Turbo"), // machine translation
|
||||||
|
|
||||||
|
//Open source
|
||||||
|
new Model("qwen2.5-14b-instruct-1m", "Qwen2.5 14b 1m context"),
|
||||||
|
new Model("qwen2.5-7b-instruct-1m", "Qwen2.5 7b 1m context"),
|
||||||
|
new Model("qwen2.5-72b-instruct", "Qwen2.5 72b"),
|
||||||
|
new Model("qwen2.5-32b-instruct", "Qwen2.5 32b"),
|
||||||
|
new Model("qwen2.5-14b-instruct", "Qwen2.5 14b"),
|
||||||
|
new Model("qwen2.5-7b-instruct", "Qwen2.5 7b"),
|
||||||
|
new Model("qwen2.5-omni-7b", "Qwen2.5-Omni 7b"), // omni-modal understanding and generation model
|
||||||
|
new Model("qwen2.5-vl-72b-instruct", "Qwen2.5-VL 72b"),
|
||||||
|
new Model("qwen2.5-vl-32b-instruct", "Qwen2.5-VL 32b"),
|
||||||
|
new Model("qwen2.5-vl-7b-instruct", "Qwen2.5-VL 7b"),
|
||||||
|
new Model("qwen2.5-vl-3b-instruct", "Qwen2.5-VL 3b"),
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.LoadModels(["q"],token, apiKeyProvisional).ContinueWith(t => t.Result.Concat(additionalModels).OrderBy(x => x.Id).AsEnumerable(), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task<IEnumerable<Model>> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult(Enumerable.Empty<Model>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task<IEnumerable<Model>> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
|
||||||
|
var additionalModels = new[]
|
||||||
|
{
|
||||||
|
new Model("text-embedding-v3", "text-embedding-v3"),
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.LoadModels(["text-embedding-"], token, apiKeyProvisional).ContinueWith(t => t.Result.Concat(additionalModels).OrderBy(x => x.Id).AsEnumerable(), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
private async Task<IEnumerable<Model>> LoadModels(string[] prefixes, CancellationToken token, string? apiKeyProvisional = null)
|
||||||
|
{
|
||||||
|
var secretKey = apiKeyProvisional switch
|
||||||
|
{
|
||||||
|
not null => apiKeyProvisional,
|
||||||
|
_ => await RUST_SERVICE.GetAPIKey(this) switch
|
||||||
|
{
|
||||||
|
{ Success: true } result => await result.Secret.Decrypt(ENCRYPTION),
|
||||||
|
_ => null,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (secretKey is null)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
using var request = new HttpRequestMessage(HttpMethod.Get, "models");
|
||||||
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secretKey);
|
||||||
|
|
||||||
|
using var response = await this.httpClient.SendAsync(request, token);
|
||||||
|
if(!response.IsSuccessStatusCode)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
var modelResponse = await response.Content.ReadFromJsonAsync<ModelsResponse>(token);
|
||||||
|
return modelResponse.Data.Where(model => prefixes.Any(prefix => model.Id.StartsWith(prefix, StringComparison.InvariantCulture)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -117,8 +117,49 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nextResponse.StatusCode is HttpStatusCode.Forbidden)
|
||||||
|
{
|
||||||
|
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Block, $"Tried to communicate with the LLM provider '{this.InstanceName}'. You might not be able to use this provider from your location. The provider message is: '{nextResponse.ReasonPhrase}'"));
|
||||||
|
this.logger.LogError($"Failed request with status code {nextResponse.StatusCode} (message = '{nextResponse.ReasonPhrase}').");
|
||||||
|
errorMessage = nextResponse.ReasonPhrase;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if(nextResponse.StatusCode is HttpStatusCode.BadRequest)
|
if(nextResponse.StatusCode is HttpStatusCode.BadRequest)
|
||||||
{
|
{
|
||||||
|
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, $"Tried to communicate with the LLM provider '{this.InstanceName}'. The required message format might be changed. The provider message is: '{nextResponse.ReasonPhrase}'"));
|
||||||
|
this.logger.LogError($"Failed request with status code {nextResponse.StatusCode} (message = '{nextResponse.ReasonPhrase}').");
|
||||||
|
errorMessage = nextResponse.ReasonPhrase;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nextResponse.StatusCode is HttpStatusCode.NotFound)
|
||||||
|
{
|
||||||
|
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, $"Tried to communicate with the LLM provider '{this.InstanceName}'. Something was not found. The provider message is: '{nextResponse.ReasonPhrase}'"));
|
||||||
|
this.logger.LogError($"Failed request with status code {nextResponse.StatusCode} (message = '{nextResponse.ReasonPhrase}').");
|
||||||
|
errorMessage = nextResponse.ReasonPhrase;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nextResponse.StatusCode is HttpStatusCode.Unauthorized)
|
||||||
|
{
|
||||||
|
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Key, $"Tried to communicate with the LLM provider '{this.InstanceName}'. The API key might be invalid. The provider message is: '{nextResponse.ReasonPhrase}'"));
|
||||||
|
this.logger.LogError($"Failed request with status code {nextResponse.StatusCode} (message = '{nextResponse.ReasonPhrase}').");
|
||||||
|
errorMessage = nextResponse.ReasonPhrase;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nextResponse.StatusCode is HttpStatusCode.InternalServerError)
|
||||||
|
{
|
||||||
|
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, $"Tried to communicate with the LLM provider '{this.InstanceName}'. The server might be down or having issues. The provider message is: '{nextResponse.ReasonPhrase}'"));
|
||||||
|
this.logger.LogError($"Failed request with status code {nextResponse.StatusCode} (message = '{nextResponse.ReasonPhrase}').");
|
||||||
|
errorMessage = nextResponse.ReasonPhrase;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nextResponse.StatusCode is HttpStatusCode.ServiceUnavailable)
|
||||||
|
{
|
||||||
|
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, $"Tried to communicate with the LLM provider '{this.InstanceName}'. The provider is overloaded. The message is: '{nextResponse.ReasonPhrase}'"));
|
||||||
this.logger.LogError($"Failed request with status code {nextResponse.StatusCode} (message = '{nextResponse.ReasonPhrase}').");
|
this.logger.LogError($"Failed request with status code {nextResponse.StatusCode} (message = '{nextResponse.ReasonPhrase}').");
|
||||||
errorMessage = nextResponse.ReasonPhrase;
|
errorMessage = nextResponse.ReasonPhrase;
|
||||||
break;
|
break;
|
||||||
@@ -134,7 +175,10 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(retry >= MAX_RETRIES || !string.IsNullOrWhiteSpace(errorMessage))
|
if(retry >= MAX_RETRIES || !string.IsNullOrWhiteSpace(errorMessage))
|
||||||
|
{
|
||||||
|
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, $"Tried to communicate with the LLM provider '{this.InstanceName}'. Even after {MAX_RETRIES} retries, there were some problems with the request. The provider message is: '{errorMessage}'"));
|
||||||
return new HttpRateLimitedStreamResult(false, true, errorMessage ?? $"Failed after {MAX_RETRIES} retries; no provider message available", response);
|
return new HttpRateLimitedStreamResult(false, true, errorMessage ?? $"Failed after {MAX_RETRIES} retries; no provider message available", response);
|
||||||
|
}
|
||||||
|
|
||||||
return new HttpRateLimitedStreamResult(true, false, string.Empty, response);
|
return new HttpRateLimitedStreamResult(true, false, string.Empty, response);
|
||||||
}
|
}
|
||||||
@@ -160,6 +204,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
|
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Stream, $"Tried to communicate with the LLM provider '{this.InstanceName}'. There were some problems with the request. The provider message is: '{e.Message}'"));
|
||||||
this.logger.LogError($"Failed to stream chat completion from {providerName} '{this.InstanceName}': {e.Message}");
|
this.logger.LogError($"Failed to stream chat completion from {providerName} '{this.InstanceName}': {e.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,6 +221,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Stream, $"Tried to stream the LLM provider '{this.InstanceName}' answer. There were some problems with the stream. The message is: '{e.Message}'"));
|
||||||
this.logger.LogWarning($"Failed to read the end-of-stream state from {providerName} '{this.InstanceName}': {e.Message}");
|
this.logger.LogWarning($"Failed to read the end-of-stream state from {providerName} '{this.InstanceName}': {e.Message}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -196,6 +242,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Stream, $"Tried to stream the LLM provider '{this.InstanceName}' answer. Was not able to read the stream. The message is: '{e.Message}'"));
|
||||||
this.logger.LogError($"Failed to read the stream from {providerName} '{this.InstanceName}': {e.Message}");
|
this.logger.LogError($"Failed to read the stream from {providerName} '{this.InstanceName}': {e.Message}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,10 +35,10 @@ public sealed record Confidence
|
|||||||
""",
|
""",
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly Confidence USA_NOT_TRUSTED = new()
|
public static readonly Confidence USA_HUB = new()
|
||||||
{
|
{
|
||||||
Level = ConfidenceLevel.UNTRUSTED,
|
Level = ConfidenceLevel.UNKNOWN,
|
||||||
Description = "The provider operates its service from the USA and is subject to **U.S. jurisdiction**. In case of suspicion, authorities in the USA can access your data. The provider's terms of service state that **all your data can be used by the provider at will.**",
|
Description = "The provider operates its service from the USA and is subject to **U.S. jurisdiction**. In case of suspicion, authorities in the USA can access your data. Please inform yourself about the use of your data. We do not know if your data is safe.",
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly Confidence UNKNOWN = new()
|
public static readonly Confidence UNKNOWN = new()
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public sealed class ProviderDeepSeek(ILogger logger) : BaseProvider("https://api
|
|||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine>("Helmholtz", RequestBuilder, token))
|
await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine>("DeepSeek", RequestBuilder, token))
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
namespace AIStudio.Provider.HuggingFace;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enum for inference providers that Hugging Face supports.
|
||||||
|
/// </summary>
|
||||||
|
public enum HFInferenceProvider
|
||||||
|
{
|
||||||
|
NONE,
|
||||||
|
|
||||||
|
CEREBRAS,
|
||||||
|
NEBIUS_AI_STUDIO,
|
||||||
|
SAMBANOVA,
|
||||||
|
NOVITA,
|
||||||
|
HYPERBOLIC,
|
||||||
|
TOGETHER_AI,
|
||||||
|
FIREWORKS,
|
||||||
|
HF_INFERENCE_API,
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
namespace AIStudio.Provider.HuggingFace;
|
||||||
|
|
||||||
|
public static class HFInferenceProviderExtensions
|
||||||
|
{
|
||||||
|
public static string Endpoints(this HFInferenceProvider provider, Model model) => provider switch
|
||||||
|
{
|
||||||
|
HFInferenceProvider.CEREBRAS => "cerebras/v1/",
|
||||||
|
HFInferenceProvider.NEBIUS_AI_STUDIO => "nebius/v1/",
|
||||||
|
HFInferenceProvider.SAMBANOVA => "sambanova/v1/",
|
||||||
|
HFInferenceProvider.NOVITA => "novita/v3/openai/",
|
||||||
|
HFInferenceProvider.HYPERBOLIC => "hyperbolic/v1/",
|
||||||
|
HFInferenceProvider.TOGETHER_AI => "together/v1/",
|
||||||
|
HFInferenceProvider.FIREWORKS => "fireworks-ai/inference/v1/",
|
||||||
|
HFInferenceProvider.HF_INFERENCE_API => $"hf-inference/models/{model.ToString()}/v1/",
|
||||||
|
_ => string.Empty,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string EndpointsId(this HFInferenceProvider provider) => provider switch
|
||||||
|
{
|
||||||
|
HFInferenceProvider.CEREBRAS => "cerebras",
|
||||||
|
HFInferenceProvider.NEBIUS_AI_STUDIO => "nebius",
|
||||||
|
HFInferenceProvider.SAMBANOVA => "sambanova",
|
||||||
|
HFInferenceProvider.NOVITA => "novita",
|
||||||
|
HFInferenceProvider.HYPERBOLIC => "hyperbolic",
|
||||||
|
HFInferenceProvider.TOGETHER_AI => "together",
|
||||||
|
HFInferenceProvider.FIREWORKS => "fireworks",
|
||||||
|
HFInferenceProvider.HF_INFERENCE_API => "hf-inference",
|
||||||
|
_ => string.Empty,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string ToName(this HFInferenceProvider provider) => provider switch
|
||||||
|
{
|
||||||
|
HFInferenceProvider.CEREBRAS => "Cerebras",
|
||||||
|
HFInferenceProvider.NEBIUS_AI_STUDIO => "Nebius AI Studio",
|
||||||
|
HFInferenceProvider.SAMBANOVA => "Sambanova",
|
||||||
|
HFInferenceProvider.NOVITA => "Novita",
|
||||||
|
HFInferenceProvider.HYPERBOLIC => "Hyperbolic",
|
||||||
|
HFInferenceProvider.TOGETHER_AI => "Together AI",
|
||||||
|
HFInferenceProvider.FIREWORKS => "Fireworks AI",
|
||||||
|
HFInferenceProvider.HF_INFERENCE_API => "Hugging Face Inference API",
|
||||||
|
_ => string.Empty,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
using AIStudio.Chat;
|
||||||
|
using AIStudio.Provider.OpenAI;
|
||||||
|
using AIStudio.Settings;
|
||||||
|
|
||||||
|
namespace AIStudio.Provider.HuggingFace;
|
||||||
|
|
||||||
|
public sealed class ProviderHuggingFace : BaseProvider
|
||||||
|
{
|
||||||
|
public ProviderHuggingFace(ILogger logger, HFInferenceProvider hfProvider, Model model) : base($"https://router.huggingface.co/{hfProvider.Endpoints(model)}", logger)
|
||||||
|
{
|
||||||
|
logger.LogInformation($"We use the inferende provider '{hfProvider}'. Thus we use the base URL 'https://router.huggingface.co/{hfProvider.Endpoints(model)}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Implementation of IProvider
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string Id => LLMProviders.HUGGINGFACE.ToName();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string InstanceName { get; set; } = "HuggingFace";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
|
{
|
||||||
|
// Get the API key:
|
||||||
|
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||||
|
if(!requestedSecret.Success)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
// Prepare the system prompt:
|
||||||
|
var systemPrompt = new Message
|
||||||
|
{
|
||||||
|
Role = "system",
|
||||||
|
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread, this.logger),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prepare the HuggingFace HTTP chat request:
|
||||||
|
var huggingfaceChatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||||
|
{
|
||||||
|
Model = chatModel.Id,
|
||||||
|
|
||||||
|
// Build the messages:
|
||||||
|
// - First of all the system prompt
|
||||||
|
// - Then none-empty user and AI messages
|
||||||
|
Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message
|
||||||
|
{
|
||||||
|
Role = n.Role switch
|
||||||
|
{
|
||||||
|
ChatRole.USER => "user",
|
||||||
|
ChatRole.AI => "assistant",
|
||||||
|
ChatRole.AGENT => "assistant",
|
||||||
|
ChatRole.SYSTEM => "system",
|
||||||
|
|
||||||
|
_ => "user",
|
||||||
|
},
|
||||||
|
|
||||||
|
Content = n.Content switch
|
||||||
|
{
|
||||||
|
ContentText text => text.Text,
|
||||||
|
_ => string.Empty,
|
||||||
|
}
|
||||||
|
}).ToList()],
|
||||||
|
Stream = true,
|
||||||
|
}, JSON_SERIALIZER_OPTIONS);
|
||||||
|
|
||||||
|
async Task<HttpRequestMessage> RequestBuilder()
|
||||||
|
{
|
||||||
|
// Build the HTTP post request:
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
|
||||||
|
|
||||||
|
// Set the authorization header:
|
||||||
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
||||||
|
|
||||||
|
// Set the content:
|
||||||
|
request.Content = new StringContent(huggingfaceChatRequest, Encoding.UTF8, "application/json");
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine>("HuggingFace", RequestBuilder, token))
|
||||||
|
yield return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async IAsyncEnumerable<ImageURL> StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult(Enumerable.Empty<Model>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task<IEnumerable<Model>> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult(Enumerable.Empty<Model>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task<IEnumerable<Model>> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult(Enumerable.Empty<Model>());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -13,9 +13,11 @@ public enum LLMProviders
|
|||||||
GOOGLE = 7,
|
GOOGLE = 7,
|
||||||
X = 8,
|
X = 8,
|
||||||
DEEP_SEEK = 11,
|
DEEP_SEEK = 11,
|
||||||
|
ALIBABA_CLOUD = 12,
|
||||||
|
|
||||||
FIREWORKS = 5,
|
FIREWORKS = 5,
|
||||||
GROQ = 6,
|
GROQ = 6,
|
||||||
|
HUGGINGFACE = 13,
|
||||||
|
|
||||||
SELF_HOSTED = 4,
|
SELF_HOSTED = 4,
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using AIStudio.Provider.AlibabaCloud;
|
||||||
using AIStudio.Provider.Anthropic;
|
using AIStudio.Provider.Anthropic;
|
||||||
using AIStudio.Provider.DeepSeek;
|
using AIStudio.Provider.DeepSeek;
|
||||||
using AIStudio.Provider.Fireworks;
|
using AIStudio.Provider.Fireworks;
|
||||||
@@ -5,6 +6,7 @@ using AIStudio.Provider.Google;
|
|||||||
using AIStudio.Provider.Groq;
|
using AIStudio.Provider.Groq;
|
||||||
using AIStudio.Provider.GWDG;
|
using AIStudio.Provider.GWDG;
|
||||||
using AIStudio.Provider.Helmholtz;
|
using AIStudio.Provider.Helmholtz;
|
||||||
|
using AIStudio.Provider.HuggingFace;
|
||||||
using AIStudio.Provider.Mistral;
|
using AIStudio.Provider.Mistral;
|
||||||
using AIStudio.Provider.OpenAI;
|
using AIStudio.Provider.OpenAI;
|
||||||
using AIStudio.Provider.SelfHosted;
|
using AIStudio.Provider.SelfHosted;
|
||||||
@@ -32,9 +34,11 @@ public static class LLMProvidersExtensions
|
|||||||
LLMProviders.GOOGLE => "Google",
|
LLMProviders.GOOGLE => "Google",
|
||||||
LLMProviders.X => "xAI",
|
LLMProviders.X => "xAI",
|
||||||
LLMProviders.DEEP_SEEK => "DeepSeek",
|
LLMProviders.DEEP_SEEK => "DeepSeek",
|
||||||
|
LLMProviders.ALIBABA_CLOUD => "Alibaba Cloud",
|
||||||
|
|
||||||
LLMProviders.GROQ => "Groq",
|
LLMProviders.GROQ => "Groq",
|
||||||
LLMProviders.FIREWORKS => "Fireworks.ai",
|
LLMProviders.FIREWORKS => "Fireworks.ai",
|
||||||
|
LLMProviders.HUGGINGFACE => "Hugging Face",
|
||||||
|
|
||||||
LLMProviders.SELF_HOSTED => "Self-hosted",
|
LLMProviders.SELF_HOSTED => "Self-hosted",
|
||||||
|
|
||||||
@@ -54,7 +58,10 @@ public static class LLMProvidersExtensions
|
|||||||
{
|
{
|
||||||
LLMProviders.NONE => Confidence.NONE,
|
LLMProviders.NONE => Confidence.NONE,
|
||||||
|
|
||||||
LLMProviders.FIREWORKS => Confidence.USA_NOT_TRUSTED.WithRegion("America, U.S.").WithSources("https://fireworks.ai/terms-of-service").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
LLMProviders.FIREWORKS => Confidence.USA_HUB.WithRegion("America, U.S.").WithSources("https://fireworks.ai/terms-of-service").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||||
|
|
||||||
|
// Not trusted, because huggingface only routes you to a third-party-provider and we can't make sure they do not use your data
|
||||||
|
LLMProviders.HUGGINGFACE => Confidence.USA_HUB.WithRegion("America, U.S.").WithSources("https://huggingface.co/terms-of-service").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||||
|
|
||||||
LLMProviders.OPEN_AI => Confidence.USA_NO_TRAINING.WithRegion("America, U.S.").WithSources(
|
LLMProviders.OPEN_AI => Confidence.USA_NO_TRAINING.WithRegion("America, U.S.").WithSources(
|
||||||
"https://platform.openai.com/docs/models/default-usage-policies-by-endpoint",
|
"https://platform.openai.com/docs/models/default-usage-policies-by-endpoint",
|
||||||
@@ -75,6 +82,8 @@ public static class LLMProvidersExtensions
|
|||||||
|
|
||||||
LLMProviders.DEEP_SEEK => Confidence.CHINA_NO_TRAINING.WithRegion("Asia").WithSources("https://cdn.deepseek.com/policies/en-US/deepseek-open-platform-terms-of-service.html").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
LLMProviders.DEEP_SEEK => Confidence.CHINA_NO_TRAINING.WithRegion("Asia").WithSources("https://cdn.deepseek.com/policies/en-US/deepseek-open-platform-terms-of-service.html").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||||
|
|
||||||
|
LLMProviders.ALIBABA_CLOUD => Confidence.CHINA_NO_TRAINING.WithRegion("Asia").WithSources("https://www.alibabacloud.com/help/en/model-studio/support/faq-about-alibaba-cloud-model-studio").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||||
|
|
||||||
LLMProviders.SELF_HOSTED => Confidence.SELF_HOSTED.WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
LLMProviders.SELF_HOSTED => Confidence.SELF_HOSTED.WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||||
|
|
||||||
LLMProviders.HELMHOLTZ => Confidence.GDPR_NO_TRAINING.WithRegion("Europe, Germany").WithSources("https://helmholtz.cloud/services/?serviceID=d7d5c597-a2f6-4bd1-b71e-4d6499d98570").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
LLMProviders.HELMHOLTZ => Confidence.GDPR_NO_TRAINING.WithRegion("Europe, Germany").WithSources("https://helmholtz.cloud/services/?serviceID=d7d5c597-a2f6-4bd1-b71e-4d6499d98570").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||||
@@ -97,6 +106,7 @@ public static class LLMProvidersExtensions
|
|||||||
LLMProviders.MISTRAL => true,
|
LLMProviders.MISTRAL => true,
|
||||||
LLMProviders.GOOGLE => true,
|
LLMProviders.GOOGLE => true,
|
||||||
LLMProviders.HELMHOLTZ => true,
|
LLMProviders.HELMHOLTZ => true,
|
||||||
|
LLMProviders.ALIBABA_CLOUD => true,
|
||||||
|
|
||||||
//
|
//
|
||||||
// Providers that do not support embeddings:
|
// Providers that do not support embeddings:
|
||||||
@@ -107,6 +117,7 @@ public static class LLMProvidersExtensions
|
|||||||
LLMProviders.X => false,
|
LLMProviders.X => false,
|
||||||
LLMProviders.GWDG => false,
|
LLMProviders.GWDG => false,
|
||||||
LLMProviders.DEEP_SEEK => false,
|
LLMProviders.DEEP_SEEK => false,
|
||||||
|
LLMProviders.HUGGINGFACE => false,
|
||||||
|
|
||||||
//
|
//
|
||||||
// Self-hosted providers are treated as a special case anyway.
|
// Self-hosted providers are treated as a special case anyway.
|
||||||
@@ -124,7 +135,7 @@ public static class LLMProvidersExtensions
|
|||||||
/// <returns>The provider instance.</returns>
|
/// <returns>The provider instance.</returns>
|
||||||
public static IProvider CreateProvider(this AIStudio.Settings.Provider providerSettings, ILogger logger)
|
public static IProvider CreateProvider(this AIStudio.Settings.Provider providerSettings, ILogger logger)
|
||||||
{
|
{
|
||||||
return providerSettings.UsedLLMProvider.CreateProvider(providerSettings.InstanceName, providerSettings.Host, providerSettings.Hostname, logger);
|
return providerSettings.UsedLLMProvider.CreateProvider(providerSettings.InstanceName, providerSettings.Host, providerSettings.Hostname, providerSettings.Model, providerSettings.HFInferenceProvider ,logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -135,10 +146,10 @@ public static class LLMProvidersExtensions
|
|||||||
/// <returns>The provider instance.</returns>
|
/// <returns>The provider instance.</returns>
|
||||||
public static IProvider CreateProvider(this EmbeddingProvider embeddingProviderSettings, ILogger logger)
|
public static IProvider CreateProvider(this EmbeddingProvider embeddingProviderSettings, ILogger logger)
|
||||||
{
|
{
|
||||||
return embeddingProviderSettings.UsedLLMProvider.CreateProvider(embeddingProviderSettings.Name, embeddingProviderSettings.Host, embeddingProviderSettings.Hostname, logger);
|
return embeddingProviderSettings.UsedLLMProvider.CreateProvider(embeddingProviderSettings.Name, embeddingProviderSettings.Host, embeddingProviderSettings.Hostname, embeddingProviderSettings.Model, HFInferenceProvider.NONE,logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IProvider CreateProvider(this LLMProviders provider, string instanceName, Host host, string hostname, ILogger logger)
|
private static IProvider CreateProvider(this LLMProviders provider, string instanceName, Host host, string hostname, Model model, HFInferenceProvider inferenceProvider , ILogger logger)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -150,9 +161,11 @@ public static class LLMProvidersExtensions
|
|||||||
LLMProviders.GOOGLE => new ProviderGoogle(logger) { InstanceName = instanceName },
|
LLMProviders.GOOGLE => new ProviderGoogle(logger) { InstanceName = instanceName },
|
||||||
LLMProviders.X => new ProviderX(logger) { InstanceName = instanceName },
|
LLMProviders.X => new ProviderX(logger) { InstanceName = instanceName },
|
||||||
LLMProviders.DEEP_SEEK => new ProviderDeepSeek(logger) { InstanceName = instanceName },
|
LLMProviders.DEEP_SEEK => new ProviderDeepSeek(logger) { InstanceName = instanceName },
|
||||||
|
LLMProviders.ALIBABA_CLOUD => new ProviderAlibabaCloud(logger) { InstanceName = instanceName },
|
||||||
|
|
||||||
LLMProviders.GROQ => new ProviderGroq(logger) { InstanceName = instanceName },
|
LLMProviders.GROQ => new ProviderGroq(logger) { InstanceName = instanceName },
|
||||||
LLMProviders.FIREWORKS => new ProviderFireworks(logger) { InstanceName = instanceName },
|
LLMProviders.FIREWORKS => new ProviderFireworks(logger) { InstanceName = instanceName },
|
||||||
|
LLMProviders.HUGGINGFACE => new ProviderHuggingFace(logger, inferenceProvider, model) { InstanceName = instanceName },
|
||||||
|
|
||||||
LLMProviders.SELF_HOSTED => new ProviderSelfHosted(logger, host, hostname) { InstanceName = instanceName },
|
LLMProviders.SELF_HOSTED => new ProviderSelfHosted(logger, host, hostname) { InstanceName = instanceName },
|
||||||
|
|
||||||
@@ -177,9 +190,11 @@ public static class LLMProvidersExtensions
|
|||||||
LLMProviders.GOOGLE => "https://console.cloud.google.com/",
|
LLMProviders.GOOGLE => "https://console.cloud.google.com/",
|
||||||
LLMProviders.X => "https://accounts.x.ai/sign-up",
|
LLMProviders.X => "https://accounts.x.ai/sign-up",
|
||||||
LLMProviders.DEEP_SEEK => "https://platform.deepseek.com/sign_up",
|
LLMProviders.DEEP_SEEK => "https://platform.deepseek.com/sign_up",
|
||||||
|
LLMProviders.ALIBABA_CLOUD => "https://account.alibabacloud.com/register/intl_register.htm",
|
||||||
|
|
||||||
LLMProviders.GROQ => "https://console.groq.com/",
|
LLMProviders.GROQ => "https://console.groq.com/",
|
||||||
LLMProviders.FIREWORKS => "https://fireworks.ai/login",
|
LLMProviders.FIREWORKS => "https://fireworks.ai/login",
|
||||||
|
LLMProviders.HUGGINGFACE => "https://huggingface.co/login",
|
||||||
|
|
||||||
LLMProviders.HELMHOLTZ => "https://sdlaml.pages.jsc.fz-juelich.de/ai/guides/blablador_api_access/#step-1-register-on-gitlab",
|
LLMProviders.HELMHOLTZ => "https://sdlaml.pages.jsc.fz-juelich.de/ai/guides/blablador_api_access/#step-1-register-on-gitlab",
|
||||||
LLMProviders.GWDG => "https://docs.hpc.gwdg.de/services/saia/index.html#api-request",
|
LLMProviders.GWDG => "https://docs.hpc.gwdg.de/services/saia/index.html#api-request",
|
||||||
@@ -197,6 +212,8 @@ public static class LLMProvidersExtensions
|
|||||||
LLMProviders.GOOGLE => "https://console.cloud.google.com/billing",
|
LLMProviders.GOOGLE => "https://console.cloud.google.com/billing",
|
||||||
LLMProviders.FIREWORKS => "https://fireworks.ai/account/billing",
|
LLMProviders.FIREWORKS => "https://fireworks.ai/account/billing",
|
||||||
LLMProviders.DEEP_SEEK => "https://platform.deepseek.com/usage",
|
LLMProviders.DEEP_SEEK => "https://platform.deepseek.com/usage",
|
||||||
|
LLMProviders.ALIBABA_CLOUD => "https://usercenter2-intl.aliyun.com/billing",
|
||||||
|
LLMProviders.HUGGINGFACE => "https://huggingface.co/settings/billing",
|
||||||
|
|
||||||
_ => string.Empty,
|
_ => string.Empty,
|
||||||
};
|
};
|
||||||
@@ -211,19 +228,23 @@ public static class LLMProvidersExtensions
|
|||||||
LLMProviders.FIREWORKS => true,
|
LLMProviders.FIREWORKS => true,
|
||||||
LLMProviders.GOOGLE => true,
|
LLMProviders.GOOGLE => true,
|
||||||
LLMProviders.DEEP_SEEK => true,
|
LLMProviders.DEEP_SEEK => true,
|
||||||
|
LLMProviders.ALIBABA_CLOUD => true,
|
||||||
|
LLMProviders.HUGGINGFACE => true,
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static string GetModelsOverviewURL(this LLMProviders provider) => provider switch
|
public static string GetModelsOverviewURL(this LLMProviders provider, HFInferenceProvider inferenceProvider) => provider switch
|
||||||
{
|
{
|
||||||
LLMProviders.FIREWORKS => "https://fireworks.ai/models?show=Serverless",
|
LLMProviders.FIREWORKS => "https://fireworks.ai/models?show=Serverless",
|
||||||
|
LLMProviders.HUGGINGFACE => $"https://huggingface.co/models?inference_provider={inferenceProvider.EndpointsId()}",
|
||||||
_ => string.Empty,
|
_ => string.Empty,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static bool IsLLMModelProvidedManually(this LLMProviders provider) => provider switch
|
public static bool IsLLMModelProvidedManually(this LLMProviders provider) => provider switch
|
||||||
{
|
{
|
||||||
LLMProviders.FIREWORKS => true,
|
LLMProviders.FIREWORKS => true,
|
||||||
|
LLMProviders.HUGGINGFACE => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -253,11 +274,13 @@ public static class LLMProvidersExtensions
|
|||||||
LLMProviders.GOOGLE => true,
|
LLMProviders.GOOGLE => true,
|
||||||
LLMProviders.X => true,
|
LLMProviders.X => true,
|
||||||
LLMProviders.DEEP_SEEK => true,
|
LLMProviders.DEEP_SEEK => true,
|
||||||
|
LLMProviders.ALIBABA_CLOUD => true,
|
||||||
|
|
||||||
LLMProviders.GROQ => true,
|
LLMProviders.GROQ => true,
|
||||||
LLMProviders.FIREWORKS => true,
|
LLMProviders.FIREWORKS => true,
|
||||||
LLMProviders.HELMHOLTZ => true,
|
LLMProviders.HELMHOLTZ => true,
|
||||||
LLMProviders.GWDG => true,
|
LLMProviders.GWDG => true,
|
||||||
|
LLMProviders.HUGGINGFACE => true,
|
||||||
|
|
||||||
LLMProviders.SELF_HOSTED => host is Host.OLLAMA,
|
LLMProviders.SELF_HOSTED => host is Host.OLLAMA,
|
||||||
|
|
||||||
@@ -272,11 +295,13 @@ public static class LLMProvidersExtensions
|
|||||||
LLMProviders.GOOGLE => true,
|
LLMProviders.GOOGLE => true,
|
||||||
LLMProviders.X => true,
|
LLMProviders.X => true,
|
||||||
LLMProviders.DEEP_SEEK => true,
|
LLMProviders.DEEP_SEEK => true,
|
||||||
|
LLMProviders.ALIBABA_CLOUD => true,
|
||||||
|
|
||||||
LLMProviders.GROQ => true,
|
LLMProviders.GROQ => true,
|
||||||
LLMProviders.FIREWORKS => true,
|
LLMProviders.FIREWORKS => true,
|
||||||
LLMProviders.HELMHOLTZ => true,
|
LLMProviders.HELMHOLTZ => true,
|
||||||
LLMProviders.GWDG => true,
|
LLMProviders.GWDG => true,
|
||||||
|
LLMProviders.HUGGINGFACE => true,
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
@@ -306,4 +331,10 @@ public static class LLMProvidersExtensions
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsHFInstanceProviderNeeded(this LLMProviders provider) => provider switch
|
||||||
|
{
|
||||||
|
LLMProviders.HUGGINGFACE => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -122,7 +122,7 @@ public sealed class ProviderOpenAI(ILogger logger) : BaseProvider("https://api.o
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
public override Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
return this.LoadModels(["gpt-", "o1-"], token, apiKeyProvisional);
|
return this.LoadModels(["gpt-", "o1-", "o3-", "o4-"], token, apiKeyProvisional);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ public sealed partial class Routes
|
|||||||
public const string SETTINGS = "/settings";
|
public const string SETTINGS = "/settings";
|
||||||
public const string SUPPORTERS = "/supporters";
|
public const string SUPPORTERS = "/supporters";
|
||||||
public const string WRITER = "/writer";
|
public const string WRITER = "/writer";
|
||||||
|
public const string PLUGINS = "/plugins";
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
// ReSharper disable InconsistentNaming
|
||||||
public const string ASSISTANT_TRANSLATION = "/assistant/translation";
|
public const string ASSISTANT_TRANSLATION = "/assistant/translation";
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using AIStudio.Assistants.TextSummarizer;
|
|||||||
using AIStudio.Assistants.EMail;
|
using AIStudio.Assistants.EMail;
|
||||||
using AIStudio.Provider;
|
using AIStudio.Provider;
|
||||||
using AIStudio.Settings.DataModel;
|
using AIStudio.Settings.DataModel;
|
||||||
|
using AIStudio.Tools.PluginSystem;
|
||||||
|
|
||||||
using WritingStylesRewrite = AIStudio.Assistants.RewriteImprove.WritingStyles;
|
using WritingStylesRewrite = AIStudio.Assistants.RewriteImprove.WritingStyles;
|
||||||
using WritingStylesEMail = AIStudio.Assistants.EMail.WritingStyles;
|
using WritingStylesEMail = AIStudio.Assistants.EMail.WritingStyles;
|
||||||
@@ -25,6 +26,21 @@ public readonly record struct ConfigurationSelectData<T>(string Name, T Value);
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ConfigurationSelectDataFactory
|
public static class ConfigurationSelectDataFactory
|
||||||
{
|
{
|
||||||
|
public static IEnumerable<ConfigurationSelectData<LangBehavior>> GetLangBehaviorData()
|
||||||
|
{
|
||||||
|
foreach (var behavior in Enum.GetValues<LangBehavior>())
|
||||||
|
yield return new(behavior.Name(), behavior);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<ConfigurationSelectData<Guid>> GetLanguagesData()
|
||||||
|
{
|
||||||
|
foreach (var runningPlugin in PluginFactory.RunningPlugins)
|
||||||
|
{
|
||||||
|
if(runningPlugin is ILanguagePlugin languagePlugin)
|
||||||
|
yield return new(languagePlugin.LangName, runningPlugin.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static IEnumerable<ConfigurationSelectData<LoadingChatProviderBehavior>> GetLoadingChatProviderBehavior()
|
public static IEnumerable<ConfigurationSelectData<LoadingChatProviderBehavior>> GetLoadingChatProviderBehavior()
|
||||||
{
|
{
|
||||||
yield return new("When possible, use the LLM provider which was used for each chat in the first place", LoadingChatProviderBehavior.USE_CHAT_PROVIDER_IF_AVAILABLE);
|
yield return new("When possible, use the LLM provider which was used for each chat in the first place", LoadingChatProviderBehavior.USE_CHAT_PROVIDER_IF_AVAILABLE);
|
||||||
|
|||||||
Loaded 100 of 178 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user