mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-19 22:38: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 | ||
|
|
3ab8e7324e | ||
|
|
cf97fa888c | ||
|
|
dd23cfa1ff | ||
|
|
d6ead37102 | ||
|
|
686bb2ccb3 | ||
|
|
69d64f8b1f | ||
|
|
c3ab0ebb38 | ||
|
|
6af3cf4213 | ||
|
|
52c8052ce6 | ||
|
|
471536cc8f | ||
|
|
e5ce3263bc | ||
|
|
264b6dd716 | ||
|
|
d8ac2f3d49 | ||
|
|
64a7c9ab53 | ||
|
|
ee61425153 | ||
|
|
ce8d84a12a | ||
|
|
2e8444b5e3 | ||
|
|
5cc6e141c2 | ||
|
|
850d56cfcf | ||
|
|
d399b025a9 | ||
|
|
200964422d | ||
|
|
8a23b54e56 | ||
|
|
eccf65f0a6 | ||
|
|
2429788ccc | ||
|
|
0125f48f61 | ||
|
|
94028b2c06 | ||
|
|
c5acce7278 | ||
|
|
4eb51f947a | ||
|
|
e11211d215 | ||
|
|
032c7ee56e | ||
|
|
030990ee90 | ||
|
|
371731ac13 | ||
|
|
99622cec48 | ||
|
|
6a4a7dc0d6 | ||
|
|
df01ce188e | ||
|
|
3bfca45e11 | ||
|
|
ccaf2e894a | ||
|
|
1d22232005 | ||
|
|
a66d88c989 | ||
|
|
2e84f10a97 | ||
|
|
dbc2b4f6bc | ||
|
|
9d2b63bbaa | ||
|
|
277dc73468 | ||
|
|
ccac2b04ac | ||
|
|
0c4b94c527 | ||
|
|
37a9046be4 | ||
|
|
b984b4432e | ||
|
|
5e3b632eee |
No files matched your search
+5
-5
@@ -2,13 +2,13 @@
|
||||
* @MindWorkAI/maintainer
|
||||
|
||||
# The release team is responsible for anything inside the .github directory, such as workflows, actions, and issue templates:
|
||||
/.github/ @MindWorkAI/release
|
||||
|
||||
# The release team is responsible for the update directory:
|
||||
/.updates/ @MindWorkAI/release
|
||||
/.github/ @MindWorkAI/release @SommerEngineering
|
||||
|
||||
# Our Rust experts are responsible for the Rust codebase:
|
||||
/runtime/ @MindWorkAI/rust-experts
|
||||
|
||||
# Our .NET experts are responsible for the .NET codebase:
|
||||
/app/ @MindWorkAI/net-experts
|
||||
/app/ @MindWorkAI/net-experts
|
||||
|
||||
# The source code rules must be reviewed by the release team:
|
||||
/app/SourceCodeRules/ @MindWorkAI/release @SommerEngineering
|
||||
+1
-2
@@ -1,2 +1 @@
|
||||
github: [MindWorkAI]
|
||||
open_collective: mindwork-ai
|
||||
github: [MindWorkAI]
|
||||
@@ -1,6 +1,8 @@
|
||||
name: Build and Release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- "v*.*.*"
|
||||
|
||||
@@ -46,6 +48,7 @@ jobs:
|
||||
echo "version=${version}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Check tag vs. metadata version
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
run: |
|
||||
# Ensure, that the tag matches the version in the metadata file:
|
||||
if [ "${GITHUB_REF}" != "refs/tags/${FORMATTED_VERSION}" ]; then
|
||||
@@ -99,6 +102,12 @@ jobs:
|
||||
dotnet_runtime: 'linux-x64'
|
||||
dotnet_name_postfix: '-x86_64-unknown-linux-gnu'
|
||||
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
|
||||
rust_target: 'x86_64-pc-windows-msvc'
|
||||
@@ -151,6 +160,13 @@ jobs:
|
||||
# Format the 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:
|
||||
echo "APP_VERSION=${app_version}" >> $GITHUB_ENV
|
||||
echo "FORMATTED_APP_VERSION=${formatted_app_version}" >> $GITHUB_ENV
|
||||
@@ -161,6 +177,7 @@ jobs:
|
||||
echo "RUST_VERSION=${rust_version}" >> $GITHUB_ENV
|
||||
echo "MUD_BLAZOR_VERSION=${mud_blazor_version}" >> $GITHUB_ENV
|
||||
echo "TAURI_VERSION=${tauri_version}" >> $GITHUB_ENV
|
||||
echo "ARCHITECTURE=${{ matrix.dotnet_runtime }}" >> $GITHUB_ENV
|
||||
|
||||
# Log the metadata:
|
||||
echo "App version: '${formatted_app_version}'"
|
||||
@@ -171,6 +188,7 @@ jobs:
|
||||
echo "Rust version: '${rust_version}'"
|
||||
echo "MudBlazor version: '${mud_blazor_version}'"
|
||||
echo "Tauri version: '${tauri_version}'"
|
||||
echo "Architecture: '${{ matrix.dotnet_runtime }}'"
|
||||
|
||||
- name: Read and format metadata (Windows)
|
||||
if: matrix.platform == 'windows-latest'
|
||||
@@ -203,6 +221,12 @@ jobs:
|
||||
# Format the 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-Output "APP_VERSION=${app_version}" >> $env:GITHUB_ENV
|
||||
Write-Output "FORMATTED_APP_VERSION=${formatted_app_version}" >> $env:GITHUB_ENV
|
||||
@@ -212,6 +236,7 @@ jobs:
|
||||
Write-Output "DOTNET_RUNTIME_VERSION=${dotnet_runtime_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 "ARCHITECTURE=${{ matrix.dotnet_runtime }}" >> $env:GITHUB_ENV
|
||||
|
||||
# Log the metadata:
|
||||
Write-Output "App version: '${formatted_app_version}'"
|
||||
@@ -222,6 +247,7 @@ jobs:
|
||||
Write-Output "Rust version: '${rust_version}'"
|
||||
Write-Output "MudBlazor version: '${mud_blazor_version}'"
|
||||
Write-Output "Tauri version: '${tauri_version}'"
|
||||
Write-Output "Architecture: '${{ matrix.dotnet_runtime }}'"
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
@@ -274,6 +300,12 @@ jobs:
|
||||
sudo apt-get update
|
||||
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)
|
||||
if: matrix.platform != 'windows-latest'
|
||||
run: |
|
||||
@@ -291,6 +323,35 @@ jobs:
|
||||
} else {
|
||||
Write-Output "Tauri is already installed"
|
||||
}
|
||||
|
||||
- name: Delete previous artifact, which may exist due to caching (macOS)
|
||||
if: startsWith(matrix.platform, 'macos')
|
||||
run: |
|
||||
rm -f runtime/target/${{ matrix.rust_target }}/release/bundle/dmg/MindWork AI Studio_*.dmg
|
||||
rm -f runtime/target/${{ matrix.rust_target }}/release/bundle/macos/MindWork AI Studio.app.tar.gz*
|
||||
|
||||
- name: Delete previous artifact, which may exist due to caching (Windows - MSI)
|
||||
if: startsWith(matrix.platform, 'windows') && contains(matrix.tauri_bundle, 'msi')
|
||||
run: |
|
||||
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*" -ErrorAction SilentlyContinue
|
||||
|
||||
- name: Delete previous artifact, which may exist due to caching (Windows - NSIS)
|
||||
if: startsWith(matrix.platform, 'windows') && contains(matrix.tauri_bundle, 'nsis')
|
||||
run: |
|
||||
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*" -ErrorAction SilentlyContinue
|
||||
|
||||
- name: Delete previous artifact, which may exist due to caching (Linux - Debian Package)
|
||||
if: startsWith(matrix.platform, 'ubuntu') && contains(matrix.tauri_bundle, 'deb')
|
||||
run: |
|
||||
rm -f runtime/target/${{ matrix.rust_target }}/release/bundle/deb/mind-work-ai-studio_*.deb
|
||||
|
||||
- name: Delete previous artifact, which may exist due to caching (Linux - AppImage)
|
||||
if: startsWith(matrix.platform, 'ubuntu') && contains(matrix.tauri_bundle, 'appimage')
|
||||
run: |
|
||||
rm -f runtime/target/${{ matrix.rust_target }}/release/bundle/appimage/mind-work-ai-studio_*.AppImage
|
||||
rm -f runtime/target/${{ matrix.rust_target }}/release/bundle/appimage/mind-work-ai-studio*AppImage.tar.gz*
|
||||
|
||||
- name: Build Tauri project (Unix)
|
||||
if: matrix.platform != 'windows-latest'
|
||||
@@ -315,7 +376,7 @@ jobs:
|
||||
cargo tauri build --target ${{ matrix.rust_target }} --bundles ${{ matrix.tauri_bundle }}
|
||||
|
||||
- name: Upload artifact (macOS)
|
||||
if: startsWith(matrix.platform, 'macos')
|
||||
if: startsWith(matrix.platform, 'macos') && startsWith(github.ref, 'refs/tags/v')
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: MindWork AI Studio (macOS ${{ matrix.dotnet_runtime }})
|
||||
@@ -326,7 +387,7 @@ jobs:
|
||||
retention-days: ${{ env.RETENTION_INTERMEDIATE_ASSETS }}
|
||||
|
||||
- name: Upload artifact (Windows - MSI)
|
||||
if: startsWith(matrix.platform, 'windows') && contains(matrix.tauri_bundle, 'msi')
|
||||
if: startsWith(matrix.platform, 'windows') && contains(matrix.tauri_bundle, 'msi') && startsWith(github.ref, 'refs/tags/v')
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: MindWork AI Studio (Windows - MSI ${{ matrix.dotnet_runtime }})
|
||||
@@ -337,7 +398,7 @@ jobs:
|
||||
retention-days: ${{ env.RETENTION_INTERMEDIATE_ASSETS }}
|
||||
|
||||
- name: Upload artifact (Windows - NSIS)
|
||||
if: startsWith(matrix.platform, 'windows') && contains(matrix.tauri_bundle, 'nsis')
|
||||
if: startsWith(matrix.platform, 'windows') && contains(matrix.tauri_bundle, 'nsis') && startsWith(github.ref, 'refs/tags/v')
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: MindWork AI Studio (Windows - NSIS ${{ matrix.dotnet_runtime }})
|
||||
@@ -348,7 +409,7 @@ jobs:
|
||||
retention-days: ${{ env.RETENTION_INTERMEDIATE_ASSETS }}
|
||||
|
||||
- name: Upload artifact (Linux - Debian Package)
|
||||
if: startsWith(matrix.platform, 'ubuntu') && contains(matrix.tauri_bundle, 'deb')
|
||||
if: startsWith(matrix.platform, 'ubuntu') && contains(matrix.tauri_bundle, 'deb') && startsWith(github.ref, 'refs/tags/v')
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: MindWork AI Studio (Linux - deb ${{ matrix.dotnet_runtime }})
|
||||
@@ -358,7 +419,7 @@ jobs:
|
||||
retention-days: ${{ env.RETENTION_INTERMEDIATE_ASSETS }}
|
||||
|
||||
- name: Upload artifact (Linux - AppImage)
|
||||
if: startsWith(matrix.platform, 'ubuntu') && contains(matrix.tauri_bundle, 'appimage')
|
||||
if: startsWith(matrix.platform, 'ubuntu') && contains(matrix.tauri_bundle, 'appimage') && startsWith(github.ref, 'refs/tags/v')
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: MindWork AI Studio (Linux - AppImage ${{ matrix.dotnet_runtime }})
|
||||
@@ -367,205 +428,12 @@ jobs:
|
||||
runtime/target/${{ matrix.rust_target }}/release/bundle/appimage/mind-work-ai-studio*AppImage.tar.gz*
|
||||
if-no-files-found: error
|
||||
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
|
||||
|
||||
- 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: |
|
||||
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' }}
|
||||
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:
|
||||
name: Prepare & create release
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_main, read_metadata, build_linux_arm64]
|
||||
needs: [build_main, read_metadata]
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- name: Create artifact directory
|
||||
run: mkdir -p $GITHUB_WORKSPACE/artifacts
|
||||
@@ -723,6 +591,7 @@ jobs:
|
||||
name: Publish release
|
||||
runs-on: ubuntu-latest
|
||||
needs: [read_metadata, create_release]
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
@@ -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: 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))~~
|
||||
- [ ] 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)
|
||||
- [ ] 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 the process to vectorize one local file using embeddings
|
||||
- [ ] 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))~~
|
||||
|
||||
|
||||
- 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:
|
||||
|
||||
- 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:
|
||||
|
||||
- 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.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.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?
|
||||
|
||||
@@ -56,7 +68,19 @@ MindWork AI Studio is a free desktop app for macOS, Windows, and Linux. It provi
|
||||
|
||||
**Key advantages:**
|
||||
- **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), 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/).
|
||||
- **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.
|
||||
- **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.
|
||||
|
||||
@@ -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
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceCodeRules", "SourceCodeRules\SourceCodeRules\SourceCodeRules.csproj", "{0976C1CB-D499-4C86-8ADA-B7A7A4DE0BF8}"
|
||||
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
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
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}.Release|Any CPU.ActiveCfg = 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
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
EndGlobalSection
|
||||
|
||||
@@ -2,13 +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/=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/=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/=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/=LM/@EntryIndexedValue">LM</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MSG/@EntryIndexedValue">MSG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OS/@EntryIndexedValue">OS</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=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/=URL/@EntryIndexedValue">URL</s:String>
|
||||
<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/=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/=ollama/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=tauri_0027s/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@@ -199,7 +199,7 @@ public sealed class AgentDataSourceSelection (ILogger<AgentDataSourceSelection>
|
||||
// Call the ERI server to get the server description:
|
||||
//
|
||||
using var eriClient = ERIClientFactory.Get(eriDataSource.Version, eriDataSource)!;
|
||||
var authResponse = await eriClient.AuthenticateAsync(eriDataSource, rustService, token);
|
||||
var authResponse = await eriClient.AuthenticateAsync(rustService, cancellationToken: token);
|
||||
if (authResponse.Successful)
|
||||
{
|
||||
var serverDescriptionResponse = await eriClient.GetDataSourceInfoAsync(token);
|
||||
@@ -315,26 +315,6 @@ public sealed class AgentDataSourceSelection (ILogger<AgentDataSourceSelection>
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the JSON list from the given text. The text may contain additional
|
||||
/// information around the JSON list. The method tries to extract the JSON list
|
||||
/// from the text.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Algorithm: The method searches for the first line that contains only a '[' character.
|
||||
/// Then, it searches for the first line that contains only a ']' character. The method
|
||||
/// returns the text between these two lines (including the brackets). When the method
|
||||
/// cannot find the JSON list, it returns an empty string.
|
||||
/// <br/><br/>
|
||||
/// This overload is using strings instead of spans. We can use this overload in any
|
||||
/// async method. Thus, it is a wrapper around the span-based method. Yes, we are losing
|
||||
/// the memory efficiency of the span-based method, but we still gain the performance
|
||||
/// of the span-based method: the entire search algorithm is span-based.
|
||||
/// </remarks>
|
||||
/// <param name="text">The text that may contain the JSON list.</param>
|
||||
/// <returns>The extracted JSON list.</returns>
|
||||
private static string ExtractJson(string text) => ExtractJson(text.AsSpan()).ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the JSON list from the given text. The text may contain additional
|
||||
|
||||
@@ -319,9 +319,6 @@ public sealed class AgentRetrievalContextValidation (ILogger<AgentRetrievalConte
|
||||
}
|
||||
}
|
||||
|
||||
// A wrapper around the span version, because we need to call this method from an async context.
|
||||
private static string ExtractJson(string text) => ExtractJson(text.AsSpan()).ToString();
|
||||
|
||||
private static ReadOnlySpan<char> ExtractJson(ReadOnlySpan<char> input)
|
||||
{
|
||||
//
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@attribute [Route(Routes.ASSISTANT_AGENDA)]
|
||||
@inherits AssistantBaseCore
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogAgenda>
|
||||
|
||||
<MudTextField T="string" @bind-Text="@this.inputName" Validation="@this.ValidateName" Label="Meeting Name" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Tag" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Name the meeting, seminar, etc." Placeholder="Weekly jour fixe" Class="mb-3"/>
|
||||
<MudTextField T="string" @bind-Text="@this.inputTopic" Validation="@this.ValidateTopic" Label="Topic" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.EventNote" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Describe the topic of the meeting, seminar, etc. Is it about quantum computing, software engineering, or is it a general business meeting?" Placeholder="Project meeting" Class="mb-3"/>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using System.Text;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
|
||||
namespace AIStudio.Assistants.Agenda;
|
||||
|
||||
public partial class AssistantAgenda : AssistantBaseCore
|
||||
public partial class AssistantAgenda : AssistantBaseCore<SettingsDialogAgenda>
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.AGENDA_ASSISTANT;
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
@using AIStudio.Chat
|
||||
@inherits AssistantLowerBase
|
||||
@typeparam TSettings
|
||||
|
||||
<div class="inner-scrolling-context">
|
||||
|
||||
@@ -9,9 +11,16 @@
|
||||
<InnerScrolling>
|
||||
<ChildContent>
|
||||
<MudForm @ref="@(this.form)" @bind-IsValid="@(this.inputIsValid)" @bind-Errors="@(this.inputIssues)" FieldChanged="@this.TriggerFormChange" Class="pr-2">
|
||||
<MudText Typo="Typo.body1" Align="Align.Justify" Class="mb-6">
|
||||
@this.Description
|
||||
</MudText>
|
||||
<MudGrid Class="mb-2">
|
||||
<MudItem xs="10">
|
||||
<MudText Typo="Typo.body1" Align="Align.Justify">
|
||||
@this.Description
|
||||
</MudText>
|
||||
</MudItem>
|
||||
<MudItem xs="2" Class="d-flex justify-end align-start">
|
||||
<MudIconButton Variant="Variant.Filled" Icon="@Icons.Material.Filled.Settings" OnClick="() => this.OpenSettingsDialog()"/>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
|
||||
@if (this.Body is not null)
|
||||
{
|
||||
@@ -19,9 +28,17 @@
|
||||
@this.Body
|
||||
</CascadingValue>
|
||||
|
||||
<MudButton Disabled="@this.SubmitDisabled" Variant="Variant.Filled" Class="mb-3" OnClick="() => this.SubmitAction()" Style="@this.SubmitButtonStyle">
|
||||
@this.SubmitText
|
||||
</MudButton>
|
||||
<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
|
||||
</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>
|
||||
<Issues IssuesData="@(this.inputIssues)"/>
|
||||
@@ -63,7 +80,7 @@
|
||||
{
|
||||
@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))
|
||||
{
|
||||
<MudMenuItem OnClick="() => this.SendToAssistant(assistant, new())">
|
||||
@@ -93,7 +110,7 @@
|
||||
break;
|
||||
|
||||
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))
|
||||
{
|
||||
<MudMenuItem OnClick="() => this.SendToAssistant(assistant, sendToButton)">
|
||||
|
||||
@@ -9,13 +9,18 @@ using MudBlazor.Utilities;
|
||||
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
namespace AIStudio.Assistants;
|
||||
|
||||
public abstract partial class AssistantBase : ComponentBase, IMessageBusReceiver, IDisposable
|
||||
public abstract partial class AssistantBase<TSettings> : AssistantLowerBase, IMessageBusReceiver, IDisposable where TSettings : IComponent
|
||||
{
|
||||
[Inject]
|
||||
protected SettingsManager SettingsManager { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected IJSRuntime JsRuntime { get; init; } = null!;
|
||||
|
||||
@@ -28,14 +33,11 @@ public abstract partial class AssistantBase : ComponentBase, IMessageBusReceiver
|
||||
[Inject]
|
||||
protected RustService RustService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected DataSourceService DataSourceService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected NavigationManager NavigationManager { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected ILogger<AssistantBase> Logger { get; init; } = null!;
|
||||
protected ILogger<AssistantBase<TSettings>> Logger { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private MudTheme ColorTheme { get; init; } = null!;
|
||||
@@ -43,10 +45,6 @@ public abstract partial class AssistantBase : ComponentBase, IMessageBusReceiver
|
||||
[Inject]
|
||||
private MessageBus MessageBus { get; init; } = null!;
|
||||
|
||||
internal const string RESULT_DIV_ID = "assistantResult";
|
||||
internal const string BEFORE_RESULT_DIV_ID = "beforeAssistantResult";
|
||||
internal const string AFTER_RESULT_DIV_ID = "afterAssistantResult";
|
||||
|
||||
protected abstract string Title { get; }
|
||||
|
||||
protected abstract string Description { get; }
|
||||
@@ -68,7 +66,7 @@ public abstract partial class AssistantBase : ComponentBase, IMessageBusReceiver
|
||||
protected abstract string SubmitText { get; }
|
||||
|
||||
protected abstract Func<Task> SubmitAction { get; }
|
||||
|
||||
|
||||
protected virtual bool SubmitDisabled => false;
|
||||
|
||||
private protected virtual RenderFragment? Body => null;
|
||||
@@ -92,8 +90,6 @@ public abstract partial class AssistantBase : ComponentBase, IMessageBusReceiver
|
||||
protected virtual ChatThread ConvertToChatThread => this.chatThread ?? new();
|
||||
|
||||
protected virtual IReadOnlyList<IButtonData> FooterButtons => [];
|
||||
|
||||
protected static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();
|
||||
|
||||
protected AIStudio.Settings.Provider providerSettings;
|
||||
protected MudForm? form;
|
||||
@@ -104,6 +100,7 @@ public abstract partial class AssistantBase : ComponentBase, IMessageBusReceiver
|
||||
|
||||
private readonly Timer formChangeTimer = new(TimeSpan.FromSeconds(1.6));
|
||||
|
||||
private CancellationTokenSource? cancellationTokenSource;
|
||||
private ContentBlock? resultingContentBlock;
|
||||
private string[] inputIssues = [];
|
||||
private bool isProcessing;
|
||||
@@ -151,7 +148,7 @@ public abstract partial class AssistantBase : ComponentBase, IMessageBusReceiver
|
||||
|
||||
#region Implementation of IMessageBusReceiver
|
||||
|
||||
public string ComponentName => nameof(AssistantBase);
|
||||
public string ComponentName => nameof(AssistantBase<TSettings>);
|
||||
|
||||
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
||||
{
|
||||
@@ -289,12 +286,16 @@ public abstract partial class AssistantBase : ComponentBase, IMessageBusReceiver
|
||||
|
||||
this.isProcessing = true;
|
||||
this.StateHasChanged();
|
||||
|
||||
// Use the selected provider to get the AI response.
|
||||
// By awaiting this line, we wait for the entire
|
||||
// content to be streamed.
|
||||
await aiText.CreateFromProviderAsync(this.providerSettings.CreateProvider(this.Logger), this.providerSettings.Model, this.lastUserPrompt, this.chatThread);
|
||||
|
||||
|
||||
using (this.cancellationTokenSource = new())
|
||||
{
|
||||
// Use the selected provider to get the AI response.
|
||||
// By awaiting this line, we wait for the entire
|
||||
// content to be streamed.
|
||||
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.StateHasChanged();
|
||||
|
||||
@@ -302,6 +303,13 @@ public abstract partial class AssistantBase : ComponentBase, IMessageBusReceiver
|
||||
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()
|
||||
{
|
||||
await this.RustService.CopyText2Clipboard(this.Snackbar, this.Result2Copy());
|
||||
@@ -315,6 +323,12 @@ public abstract partial class AssistantBase : ComponentBase, IMessageBusReceiver
|
||||
return icon;
|
||||
}
|
||||
|
||||
protected async Task OpenSettingsDialog()
|
||||
{
|
||||
var dialogParameters = new DialogParameters();
|
||||
await this.DialogService.ShowAsync<TSettings>(null, dialogParameters, DialogOptions.FULLSCREEN);
|
||||
}
|
||||
|
||||
protected Task SendToAssistant(Tools.Components destination, SendToButton sendToButton)
|
||||
{
|
||||
if (!destination.AllowSendTo())
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace AIStudio.Assistants;
|
||||
// See https://stackoverflow.com/a/77300384/2258393 for why this class is necessary
|
||||
//
|
||||
|
||||
public abstract class AssistantBaseCore : AssistantBase
|
||||
public abstract class AssistantBaseCore<TSettings> : AssistantBase<TSettings> where TSettings : IComponent
|
||||
{
|
||||
private protected sealed override RenderFragment Body => this.BuildRenderTree;
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Assistants;
|
||||
|
||||
public abstract class AssistantLowerBase : ComponentBase
|
||||
{
|
||||
protected static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();
|
||||
|
||||
internal const string RESULT_DIV_ID = "assistantResult";
|
||||
internal const string BEFORE_RESULT_DIV_ID = "beforeAssistantResult";
|
||||
internal const string AFTER_RESULT_DIV_ID = "afterAssistantResult";
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
@attribute [Route(Routes.ASSISTANT_BIAS)]
|
||||
@inherits AssistantBaseCore
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogAssistantBias>
|
||||
|
||||
<MudText Typo="Typo.body1">
|
||||
<b>Links:</b>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using System.Text;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
|
||||
namespace AIStudio.Assistants.BiasDay;
|
||||
|
||||
public partial class BiasOfTheDayAssistant : AssistantBaseCore
|
||||
public partial class BiasOfTheDayAssistant : AssistantBaseCore<SettingsDialogAssistantBias>
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.BIAS_DAY_ASSISTANT;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@attribute [Route(Routes.ASSISTANT_CODING)]
|
||||
@inherits AssistantBaseCore
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogCoding>
|
||||
|
||||
<MudExpansionPanels Class="mb-3">
|
||||
@for (var contextIndex = 0; contextIndex < this.codingContexts.Count; contextIndex++)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Text;
|
||||
|
||||
using AIStudio.Dialogs.Settings;
|
||||
|
||||
namespace AIStudio.Assistants.Coding;
|
||||
|
||||
public partial class AssistantCoding : AssistantBaseCore
|
||||
public partial class AssistantCoding : AssistantBaseCore<SettingsDialogCoding>
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.CODING_ASSISTANT;
|
||||
|
||||
@@ -31,7 +33,7 @@ public partial class AssistantCoding : AssistantBaseCore
|
||||
protected override string SubmitText => "Get Support";
|
||||
|
||||
protected override Func<Task> SubmitAction => this.GetSupport;
|
||||
|
||||
|
||||
protected override void ResetForm()
|
||||
{
|
||||
this.codingContexts.Clear();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@attribute [Route(Routes.ASSISTANT_EMAIL)]
|
||||
@inherits AssistantBaseCore
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogWritingEMails>
|
||||
|
||||
<MudTextSwitch Label="Is there a history, a previous conversation?" @bind-Value="@this.provideHistory" LabelOn="Yes, I provide the previous conversation" LabelOff="No, I don't provide a previous conversation" />
|
||||
@if (this.provideHistory)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using System.Text;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
|
||||
namespace AIStudio.Assistants.EMail;
|
||||
|
||||
public partial class AssistantEMail : AssistantBaseCore
|
||||
public partial class AssistantEMail : AssistantBaseCore<SettingsDialogWritingEMails>
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.EMAIL_ASSISTANT;
|
||||
|
||||
@@ -27,7 +28,7 @@ public partial class AssistantEMail : AssistantBaseCore
|
||||
protected override string SubmitText => "Create email";
|
||||
|
||||
protected override Func<Task> SubmitAction => this.CreateMail;
|
||||
|
||||
|
||||
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||
{
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
|
||||
@@ -2,15 +2,12 @@ namespace AIStudio.Assistants.ERI;
|
||||
|
||||
public static class AllowedLLMProvidersExtensions
|
||||
{
|
||||
public static string Description(this AllowedLLMProviders provider)
|
||||
public static string Description(this AllowedLLMProviders provider) => provider switch
|
||||
{
|
||||
return provider switch
|
||||
{
|
||||
AllowedLLMProviders.NONE => "Please select what kind of LLM provider are allowed for this data source",
|
||||
AllowedLLMProviders.ANY => "Any LLM provider is allowed: users might choose a cloud-based or a self-hosted provider",
|
||||
AllowedLLMProviders.SELF_HOSTED => "Self-hosted LLM providers are allowed: users cannot choose any cloud-based provider",
|
||||
|
||||
_ => "Unknown option was selected"
|
||||
};
|
||||
}
|
||||
AllowedLLMProviders.NONE => "Please select what kind of LLM provider are allowed for this data source",
|
||||
AllowedLLMProviders.ANY => "Any LLM provider is allowed: users might choose a cloud-based or a self-hosted provider",
|
||||
AllowedLLMProviders.SELF_HOSTED => "Self-hosted LLM providers are allowed: users cannot choose any cloud-based provider",
|
||||
|
||||
_ => "Unknown option was selected"
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
@attribute [Route(Routes.ASSISTANT_ERI)]
|
||||
@using AIStudio.Settings.DataModel
|
||||
@using MudExtensions
|
||||
@inherits AssistantBaseCore
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogERIServer>
|
||||
|
||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||
You can imagine it like this: Hypothetically, when Wikipedia implemented the ERI, it would vectorize
|
||||
@@ -228,18 +228,20 @@ else
|
||||
<HeaderContent>
|
||||
<MudTh>Name</MudTh>
|
||||
<MudTh>Type</MudTh>
|
||||
<MudTh Style="text-align: left;">Actions</MudTh>
|
||||
<MudTh>Actions</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd>@context.EmbeddingName</MudTd>
|
||||
<MudTd>@context.EmbeddingType</MudTd>
|
||||
<MudTd Style="text-align: left;">
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" Class="ma-2" OnClick="() => this.EditEmbedding(context)">
|
||||
Edit
|
||||
</MudButton>
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" Class="ma-2" OnClick="() => this.DeleteEmbedding(context)">
|
||||
Delete
|
||||
</MudButton>
|
||||
<MudTd>
|
||||
<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
|
||||
</MudButton>
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteEmbedding(context)">
|
||||
Delete
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
@@ -274,17 +276,19 @@ else
|
||||
</ColGroup>
|
||||
<HeaderContent>
|
||||
<MudTh>Name</MudTh>
|
||||
<MudTh Style="text-align: left;">Actions</MudTh>
|
||||
<MudTh>Actions</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd>@context.Name</MudTd>
|
||||
<MudTd Style="text-align: left;">
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" Class="ma-2" OnClick="() => this.EditRetrievalProcess(context)">
|
||||
Edit
|
||||
</MudButton>
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" Class="ma-2" OnClick="() => this.DeleteRetrievalProcess(context)">
|
||||
Delete
|
||||
</MudButton>
|
||||
<MudTd>
|
||||
<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
|
||||
</MudButton>
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteRetrievalProcess(context)">
|
||||
Delete
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Text.RegularExpressions;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
@@ -11,7 +12,7 @@ using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
namespace AIStudio.Assistants.ERI;
|
||||
|
||||
public partial class AssistantERI : AssistantBaseCore
|
||||
public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
||||
{
|
||||
[Inject]
|
||||
private HttpClient HttpClient { get; set; } = null!;
|
||||
@@ -290,6 +291,7 @@ public partial class AssistantERI : AssistantBaseCore
|
||||
- You consider the security of the implementation by applying the Security by Design principle.
|
||||
- Your output is formatted as Markdown. Code is formatted as code blocks. For every file, you
|
||||
create a separate code block with its file path and name as chapter title.
|
||||
- Important: The JSON objects of the API messages use camel case for the data field names.
|
||||
""");
|
||||
|
||||
return sb.ToString();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@attribute [Route(Routes.ASSISTANT_GRAMMAR_SPELLING)]
|
||||
@inherits AssistantBaseCore
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogGrammarSpelling>
|
||||
|
||||
<MudTextField T="string" @bind-Text="@this.inputText" Validation="@this.ValidateText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="Your input to check" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom language" />
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
|
||||
namespace AIStudio.Assistants.GrammarSpelling;
|
||||
|
||||
public partial class AssistantGrammarSpelling : AssistantBaseCore
|
||||
public partial class AssistantGrammarSpelling : AssistantBaseCore<SettingsDialogGrammarSpelling>
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.GRAMMAR_SPELLING_ASSISTANT;
|
||||
|
||||
@@ -42,7 +43,7 @@ public partial class AssistantGrammarSpelling : AssistantBaseCore
|
||||
protected override string SubmitText => "Proofread";
|
||||
|
||||
protected override Func<Task> SubmitAction => this.ProofreadText;
|
||||
|
||||
|
||||
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||
{
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@attribute [Route(Routes.ASSISTANT_ICON_FINDER)]
|
||||
@inherits AssistantBaseCore
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogIconFinder>
|
||||
|
||||
<MudTextField T="string" @bind-Text="@this.inputContext" Validation="@this.ValidatingContext" AdornmentIcon="@Icons.Material.Filled.Description" Adornment="Adornment.Start" Label="Your context" Variant="Variant.Outlined" Lines="3" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using AIStudio.Dialogs.Settings;
|
||||
|
||||
namespace AIStudio.Assistants.IconFinder;
|
||||
|
||||
public partial class AssistantIconFinder : AssistantBaseCore
|
||||
public partial class AssistantIconFinder : AssistantBaseCore<SettingsDialogIconFinder>
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.ICON_FINDER_ASSISTANT;
|
||||
|
||||
@@ -32,7 +34,7 @@ public partial class AssistantIconFinder : AssistantBaseCore
|
||||
protected override string SubmitText => "Find Icon";
|
||||
|
||||
protected override Func<Task> SubmitAction => this.FindIcon;
|
||||
|
||||
|
||||
protected override void ResetForm()
|
||||
{
|
||||
this.inputContext = string.Empty;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@attribute [Route(Routes.ASSISTANT_JOB_POSTING)]
|
||||
@inherits AssistantBaseCore
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogJobPostings>
|
||||
|
||||
<MudTextField T="string" @bind-Text="@this.inputCompanyName" Label="(Optional) The company name" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Warehouse" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||
<MudTextField T="string" @bind-Text="@this.inputCountryLegalFramework" Label="Provide the country, where the company is located" Validation="@this.ValidateCountryLegalFramework" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Flag" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3" HelperText="This is important to consider the legal framework of the country."/>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
|
||||
namespace AIStudio.Assistants.JobPosting;
|
||||
|
||||
public partial class AssistantJobPostings : AssistantBaseCore
|
||||
public partial class AssistantJobPostings : AssistantBaseCore<SettingsDialogJobPostings>
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.JOB_POSTING_ASSISTANT;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@attribute [Route(Routes.ASSISTANT_LEGAL_CHECK)]
|
||||
@inherits AssistantBaseCore
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogLegalCheck>
|
||||
|
||||
@if (!this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
|
||||
namespace AIStudio.Assistants.LegalCheck;
|
||||
|
||||
public partial class AssistantLegalCheck : AssistantBaseCore
|
||||
public partial class AssistantLegalCheck : AssistantBaseCore<SettingsDialogLegalCheck>
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.LEGAL_CHECK_ASSISTANT;
|
||||
|
||||
@@ -29,7 +30,7 @@ public partial class AssistantLegalCheck : AssistantBaseCore
|
||||
protected override string SubmitText => "Ask your questions";
|
||||
|
||||
protected override Func<Task> SubmitAction => this.AksQuestions;
|
||||
|
||||
|
||||
protected override bool SubmitDisabled => this.isAgentRunning;
|
||||
|
||||
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@attribute [Route(Routes.ASSISTANT_MY_TASKS)]
|
||||
@inherits AssistantBaseCore
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogMyTasks>
|
||||
|
||||
<ProfileFormSelection Validation="@this.ValidateProfile" @bind-Profile="@this.currentProfile"/>
|
||||
<MudTextField T="string" @bind-Text="@this.inputText" Validation="@this.ValidatingText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="Text or email" Variant="Variant.Outlined" Lines="12" AutoGrow="@true" MaxLines="24" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Assistants.MyTasks;
|
||||
|
||||
public partial class AssistantMyTasks : AssistantBaseCore
|
||||
public partial class AssistantMyTasks : AssistantBaseCore<SettingsDialogMyTasks>
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.MY_TASKS_ASSISTANT;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@attribute [Route(Routes.ASSISTANT_REWRITE)]
|
||||
@inherits AssistantBaseCore
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogRewrite>
|
||||
|
||||
<MudTextField T="string" @bind-Text="@this.inputText" Validation="@this.ValidateText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="Your input to improve" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom language" />
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
|
||||
namespace AIStudio.Assistants.RewriteImprove;
|
||||
|
||||
public partial class AssistantRewriteImprove : AssistantBaseCore
|
||||
public partial class AssistantRewriteImprove : AssistantBaseCore<SettingsDialogRewrite>
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.REWRITE_ASSISTANT;
|
||||
|
||||
@@ -43,7 +44,7 @@ public partial class AssistantRewriteImprove : AssistantBaseCore
|
||||
protected override string SubmitText => "Improve";
|
||||
|
||||
protected override Func<Task> SubmitAction => this.RewriteText;
|
||||
|
||||
|
||||
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||
{
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@attribute [Route(Routes.ASSISTANT_SYNONYMS)]
|
||||
@inherits AssistantBaseCore
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogSynonyms>
|
||||
|
||||
<MudTextField T="string" @bind-Text="@this.inputText" Validation="@this.ValidatingText" AdornmentIcon="@Icons.Material.Filled.Spellcheck" Adornment="Adornment.Start" Label="Your word or phrase" Variant="Variant.Outlined" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
<MudTextField T="string" @bind-Text="@this.inputContext" AdornmentIcon="@Icons.Material.Filled.Description" Adornment="Adornment.Start" Lines="2" AutoGrow="@false" Label="(Optional) The context for the given word or phrase" Variant="Variant.Outlined" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
|
||||
namespace AIStudio.Assistants.Synonym;
|
||||
|
||||
public partial class AssistantSynonyms : AssistantBaseCore
|
||||
public partial class AssistantSynonyms : AssistantBaseCore<SettingsDialogSynonyms>
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.SYNONYMS_ASSISTANT;
|
||||
|
||||
@@ -54,7 +55,7 @@ public partial class AssistantSynonyms : AssistantBaseCore
|
||||
protected override string SubmitText => "Find synonyms";
|
||||
|
||||
protected override Func<Task> SubmitAction => this.FindSynonyms;
|
||||
|
||||
|
||||
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||
{
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@attribute [Route(Routes.ASSISTANT_SUMMARIZER)]
|
||||
@inherits AssistantBaseCore
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogTextSummarizer>
|
||||
|
||||
@if (!this.SettingsManager.ConfigurationData.TextSummarizer.HideWebContentReader)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
|
||||
namespace AIStudio.Assistants.TextSummarizer;
|
||||
|
||||
public partial class AssistantTextSummarizer : AssistantBaseCore
|
||||
public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogTextSummarizer>
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.TEXT_SUMMARIZER_ASSISTANT;
|
||||
|
||||
@@ -32,7 +33,7 @@ public partial class AssistantTextSummarizer : AssistantBaseCore
|
||||
protected override string SubmitText => "Summarize";
|
||||
|
||||
protected override Func<Task> SubmitAction => this.SummarizeText;
|
||||
|
||||
|
||||
protected override bool SubmitDisabled => this.isAgentRunning;
|
||||
|
||||
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@attribute [Route(Routes.ASSISTANT_TRANSLATION)]
|
||||
@inherits AssistantBaseCore
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogTranslation>
|
||||
|
||||
@if (!this.SettingsManager.ConfigurationData.Translation.HideWebContentReader)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
|
||||
namespace AIStudio.Assistants.Translation;
|
||||
|
||||
public partial class AssistantTranslation : AssistantBaseCore
|
||||
public partial class AssistantTranslation : AssistantBaseCore<SettingsDialogTranslation>
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.TRANSLATION_ASSISTANT;
|
||||
|
||||
@@ -28,7 +29,7 @@ public partial class AssistantTranslation : AssistantBaseCore
|
||||
protected override string SubmitText => "Translate";
|
||||
|
||||
protected override Func<Task> SubmitAction => () => this.TranslateText(true);
|
||||
|
||||
|
||||
protected override bool SubmitDisabled => this.isAgentRunning;
|
||||
|
||||
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||
|
||||
@@ -12,7 +12,6 @@ public enum ChatRole
|
||||
USER,
|
||||
AI,
|
||||
AGENT,
|
||||
RAG,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -40,6 +40,16 @@ public sealed record ChatThread
|
||||
/// </summary>
|
||||
public IReadOnlyList<DataSourceAgentSelected> AISelectedDataSources { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The augmented data for this chat thread. Will be inserted into the system prompt.
|
||||
/// </summary>
|
||||
public string AugmentedData { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The data security to use, derived from the data sources used so far.
|
||||
/// </summary>
|
||||
public DataSourceSecurity DataSecurity { get; set; } = DataSourceSecurity.NOT_SPECIFIED;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the chat thread. Usually generated by an AI model or manually edited by the user.
|
||||
/// </summary>
|
||||
@@ -74,31 +84,48 @@ public sealed record ChatThread
|
||||
/// <returns>The prepared system prompt.</returns>
|
||||
public string PrepareSystemPrompt(SettingsManager settingsManager, ChatThread chatThread, ILogger logger)
|
||||
{
|
||||
var isAugmentedDataAvailable = !string.IsNullOrWhiteSpace(chatThread.AugmentedData);
|
||||
var systemPromptWithAugmentedData = isAugmentedDataAvailable switch
|
||||
{
|
||||
true => $"""
|
||||
{chatThread.SystemPrompt}
|
||||
|
||||
{chatThread.AugmentedData}
|
||||
""",
|
||||
|
||||
false => chatThread.SystemPrompt,
|
||||
};
|
||||
|
||||
if(isAugmentedDataAvailable)
|
||||
logger.LogInformation("Augmented data is available for the chat thread.");
|
||||
else
|
||||
logger.LogInformation("No augmented data is available for the chat thread.");
|
||||
|
||||
//
|
||||
// Prepare the system prompt:
|
||||
//
|
||||
string systemPromptText;
|
||||
var logMessage = $"Using no profile for chat thread '{chatThread.Name}'.";
|
||||
if (string.IsNullOrWhiteSpace(chatThread.SelectedProfile))
|
||||
systemPromptText = chatThread.SystemPrompt;
|
||||
systemPromptText = systemPromptWithAugmentedData;
|
||||
else
|
||||
{
|
||||
if(!Guid.TryParse(chatThread.SelectedProfile, out var profileId))
|
||||
systemPromptText = chatThread.SystemPrompt;
|
||||
systemPromptText = systemPromptWithAugmentedData;
|
||||
else
|
||||
{
|
||||
if(chatThread.SelectedProfile == Profile.NO_PROFILE.Id || profileId == Guid.Empty)
|
||||
systemPromptText = chatThread.SystemPrompt;
|
||||
systemPromptText = systemPromptWithAugmentedData;
|
||||
else
|
||||
{
|
||||
var profile = settingsManager.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == chatThread.SelectedProfile);
|
||||
if(profile == default)
|
||||
systemPromptText = chatThread.SystemPrompt;
|
||||
systemPromptText = systemPromptWithAugmentedData;
|
||||
else
|
||||
{
|
||||
logMessage = $"Using profile '{profile.Name}' for chat thread '{chatThread.Name}'.";
|
||||
systemPromptText = $"""
|
||||
{chatThread.SystemPrompt}
|
||||
{systemPromptWithAugmentedData}
|
||||
|
||||
{profile.ToSystemPrompt()}
|
||||
""";
|
||||
@@ -183,7 +210,7 @@ public sealed record ChatThread
|
||||
ChatRole.SYSTEM => Role.SYSTEM,
|
||||
ChatRole.NONE => Role.NONE,
|
||||
|
||||
_ => Role.UNKNOW,
|
||||
_ => Role.UNKNOWN,
|
||||
},
|
||||
|
||||
Content = contentData,
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
using AIStudio.Provider.SelfHosted;
|
||||
using AIStudio.Settings.DataModel;
|
||||
|
||||
namespace AIStudio.Chat;
|
||||
|
||||
public static class ChatThreadExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the specified provider is allowed for the chat thread.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We don't check if the provider is allowed to use the data sources of the chat thread.
|
||||
/// That kind of check is done in the RAG process itself.<br/><br/>
|
||||
///
|
||||
/// One thing which is not so obvious: after RAG was used on this thread, the entire chat
|
||||
/// thread is kind of a data source by itself. Why? Because the augmentation data collected
|
||||
/// from the data sources is stored in the chat thread. This means we must check if the
|
||||
/// selected provider is allowed to use this thread's data.
|
||||
/// </remarks>
|
||||
/// <param name="chatThread">The chat thread to check.</param>
|
||||
/// <param name="provider">The provider to check.</param>
|
||||
/// <returns>True, when the provider is allowed for the chat thread. False, otherwise.</returns>
|
||||
public static bool IsLLMProviderAllowed<T>(this ChatThread? chatThread, T provider)
|
||||
{
|
||||
// No chat thread available means we have a new chat. That's fine:
|
||||
if (chatThread is null)
|
||||
return true;
|
||||
|
||||
// The chat thread is available, but the data security is not specified.
|
||||
// Means, we never used RAG or RAG was enabled, but no data sources were selected.
|
||||
// That's fine as well:
|
||||
if (chatThread.DataSecurity is DataSourceSecurity.NOT_SPECIFIED)
|
||||
return true;
|
||||
|
||||
//
|
||||
// Is the provider self-hosted?
|
||||
//
|
||||
var isSelfHostedProvider = provider switch
|
||||
{
|
||||
ProviderSelfHosted => true,
|
||||
AIStudio.Settings.Provider p => p.IsSelfHosted,
|
||||
|
||||
_ => false,
|
||||
};
|
||||
|
||||
//
|
||||
// Check the chat data security against the selected provider:
|
||||
//
|
||||
return isSelfHostedProvider switch
|
||||
{
|
||||
// The provider is self-hosted -- we can use any data source:
|
||||
true => true,
|
||||
|
||||
// The provider is not self-hosted -- it depends on the data security of the chat thread:
|
||||
false => chatThread.DataSecurity is not DataSourceSecurity.SELF_HOSTED,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@
|
||||
@if (this.IsLastContentBlock && this.Role is ChatRole.AI && this.RegenerateFunc is not null)
|
||||
{
|
||||
<MudTooltip Text="Regenerate" Placement="Placement.Bottom">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Recycling" Color="Color.Default" OnClick="@this.RegenerateBlock"/>
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Recycling" Color="Color.Default" Disabled="@(!this.RegenerateEnabled())" OnClick="@this.RegenerateBlock"/>
|
||||
</MudTooltip>
|
||||
}
|
||||
@if (this.RemoveBlockFunc is not null)
|
||||
|
||||
@@ -41,10 +41,10 @@ public partial class ContentBlockComponent : ComponentBase
|
||||
public string Class { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public bool IsLastContentBlock { get; set; } = false;
|
||||
public bool IsLastContentBlock { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool IsSecondToLastBlock { get; set; } = false;
|
||||
public bool IsSecondToLastBlock { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<IContent, Task>? RemoveBlockFunc { get; set; }
|
||||
|
||||
@@ -28,7 +28,7 @@ public sealed class ContentImage : IContent, IImageSource
|
||||
public Func<Task> StreamingEvent { get; set; } = () => Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastPrompt, ChatThread? chatChatThread, CancellationToken token = default)
|
||||
public Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastPrompt, ChatThread? chatChatThread, CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -36,16 +36,31 @@ public sealed class ContentText : IContent
|
||||
public Func<Task> StreamingEvent { get; set; } = () => Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastPrompt, ChatThread? chatThread, CancellationToken token = default)
|
||||
public async Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastPrompt, ChatThread? chatThread, CancellationToken token = default)
|
||||
{
|
||||
if(chatThread is null)
|
||||
return;
|
||||
return new();
|
||||
|
||||
if(!chatThread.IsLLMProviderAllowed(provider))
|
||||
{
|
||||
var logger = Program.SERVICE_PROVIDER.GetService<ILogger<ContentText>>()!;
|
||||
logger.LogError("The provider is not allowed for this chat thread due to data security reasons. Skipping the AI process.");
|
||||
return chatThread;
|
||||
}
|
||||
|
||||
// Call the RAG process. Right now, we only have one RAG process:
|
||||
if (lastPrompt is not null)
|
||||
{
|
||||
var rag = new AISrcSelWithRetCtxVal();
|
||||
chatThread = await rag.ProcessAsync(provider, lastPrompt, chatThread, token);
|
||||
try
|
||||
{
|
||||
var rag = new AISrcSelWithRetCtxVal();
|
||||
chatThread = await rag.ProcessAsync(provider, lastPrompt, chatThread, token);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var logger = Program.SERVICE_PROVIDER.GetService<ILogger<ContentText>>()!;
|
||||
logger.LogError(e, "Skipping the RAG process due to an error.");
|
||||
}
|
||||
}
|
||||
|
||||
// Store the last time we got a response. We use this later
|
||||
@@ -107,6 +122,7 @@ public sealed class ContentText : IContent
|
||||
|
||||
// Inform the UI that the streaming is done:
|
||||
await this.StreamingDone();
|
||||
return chatThread;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -41,7 +41,7 @@ public interface IContent
|
||||
/// <summary>
|
||||
/// Uses the provider to create the content.
|
||||
/// </summary>
|
||||
public Task CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastPrompt, ChatThread? chatChatThread, CancellationToken token = default);
|
||||
public Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastPrompt, ChatThread? chatChatThread, CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the corresponding ERI content type.
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace AIStudio.Chat;
|
||||
|
||||
/// <summary>
|
||||
/// Data about a workspace.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the workspace.</param>
|
||||
public sealed class Workspace(string name)
|
||||
{
|
||||
public string Name { get; set; } = name;
|
||||
|
||||
public List<ChatThread> Threads { get; set; } = new();
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
@typeparam TSettings
|
||||
|
||||
<MudCard Outlined="@true" Style="@this.BlockStyle">
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
@@ -17,8 +19,11 @@
|
||||
</MudStack>
|
||||
</MudCardContent>
|
||||
<MudCardActions>
|
||||
<MudButton Size="Size.Large" Variant="Variant.Filled" StartIcon="@this.Icon" Color="Color.Default" Href="@this.Link">
|
||||
@this.ButtonText
|
||||
</MudButton>
|
||||
<MudButtonGroup Variant="Variant.Outlined">
|
||||
<MudButton Size="Size.Large" Variant="Variant.Filled" StartIcon="@this.Icon" Color="Color.Default" Href="@this.Link">
|
||||
@this.ButtonText
|
||||
</MudButton>
|
||||
<MudIconButton Variant="Variant.Text" Icon="@Icons.Material.Filled.Settings" Color="Color.Default" OnClick="@this.OpenSettingsDialog"/>
|
||||
</MudButtonGroup>
|
||||
</MudCardActions>
|
||||
</MudCard>
|
||||
@@ -2,9 +2,11 @@ using AIStudio.Settings;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class AssistantBlock : ComponentBase, IMessageBusReceiver, IDisposable
|
||||
public partial class AssistantBlock<TSettings> : ComponentBase, IMessageBusReceiver, IDisposable where TSettings : IComponent
|
||||
{
|
||||
[Parameter]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
@@ -29,6 +31,16 @@ public partial class AssistantBlock : ComponentBase, IMessageBusReceiver, IDispo
|
||||
|
||||
[Inject]
|
||||
private MessageBus MessageBus { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
private async Task OpenSettingsDialog()
|
||||
{
|
||||
var dialogParameters = new DialogParameters();
|
||||
|
||||
await this.DialogService.ShowAsync<TSettings>("Open Settings", dialogParameters, DialogOptions.FULLSCREEN);
|
||||
}
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
@@ -44,7 +56,7 @@ public partial class AssistantBlock : ComponentBase, IMessageBusReceiver, IDispo
|
||||
|
||||
#region Implementation of IMessageBusReceiver
|
||||
|
||||
public string ComponentName => nameof(AssistantBlock);
|
||||
public string ComponentName => nameof(AssistantBlock<TSettings>);
|
||||
|
||||
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
||||
{
|
||||
@@ -64,7 +76,7 @@ public partial class AssistantBlock : ComponentBase, IMessageBusReceiver, IDispo
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
private string BorderColor => this.SettingsManager.IsDarkMode switch
|
||||
{
|
||||
true => this.ColorTheme.GetCurrentPalette(this.SettingsManager).GrayLight,
|
||||
|
||||
@@ -13,6 +13,16 @@ public partial class Changelog
|
||||
|
||||
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 (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 (208, "v0.9.33, build 208 (2025-03-11 08:14 UTC)", "v0.9.33.md"),
|
||||
new (207, "v0.9.32, build 207 (2025-03-08 20:15 UTC)", "v0.9.32.md"),
|
||||
new (206, "v0.9.31, build 206 (2025-03-03 15:33 UTC)", "v0.9.31.md"),
|
||||
new (205, "v0.9.30, build 205 (2025-02-24 19:55 UTC)", "v0.9.30.md"),
|
||||
new (204, "v0.9.29, build 204 (2025-02-24 13:48 UTC)", "v0.9.29.md"),
|
||||
new (203, "v0.9.28, build 203 (2025-02-09 16:33 UTC)", "v0.9.28.md"),
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
IsLastContentBlock="@isLastBlock"
|
||||
IsSecondToLastBlock="@isSecondLastBlock"
|
||||
RegenerateFunc="@this.RegenerateBlock"
|
||||
RegenerateEnabled="@(() => this.IsProviderSelected)"
|
||||
RegenerateEnabled="@(() => this.IsProviderSelected && this.ChatThread.IsLLMProviderAllowed(this.Provider))"
|
||||
EditLastBlockFunc="@this.EditLastBlock"
|
||||
EditLastUserBlockFunc="@this.EditLastUserBlock"/>
|
||||
}
|
||||
@@ -46,7 +46,7 @@
|
||||
Adornment="Adornment.End"
|
||||
AdornmentIcon="@Icons.Material.Filled.Send"
|
||||
OnAdornmentClick="() => this.SendMessage()"
|
||||
ReadOnly="!this.IsProviderSelected || this.isStreaming"
|
||||
Disabled="@this.IsInputForbidden()"
|
||||
Immediate="@true"
|
||||
OnKeyUp="this.InputKeyEvent"
|
||||
UserAttributes="@USER_INPUT_ATTRIBUTES"
|
||||
@@ -113,6 +113,14 @@
|
||||
{
|
||||
<DataSourceSelection @ref="@this.dataSourceSelectionComponent" PopoverTriggerMode="PopoverTriggerMode.BUTTON" PopoverButtonClasses="ma-3" LLMProvider="@this.Provider" DataSourceOptions="@this.GetCurrentDataSourceOptions()" DataSourceOptionsChanged="@(async options => await this.SetCurrentDataSourceOptions(options))" DataSourcesAISelected="@this.GetAgentSelectedDataSources()"/>
|
||||
}
|
||||
|
||||
@if (!this.ChatThread.IsLLMProviderAllowed(this.Provider))
|
||||
{
|
||||
<MudTooltip Text="The selected provider is not allowed in this chat due to data security reasons." Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Error" Color="Color.Error"/>
|
||||
</MudTooltip>
|
||||
}
|
||||
<MudIconButton />
|
||||
</MudToolBar>
|
||||
</FooterContent>
|
||||
</InnerScrolling>
|
||||
@@ -3,7 +3,6 @@ using AIStudio.Dialogs;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
@@ -41,9 +40,6 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private DataSourceService DataSourceService { get; init; } = null!;
|
||||
|
||||
private const Placement TOOLBAR_TOOLTIP_PLACEMENT = Placement.Top;
|
||||
private static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();
|
||||
|
||||
@@ -275,6 +271,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
{
|
||||
var chatDefaultOptions = this.SettingsManager.ConfigurationData.Chat.PreselectedDataSourceOptions.CreateCopy();
|
||||
this.earlyDataSourceOptions = chatDefaultOptions;
|
||||
if(this.ChatThread is not null)
|
||||
this.ChatThread.DataSourceOptions = chatDefaultOptions;
|
||||
|
||||
this.dataSourceSelectionComponent?.ChangeOptionWithoutSaving(chatDefaultOptions);
|
||||
}
|
||||
|
||||
@@ -337,6 +336,20 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
this.earlyDataSourceOptions = updatedOptions;
|
||||
}
|
||||
|
||||
private bool IsInputForbidden()
|
||||
{
|
||||
if (!this.IsProviderSelected)
|
||||
return true;
|
||||
|
||||
if(this.isStreaming)
|
||||
return true;
|
||||
|
||||
if(!this.ChatThread.IsLLMProviderAllowed(this.Provider))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task InputKeyEvent(KeyboardEventArgs keyEvent)
|
||||
{
|
||||
if(this.dataSourceSelectionComponent?.IsVisible ?? false)
|
||||
@@ -371,6 +384,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
if (!this.IsProviderSelected)
|
||||
return;
|
||||
|
||||
if(!this.ChatThread.IsLLMProviderAllowed(this.Provider))
|
||||
return;
|
||||
|
||||
// We need to blur the focus away from the input field
|
||||
// to be able to clear the field:
|
||||
await this.inputField.BlurAsync();
|
||||
@@ -475,7 +491,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
// Use the selected provider to get the AI response.
|
||||
// By awaiting this line, we wait for the entire
|
||||
// content to be streamed.
|
||||
await aiText.CreateFromProviderAsync(this.Provider.CreateProvider(this.Logger), this.Provider.Model, lastUserPrompt, this.ChatThread, this.cancellationTokenSource.Token);
|
||||
this.ChatThread = await aiText.CreateFromProviderAsync(this.Provider.CreateProvider(this.Logger), this.Provider.Model, lastUserPrompt, this.ChatThread, this.cancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
this.cancellationTokenSource = null;
|
||||
@@ -773,6 +789,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
if(this.ChatThread is null)
|
||||
return;
|
||||
|
||||
if(!this.ChatThread.IsLLMProviderAllowed(this.Provider))
|
||||
return;
|
||||
|
||||
this.ChatThread.Remove(aiBlock, removeForRegenerate: true);
|
||||
this.hasUnsavedChanges = true;
|
||||
this.StateHasChanged();
|
||||
@@ -821,8 +840,8 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
#region Overrides of MSGComponentBase
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -841,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)
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<MudText Typo="Typo.h5">Confidence Card</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
<MudCardContent Style="max-height: 50vh; overflow: auto;">
|
||||
<MudCardContent Style="max-height: 50vh; max-width: 35vw; overflow: auto;">
|
||||
<MudText Typo="Typo.h6">Description</MudText>
|
||||
<MudMarkdown Value="@this.currentConfidence.Description"/>
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ public partial class ConfidenceInfo : ComponentBase, IMessageBusReceiver, IDispo
|
||||
|
||||
private string GetCurrentConfidenceColor() => $"color: {this.currentConfidence.Level.GetColor(this.SettingsManager)};";
|
||||
|
||||
private string GetPopoverStyle() => $"border-color: {this.currentConfidence.Level.GetColor(this.SettingsManager)}; max-width: calc(35vw);";
|
||||
private string GetPopoverStyle() => $"border-color: {this.currentConfidence.Level.GetColor(this.SettingsManager)};";
|
||||
|
||||
#region Implementation of IMessageBusReceiver
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@if (this.SelectionMode is DataSourceSelectionMode.SELECTION_MODE)
|
||||
{
|
||||
<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)
|
||||
{
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Source" Class="@this.PopoverButtonClasses" OnClick="@(() => this.ToggleDataSourceSelection())"/>
|
||||
@@ -11,7 +11,7 @@
|
||||
else
|
||||
{
|
||||
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.Source" Class="@this.PopoverButtonClasses" OnClick="@(() => this.ToggleDataSourceSelection())">
|
||||
Select data sources
|
||||
Select data
|
||||
</MudButton>
|
||||
}
|
||||
</MudTooltip>
|
||||
@@ -21,7 +21,13 @@
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<PreviewPrototype/>
|
||||
<MudText Typo="Typo.h5">Data Source Selection</MudText>
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center">
|
||||
<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>
|
||||
</MudCardHeader>
|
||||
<MudCardContent Style="min-width: 24em; max-height: 60vh; max-width: 45vw; overflow: auto;">
|
||||
@@ -31,6 +37,26 @@
|
||||
<MudSkeleton Width="80%"/>
|
||||
<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)
|
||||
{
|
||||
<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,24 +69,29 @@
|
||||
<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)
|
||||
{
|
||||
<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;">
|
||||
@foreach (var source in this.availableDataSources)
|
||||
{
|
||||
<MudListItem Value="@source">
|
||||
@source.Name
|
||||
</MudListItem>
|
||||
}
|
||||
</MudList>
|
||||
</MudField>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudExpansionPanels MultiExpansion="@false" Class="mt-3" Style="max-height: 14em;">
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.TouchApp" HeaderText="Available Data Sources">
|
||||
<MudList T="IDataSource" SelectionMode="MudBlazor.SelectionMode.SingleSelection" SelectedValues="@this.selectedDataSources" Style="max-height: 14em;">
|
||||
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">
|
||||
<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)
|
||||
{
|
||||
<MudListItem Value="@source">
|
||||
@@ -68,27 +99,42 @@
|
||||
</MudListItem>
|
||||
}
|
||||
</MudList>
|
||||
</ExpansionPanel>
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Filter" HeaderText="AI-Selected Data Sources">
|
||||
<MudList T="DataSourceAgentSelected" SelectionMode="MudBlazor.SelectionMode.MultiSelection" ReadOnly="@true" SelectedValues="@this.GetSelectedDataSourcesWithAI()" Style="max-height: 14em;">
|
||||
@foreach (var source in this.DataSourcesAISelected)
|
||||
{
|
||||
<MudListItem Value="@source">
|
||||
<ChildContent>
|
||||
<MudText Typo="Typo.body1">
|
||||
@source.DataSource.Name
|
||||
</MudText>
|
||||
</MudField>
|
||||
break;
|
||||
|
||||
case true:
|
||||
<MudExpansionPanels MultiExpansion="@false" Class="mt-3" Style="max-height: 14em;">
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.TouchApp" HeaderText="Available Data Sources">
|
||||
<MudList T="IDataSource" SelectionMode="MudBlazor.SelectionMode.SingleSelection" SelectedValues="@this.selectedDataSources" Style="max-height: 14em;">
|
||||
@foreach (var source in this.availableDataSources)
|
||||
{
|
||||
<MudListItem Value="@source">
|
||||
@source.Name
|
||||
</MudListItem>
|
||||
}
|
||||
</MudList>
|
||||
</ExpansionPanel>
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Filter" HeaderText="AI-Selected Data Sources">
|
||||
<MudList T="DataSourceAgentSelected" SelectionMode="MudBlazor.SelectionMode.MultiSelection" ReadOnly="@true" SelectedValues="@this.GetSelectedDataSourcesWithAI()" Style="max-height: 14em;">
|
||||
@foreach (var source in this.DataSourcesAISelected)
|
||||
{
|
||||
<MudListItem Value="@source">
|
||||
<ChildContent>
|
||||
<MudText Typo="Typo.body1">
|
||||
@source.DataSource.Name
|
||||
</MudText>
|
||||
|
||||
<MudProgressLinear Color="Color.Info" Min="0" Max="1" Value="@source.AIDecision.Confidence"/>
|
||||
<MudJustifiedText Typo="Typo.body2">
|
||||
@this.GetAIReasoning(source)
|
||||
</MudJustifiedText>
|
||||
</ChildContent>
|
||||
</MudListItem>
|
||||
}
|
||||
</MudList>
|
||||
</ExpansionPanel>
|
||||
</MudExpansionPanels>
|
||||
<MudProgressLinear Color="Color.Info" Min="0" Max="1" Value="@source.AIDecision.Confidence"/>
|
||||
<MudJustifiedText Typo="Typo.body2">
|
||||
@(this.GetAIReasoning(source))
|
||||
</MudJustifiedText>
|
||||
</ChildContent>
|
||||
</MudListItem>
|
||||
}
|
||||
</MudList>
|
||||
</ExpansionPanel>
|
||||
</MudExpansionPanels>
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class DataSourceSelection : ComponentBase, IMessageBusReceiver, IDisposable
|
||||
@@ -45,7 +48,7 @@ public partial class DataSourceSelection : ComponentBase, IMessageBusReceiver, I
|
||||
private DataSourceService DataSourceService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ILogger<DataSourceSelection> Logger { get; init; } = null!;
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
private bool internalChange;
|
||||
private bool showDataSourceSelection;
|
||||
@@ -131,6 +134,20 @@ public partial class DataSourceSelection : ComponentBase, IMessageBusReceiver, I
|
||||
}
|
||||
|
||||
#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;
|
||||
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
</MudSelect>
|
||||
@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>
|
||||
@@ -1,5 +1,3 @@
|
||||
using AIStudio.Layout;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
@@ -30,9 +28,6 @@ public partial class InnerScrolling : MSGComponentBase
|
||||
[Parameter]
|
||||
public string Style { get; set; } = string.Empty;
|
||||
|
||||
[CascadingParameter]
|
||||
private MainLayout MainLayout { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IJSRuntime JsRuntime { get; init; } = null!;
|
||||
|
||||
@@ -51,8 +46,8 @@ public partial class InnerScrolling : MSGComponentBase
|
||||
#region Overrides of MSGComponentBase
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -64,11 +59,6 @@ public partial class InnerScrolling : MSGComponentBase
|
||||
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
|
||||
|
||||
private string MinWidthStyle => string.IsNullOrWhiteSpace(this.MinWidth) ? string.Empty : $"min-width: {this.MinWidth}; ";
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using SharedTools;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBusReceiver
|
||||
public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBusReceiver, ILang
|
||||
{
|
||||
[Inject]
|
||||
protected SettingsManager SettingsManager { get; init; } = null!;
|
||||
@@ -12,12 +15,37 @@ public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBus
|
||||
[Inject]
|
||||
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
|
||||
|
||||
protected override void OnInitialized()
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
this.Lang = await this.SettingsManager.GetActiveLanguagePlugin();
|
||||
|
||||
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
|
||||
@@ -26,7 +54,7 @@ public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBus
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -34,19 +62,34 @@ public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBus
|
||||
this.StateHasChanged();
|
||||
break;
|
||||
|
||||
case Event.PLUGINS_RELOADED:
|
||||
this.Lang = await this.SettingsManager.GetActiveLanguagePlugin();
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
break;
|
||||
|
||||
default:
|
||||
return this.ProcessIncomingMessage(sendingComponent, triggeredEvent, data);
|
||||
await this.ProcessIncomingMessage(sendingComponent, triggeredEvent, data);
|
||||
break;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public abstract Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data);
|
||||
|
||||
public abstract Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
protected virtual Task<TResult?> ProcessIncomingMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
|
||||
{
|
||||
return Task.FromResult<TResult?>(default);
|
||||
}
|
||||
|
||||
#region Implementation of IDisposable
|
||||
|
||||
public void Dispose()
|
||||
@@ -71,7 +114,8 @@ public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBus
|
||||
// Append the color theme changed event to the list of 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());
|
||||
|
||||
@@ -14,7 +14,7 @@ public partial class MudTextList : ComponentBase
|
||||
public string Icon { get; set; } = Icons.Material.Filled.CheckCircle;
|
||||
|
||||
[Parameter]
|
||||
public string Class { get; set; } = "";
|
||||
public string Class { get; set; } = string.Empty;
|
||||
|
||||
private string Classes => $"mud-text-list {this.Class}";
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<MudTooltip Placement="Placement.Bottom" Arrow="@true">
|
||||
<MudTooltip Placement="Placement.Bottom" Arrow="@true" Class="@this.Classes">
|
||||
<ChildContent>
|
||||
<MudChip T="string" Icon="@Icons.Material.Filled.FirstPage" Color="Color.Error" Class="mb-3">
|
||||
Alpha
|
||||
|
||||
@@ -2,4 +2,10 @@ using Microsoft.AspNetCore.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>
|
||||
<MudChip T="string" Icon="@Icons.Material.Filled.HourglassTop" Color="Color.Info" Class="mb-3">
|
||||
Beta
|
||||
|
||||
@@ -2,4 +2,10 @@ using Microsoft.AspNetCore.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>
|
||||
<MudChip T="string" Icon="@Icons.Material.Filled.Science" Color="Color.Error" Class="mb-3">
|
||||
Experimental
|
||||
|
||||
@@ -2,4 +2,10 @@ using Microsoft.AspNetCore.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>
|
||||
<MudChip T="string" Icon="@Icons.Material.Filled.HourglassBottom" Color="Color.Error" Class="mb-3">
|
||||
Prototype
|
||||
|
||||
@@ -2,4 +2,10 @@ using Microsoft.AspNetCore.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>
|
||||
<MudChip T="string" Icon="@Icons.Material.Filled.VerifiedUser" Color="Color.Success" Class="mb-3">
|
||||
Release Candidate
|
||||
|
||||
@@ -2,4 +2,10 @@ using Microsoft.AspNetCore.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;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<MudTooltip Text="You can switch between your profiles here" Placement="Placement.Top">
|
||||
<MudMenu StartIcon="@Icons.Material.Filled.Person4" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="@this.CurrentProfile.Name" Variant="Variant.Filled" Color="Color.Default" Class="@this.MarginClass">
|
||||
<MudMenu TransformOrigin="@Origin.BottomLeft" AnchorOrigin="Origin.TopLeft" StartIcon="@Icons.Material.Filled.Person4" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="@this.CurrentProfile.Name" Variant="Variant.Filled" Color="Color.Default" Class="@this.MarginClass">
|
||||
@foreach (var profile in this.SettingsManager.ConfigurationData.Profiles.GetAllProfiles())
|
||||
{
|
||||
<MudMenuItem OnClick="() => this.SelectionChanged(profile)">
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace AIStudio.Components;
|
||||
public partial class ProviderSelection : ComponentBase
|
||||
{
|
||||
[CascadingParameter]
|
||||
public AssistantBase? AssistantBase { get; set; }
|
||||
public AssistantBase<NoComponent>? AssistantBase { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public AIStudio.Settings.Provider ProviderSettings { get; set; }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<MudTextSwitch Label="Read content from web?" Disabled="@this.AgentIsRunning" @bind-Value="@this.showWebContentReader" LabelOn="Show web content options" LabelOff="Hide web content options" />
|
||||
@if (this.showWebContentReader)
|
||||
{
|
||||
<MudTextSwitch Label="Cleanup content by using a LLM agent?" @bind-Value="@this.useContentCleanerAgent" Validation="@this.ValidateProvider" Disabled="@this.AgentIsRunning" LabelOn="The content is cleaned using an LLM agent: the main content is extracted, advertisements and other irrelevant things are attempted to be removed; relative links are attempted to be converted into absolute links so that they can be used." LabelOff="No content cleaning" />
|
||||
<MudTextSwitch Label="Cleanup content by using an LLM agent?" @bind-Value="@this.useContentCleanerAgent" Validation="@this.ValidateProvider" Disabled="@this.AgentIsRunning" LabelOn="The content is cleaned using an LLM agent: the main content is extracted, advertisements and other irrelevant things are attempted to be removed; relative links are attempted to be converted into absolute links so that they can be used." LabelOff="No content cleaning" />
|
||||
<MudStack Row="@true" AlignItems="@AlignItems.Baseline" Class="mb-3">
|
||||
<MudTextField T="string" Label="URL from which to load the content" @bind-Value="@this.providedURL" Validation="@this.ValidateURL" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Link" Placeholder="https://..." HelperText="Loads the content from your URL. Does not work when the content is hidden behind a paywall." Variant="Variant.Outlined" Immediate="@true" Disabled="@this.AgentIsRunning"/>
|
||||
<MudButton Disabled="@(!this.IsReady || this.AgentIsRunning)" Variant="Variant.Filled" Size="Size.Large" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Download" OnClick="() => this.LoadFromWeb()">
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
@using AIStudio.Settings
|
||||
@inherits SettingsPanelBase
|
||||
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.CalendarToday" HeaderText="Assistant: Agenda Options">
|
||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||
<ConfigurationOption OptionDescription="Preselect agenda options?" LabelOn="Agenda options are preselected" LabelOff="No agenda options are preselected" State="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Agenda.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect most agenda options. This is might be useful when you need to create similar agendas often."/>
|
||||
<ConfigurationText OptionDescription="Preselect a name?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" Icon="@Icons.Material.Filled.Tag" Text="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectName)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Agenda.PreselectName = updatedText)" />
|
||||
<ConfigurationText OptionDescription="Preselect a topic?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" Icon="@Icons.Material.Filled.EventNote" Text="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectTopic)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Agenda.PreselectTopic = updatedText)" />
|
||||
<ConfigurationText OptionDescription="Preselect an objective?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" Icon="@Icons.Material.Filled.Flag" Text="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectObjective)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Agenda.PreselectObjective = updatedText)" />
|
||||
<ConfigurationText OptionDescription="Preselect a moderator?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" Icon="@Icons.Material.Filled.Person3" Text="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectModerator)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Agenda.PreselectModerator = updatedText)" />
|
||||
<ConfigurationText OptionDescription="Preselect a duration?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" Icon="@Icons.Material.Filled.Schedule" Text="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectDuration)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Agenda.PreselectDuration = updatedText)" />
|
||||
<ConfigurationText OptionDescription="Preselect a start time?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" Icon="@Icons.Material.Filled.Schedule" Text="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectStartTime)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Agenda.PreselectStartTime = updatedText)" />
|
||||
<ConfigurationOption OptionDescription="Preselect whether the participants should get to know each other" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" LabelOn="Participants should get to know each other" LabelOff="Participants do not need to get to know each other" State="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectIntroduceParticipants)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Agenda.PreselectIntroduceParticipants = updatedState)" />
|
||||
<ConfigurationSelect OptionDescription="Preselect the number of participants" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectNumberParticipants)" Data="@ConfigurationSelectDataFactory.GetNumberParticipantsData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectNumberParticipants = selectedValue)" OptionHelp="How many participants should be preselected?"/>
|
||||
<ConfigurationOption OptionDescription="Preselect whether the participants should actively involved" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" LabelOn="Participants should be actively involved" LabelOff="Participants do not need to be actively involved" State="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectActiveParticipation)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Agenda.PreselectActiveParticipation = updatedState)" />
|
||||
<ConfigurationOption OptionDescription="Preselect whether the meeting is virtual" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" LabelOn="Meeting is virtual" LabelOff="Meeting is in person" State="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectIsMeetingVirtual)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Agenda.PreselectIsMeetingVirtual = updatedState)" />
|
||||
<ConfigurationText OptionDescription="Preselect a location?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" Icon="@Icons.Material.Filled.MyLocation" Text="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectLocation)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Agenda.PreselectLocation = updatedText)" />
|
||||
<ConfigurationOption OptionDescription="Preselect whether there is a joint dinner" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" LabelOn="There is a joint dinner" LabelOff="There is no joint dinner" State="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectJointDinner)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Agenda.PreselectJointDinner = updatedState)" />
|
||||
<ConfigurationOption OptionDescription="Preselect whether there is a social event" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" LabelOn="There is a social event" LabelOff="There is no social event" State="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectSocialActivity)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Agenda.PreselectSocialActivity = updatedState)" />
|
||||
<ConfigurationOption OptionDescription="Preselect whether participants needs to arrive and depart" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" LabelOn="Participants need to arrive and depart" LabelOff="Participants do not need to arrive and depart" State="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectArriveAndDepart)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Agenda.PreselectArriveAndDepart = updatedState)" />
|
||||
<ConfigurationSlider T="int" OptionDescription="Preselect the approx. lunch time" Min="30" Max="120" Step="5" Unit="minutes" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" Value="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectLunchTime)" ValueUpdate="@(updatedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectLunchTime = updatedValue)" />
|
||||
<ConfigurationSlider T="int" OptionDescription="Preselect the approx. break time" Min="10" Max="60" Step="5" Unit="minutes" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" Value="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectBreakTime)" ValueUpdate="@(updatedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectBreakTime = updatedValue)" />
|
||||
<ConfigurationSelect OptionDescription="Preselect the agenda language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectedTargetLanguage)" Data="@ConfigurationSelectDataFactory.GetCommonLanguagesTranslationData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectedTargetLanguage = selectedValue)" OptionHelp="Which agenda language should be preselected?"/>
|
||||
@if (this.SettingsManager.ConfigurationData.Agenda.PreselectedTargetLanguage is CommonLanguages.OTHER)
|
||||
{
|
||||
<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)"/>
|
||||
<ConfigurationProviderSelection Component="Components.AGENDA_ASSISTANT" Data="@this.AvailableLLMProvidersFunc()" 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?"/>
|
||||
</MudPaper>
|
||||
</ExpansionPanel>
|
||||
@@ -1,3 +0,0 @@
|
||||
namespace AIStudio.Components.Settings;
|
||||
|
||||
public partial class SettingsPanelAgenda : SettingsPanelBase;
|
||||
@@ -6,7 +6,7 @@
|
||||
Use Case: this agent is used to clean up text content. It extracts the main content, removes advertisements and other irrelevant things,
|
||||
and attempts to convert relative links into absolute links so that they can be used.
|
||||
</MudText>
|
||||
<ConfigurationOption OptionDescription="Preselect text content cleaner options?" LabelOn="Options are preselected" LabelOff="No options are preselected" State="@(() => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions = updatedState)" OptionHelp="When enabled, you can preselect some agent options. This is might be useful when you prefer a LLM."/>
|
||||
<ConfigurationOption OptionDescription="Preselect text content cleaner options?" LabelOn="Options are preselected" LabelOff="No options are preselected" State="@(() => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions = updatedState)" OptionHelp="When enabled, you can preselect some agent options. This is might be useful when you prefer an LLM."/>
|
||||
<ConfigurationProviderSelection Data="@this.AvailableLLMProvidersFunc()" Disabled="@(() => !this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectedAgentProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectedAgentProvider = selectedValue)"/>
|
||||
</MudPaper>
|
||||
</ExpansionPanel>
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<MudText Typo="Typo.body1" Class="mb-3">
|
||||
Use Case: this agent is used to select the appropriate data sources for the current prompt.
|
||||
</MudText>
|
||||
<ConfigurationOption OptionDescription="Preselect data source selection options?" LabelOn="Options are preselected" LabelOff="No options are preselected" State="@(() => this.SettingsManager.ConfigurationData.AgentDataSourceSelection.PreselectAgentOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.AgentDataSourceSelection.PreselectAgentOptions = updatedState)" OptionHelp="When enabled, you can preselect some agent options. This is might be useful when you prefer a LLM."/>
|
||||
<ConfigurationOption OptionDescription="Preselect data source selection options?" LabelOn="Options are preselected" LabelOff="No options are preselected" State="@(() => this.SettingsManager.ConfigurationData.AgentDataSourceSelection.PreselectAgentOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.AgentDataSourceSelection.PreselectAgentOptions = updatedState)" OptionHelp="When enabled, you can preselect some agent options. This is might be useful when you prefer an LLM."/>
|
||||
<ConfigurationProviderSelection Data="@this.AvailableLLMProvidersFunc()" Disabled="@(() => !this.SettingsManager.ConfigurationData.AgentDataSourceSelection.PreselectAgentOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.AgentDataSourceSelection.PreselectedAgentProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.AgentDataSourceSelection.PreselectedAgentProvider = selectedValue)"/>
|
||||
</MudPaper>
|
||||
</ExpansionPanel>
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
@if (this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.EnableRetrievalContextValidation)
|
||||
{
|
||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||
<ConfigurationOption OptionDescription="Preselect retrieval context validation options?" LabelOn="Options are preselected" LabelOff="No options are preselected" State="@(() => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectAgentOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectAgentOptions = updatedState)" OptionHelp="When enabled, you can preselect some agent options. This is might be useful when you prefer a LLM."/>
|
||||
<ConfigurationOption OptionDescription="Preselect retrieval context validation options?" LabelOn="Options are preselected" LabelOff="No options are preselected" State="@(() => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectAgentOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectAgentOptions = updatedState)" OptionHelp="When enabled, you can preselect some agent options. This is might be useful when you prefer an LLM."/>
|
||||
<ConfigurationSlider T="int" OptionDescription="How many validation agents should work simultaneously?" Min="1" Max="100" Step="1" Unit="agents" Value="@(() => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.NumParallelValidations)" ValueUpdate="@(updatedValue => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.NumParallelValidations = updatedValue)" OptionHelp="More active agents also mean that a corresponding number of requests are made simultaneously. Some providers limit the number of requests per minute. When you are unsure, choose a low setting between 1 to 6 agents."/>
|
||||
<ConfigurationProviderSelection Data="@this.AvailableLLMProvidersFunc()" Disabled="@(() => !this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectAgentOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectedAgentProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectedAgentProvider = selectedValue)"/>
|
||||
</MudPaper>
|
||||
|
||||
@@ -3,14 +3,25 @@
|
||||
@inherits SettingsPanelBase
|
||||
|
||||
<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."/>
|
||||
<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="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?"/>
|
||||
|
||||
@if(this.SettingsManager.ConfigurationData.App.PreviewVisibility > PreviewVisibility.NONE)
|
||||
|
||||
@if (this.SettingsManager.ConfigurationData.App.PreviewVisibility > PreviewVisibility.NONE)
|
||||
{
|
||||
var availablePreviewFeatures = ConfigurationSelectDataFactory.GetPreviewFeaturesData(this.SettingsManager).ToList();
|
||||
if (availablePreviewFeatures.Count > 0)
|
||||
@@ -18,7 +29,7 @@
|
||||
<ConfigurationMultiSelect OptionDescription="Select preview features" SelectedValues="@(() => this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures)" Data="@availablePreviewFeatures" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures = selectedValue)" OptionHelp="Which preview features would you like to enable?"/>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
<ConfigurationProviderSelection Component="Components.APP_SETTINGS" Data="@this.AvailableLLMProvidersFunc()" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProvider = selectedValue)" HelpText="@(() => "Would you like to set one provider as the default for the entire app? When you configure a different provider for an assistant, it will always take precedence.")"/>
|
||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProfile = selectedValue)" OptionHelp="Would you like to set one of your profiles as the default for the entire app? When you configure a different profile for an assistant, it will always take precedence."/>
|
||||
</ExpansionPanel>
|
||||
@@ -1,29 +0,0 @@
|
||||
@using AIStudio.Settings
|
||||
@using AIStudio.Settings.DataModel
|
||||
@inherits SettingsPanelBase
|
||||
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Psychology" HeaderText="Assistant: Bias of the Day">
|
||||
|
||||
<ConfigurationOption OptionDescription="Restrict to one bias a day?" LabelOn="Yes, you can only retrieve one bias per day" LabelOff="No restriction. You can retrieve as many biases as you want per day." State="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.RestrictOneBiasPerDay)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.BiasOfTheDay.RestrictOneBiasPerDay = updatedState)"/>
|
||||
|
||||
<MudField Label="Statistics" Variant="Variant.Outlined" Class="mb-2">
|
||||
<MudText Typo="Typo.body1">
|
||||
You have learned about @this.SettingsManager.ConfigurationData.BiasOfTheDay.UsedBias.Count out of @BiasCatalog.ALL_BIAS.Count biases.
|
||||
</MudText>
|
||||
<MudButton Size="Size.Small" Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.Restore" Color="Color.Error" OnClick="@(() => this.ResetBiasOfTheDayHistory())">
|
||||
Reset
|
||||
</MudButton>
|
||||
</MudField>
|
||||
|
||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||
<ConfigurationOption OptionDescription="Preselect options?" LabelOn="Options are preselected" LabelOff="No options are preselected" State="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect options. This is might be useful when you prefer a specific language or LLM model."/>
|
||||
<ConfigurationSelect OptionDescription="Preselect the language" Disabled="@(() => !this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedTargetLanguage)" Data="@ConfigurationSelectDataFactory.GetCommonLanguagesOptionalData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedTargetLanguage = selectedValue)" OptionHelp="Which language should be preselected?"/>
|
||||
@if (this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedTargetLanguage is CommonLanguages.OTHER)
|
||||
{
|
||||
<ConfigurationText OptionDescription="Preselect another language" Disabled="@(() => !this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedOtherLanguage = updatedText)"/>
|
||||
}
|
||||
<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)"/>
|
||||
<ConfigurationProviderSelection Component="Components.BIAS_DAY_ASSISTANT" Data="@this.AvailableLLMProvidersFunc()" Disabled="@(() => !this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedProvider = selectedValue)"/>
|
||||
</MudPaper>
|
||||
</ExpansionPanel>
|
||||
@@ -1,18 +0,0 @@
|
||||
@using AIStudio.Assistants.Coding
|
||||
@using AIStudio.Settings
|
||||
@inherits SettingsPanelBase
|
||||
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Code" HeaderText="Assistant: Coding Options">
|
||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||
<ConfigurationOption OptionDescription="Preselect coding options?" LabelOn="Coding options are preselected" LabelOff="No coding options are preselected" State="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Coding.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect the coding options. This is might be useful when you prefer a specific programming language or LLM model."/>
|
||||
<ConfigurationOption OptionDescription="Preselect compiler messages?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" LabelOn="Compiler messages are preselected" LabelOff="Compiler messages are not preselected" State="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectCompilerMessages)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Coding.PreselectCompilerMessages = updatedState)" />
|
||||
<ConfigurationSelect OptionDescription="Preselect a programming language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectedProgrammingLanguage)" Data="@ConfigurationSelectDataFactory.GetCommonCodingLanguagesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Coding.PreselectedProgrammingLanguage = selectedValue)" OptionHelp="Which programming language should be preselected for added contexts?"/>
|
||||
@if (this.SettingsManager.ConfigurationData.Coding.PreselectedProgrammingLanguage is CommonCodingLanguages.OTHER)
|
||||
{
|
||||
<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)"/>
|
||||
<ConfigurationProviderSelection Component="Components.CODING_ASSISTANT" Data="@this.AvailableLLMProvidersFunc()" 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?"/>
|
||||
</MudPaper>
|
||||
</ExpansionPanel>
|
||||
@@ -1,3 +0,0 @@
|
||||
namespace AIStudio.Components.Settings;
|
||||
|
||||
public partial class SettingsPanelCoding : SettingsPanelBase;
|
||||
@@ -1,19 +0,0 @@
|
||||
@using AIStudio.Settings
|
||||
@inherits SettingsPanelBase
|
||||
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.PrivateConnectivity" HeaderText="Assistant: ERI Server Options">
|
||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||
<ConfigurationOption OptionDescription="Preselect ERI server options?" LabelOn="ERI server options are preselected" LabelOff="No ERI server options are preselected" State="@(() => this.SettingsManager.ConfigurationData.ERI.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.ERI.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect some ERI server options."/>
|
||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.ERI.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.ERI.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.ERI.MinimumProviderConfidence = selectedValue)"/>
|
||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.ERI.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.ERI.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.ERI.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
||||
|
||||
<MudText Typo="Typo.body1" Class="mb-3">
|
||||
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.
|
||||
</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>
|
||||
</ExpansionPanel>
|
||||
@@ -1,3 +0,0 @@
|
||||
namespace AIStudio.Components.Settings;
|
||||
|
||||
public partial class SettingsPanelERIServer : SettingsPanelBase;
|
||||
@@ -36,24 +36,26 @@
|
||||
<MudTh>Name</MudTh>
|
||||
<MudTh>Provider</MudTh>
|
||||
<MudTh>Model</MudTh>
|
||||
<MudTh Style="text-align: left;">Actions</MudTh>
|
||||
<MudTh>Actions</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd>@context.Num</MudTd>
|
||||
<MudTd>@context.Name</MudTd>
|
||||
<MudTd>@context.UsedLLMProvider</MudTd>
|
||||
<MudTd>@context.UsedLLMProvider.ToName()</MudTd>
|
||||
<MudTd>@this.GetEmbeddingProviderModelName(context)</MudTd>
|
||||
|
||||
<MudTd Style="text-align: left;">
|
||||
<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())">
|
||||
Open Dashboard
|
||||
</MudButton>
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" Class="ma-2" OnClick="() => this.EditEmbeddingProvider(context)">
|
||||
Edit
|
||||
</MudButton>
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" Class="ma-2" OnClick="() => this.DeleteEmbeddingProvider(context)">
|
||||
Delete
|
||||
</MudButton>
|
||||
<MudTd>
|
||||
<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
|
||||
</MudButton>
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" OnClick="() => this.EditEmbeddingProvider(context)">
|
||||
Edit
|
||||
</MudButton>
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteEmbeddingProvider(context)">
|
||||
Delete
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
@using AIStudio.Settings
|
||||
@inherits SettingsPanelBase
|
||||
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Edit" HeaderText="Assistant: Grammar & Spelling Checker">
|
||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||
<ConfigurationOption OptionDescription="Preselect grammar & spell checker options?" LabelOn="Grammar & spell checker options are preselected" LabelOff="No grammar & spell checker options are preselected" State="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect the grammar & spell checker options. This is might be useful when you prefer a specific language or LLM model."/>
|
||||
<ConfigurationSelect OptionDescription="Preselect the target language" Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedTargetLanguage)" Data="@ConfigurationSelectDataFactory.GetCommonLanguagesOptionalData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedTargetLanguage = selectedValue)" OptionHelp="Which target language should be preselected?"/>
|
||||
@if (this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedTargetLanguage is CommonLanguages.OTHER)
|
||||
{
|
||||
<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)"/>
|
||||
<ConfigurationProviderSelection Component="Components.GRAMMAR_SPELLING_ASSISTANT" Data="@this.AvailableLLMProvidersFunc()" Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider = selectedValue)"/>
|
||||
</MudPaper>
|
||||
</ExpansionPanel>
|
||||
@@ -1,3 +0,0 @@
|
||||
namespace AIStudio.Components.Settings;
|
||||
|
||||
public partial class SettingsPanelGrammarSpelling : SettingsPanelBase;
|
||||
@@ -1,11 +0,0 @@
|
||||
@using AIStudio.Settings
|
||||
@inherits SettingsPanelBase
|
||||
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.FindInPage" HeaderText="Assistant: Icon Finder Options">
|
||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||
<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?"/>
|
||||
<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.AvailableLLMProvidersFunc()" Disabled="@(() => !this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.IconFinder.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.IconFinder.PreselectedProvider = selectedValue)"/>
|
||||
</MudPaper>
|
||||
</ExpansionPanel>
|
||||
Loaded 100 of 837 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user