mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-19 21:18:00 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7250b270f5 | ||
|
|
d437d08640 | ||
|
|
61f7a3dd72 | ||
|
|
55d7895f58 | ||
|
|
e2859b3d76 | ||
|
|
0a951ead3e | ||
|
|
432ae30fdf | ||
|
|
799112eb9d | ||
|
|
f806ee28c5 | ||
|
|
32ff143b21 | ||
|
|
90a1f681c5 | ||
|
|
99fe9f3a4f | ||
|
|
cf8a9800f3 | ||
|
|
f57e971f80 | ||
|
|
0d104f5abc | ||
|
|
ba6b8d2b3c | ||
|
|
1c72c4d6ec | ||
|
|
4ca5aba58f | ||
|
|
c4d2574508 | ||
|
|
ceac9cefe5 | ||
|
|
ebe2ad62c6 | ||
|
|
9c0dea9927 | ||
|
|
d2a2d658e6 | ||
|
|
d82427ba56 | ||
|
|
386121d198 | ||
|
|
f9245d52c2 | ||
|
|
9fa2a1a135 | ||
|
|
69288699b3 | ||
|
|
826d15729b | ||
|
|
2ae40c59a3 | ||
|
|
119100bbce | ||
|
|
f92fab24b3 | ||
|
|
671abf3b07 | ||
|
|
d0a366258a | ||
|
|
58d7478f3a | ||
|
|
e8216b70a5 | ||
|
|
152d5f9b4f | ||
|
|
f1104c5e09 | ||
|
|
3ccb097705 | ||
|
|
b9416e66c3 | ||
|
|
9f576e8572 | ||
|
|
629613f75a | ||
|
|
abbb8c1650 | ||
|
|
d2ac4e26b0 | ||
|
|
bd97c0fb4b | ||
|
|
8bd54e6d61 | ||
|
|
e419c5561b | ||
|
|
24a73e92e1 | ||
|
|
832e054dbf | ||
|
|
4703ab04ab | ||
|
|
c1d5be63cc | ||
|
|
5d3ea3d57a | ||
|
|
217b9c4db3 | ||
|
|
e9c805f0df | ||
|
|
11475ea4af | ||
|
|
46a7e98141 | ||
|
|
0d6ccbd237 | ||
|
|
efb30cad87 | ||
|
|
fe33867cb6 | ||
|
|
1b1989e8eb | ||
|
|
afa5df894e | ||
|
|
c8d67b1e4c | ||
|
|
e717b84fb4 | ||
|
|
2463c85b77 | ||
|
|
cbf34580b3 | ||
|
|
e60f815d77 | ||
|
|
a19a6b2188 | ||
|
|
d2dd90d089 | ||
|
|
37e113af0e | ||
|
|
776fa8ac58 | ||
|
|
8715054dda | ||
|
|
95da4c6410 | ||
|
|
92f5b49543 | ||
|
|
e6be9a593e | ||
|
|
375be805a1 | ||
|
|
b99866b8fe | ||
|
|
e9998348c5 | ||
|
|
0ad7b8d4dd | ||
|
|
4175387a93 | ||
|
|
1cac1a7913 | ||
|
|
5fa2f3c822 | ||
|
|
5608b48a2d | ||
|
|
bf01fbd706 | ||
|
|
4722c1baa1 | ||
|
|
222c1e929f | ||
|
|
2a648105e6 | ||
|
|
09f5b83e66 | ||
|
|
7a5f2d4aec | ||
|
|
0d1164c0eb | ||
|
|
54588fc92d | ||
|
|
a5bdb3267b | ||
|
|
d7c124926b | ||
|
|
00f45f8998 | ||
|
|
d0d9fa3d22 | ||
|
|
b3dd213538 | ||
|
|
9dfabb3c55 | ||
|
|
d0dbbccd56 | ||
|
|
75576129fe | ||
|
|
2efd08dbbc | ||
|
|
4bd22108de | ||
|
|
4ea767cb45 | ||
|
|
11846e279c | ||
|
|
c861d71383 | ||
|
|
6fbf32d8a2 | ||
|
|
4216a29325 | ||
|
|
45770c2cd7 | ||
|
|
57905d5510 | ||
|
|
e999aab7c0 | ||
|
|
c47d3893ff | ||
|
|
09c1b77414 | ||
|
|
ea083fdec2 | ||
|
|
ceabcdb26e | ||
|
|
a25793ff11 | ||
|
|
8d8a47b43b | ||
|
|
2889581702 | ||
|
|
12602ba8dd | ||
|
|
e4b8487945 | ||
|
|
fc64c0b505 | ||
|
|
47f5756e5a | ||
|
|
e4ef4bc06e | ||
|
|
ae17104886 | ||
|
|
2d82fcc5e1 | ||
|
|
69a6fcf094 | ||
|
|
4aaa2fcbea | ||
|
|
d855a33e6f | ||
|
|
ca16a749fd | ||
|
|
bcdbf7dcbd | ||
|
|
537a608e7a | ||
|
|
a6c5b3de23 | ||
|
|
3fd0d4d5c1 | ||
|
|
5efb6b0158 | ||
|
|
1b3fff67ec | ||
|
|
3409872090 | ||
|
|
18718a6c47 | ||
|
|
34884859d7 | ||
|
|
50bf0baff7 | ||
|
|
83fe126c9f | ||
|
|
37b38e6a40 | ||
|
|
1d1af91f3e | ||
|
|
a18cb22263 | ||
|
|
f8e06faea5 | ||
|
|
8aeeda5dca | ||
|
|
909fbe89bb | ||
|
|
ea0ead58f8 | ||
|
|
3e59f23193 | ||
|
|
05a7e44de4 | ||
|
|
77e7e044e3 | ||
|
|
6b7800b615 | ||
|
|
1ec86ec1a6 | ||
|
|
e08687a5ca | ||
|
|
ab0b855769 | ||
|
|
4cb49d9f27 | ||
|
|
2cea53d2a9 | ||
|
|
155c2d72f8 | ||
|
|
60febfec86 | ||
|
|
76dd08ee11 | ||
|
|
c1b92a05fc | ||
|
|
cac2373269 | ||
|
|
f3aa38e090 | ||
|
|
b3b8bf23dc | ||
|
|
a1c4618796 | ||
|
|
0f06b96943 | ||
|
|
d6bd0dc45b | ||
|
|
426f223a84 | ||
|
|
79892eb829 | ||
|
|
d6cf65e973 | ||
|
|
9267ef865d | ||
|
|
4f5815272a | ||
|
|
5250e5d2fb | ||
|
|
a956b55d6b | ||
|
|
a9ca51377e | ||
|
|
880f86316b | ||
|
|
063fd0fcfa | ||
|
|
179f350af5 | ||
|
|
58c9d8ac33 |
No files matched your search
@@ -11,3 +11,7 @@ media/**/*.pptx filter=lfs diff=lfs merge=lfs -text
|
||||
media/**/*.png filter=lfs diff=lfs merge=lfs -text
|
||||
media/**/*.ico filter=lfs diff=lfs merge=lfs -text
|
||||
media/**/*.icns filter=lfs diff=lfs merge=lfs -text
|
||||
*.woff2 filter=lfs diff=lfs merge=lfs -text
|
||||
*.afpub filter=lfs diff=lfs merge=lfs -text
|
||||
*.afdesign filter=lfs diff=lfs merge=lfs -text
|
||||
*.afphoto filter=lfs diff=lfs merge=lfs -text
|
||||
@@ -249,18 +249,6 @@ jobs:
|
||||
cd publish/dotnet
|
||||
mv mindworkAIStudio.exe "../../app/MindWork AI Studio/bin/dist/mindworkAIStudioServer${{ matrix.dotnet_name_postfix }}"
|
||||
|
||||
- name: Create parts for the Rust cache key (Unix)
|
||||
if: matrix.platform != 'windows-latest'
|
||||
run: |
|
||||
cd runtime
|
||||
echo "CARGO_LOCK_HASH=${{ hashFiles('**/Cargo.lock') }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Create parts for the Rust cache key (Windows)
|
||||
if: matrix.platform == 'windows-latest'
|
||||
run: |
|
||||
cd runtime
|
||||
echo "CARGO_LOCK_HASH=${{ hashFiles('**/Cargo.lock') }}" >> $env:GITHUB_ENV
|
||||
|
||||
- name: Cache Rust
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
@@ -272,17 +260,7 @@ jobs:
|
||||
~/.rustup/toolchains
|
||||
runtime/target
|
||||
|
||||
# When the entire key matches, Rust might just create the bundles using the current .NET build:
|
||||
key: target-${{ matrix.dotnet_runtime }}-rust-${{ env.RUST_VERSION }}-dependencies-${{ env.CARGO_LOCK_HASH }}
|
||||
|
||||
# - 1st key: the Rust runtime dependencies changed. Anyway, we might be able to re-use many previously built packages.
|
||||
#
|
||||
# - No match: alright, a new Rust version was released. Sadly, we cannot re-use anything now. Why?
|
||||
# The updated Rust compiler might mitigate some bugs or vulnerabilities. In order to apply
|
||||
# these changes to our app, we have to re-compile everything. That's the reason why it makes
|
||||
# no sense to use more parts for the cache key, like Tauri or Tauri build versions.
|
||||
restore-keys: |
|
||||
target-${{ matrix.dotnet_runtime }}-rust-${{ env.RUST_VERSION }}-dependencies-
|
||||
key: target-${{ matrix.dotnet_runtime }}-rust-${{ env.RUST_VERSION }}
|
||||
|
||||
- name: Setup Rust (stable)
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
@@ -300,7 +278,7 @@ jobs:
|
||||
if: matrix.platform != 'windows-latest'
|
||||
run: |
|
||||
if ! cargo tauri --version > /dev/null 2>&1; then
|
||||
cargo install tauri-cli
|
||||
cargo install --version 1.6.2 tauri-cli
|
||||
else
|
||||
echo "Tauri is already installed"
|
||||
fi
|
||||
@@ -309,7 +287,7 @@ jobs:
|
||||
if: matrix.platform == 'windows-latest'
|
||||
run: |
|
||||
if (-not (cargo tauri --version 2>$null)) {
|
||||
cargo install tauri-cli
|
||||
cargo install --version 1.6.2 tauri-cli
|
||||
} else {
|
||||
Write-Output "Tauri is already installed"
|
||||
}
|
||||
@@ -478,30 +456,13 @@ jobs:
|
||||
cd publish/dotnet
|
||||
mv mindworkAIStudio "../../app/MindWork AI Studio/bin/dist/mindworkAIStudioServer-aarch64-unknown-linux-gnu"
|
||||
|
||||
- name: Create parts for the Rust cache key
|
||||
if: ${{ env.SKIP != 'true' }}
|
||||
run: |
|
||||
cd runtime
|
||||
echo "CARGO_LOCK_HASH=${{ hashFiles('**/Cargo.lock') }}" >> $GITHUB_ENV
|
||||
|
||||
- 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
|
||||
|
||||
# When the entire key matches, Rust might just create the bundles using the current .NET build:
|
||||
key: target-linux-arm64-rust-${{ env.RUST_VERSION }}-dependencies-${{ env.CARGO_LOCK_HASH }}
|
||||
|
||||
# - 1st key: the Rust runtime dependencies changed. Anyway, we might be able to re-use many previously built packages.
|
||||
#
|
||||
# - No match: alright, a new Rust version was released. Sadly, we cannot re-use anything now. Why?
|
||||
# The updated Rust compiler might mitigate some bugs or vulnerabilities. In order to apply
|
||||
# these changes to our app, we have to re-compile everything. That's the reason why it makes
|
||||
# no sense to use more parts for the cache key, like Tauri or Tauri build versions.
|
||||
restore-keys: |
|
||||
target-linux-arm64-rust-${{ env.RUST_VERSION }}-dependencies-
|
||||
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
|
||||
@@ -539,12 +500,12 @@ jobs:
|
||||
apt-get install --yes libwebkit2gtk-4.0-dev build-essential libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev
|
||||
|
||||
# Setup Tauri:
|
||||
cargo install tauri-cli
|
||||
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
|
||||
mv ${{ steps.build-linux-arm-runner.outputs.image }} ${{ runner.temp }}/linux_arm_qemu_cache.img
|
||||
|
||||
- name: Build Tauri project
|
||||
if: ${{ env.SKIP != 'true' }}
|
||||
@@ -552,11 +513,12 @@ jobs:
|
||||
id: build-linux-arm
|
||||
|
||||
with:
|
||||
base_image: file://$RUNNER_TEMP/linux_arm_qemu_cache.img
|
||||
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,
|
||||
@@ -573,19 +535,17 @@ jobs:
|
||||
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
|
||||
|
||||
- name: Debug
|
||||
if: ${{ env.SKIP != 'true' }}
|
||||
run: |
|
||||
echo "Current directory: $(pwd)"
|
||||
ls -lhat
|
||||
|
||||
echo "Searching for linux_arm_qemu_cache.img"
|
||||
find $RUNNER_TEMP -name 'linux_arm_qemu_cache.img' -print 2>/dev/null
|
||||
|
||||
echo "Searching for mind-work-ai-studio_*.deb"
|
||||
find . -name 'mind-work-ai-studio_*.deb' -print 2>/dev/null
|
||||
|
||||
# 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' }}
|
||||
@@ -729,6 +689,9 @@ jobs:
|
||||
# Replace newlines in changelog with \n
|
||||
changelog=$(echo "$CHANGELOG" | awk '{printf "%s\\n", $0}')
|
||||
|
||||
# Escape double quotes in changelog:
|
||||
changelog=$(echo "$changelog" | sed 's/"/\\"/g')
|
||||
|
||||
# Create the latest.json file:
|
||||
cat <<EOOOF > $GITHUB_WORKSPACE/release/assets/latest.json
|
||||
{
|
||||
@@ -794,7 +757,11 @@ jobs:
|
||||
|
||||
- name: Append scan results to changelog
|
||||
run: |
|
||||
changelog="${{ env.CHANGELOG }}"
|
||||
changelog=$(cat <<'EOOOOOOOF'
|
||||
${{ env.CHANGELOG }}
|
||||
EOOOOOOOF
|
||||
)
|
||||
|
||||
links="${{ steps.virus_total.outputs.analysis }}"
|
||||
|
||||
# Create a temporary file for the changelog:
|
||||
@@ -838,7 +805,7 @@ jobs:
|
||||
- name: Create release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
prerelease: false
|
||||
prerelease: true
|
||||
draft: false
|
||||
make_latest: true
|
||||
body: ${{ env.CHANGELOG }}
|
||||
|
||||
@@ -1,11 +1,58 @@
|
||||
# MindWork AI Studio
|
||||

|
||||
|
||||
## News
|
||||
Things we are currently working on:
|
||||
|
||||
- Since November 2024: Work on RAG (integration of your data and files) has begun. We will support the integration of local and external data sources. We need to implement the following runtime (Rust) and app (.NET) steps:
|
||||
|
||||
- [x] ~~Runtime: Restructuring the code into meaningful modules (PR [#192](https://github.com/MindWorkAI/AI-Studio/pull/192))~~
|
||||
- [x] ~~Define the [External Data API (EDI)](https://github.com/MindWorkAI/EDI) as a contract for integrating arbitrary external data (PR [#1](https://github.com/MindWorkAI/EDI/pull/1))~~
|
||||
- [x] ~~App: Metadata for providers (which provider offers embeddings?) (PR [#205](https://github.com/MindWorkAI/AI-Studio/pull/205))~~
|
||||
- [x] ~~App: Add an option to show preview features (PR [#222](https://github.com/MindWorkAI/AI-Studio/pull/222))~~
|
||||
- [x] ~~App: Configure embedding providers (PR [#224](https://github.com/MindWorkAI/AI-Studio/pull/224))~~
|
||||
- [ ] App: Management of data sources (local & external data via [EDI](https://github.com/MindWorkAI/EDI))
|
||||
- [ ] Runtime: Extract data from txt / md / pdf / docx / xlsx files
|
||||
- [ ] (*Optional*) Runtime: Implement internal embedding provider through [fastembed-rs](https://github.com/Anush008/fastembed-rs)
|
||||
- [ ] 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)
|
||||
- [ ] App: Implement the continuous process of vectorizing data
|
||||
- [ ] App: Define a common retrieval context interface for the integration of RAG processes in chats
|
||||
- [ ] App: Define a common augmentation interface for the integration of RAG processes in chats
|
||||
- [ ] App: Integrate data sources in chats
|
||||
|
||||
|
||||
- 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)~~.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
- 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.
|
||||
|
||||
|
||||
Features we have recently released:
|
||||
|
||||
- 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.
|
||||
- v0.9.12: Added a job posting assistant to the business category and improved grammar & spelling check and rewrite assistants.
|
||||
- v0.9.11: Added enforcement of minimal confidence levels & dark mode.
|
||||
|
||||
## What is AI Studio?
|
||||
|
||||

|
||||

|
||||
|
||||
MindWork AI Studio is a desktop application available for macOS, Windows, and Linux. It provides a unified chat interface for Large Language Models (LLMs). You bring your own API key for the respective LLM provider to use the models. The API keys are securely stored by the operating system.
|
||||
|
||||
**Key advantages:**
|
||||
- **Free of charge**: The app is free to use, both for personal and commercial purposes.
|
||||
- **Independence**: Users are not tied to any single provider. Instead, they can choose the provider that best suits their needs. Right now, we support OpenAI (GPT4o etc.), Mistral, Anthropic (Claude), and self-hosted models using [llama.cpp](https://github.com/ggerganov/llama.cpp) or [LM Studio](https://lmstudio.ai/). Support for Google Gemini, [ollama](https://github.com/ollama/ollama), [Replicate](https://replicate.com/), and [Fireworks](https://fireworks.ai/) is planned.
|
||||
- **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 etc.), Mistral, Anthropic (Claude), Google Gemini, 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/).
|
||||
- **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.
|
||||
- **Privacy**: The data entered into the app is not used for training by the providers since we are using the provider's API.
|
||||
@@ -24,16 +71,18 @@ We offer various ways you can support the project:
|
||||
- **One-Time Contributions**: Make a one-time donation and have your name or company logo included in the app as a gesture of our gratitude.
|
||||
|
||||
For companies, sponsoring MindWork AI Studio is not only a way to support innovation but also a valuable opportunity for public relations and marketing. Your company's name and logo will be featured prominently, showcasing your commitment to using cutting-edge AI tools and enhancing your reputation as an innovative enterprise.
|
||||
|
||||
|
||||
To view all available tiers, please visit our [GitHub Sponsors page](https://github.com/sponsors/MindWorkAI).
|
||||
Your support, whether big or small, keeps the wheels turning and is deeply appreciated ❤️.
|
||||
|
||||
## Planned Features
|
||||
Here's an exciting look at some of the features we're planning to add to MindWork AI Studio in future releases:
|
||||
- **More providers**: We plan to add support for additional LLM providers, such as Google Gemini, giving you more options to choose from.
|
||||
- **System prompts**: Integration of a system prompt library will allow you to control the behavior of the LLM with predefined prompts, ensuring consistency and efficiency.
|
||||
- **Text replacement for better privacy**: Define keywords that will be replaced in your chats before sending content to the provider, enhancing your privacy.
|
||||
- **Advanced interactions**: We're full of ideas for advanced interactions tailored for specific use cases, whether in a business context or for writers and other professionals.
|
||||
Here's an exciting look at some of the features we're planning to add to AI Studio in future releases:
|
||||
- **Integrating your data**: You should be able to integrate your data into AI Studio. For example, your PDF or Office files, or your Markdown notes.
|
||||
- **Integration of enterprise data:** Soon, it will also be possible to integrate data from the corporate network using an interface that we have specified ([External Data API](https://github.com/MindWorkAI/EDI), EDI for short).
|
||||
- **Writing mode:** We want to integrate a writing mode that should support you in creating extensive works. We are thinking of comprehensive project proposals, tenders, or your next fantasy novel.
|
||||
- **Browser usage:** We're trying to offer the features from AI Studio to you in the browser via a plugin, so we could use spell-checking or rewriting text directly in the browser.
|
||||
- **Voice control:** You should be able to interact with the AI systems using your voice as well. To achieve this, we want to integrate voice input (speech-to-text) and output (text-to-speech). However, later on, it should also have a natural conversation flow, i.e., seamless conversation.
|
||||
- **Email monitoring:** You should have the option to connect your email inboxes with AI Studio. The AI reads your emails and sends you a notification when something important happens. At the same time, you can access knowledge from your emails in your chats.
|
||||
|
||||
Stay tuned for more updates and enhancements to make MindWork AI Studio even more powerful and versatile 🤩.
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="AI Studio" type="DotNetProject" factoryName=".NET Project">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/MindWork AI Studio/bin/Debug/net8.0/osx-arm64/mindworkAIStudio.dll" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/MindWork AI Studio" />
|
||||
<option name="PASS_PARENT_ENVS" value="1" />
|
||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||
<option name="USE_MONO" value="0" />
|
||||
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||
<option name="RUNTIME_TYPE" value="coreclr" />
|
||||
<option name="PROJECT_PATH" value="$PROJECT_DIR$/MindWork AI Studio/MindWork AI Studio.csproj" />
|
||||
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||
<option name="PROJECT_TFM" value="net8.0" />
|
||||
<method v="2">
|
||||
<option name="Build" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,7 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run Dev" type="CompoundRunConfigurationType">
|
||||
<toRun name="AI Studio" type="DotNetProject" />
|
||||
<toRun name="Tauri Dev" type="ShConfigurationType" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,4 +1,8 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AI/@EntryIndexedValue">AI</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:Boolean x:Key="/Default/UserDictionary/Words/=groq/@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>
|
||||
@@ -1 +0,0 @@
|
||||
*.woff2 filter=lfs diff=lfs merge=lfs -text
|
||||
@@ -0,0 +1,108 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
|
||||
namespace AIStudio.Agents;
|
||||
|
||||
public abstract class AgentBase(ILogger<AgentBase> logger, SettingsManager settingsManager, ThreadSafeRandom rng) : IAgent
|
||||
{
|
||||
protected SettingsManager SettingsManager { get; init; } = settingsManager;
|
||||
|
||||
protected ThreadSafeRandom RNG { get; init; } = rng;
|
||||
|
||||
protected ILogger<AgentBase> Logger { get; init; } = logger;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the type or category of this agent.
|
||||
/// </summary>
|
||||
protected abstract Type Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the agent.
|
||||
/// </summary>
|
||||
public abstract string Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The agent's job description. Will be used for the system prompt.
|
||||
/// </summary>
|
||||
protected abstract string JobDescription { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Represents the system prompt provided for the agent.
|
||||
/// </summary>
|
||||
protected abstract string SystemPrompt(string additionalData);
|
||||
|
||||
#region Implementation of IAgent
|
||||
|
||||
public abstract AIStudio.Settings.Provider? ProviderSettings { get; set; }
|
||||
|
||||
public abstract Task<ChatThread> ProcessContext(ChatThread chatThread, IDictionary<string, string> additionalData);
|
||||
|
||||
public abstract Task<ContentBlock> ProcessInput(ContentBlock input, IDictionary<string, string> additionalData);
|
||||
|
||||
public abstract Task<bool> MadeDecision(ContentBlock input);
|
||||
|
||||
public abstract IReadOnlyCollection<ContentBlock> GetContext();
|
||||
|
||||
public abstract IReadOnlyCollection<ContentBlock> GetAnswers();
|
||||
|
||||
#endregion
|
||||
|
||||
protected ChatThread CreateChatThread(string systemPrompt) => new()
|
||||
{
|
||||
WorkspaceId = Guid.Empty,
|
||||
ChatId = Guid.NewGuid(),
|
||||
Name = string.Empty,
|
||||
Seed = this.RNG.Next(),
|
||||
SystemPrompt = systemPrompt,
|
||||
Blocks = [],
|
||||
};
|
||||
|
||||
protected DateTimeOffset AddUserRequest(ChatThread thread, string request)
|
||||
{
|
||||
var time = DateTimeOffset.Now;
|
||||
thread.Blocks.Add(new ContentBlock
|
||||
{
|
||||
Time = time,
|
||||
ContentType = ContentType.TEXT,
|
||||
Role = ChatRole.USER,
|
||||
Content = new ContentText
|
||||
{
|
||||
Text = request,
|
||||
},
|
||||
});
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
protected async Task AddAIResponseAsync(ChatThread thread, DateTimeOffset time)
|
||||
{
|
||||
if(this.ProviderSettings is null)
|
||||
return;
|
||||
|
||||
var providerSettings = this.ProviderSettings.Value;
|
||||
var aiText = new ContentText
|
||||
{
|
||||
// We have to wait for the remote
|
||||
// for the content stream:
|
||||
InitialRemoteWait = true,
|
||||
};
|
||||
|
||||
var resultingContentBlock = new ContentBlock
|
||||
{
|
||||
Time = time,
|
||||
ContentType = ContentType.TEXT,
|
||||
Role = ChatRole.AI,
|
||||
Content = aiText,
|
||||
};
|
||||
|
||||
thread.Blocks.Add(resultingContentBlock);
|
||||
|
||||
// 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(providerSettings.CreateProvider(this.Logger), this.SettingsManager, providerSettings.Model, thread);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Agents;
|
||||
|
||||
public sealed class AgentTextContentCleaner(ILogger<AgentBase> logger, SettingsManager settingsManager, ThreadSafeRandom rng) : AgentBase(logger, settingsManager, rng)
|
||||
{
|
||||
private static readonly ContentBlock EMPTY_BLOCK = new()
|
||||
{
|
||||
Content = null,
|
||||
ContentType = ContentType.NONE,
|
||||
Role = ChatRole.AGENT,
|
||||
Time = DateTimeOffset.UtcNow,
|
||||
};
|
||||
|
||||
private readonly List<ContentBlock> context = new();
|
||||
private readonly List<ContentBlock> answers = new();
|
||||
|
||||
#region Overrides of AgentBase
|
||||
|
||||
public override Settings.Provider? ProviderSettings { get; set; }
|
||||
|
||||
protected override Type Type => Type.SYSTEM;
|
||||
|
||||
public override string Id => "Text Content Cleaner";
|
||||
|
||||
protected override string JobDescription =>
|
||||
"""
|
||||
You receive a Markdown document as input. Your goal is to identify the main content of the document
|
||||
and return it including Markdown formatting. Remove areas that do not belong to the main part of the
|
||||
document. For a blog article, return only the text of the article with its formatting. For a scientific
|
||||
paper, only the contents of the paper. Delete elements of navigation, advertisements, HTML artifacts,
|
||||
cookie banners, etc. If the content contains images, these images remain. The same applies to links.
|
||||
Ensure that links and images are present as valid Markdown:
|
||||
|
||||
- Syntax of links: [link text](URL)
|
||||
- Syntax of images: 
|
||||
|
||||
If you find relative links or images with relative paths, correct them to absolute paths. For this
|
||||
purpose, here is the source URL:
|
||||
""";
|
||||
|
||||
protected override string SystemPrompt(string additionalData) => $"{this.JobDescription} `{additionalData}`.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<ChatThread> ProcessContext(ChatThread chatThread, IDictionary<string, string> additionalData)
|
||||
{
|
||||
// We process the last block of the chat thread. Then, we add the result
|
||||
// to the chat thread as the last block:
|
||||
var answer = await this.ProcessInput(chatThread.Blocks[^1], additionalData);
|
||||
chatThread.Blocks.Add(answer);
|
||||
|
||||
this.context.Clear();
|
||||
this.context.AddRange(chatThread.Blocks);
|
||||
|
||||
return chatThread;
|
||||
}
|
||||
|
||||
// <inheritdoc />
|
||||
public override async Task<ContentBlock> ProcessInput(ContentBlock input, IDictionary<string, string> additionalData)
|
||||
{
|
||||
if (input.Content is not ContentText text)
|
||||
return EMPTY_BLOCK;
|
||||
|
||||
if(text.InitialRemoteWait || text.IsStreaming)
|
||||
return EMPTY_BLOCK;
|
||||
|
||||
if(string.IsNullOrWhiteSpace(text.Text))
|
||||
return EMPTY_BLOCK;
|
||||
|
||||
if(!additionalData.TryGetValue("sourceURL", out var sourceURL) || string.IsNullOrWhiteSpace(sourceURL))
|
||||
return EMPTY_BLOCK;
|
||||
|
||||
var thread = this.CreateChatThread(this.SystemPrompt(sourceURL));
|
||||
var time = this.AddUserRequest(thread, text.Text);
|
||||
await this.AddAIResponseAsync(thread, time);
|
||||
|
||||
var answer = thread.Blocks[^1];
|
||||
this.answers.Add(answer);
|
||||
return answer;
|
||||
}
|
||||
|
||||
// <inheritdoc />
|
||||
public override Task<bool> MadeDecision(ContentBlock input) => Task.FromResult(true);
|
||||
|
||||
// <inheritdoc />
|
||||
public override IReadOnlyCollection<ContentBlock> GetContext() => this.context;
|
||||
|
||||
// <inheritdoc />
|
||||
public override IReadOnlyCollection<ContentBlock> GetAnswers() => this.answers;
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using AIStudio.Chat;
|
||||
|
||||
namespace AIStudio.Agents;
|
||||
|
||||
public interface IAgent
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the agent.
|
||||
/// </summary>
|
||||
public string Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The provider to use for this agent.
|
||||
/// </summary>
|
||||
public AIStudio.Settings.Provider? ProviderSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Processes a chat thread (i.e., context) and returns the updated thread.
|
||||
/// </summary>
|
||||
/// <param name="chatThread">The chat thread to process. The thread is the context for the agent.</param>
|
||||
/// <param name="additionalData">Additional data to use for processing the chat thread.</param>
|
||||
/// <returns>The updated chat thread. The last content block of the thread is the agent's response.</returns>
|
||||
public Task<ChatThread> ProcessContext(ChatThread chatThread, IDictionary<string, string> additionalData);
|
||||
|
||||
/// <summary>
|
||||
/// Processes the input content block and returns the agent's response.
|
||||
/// </summary>
|
||||
/// <param name="input">The content block to process. It represents the input.</param>
|
||||
/// <param name="additionalData">Additional data to use for processing the input.</param>
|
||||
/// <returns>The content block representing the agent's response.</returns>
|
||||
public Task<ContentBlock> ProcessInput(ContentBlock input, IDictionary<string, string> additionalData);
|
||||
|
||||
/// <summary>
|
||||
/// The agent makes a decision based on the input.
|
||||
/// </summary>
|
||||
/// <param name="input">The content block to process. Should be a question or a request.</param>
|
||||
/// <returns>
|
||||
/// True if a decision has been made based on the input, false otherwise.
|
||||
/// </returns>
|
||||
public Task<bool> MadeDecision(ContentBlock input);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the context of the agent.
|
||||
/// </summary>
|
||||
/// <returns>The collection of content blocks representing the agent's context. This includes the user's and the other agent's messages.</returns>
|
||||
public IReadOnlyCollection<ContentBlock> GetContext();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the answers from the agent's context.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The collection of content blocks representing the answers provided by this agent.
|
||||
/// </returns>
|
||||
public IReadOnlyCollection<ContentBlock> GetAnswers();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace AIStudio.Agents;
|
||||
|
||||
public enum Type
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an unspecified agent type.
|
||||
/// </summary>
|
||||
UNSPECIFIED = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a conversational agent who produces human-like responses and feedback (depending on the context and its job description).
|
||||
/// For example, an expert agent for a specific domain. Answers might be detailed and informative.
|
||||
/// </summary>
|
||||
CONVERSATIONAL,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a worker agent type who performs tasks and provides information or services (depending on the context and its job description).
|
||||
/// For example, a quality assurance agent who assesses the quality of a product or service. Answers might be short and concise.
|
||||
/// </summary>
|
||||
WORKER,
|
||||
|
||||
/// <summary>
|
||||
/// Represents the system agent type who processes the input and provides a specific response (depending on the context and its job description).
|
||||
/// For example, a HTML content agent who processes the arbitrary HTML content and provides a structured Markdown response. Answers might be structured and formatted.
|
||||
/// </summary>
|
||||
SYSTEM,
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
|
||||
@* ReSharper disable Html.PathError *@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>MindWork AI Studio</title>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<base href="/"/>
|
||||
|
||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||
<link href="system/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
|
||||
<link href="system/MudBlazor.Markdown/MudBlazor.Markdown.min.css" rel="stylesheet" />
|
||||
<link href="system/CodeBeam.MudBlazor.Extensions/MudExtensions.min.css" rel="stylesheet" />
|
||||
<link href="app.css" rel="stylesheet" />
|
||||
<HeadOutlet/>
|
||||
<script src="diff.js"></script>
|
||||
</head>
|
||||
|
||||
<body style="overflow: hidden;">
|
||||
<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)"/>
|
||||
<script src="_framework/blazor.web.js" autostart="false"></script>
|
||||
<script src="boot.js"></script>
|
||||
<script src="system/MudBlazor/MudBlazor.min.js"></script>
|
||||
<script src="system/MudBlazor.Markdown/MudBlazor.Markdown.min.js"></script>
|
||||
<script src="system/CodeBeam.MudBlazor.Extensions/MudExtensions.min.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,51 @@
|
||||
@attribute [Route(Routes.ASSISTANT_AGENDA)]
|
||||
@inherits AssistantBaseCore
|
||||
|
||||
<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"/>
|
||||
<MudTextField T="string" @bind-Text="@this.inputContent" Validation="@this.ValidateContent" Label="Content list" Variant="Variant.Outlined" Lines="6" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Bullet list the content of the meeting, seminar, etc. roughly. Use dashes (-) to separate the items." Placeholder="@PLACEHOLDER_CONTENT" Class="mb-3" Immediate="@false" DebounceInterval="1_000" OnDebounceIntervalElapsed="@this.OnContentChanged" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.ListAlt"/>
|
||||
<MudSelect T="string" Label="(Optional) What topics should be the focus?" MultiSelection="@true" @bind-SelectedValues="@this.selectedFoci" Variant="Variant.Outlined" Class="mb-3" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.ListAlt">
|
||||
@foreach (var contentLine in this.contentLines)
|
||||
{
|
||||
@if(!this.justBriefly.Contains(contentLine))
|
||||
{
|
||||
<MudSelectItem T="string" Value="@contentLine">@contentLine</MudSelectItem>
|
||||
}
|
||||
}
|
||||
</MudSelect>
|
||||
|
||||
<MudSelect T="string" Label="(Optional) What topics should only be briefly addressed?" MultiSelection="@true" @bind-SelectedValues="@this.justBriefly" Variant="Variant.Outlined" Class="mb-3" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.ListAlt">
|
||||
@foreach (var contentLine in this.contentLines)
|
||||
{
|
||||
@if(!this.selectedFoci.Contains(contentLine))
|
||||
{
|
||||
<MudSelectItem T="string" Value="@contentLine">@contentLine</MudSelectItem>
|
||||
}
|
||||
}
|
||||
</MudSelect>
|
||||
|
||||
<MudTextField T="string" @bind-Text="@this.inputObjective" Validation="@this.ValidateObjective" Label="Objective" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Flag" Variant="Variant.Outlined" Lines="3" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Describe the objective(s) of the meeting, seminar, etc. What should be achieved?" Placeholder="Discuss the current project status and plan the next steps." Class="mb-3"/>
|
||||
<MudTextField T="string" @bind-Text="@this.inputModerator" Validation="@this.ValidateModerator" Label="Moderator" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Person3" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Who will moderate the meeting, seminar, etc.?" Placeholder="Jane Doe" Class="mb-3" />
|
||||
<MudTextField T="string" @bind-Text="@this.inputDuration" Validation="@this.ValidateDuration" Label="Duration" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Schedule" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="How long will the meeting, seminar, etc. last? E.g., '2 hours', or '2 days (first day 8 hours, then 4 hours)', etc." Placeholder="2 hours" Class="mb-3"/>
|
||||
<MudTextField T="string" @bind-Text="@this.inputStartTime" Validation="@this.ValidateStartTime" Label="Start time" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Schedule" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="When will the meeting, seminar, etc. start? E.g., '9:00 AM', or '9:00 AM (CET)', etc. When the meeting is a multi-day event, specify the start time for each day." Placeholder="9:00 AM" Class="mb-3"/>
|
||||
<MudTextField T="string" @bind-Text="@this.inputWhoIsPresenting" Label="(Optional) Who is presenting?" Variant="Variant.Outlined" Lines="6" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="(Optional) List the persons who will present at the meeting, seminar, etc. Use dashes (-) to separate the items." Placeholder="@PLACEHOLDER_WHO_IS_PRESENTING" Class="mb-3"/>
|
||||
<MudTextSwitch Label="Do the participants need to get to know each other first?" @bind-Value="@this.introduceParticipants" LabelOn="Yes, introduce participants" LabelOff="No, participants know each other" />
|
||||
<EnumSelection T="NumberParticipants" @bind-Value="@this.numberParticipants" NameFunc="@(participants => participants.Name())" Icon="@Icons.Material.Filled.Group" Label="Number of participants" ValidateSelection="@this.ValidateNumberParticipants"/>
|
||||
<MudTextSwitch Label="Should the participants be involved passively or actively?" @bind-Value="@this.activeParticipation" LabelOn="Active participation, like world café, discussions, etc." LabelOff="Passive participation, like presentations, lectures, etc." />
|
||||
<MudTextSwitch Label="Is this a virtual event, e.g., a call or webinar?" @bind-Value="@this.isMeetingVirtual" LabelOn="Yes, this is a virtual event" LabelOff="No, this is a physical event" />
|
||||
@if (!this.isMeetingVirtual)
|
||||
{
|
||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||
<MudTextField T="string" @bind-Text="@this.inputLocation" Validation="@this.ValidateLocation" Label="Location" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.MyLocation" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Where will the meeting, seminar, etc. take place?" Placeholder="Hamburg, Germany" Class="mb-3"/>
|
||||
<MudTextSwitch Label="Should there be a joint dinner?" @bind-Value="@this.goingToDinner" LabelOn="Yes, there should be a joint dinner" LabelOff="No, there should be no joint dinner" />
|
||||
<MudTextSwitch Label="Should there be a social activity?" @bind-Value="@this.doingSocialActivity" LabelOn="Yes, there should be a social activity" LabelOff="No, there should be no social activity" />
|
||||
<MudTextSwitch Label="Do participants need to arrive and depart?" @bind-Value="@this.needToArriveAndDepart" LabelOn="Yes, participants need to arrive and depart" LabelOff="No, participants do not need to arrive and depart" />
|
||||
<MudStack Row="@true" Wrap="Wrap.Wrap">
|
||||
<MudTextSlider T="int" Label="Approx. duration of the lunch break" @bind-Value="@this.durationLunchBreak" Min="30" Max="120" Step="5" Unit="minutes"/>
|
||||
<MudTextSlider T="int" Label="Approx. duration of the coffee or tea breaks" @bind-Value="@this.durationBreaks" Min="10" Max="60" Step="5" Unit="minutes"/>
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
}
|
||||
|
||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelecting())" @bind-Value="@this.selectedTargetLanguage" ValidateSelection="@this.ValidateTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Target language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom target language" />
|
||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||
@@ -0,0 +1,507 @@
|
||||
using System.Text;
|
||||
|
||||
using AIStudio.Chat;
|
||||
|
||||
namespace AIStudio.Assistants.Agenda;
|
||||
|
||||
public partial class AssistantAgenda : AssistantBaseCore
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.AGENDA_ASSISTANT;
|
||||
|
||||
protected override string Title => "Agenda Planner";
|
||||
|
||||
protected override string Description =>
|
||||
"""
|
||||
This agenda planner helps you create a structured agenda for your meeting or seminar. Just provide some basic
|
||||
information about the event, and the assistant will generate an agenda for you. You can also specify the
|
||||
duration, the start time, the location, the target language, and other details.
|
||||
""";
|
||||
|
||||
protected override string SystemPrompt =>
|
||||
$"""
|
||||
You are a friendly assistant who supports the planning and consultation of events. You receive
|
||||
metadata about an event and create an agenda from it. Additionally, you provide advice on what
|
||||
the organizer and moderator should consider. When creating the agenda, you take into account that
|
||||
time for questions and discussions needs to be scheduled. For large events, you take into account
|
||||
the logistical challenges that come with an increasing number of participants.
|
||||
|
||||
If the user is planning interactions with participants, you know various methods. For example:
|
||||
|
||||
- **Townhall Meeting**: An open assembly where participants can ask questions and provide feedback directly to leaders or speakers. Good moderation is necessary to queue contributions and hand the floor to the respective person. Additionally, one person should take notes visible to all. Ideally, this should not be the moderator.
|
||||
|
||||
- **Speed Dating**: Short, structured interaction rounds where participants get the chance to quickly introduce themselves and make initial connections. A long row of tables is needed so that two people can sit opposite each other. A moderator with a stopwatch must ensure that participants rotate and change seats every 3 to 5 minutes.
|
||||
|
||||
- **World Café**: Participants discuss in small groups at tables and rotate after set intervals to exchange thoughts and develop ideas further. Each table needs a host. Notes should be taken on a flipchart or digitally at each table. At the end, the table hosts could present their insights to all participants in a plenary session.
|
||||
|
||||
- **Fishbowl**: A group discusses in an inner circle while a larger group listens from the outside. Occasionally, listeners can move into the inner circle to actively participate.
|
||||
|
||||
- **BarCamp (Unconference)**: Participants contribute to part of the agenda themselves and can act as both presenters and listeners. Anyone can propose and lead a session. However, there should be initial moderation to guide this meta-process.
|
||||
|
||||
- **Open Space Technology**: A method for large groups where participants bring up their own topics and then work intensively in small groups. At the end, the groups can present their findings to all participants in a plenary session.
|
||||
|
||||
- **Panel Discussion**: A group of experts discusses a specific topic in front of an audience, followed by a question-and-answer session.
|
||||
|
||||
- **Breakout Sessions**: Larger groups are divided into smaller, topic-related groups to discuss specific issues more deeply and interactively.
|
||||
|
||||
- **Interactive Polling**: Real-time surveys during the meeting or seminar using digital technology to collect immediate feedback from participants. With an increasing number of participants, technical requirements also become more complex: not every Wi-Fi router can handle several hundred or thousand devices. Additionally, the moderator or speaker must have a stable network connection to display the results. It would be problematic if the speaker depends on the same Wi-Fi as the participants: if the Wi-Fi crashes due to the high number of participants, the speaker cannot show the results. Therefore, a LAN for the speaker or the use of a mobile hotspot would be a better choice.
|
||||
|
||||
- **Peer Learning Groups**: Small groups of participants work together to learn from each other and collaboratively develop solutions for specific challenges.
|
||||
|
||||
- **Role Play/Simulation**: Participants take on roles to simulate certain scenarios, gaining practical experience and promoting dynamic discussions.
|
||||
|
||||
- **Round Table Discussions**: Small group discussions on specific topics, often without formal moderation, to encourage open and equal dialogue.
|
||||
|
||||
- **Mind Mapping**: A method for visualizing ideas and concepts, which can foster creative exchange within groups.
|
||||
|
||||
- **Case Study Analysis**: Groups analyze real or hypothetical case studies and present their solutions or insights.
|
||||
|
||||
- **Silent Meeting/Writing**: Participants write down their thoughts and questions, which are then collected and discussed to balance out loud or dominant voices.
|
||||
|
||||
- **Debate**: Participants argue for or against a specific topic, often with a moderator who ensures that the discussion remains constructive.
|
||||
|
||||
- **Workshops**: Interactive sessions where participants work together on specific tasks or challenges.
|
||||
|
||||
- **Brainstorming**: A method for generating ideas in a group setting, often with a focus on quantity and creativity.
|
||||
|
||||
- **Design Thinking**: A structured approach to innovation solutions. Design thinking is structured in six phases: understand, observe, point of view, ideate, prototype, and test. Depending on the number of participants, there are two moderates needed. One moderator leads the process, the other supports the participants in their work. Design thinking may takes up to two days.
|
||||
|
||||
You output the agenda in the following Markdown format:
|
||||
|
||||
# [Agenda Title]
|
||||
- 09:00 - 09:05 (5 minutes): Welcome
|
||||
- 09:05 - 09:15 (10 minutes): Introduction
|
||||
- ...
|
||||
{this.SystemPromptSocialActivity()}
|
||||
{this.SystemPromptDinner()}
|
||||
|
||||
# Organizational notes
|
||||
[Your advice for the organizer goes here, like room setup, technical requirements, etc.]
|
||||
|
||||
# Moderation notes
|
||||
[Your advice for the moderator goes here]
|
||||
|
||||
Output the agenda in the following language: {this.PromptLanguage()}.
|
||||
""";
|
||||
|
||||
private const string PLACEHOLDER_CONTENT = """
|
||||
- Project status
|
||||
- We need to discuss how to proceed
|
||||
- Problem solving in work package 3 possible?
|
||||
- Concerns of the members
|
||||
""";
|
||||
|
||||
private const string PLACEHOLDER_WHO_IS_PRESENTING = """
|
||||
- John Doe: Project status
|
||||
- Mary Jane: Work package 3
|
||||
""";
|
||||
|
||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||
|
||||
protected override string SubmitText => "Create Agenda";
|
||||
|
||||
protected override Func<Task> SubmitAction => this.CreateAgenda;
|
||||
|
||||
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||
{
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
};
|
||||
|
||||
protected override void ResetFrom()
|
||||
{
|
||||
this.inputContent = string.Empty;
|
||||
this.contentLines.Clear();
|
||||
this.selectedFoci = [];
|
||||
this.justBriefly = [];
|
||||
this.inputWhoIsPresenting = string.Empty;
|
||||
if (!this.MightPreselectValues())
|
||||
{
|
||||
this.inputTopic = string.Empty;
|
||||
this.inputName = string.Empty;
|
||||
this.inputDuration = string.Empty;
|
||||
this.inputStartTime = string.Empty;
|
||||
this.inputObjective = string.Empty;
|
||||
this.inputModerator = string.Empty;
|
||||
this.selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
this.customTargetLanguage = string.Empty;
|
||||
this.introduceParticipants = false;
|
||||
this.isMeetingVirtual = true;
|
||||
this.inputLocation = string.Empty;
|
||||
this.goingToDinner = false;
|
||||
this.doingSocialActivity = false;
|
||||
this.needToArriveAndDepart = false;
|
||||
this.durationLunchBreak = 0;
|
||||
this.durationBreaks = 0;
|
||||
this.activeParticipation = false;
|
||||
this.numberParticipants = NumberParticipants.NOT_SPECIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MightPreselectValues()
|
||||
{
|
||||
if (this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)
|
||||
{
|
||||
this.inputTopic = this.SettingsManager.ConfigurationData.Agenda.PreselectTopic;
|
||||
this.inputName = this.SettingsManager.ConfigurationData.Agenda.PreselectName;
|
||||
this.inputDuration = this.SettingsManager.ConfigurationData.Agenda.PreselectDuration;
|
||||
this.inputStartTime = this.SettingsManager.ConfigurationData.Agenda.PreselectStartTime;
|
||||
this.inputObjective = this.SettingsManager.ConfigurationData.Agenda.PreselectObjective;
|
||||
this.inputModerator = this.SettingsManager.ConfigurationData.Agenda.PreselectModerator;
|
||||
this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.Agenda.PreselectedTargetLanguage;
|
||||
this.customTargetLanguage = this.SettingsManager.ConfigurationData.Agenda.PreselectedOtherLanguage;
|
||||
this.introduceParticipants = this.SettingsManager.ConfigurationData.Agenda.PreselectIntroduceParticipants;
|
||||
this.isMeetingVirtual = this.SettingsManager.ConfigurationData.Agenda.PreselectIsMeetingVirtual;
|
||||
this.inputLocation = this.SettingsManager.ConfigurationData.Agenda.PreselectLocation;
|
||||
this.goingToDinner = this.SettingsManager.ConfigurationData.Agenda.PreselectJointDinner;
|
||||
this.doingSocialActivity = this.SettingsManager.ConfigurationData.Agenda.PreselectSocialActivity;
|
||||
this.needToArriveAndDepart = this.SettingsManager.ConfigurationData.Agenda.PreselectArriveAndDepart;
|
||||
this.durationLunchBreak = this.SettingsManager.ConfigurationData.Agenda.PreselectLunchTime;
|
||||
this.durationBreaks = this.SettingsManager.ConfigurationData.Agenda.PreselectBreakTime;
|
||||
this.activeParticipation = this.SettingsManager.ConfigurationData.Agenda.PreselectActiveParticipation;
|
||||
this.numberParticipants = this.SettingsManager.ConfigurationData.Agenda.PreselectNumberParticipants;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private string inputTopic = string.Empty;
|
||||
private string inputName = string.Empty;
|
||||
private string inputContent = string.Empty;
|
||||
private string inputDuration = string.Empty;
|
||||
private string inputStartTime = string.Empty;
|
||||
private IEnumerable<string> selectedFoci = new HashSet<string>();
|
||||
private IEnumerable<string> justBriefly = new HashSet<string>();
|
||||
private string inputObjective = string.Empty;
|
||||
private string inputModerator = string.Empty;
|
||||
private CommonLanguages selectedTargetLanguage;
|
||||
private string customTargetLanguage = string.Empty;
|
||||
private bool introduceParticipants;
|
||||
private bool isMeetingVirtual = true;
|
||||
private string inputLocation = string.Empty;
|
||||
private bool goingToDinner;
|
||||
private bool doingSocialActivity;
|
||||
private bool needToArriveAndDepart;
|
||||
private int durationLunchBreak;
|
||||
private int durationBreaks;
|
||||
private bool activeParticipation;
|
||||
private NumberParticipants numberParticipants;
|
||||
private string inputWhoIsPresenting = string.Empty;
|
||||
|
||||
private readonly List<string> contentLines = [];
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages<string>(Event.SEND_TO_AGENDA_ASSISTANT).FirstOrDefault();
|
||||
if (deferredContent is not null)
|
||||
this.inputContent = deferredContent;
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void OnContentChanged(string content)
|
||||
{
|
||||
var previousSelectedFoci = new HashSet<string>();
|
||||
var previousJustBriefly = new HashSet<string>();
|
||||
|
||||
this.contentLines.Clear();
|
||||
foreach (var line in content.AsSpan().EnumerateLines())
|
||||
{
|
||||
var trimmedLine = line.Trim();
|
||||
if (trimmedLine.StartsWith("-"))
|
||||
trimmedLine = trimmedLine[1..].Trim();
|
||||
|
||||
if (trimmedLine.Length == 0)
|
||||
continue;
|
||||
|
||||
var finalLine = trimmedLine.ToString();
|
||||
if(this.selectedFoci.Any(x => x.StartsWith(finalLine, StringComparison.InvariantCultureIgnoreCase)))
|
||||
previousSelectedFoci.Add(finalLine);
|
||||
|
||||
if(this.justBriefly.Any(x => x.StartsWith(finalLine, StringComparison.InvariantCultureIgnoreCase)))
|
||||
previousJustBriefly.Add(finalLine);
|
||||
|
||||
this.contentLines.Add(finalLine);
|
||||
}
|
||||
|
||||
this.selectedFoci = previousSelectedFoci;
|
||||
this.justBriefly = previousJustBriefly;
|
||||
}
|
||||
|
||||
private string? ValidateLocation(string location)
|
||||
{
|
||||
if(!this.isMeetingVirtual && string.IsNullOrWhiteSpace(location))
|
||||
return "Please provide a location for the meeting or the seminar.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateNumberParticipants(NumberParticipants selectedSize)
|
||||
{
|
||||
if(selectedSize is NumberParticipants.NOT_SPECIFIED)
|
||||
return "Please select the number of participants.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateTargetLanguage(CommonLanguages language)
|
||||
{
|
||||
if(language is CommonLanguages.AS_IS)
|
||||
return "Please select a target language for the agenda.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateDuration(string duration)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(duration))
|
||||
return "Please provide a duration for the meeting or the seminar, e.g. '2 hours', or '2 days (8 hours and 4 hours)', etc.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateStartTime(string startTime)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(startTime))
|
||||
return "Please provide a start time for the meeting or the seminar. When the meeting is a multi-day event, specify the start time for each day, e.g. '9:00 AM, 10:00 AM', etc.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateCustomLanguage(string language)
|
||||
{
|
||||
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||
return "Please provide a custom language.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateTopic(string topic)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(topic))
|
||||
return "Please provide a topic for the agenda. What is the meeting or the seminar about?";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateName(string name)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(name))
|
||||
return "Please provide a name for the meeting or the seminar.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateContent(string content)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(content))
|
||||
return "Please provide some content for the agenda. What are the main points of the meeting or the seminar?";
|
||||
|
||||
var lines = content.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var line in lines)
|
||||
if(!line.TrimStart().StartsWith('-'))
|
||||
return "Please start each line of your content list with a dash (-) to create a bullet point list.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateObjective(string objective)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(objective))
|
||||
return "Please provide an objective for the meeting or the seminar. What do you want to achieve?";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateModerator(string moderator)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(moderator))
|
||||
return "Please provide a moderator for the meeting or the seminar. Who will lead the discussion?";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task CreateAgenda()
|
||||
{
|
||||
await this.form!.Validate();
|
||||
if (!this.inputIsValid)
|
||||
return;
|
||||
|
||||
this.CreateChatThread();
|
||||
var time = this.AddUserRequest(
|
||||
$"""
|
||||
Create an agenda for the meeting or seminar wit the title '{this.inputName}' using the following details:
|
||||
|
||||
- The overall topic is '{this.inputTopic}'.
|
||||
- The objective of the meeting is '{this.inputObjective}'.
|
||||
- The moderator is '{this.inputModerator}'.
|
||||
- The planned duration is '{this.inputDuration}'.
|
||||
- The planned start time is '{this.inputStartTime}'.
|
||||
{this.PromptGettingToKnowEachOther()}
|
||||
- The number of participants is '{this.numberParticipants.Name()}'.
|
||||
{this.PromptActiveParticipation()}
|
||||
{this.PromptVirtualMeeting()}
|
||||
{this.PromptPhysicalMeeting()}
|
||||
|
||||
Here is draft what was planned for the meeting:
|
||||
{this.inputContent}
|
||||
|
||||
Please do not follow this draft strictly. It is just a suggestion.
|
||||
{this.PromptFoci()}
|
||||
{this.PromptBriefly()}
|
||||
{this.PromptWhoIsPresenting()}
|
||||
""");
|
||||
|
||||
await this.AddAIResponseAsync(time);
|
||||
}
|
||||
|
||||
private string PromptLanguage()
|
||||
{
|
||||
if(this.selectedTargetLanguage is CommonLanguages.AS_IS)
|
||||
return "Use the same language as the input.";
|
||||
|
||||
if(this.selectedTargetLanguage is CommonLanguages.OTHER)
|
||||
return this.customTargetLanguage;
|
||||
|
||||
return this.selectedTargetLanguage.Name();
|
||||
}
|
||||
|
||||
private string PromptPhysicalMeeting()
|
||||
{
|
||||
if(this.isMeetingVirtual)
|
||||
return string.Empty;
|
||||
|
||||
return $"""
|
||||
- The meeting takes place at the following location: '{this.inputLocation}'.
|
||||
{this.PromptJoiningDinner()}
|
||||
{this.PromptSocialActivity()}
|
||||
{this.PromptArriveAndDepart()}
|
||||
- In case a lunch break is necessary, the break should about {this.durationLunchBreak} minutes long.
|
||||
- In case any breaks are necessary, the breaks should be about {this.durationBreaks} minutes long.
|
||||
""";
|
||||
}
|
||||
|
||||
private string PromptArriveAndDepart()
|
||||
{
|
||||
if (this.needToArriveAndDepart)
|
||||
return "- The participants should have time to arrive and depart.";
|
||||
|
||||
return "- The participants do not need to arrive and depart.";
|
||||
}
|
||||
|
||||
private string PromptSocialActivity()
|
||||
{
|
||||
if (this.doingSocialActivity)
|
||||
return "- The participants will engage in a social activity after the meeting or seminar. This can be a team-building exercise, a sightseeing tour, or a visit to a local attraction. Please make a recommendation for an activity at '{this.inputLocation}'.";
|
||||
|
||||
return "- The participants will not engage in a social activity after the meeting or seminar.";
|
||||
}
|
||||
|
||||
private string SystemPromptSocialActivity()
|
||||
{
|
||||
if(this.doingSocialActivity)
|
||||
return """
|
||||
- 16:00 Departure to the social event [Consider the time needed to get to the location].
|
||||
- 17:00 [Insert your advice for the social activity here].
|
||||
""";
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private string SystemPromptDinner()
|
||||
{
|
||||
if(this.goingToDinner)
|
||||
return """
|
||||
- 19:00 Departure to the restaurant [Consider the time needed to get to the restaurant].
|
||||
- 20:00 [Insert your advice for the dinner here.].
|
||||
""";
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private string PromptJoiningDinner()
|
||||
{
|
||||
if (this.goingToDinner)
|
||||
return $"- The participants will join for a dinner after the meeting or seminar. Please make a recommendation for a restaurant at '{this.inputLocation}'.";
|
||||
|
||||
return "- The participants will not join for a dinner after the meeting or seminar.";
|
||||
}
|
||||
|
||||
private string PromptVirtualMeeting()
|
||||
{
|
||||
if (this.isMeetingVirtual)
|
||||
return "- The meeting or seminar will be held virtually, e.g, a call or webinar. The participants will join online.";
|
||||
|
||||
return "- The meeting or seminar will be held in person.";
|
||||
}
|
||||
|
||||
private string PromptActiveParticipation()
|
||||
{
|
||||
if (this.activeParticipation)
|
||||
return "- The participants should actively participate in the meeting or seminar. This can be done through discussions, questions, and contributions to the content.";
|
||||
|
||||
return "- The participants do not need to actively participate.";
|
||||
}
|
||||
|
||||
private string PromptGettingToKnowEachOther()
|
||||
{
|
||||
if (this.introduceParticipants)
|
||||
return "- The participants should introduce themselves to get to know each other. This can be done in a round or in pairs. The moderator should ensure that everyone has the opportunity to speak.";
|
||||
|
||||
return "- The participants do not need to introduce themselves.";
|
||||
}
|
||||
|
||||
private string PromptFoci()
|
||||
{
|
||||
if (this.selectedFoci.Any())
|
||||
{
|
||||
return $"""
|
||||
|
||||
Out of this content, the following points should be focused on:
|
||||
{this.ConvertItemsToMarkdown(this.selectedFoci)}
|
||||
""";
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private string PromptBriefly()
|
||||
{
|
||||
if (this.justBriefly.Any())
|
||||
{
|
||||
return $"""
|
||||
|
||||
The following points should be just briefly mentioned:
|
||||
{this.ConvertItemsToMarkdown(this.justBriefly)}
|
||||
""";
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private string PromptWhoIsPresenting()
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(this.inputWhoIsPresenting))
|
||||
{
|
||||
return $"""
|
||||
|
||||
The following persons will present the content:
|
||||
{this.inputWhoIsPresenting}
|
||||
""";
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private string ConvertItemsToMarkdown(IEnumerable<string> items)
|
||||
{
|
||||
var markdown = new StringBuilder();
|
||||
foreach (var item in items)
|
||||
markdown.AppendLine($"- {item}");
|
||||
|
||||
return markdown.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace AIStudio.Assistants.Agenda;
|
||||
|
||||
public enum NumberParticipants
|
||||
{
|
||||
NOT_SPECIFIED,
|
||||
|
||||
PEER_TO_PEER, // 2 participants
|
||||
|
||||
SMALL_GROUP, // 3 - 5 participants
|
||||
LARGE_GROUP, // 6 - 12 participants
|
||||
MULTIPLE_SMALL_GROUPS, // 13 - 20 participants
|
||||
MULTIPLE_LARGE_GROUPS, // 21 - 30 participants
|
||||
|
||||
SYMPOSIUM, // 31 - 100 participants
|
||||
CONFERENCE, // 101 - 200 participants
|
||||
CONGRESS, // 201 - 1000 participants
|
||||
LARGE_EVENT, // 1000+ participants
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace AIStudio.Assistants.Agenda;
|
||||
|
||||
public static class NumberParticipantsExtensions
|
||||
{
|
||||
public static string Name(this NumberParticipants numberParticipants) => numberParticipants switch
|
||||
{
|
||||
NumberParticipants.NOT_SPECIFIED => "Please select how many participants are expected",
|
||||
|
||||
NumberParticipants.PEER_TO_PEER => "2 (peer to peer)",
|
||||
|
||||
NumberParticipants.SMALL_GROUP => "3 - 5 (small group)",
|
||||
NumberParticipants.LARGE_GROUP => "6 - 12 (large group)",
|
||||
NumberParticipants.MULTIPLE_SMALL_GROUPS => "13 - 20 (multiple small groups)",
|
||||
NumberParticipants.MULTIPLE_LARGE_GROUPS => "21 - 30 (multiple large groups)",
|
||||
|
||||
NumberParticipants.SYMPOSIUM => "31 - 100 (symposium)",
|
||||
NumberParticipants.CONFERENCE => "101 - 200 (conference)",
|
||||
NumberParticipants.CONGRESS => "201 - 1,000 (congress)",
|
||||
|
||||
NumberParticipants.LARGE_EVENT => "1,000+ (large event)",
|
||||
|
||||
_ => "Unknown"
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
@using AIStudio.Chat
|
||||
|
||||
<MudText Typo="Typo.h3" Class="mb-2 mr-3">
|
||||
@(this.Title)
|
||||
</MudText>
|
||||
|
||||
<InnerScrolling HeaderHeight="6em">
|
||||
<ChildContent>
|
||||
<MudForm @ref="@(this.form)" @bind-IsValid="@(this.inputIsValid)" @bind-Errors="@(this.inputIssues)" Class="pr-2">
|
||||
<MudText Typo="Typo.body1" Align="Align.Justify" Class="mb-6">
|
||||
@this.Description
|
||||
</MudText>
|
||||
|
||||
@if (this.Body is not null)
|
||||
{
|
||||
<CascadingValue Value="@this">
|
||||
@this.Body
|
||||
</CascadingValue>
|
||||
|
||||
<MudButton Disabled="@this.SubmitDisabled" Variant="Variant.Filled" Class="mb-3" OnClick="() => this.SubmitAction()" Style="@this.SubmitButtonStyle">
|
||||
@this.SubmitText
|
||||
</MudButton>
|
||||
}
|
||||
</MudForm>
|
||||
<Issues IssuesData="@(this.inputIssues)"/>
|
||||
|
||||
@if (this.ShowDedicatedProgress && this.isProcessing)
|
||||
{
|
||||
<MudProgressLinear Color="Color.Primary" Indeterminate="true" Class="mb-6" />
|
||||
}
|
||||
|
||||
<div id="@RESULT_DIV_ID" class="mr-2 mt-3">
|
||||
</div>
|
||||
|
||||
<div id="@BEFORE_RESULT_DIV_ID" class="mt-3">
|
||||
</div>
|
||||
|
||||
@if (this.ShowResult && this.resultingContentBlock is not null)
|
||||
{
|
||||
<ContentBlockComponent Role="@(this.resultingContentBlock.Role)" Type="@(this.resultingContentBlock.ContentType)" Time="@(this.resultingContentBlock.Time)" Content="@(this.resultingContentBlock.Content)"/>
|
||||
}
|
||||
|
||||
<div id="@AFTER_RESULT_DIV_ID" class="mt-3">
|
||||
</div>
|
||||
</ChildContent>
|
||||
<FooterContent>
|
||||
<MudStack Row="@true" Wrap="Wrap.Wrap" Class="ma-1">
|
||||
|
||||
@if (!this.FooterButtons.Any(x => x.Type is ButtonTypes.SEND_TO))
|
||||
{
|
||||
@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">
|
||||
@foreach (var assistant in Enum.GetValues<Components>().Where(n => n.AllowSendTo()).OrderBy(n => n.Name().Length))
|
||||
{
|
||||
<MudMenuItem OnClick="() => this.SendToAssistant(assistant, new())">
|
||||
@assistant.Name()
|
||||
</MudMenuItem>
|
||||
}
|
||||
</MudMenu>
|
||||
}
|
||||
}
|
||||
|
||||
@foreach (var button in this.FooterButtons)
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case ButtonData buttonData when !string.IsNullOrWhiteSpace(buttonData.Tooltip):
|
||||
<MudTooltip Text="@buttonData.Tooltip">
|
||||
<MudButton Variant="Variant.Filled" Color="@buttonData.Color" StartIcon="@GetButtonIcon(buttonData.Icon)" OnClick="async () => await buttonData.AsyncAction()">
|
||||
@buttonData.Text
|
||||
</MudButton>
|
||||
</MudTooltip>
|
||||
break;
|
||||
|
||||
case ButtonData buttonData:
|
||||
<MudButton Variant="Variant.Filled" Color="@buttonData.Color" StartIcon="@GetButtonIcon(buttonData.Icon)" OnClick="async () => await buttonData.AsyncAction()">
|
||||
@buttonData.Text
|
||||
</MudButton>
|
||||
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">
|
||||
@foreach (var assistant in Enum.GetValues<Components>().Where(n => n.AllowSendTo()).OrderBy(n => n.Name().Length))
|
||||
{
|
||||
<MudMenuItem OnClick="() => this.SendToAssistant(assistant, sendToButton)">
|
||||
@assistant.Name()
|
||||
</MudMenuItem>
|
||||
}
|
||||
</MudMenu>
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@if (this.ShowCopyResult)
|
||||
{
|
||||
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.ContentCopy" OnClick="() => this.CopyToClipboard()">
|
||||
Copy result
|
||||
</MudButton>
|
||||
}
|
||||
|
||||
@if (this.ShowReset)
|
||||
{
|
||||
<MudButton Variant="Variant.Filled" Style="@this.GetResetColor()" StartIcon="@Icons.Material.Filled.Refresh" OnClick="() => this.InnerResetForm()">
|
||||
Reset
|
||||
</MudButton>
|
||||
}
|
||||
|
||||
@if (this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence)
|
||||
{
|
||||
<ConfidenceInfo Mode="ConfidenceInfoMode.BUTTON" LLMProvider="@this.providerSettings.UsedLLMProvider"/>
|
||||
}
|
||||
|
||||
@if (this.AllowProfiles && this.ShowProfileSelection)
|
||||
{
|
||||
<ProfileSelection MarginLeft="" @bind-CurrentProfile="@this.currentProfile"/>
|
||||
}
|
||||
</MudStack>
|
||||
</FooterContent>
|
||||
</InnerScrolling>
|
||||
@@ -0,0 +1,347 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using RustService = AIStudio.Tools.RustService;
|
||||
|
||||
namespace AIStudio.Assistants;
|
||||
|
||||
public abstract partial class AssistantBase : ComponentBase, IMessageBusReceiver, IDisposable
|
||||
{
|
||||
[Inject]
|
||||
protected SettingsManager SettingsManager { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected IJSRuntime JsRuntime { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected ThreadSafeRandom RNG { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected ISnackbar Snackbar { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected RustService RustService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected NavigationManager NavigationManager { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected ILogger<AssistantBase> Logger { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private MudTheme ColorTheme { get; init; } = null!;
|
||||
|
||||
[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; }
|
||||
|
||||
protected abstract string SystemPrompt { get; }
|
||||
|
||||
public abstract Tools.Components Component { get; }
|
||||
|
||||
protected virtual Func<string> Result2Copy => () => this.resultingContentBlock is null ? string.Empty : this.resultingContentBlock.Content switch
|
||||
{
|
||||
ContentText textBlock => textBlock.Text,
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
protected abstract void ResetFrom();
|
||||
|
||||
protected abstract bool MightPreselectValues();
|
||||
|
||||
protected abstract string SubmitText { get; }
|
||||
|
||||
protected abstract Func<Task> SubmitAction { get; }
|
||||
|
||||
protected virtual bool SubmitDisabled => false;
|
||||
|
||||
private protected virtual RenderFragment? Body => null;
|
||||
|
||||
protected virtual bool ShowResult => true;
|
||||
|
||||
protected virtual bool AllowProfiles => true;
|
||||
|
||||
protected virtual bool ShowProfileSelection => true;
|
||||
|
||||
protected virtual bool ShowDedicatedProgress => false;
|
||||
|
||||
protected virtual bool ShowSendTo => true;
|
||||
|
||||
protected virtual bool ShowCopyResult => true;
|
||||
|
||||
protected virtual bool ShowReset => true;
|
||||
|
||||
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;
|
||||
protected bool inputIsValid;
|
||||
protected Profile currentProfile = Profile.NO_PROFILE;
|
||||
|
||||
protected ChatThread? chatThread;
|
||||
private ContentBlock? resultingContentBlock;
|
||||
private string[] inputIssues = [];
|
||||
private bool isProcessing;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
this.MightPreselectValues();
|
||||
this.providerSettings = this.SettingsManager.GetPreselectedProvider(this.Component);
|
||||
this.currentProfile = this.SettingsManager.GetPreselectedProfile(this.Component);
|
||||
|
||||
this.MessageBus.RegisterComponent(this);
|
||||
this.MessageBus.ApplyFilters(this, [], [ Event.COLOR_THEME_CHANGED ]);
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
// Configure the spellchecking for the user input:
|
||||
this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES);
|
||||
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
// Reset the validation when not editing and on the first render.
|
||||
// We don't want to show validation errors when the user opens the dialog.
|
||||
if(firstRender)
|
||||
this.form?.ResetValidation();
|
||||
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IMessageBusReceiver
|
||||
|
||||
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
||||
{
|
||||
switch (triggeredEvent)
|
||||
{
|
||||
case Event.COLOR_THEME_CHANGED:
|
||||
this.StateHasChanged();
|
||||
break;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
|
||||
{
|
||||
return Task.FromResult<TResult?>(default);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private string SubmitButtonStyle => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence ? this.providerSettings.UsedLLMProvider.GetConfidence(this.SettingsManager).StyleBorder(this.SettingsManager) : string.Empty;
|
||||
|
||||
protected string? ValidatingProvider(AIStudio.Settings.Provider provider)
|
||||
{
|
||||
if(provider.UsedLLMProvider == LLMProviders.NONE)
|
||||
return "Please select a provider.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void CreateChatThread()
|
||||
{
|
||||
this.chatThread = new()
|
||||
{
|
||||
SelectedProvider = this.providerSettings.Id,
|
||||
WorkspaceId = Guid.Empty,
|
||||
ChatId = Guid.NewGuid(),
|
||||
Name = string.Empty,
|
||||
Seed = this.RNG.Next(),
|
||||
SystemPrompt = !this.AllowProfiles ? this.SystemPrompt :
|
||||
$"""
|
||||
{this.SystemPrompt}
|
||||
|
||||
{this.currentProfile.ToSystemPrompt()}
|
||||
""",
|
||||
Blocks = [],
|
||||
};
|
||||
}
|
||||
|
||||
protected Guid CreateChatThread(Guid workspaceId, string name)
|
||||
{
|
||||
var chatId = Guid.NewGuid();
|
||||
this.chatThread = new()
|
||||
{
|
||||
SelectedProvider = this.providerSettings.Id,
|
||||
WorkspaceId = workspaceId,
|
||||
ChatId = chatId,
|
||||
Name = name,
|
||||
Seed = this.RNG.Next(),
|
||||
SystemPrompt = !this.AllowProfiles ? this.SystemPrompt :
|
||||
$"""
|
||||
{this.SystemPrompt}
|
||||
|
||||
{this.currentProfile.ToSystemPrompt()}
|
||||
""",
|
||||
Blocks = [],
|
||||
};
|
||||
|
||||
return chatId;
|
||||
}
|
||||
|
||||
protected DateTimeOffset AddUserRequest(string request, bool hideContentFromUser = false)
|
||||
{
|
||||
var time = DateTimeOffset.Now;
|
||||
this.chatThread!.Blocks.Add(new ContentBlock
|
||||
{
|
||||
Time = time,
|
||||
ContentType = ContentType.TEXT,
|
||||
HideFromUser = hideContentFromUser,
|
||||
Role = ChatRole.USER,
|
||||
Content = new ContentText
|
||||
{
|
||||
Text = request,
|
||||
},
|
||||
});
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
protected async Task<string> AddAIResponseAsync(DateTimeOffset time)
|
||||
{
|
||||
var aiText = new ContentText
|
||||
{
|
||||
// We have to wait for the remote
|
||||
// for the content stream:
|
||||
InitialRemoteWait = true,
|
||||
};
|
||||
|
||||
this.resultingContentBlock = new ContentBlock
|
||||
{
|
||||
Time = time,
|
||||
ContentType = ContentType.TEXT,
|
||||
Role = ChatRole.AI,
|
||||
Content = aiText,
|
||||
};
|
||||
|
||||
if (this.chatThread is not null)
|
||||
{
|
||||
this.chatThread.Blocks.Add(this.resultingContentBlock);
|
||||
this.chatThread.SelectedProvider = this.providerSettings.Id;
|
||||
}
|
||||
|
||||
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.SettingsManager, this.providerSettings.Model, this.chatThread);
|
||||
|
||||
this.isProcessing = false;
|
||||
this.StateHasChanged();
|
||||
|
||||
// Return the AI response:
|
||||
return aiText.Text;
|
||||
}
|
||||
|
||||
protected async Task CopyToClipboard()
|
||||
{
|
||||
await this.RustService.CopyText2Clipboard(this.Snackbar, this.Result2Copy());
|
||||
}
|
||||
|
||||
private static string? GetButtonIcon(string icon)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(icon))
|
||||
return null;
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
protected Task SendToAssistant(Tools.Components destination, SendToButton sendToButton)
|
||||
{
|
||||
if (!destination.AllowSendTo())
|
||||
return Task.CompletedTask;
|
||||
|
||||
var contentToSend = sendToButton == default ? string.Empty : sendToButton.UseResultingContentBlockData switch
|
||||
{
|
||||
false => sendToButton.GetText(),
|
||||
true => this.resultingContentBlock?.Content switch
|
||||
{
|
||||
ContentText textBlock => textBlock.Text,
|
||||
_ => string.Empty,
|
||||
},
|
||||
};
|
||||
|
||||
var sendToData = destination.GetData();
|
||||
switch (destination)
|
||||
{
|
||||
case Tools.Components.CHAT:
|
||||
var convertedChatThread = this.ConvertToChatThread;
|
||||
convertedChatThread = convertedChatThread with { SelectedProvider = this.providerSettings.Id };
|
||||
MessageBus.INSTANCE.DeferMessage(this, sendToData.Event, convertedChatThread);
|
||||
break;
|
||||
|
||||
default:
|
||||
MessageBus.INSTANCE.DeferMessage(this, sendToData.Event, contentToSend);
|
||||
break;
|
||||
}
|
||||
|
||||
this.NavigationManager.NavigateTo(sendToData.Route);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task InnerResetForm()
|
||||
{
|
||||
this.resultingContentBlock = null;
|
||||
this.providerSettings = default;
|
||||
|
||||
await this.JsRuntime.ClearDiv(RESULT_DIV_ID);
|
||||
await this.JsRuntime.ClearDiv(AFTER_RESULT_DIV_ID);
|
||||
|
||||
this.ResetFrom();
|
||||
this.providerSettings = this.SettingsManager.GetPreselectedProvider(this.Component);
|
||||
|
||||
this.inputIsValid = false;
|
||||
this.inputIssues = [];
|
||||
|
||||
this.form?.ResetValidation();
|
||||
this.StateHasChanged();
|
||||
this.form?.ResetValidation();
|
||||
}
|
||||
|
||||
private string GetResetColor() => this.SettingsManager.IsDarkMode switch
|
||||
{
|
||||
true => $"background-color: #804000",
|
||||
false => $"background-color: {this.ColorTheme.GetCurrentPalette(this.SettingsManager).Warning.Value}",
|
||||
};
|
||||
|
||||
private string GetSendToColor() => this.SettingsManager.IsDarkMode switch
|
||||
{
|
||||
true => $"background-color: #004080",
|
||||
false => $"background-color: {this.ColorTheme.GetCurrentPalette(this.SettingsManager).InfoLighten}",
|
||||
};
|
||||
|
||||
#region Implementation of IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.MessageBus.Unregister(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Rendering;
|
||||
|
||||
namespace AIStudio.Assistants;
|
||||
|
||||
//
|
||||
// See https://stackoverflow.com/a/77300384/2258393 for why this class is necessary
|
||||
//
|
||||
|
||||
public abstract class AssistantBaseCore : AssistantBase
|
||||
{
|
||||
private protected sealed override RenderFragment Body => this.BuildRenderTree;
|
||||
|
||||
// Allow content to be provided by a .razor file but without
|
||||
// overriding the content of the base class
|
||||
protected new virtual void BuildRenderTree(RenderTreeBuilder builder)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
@attribute [Route(Routes.ASSISTANT_BIAS)]
|
||||
@inherits AssistantBaseCore
|
||||
|
||||
<MudText Typo="Typo.body1">
|
||||
<b>Links:</b>
|
||||
</MudText>
|
||||
<MudList T="string" Class="mb-6">
|
||||
<MudListItem T="string" Icon="@Icons.Material.Filled.Link" Target="_blank" Href="https://en.wikipedia.org/wiki/List_of_cognitive_biases">Wikipedia list of cognitive biases</MudListItem>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Filled.Link" Target="_blank" Href="https://commons.wikimedia.org/wiki/File:Cognitive_Bias_Codex_With_Definitions_1-2,_an_Extension_of_the_work_of_John_Manoogian_by_Brian_Rene_Morrissette.png">Extended bias poster</MudListItem>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Filled.Link" Target="_blank" Href="https://betterhumans.pub/cognitive-bias-cheat-sheet-55a472476b18">Blog post of Buster Benson: "Cognitive bias cheat sheet"</MudListItem>
|
||||
</MudList>
|
||||
|
||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelecting())" @bind-Value="@this.selectedTargetLanguage" ValidateSelection="@this.ValidateTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Target language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom target language" />
|
||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||
@@ -0,0 +1,164 @@
|
||||
using System.Text;
|
||||
|
||||
using AIStudio.Components;
|
||||
using AIStudio.Settings.DataModel;
|
||||
|
||||
namespace AIStudio.Assistants.BiasDay;
|
||||
|
||||
public partial class BiasOfTheDayAssistant : AssistantBaseCore
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.BIAS_DAY_ASSISTANT;
|
||||
|
||||
protected override string Title => "Bias of the Day";
|
||||
|
||||
protected override string Description =>
|
||||
"""
|
||||
Learn about a different cognitive bias every day. You can also ask the LLM your questions. The idea behind
|
||||
"Bias of the Day" is based on work by Buster Benson, John Manoogian III, and Brian Rene Morrissette. Buster
|
||||
Benson grouped the biases, and the original texts come from Wikipedia. Brian Rene Morrissette condensed them
|
||||
into a shorter version. Finally, John Manoogian III created the original poster based on Benson's work and
|
||||
Morrissette's texts. Thorsten Sommer compared all texts for integration into AI Studio with the current Wikipedia
|
||||
versions, updated them, and added source references. The idea of learning about one bias each day based on John's
|
||||
poster comes from Drew Nelson.
|
||||
""";
|
||||
|
||||
protected override string SystemPrompt => $"""
|
||||
You are a friendly, helpful expert on cognitive bias. You studied psychology and
|
||||
have a lot of experience. You explain a bias every day. Today's bias belongs to
|
||||
the category: "{this.biasOfTheDay.Category.ToName()}". We have the following
|
||||
thoughts on this category:
|
||||
|
||||
{this.biasOfTheDay.Category.GetThoughts()}
|
||||
|
||||
Today's bias is:
|
||||
{this.biasOfTheDay.Description}
|
||||
{this.SystemPromptSources()}
|
||||
Important: you use the following language: {this.SystemPromptLanguage()}. Please
|
||||
ask the user a personal question at the end to encourage them to think about
|
||||
this bias.
|
||||
""";
|
||||
|
||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||
|
||||
protected override string SubmitText => "Show me the bias of the day";
|
||||
|
||||
protected override Func<Task> SubmitAction => this.TellBias;
|
||||
|
||||
protected override bool ShowSendTo => false;
|
||||
|
||||
protected override bool ShowCopyResult => false;
|
||||
|
||||
protected override bool ShowReset => false;
|
||||
|
||||
protected override void ResetFrom()
|
||||
{
|
||||
if (!this.MightPreselectValues())
|
||||
{
|
||||
this.selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
this.customTargetLanguage = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MightPreselectValues()
|
||||
{
|
||||
if (this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)
|
||||
{
|
||||
this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedTargetLanguage;
|
||||
this.customTargetLanguage = this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedOtherLanguage;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private Bias biasOfTheDay = BiasCatalog.NONE;
|
||||
private CommonLanguages selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
private string customTargetLanguage = string.Empty;
|
||||
|
||||
private string? ValidateTargetLanguage(CommonLanguages language)
|
||||
{
|
||||
if(language is CommonLanguages.AS_IS)
|
||||
return "Please select a target language for the bias.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateCustomLanguage(string language)
|
||||
{
|
||||
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||
return "Please provide a custom language.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string SystemPromptSources()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
if (this.biasOfTheDay.Links.Count > 0)
|
||||
{
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("Please share the following sources with the user as a Markdown list:");
|
||||
foreach (var link in this.biasOfTheDay.Links)
|
||||
sb.AppendLine($"- {link}");
|
||||
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private string SystemPromptLanguage()
|
||||
{
|
||||
if(this.selectedTargetLanguage is CommonLanguages.OTHER)
|
||||
return this.customTargetLanguage;
|
||||
|
||||
return this.selectedTargetLanguage.Name();
|
||||
}
|
||||
|
||||
private async Task TellBias()
|
||||
{
|
||||
bool useDrawnBias = false;
|
||||
if(this.SettingsManager.ConfigurationData.BiasOfTheDay.RestrictOneBiasPerDay)
|
||||
{
|
||||
if(this.SettingsManager.ConfigurationData.BiasOfTheDay.DateLastBiasDrawn == DateOnly.FromDateTime(DateTime.Now))
|
||||
{
|
||||
var biasChat = new LoadChat
|
||||
{
|
||||
WorkspaceId = Workspaces.WORKSPACE_ID_BIAS,
|
||||
ChatId = this.SettingsManager.ConfigurationData.BiasOfTheDay.BiasOfTheDayChatId,
|
||||
};
|
||||
|
||||
if (WorkspaceBehaviour.IsChatExisting(biasChat))
|
||||
{
|
||||
MessageBus.INSTANCE.DeferMessage(this, Event.LOAD_CHAT, biasChat);
|
||||
this.NavigationManager.NavigateTo(Routes.CHAT);
|
||||
return;
|
||||
}
|
||||
else
|
||||
useDrawnBias = true;
|
||||
}
|
||||
}
|
||||
|
||||
await this.form!.Validate();
|
||||
if (!this.inputIsValid)
|
||||
return;
|
||||
|
||||
this.biasOfTheDay = useDrawnBias ?
|
||||
BiasCatalog.ALL_BIAS[this.SettingsManager.ConfigurationData.BiasOfTheDay.BiasOfTheDayId] :
|
||||
BiasCatalog.GetRandomBias(this.SettingsManager.ConfigurationData.BiasOfTheDay.UsedBias);
|
||||
|
||||
var chatId = this.CreateChatThread(Workspaces.WORKSPACE_ID_BIAS, this.biasOfTheDay.Name);
|
||||
this.SettingsManager.ConfigurationData.BiasOfTheDay.BiasOfTheDayId = this.biasOfTheDay.Id;
|
||||
this.SettingsManager.ConfigurationData.BiasOfTheDay.BiasOfTheDayChatId = chatId;
|
||||
this.SettingsManager.ConfigurationData.BiasOfTheDay.DateLastBiasDrawn = DateOnly.FromDateTime(DateTime.Now);
|
||||
await this.SettingsManager.StoreSettings();
|
||||
var time = this.AddUserRequest(
|
||||
"""
|
||||
Please tell me about the bias of the day.
|
||||
""", true);
|
||||
|
||||
// Start the AI response without waiting for it to finish:
|
||||
_ = this.AddAIResponseAsync(time);
|
||||
await this.SendToAssistant(Tools.Components.CHAT, default);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
@attribute [Route(Routes.ASSISTANT_CODING)]
|
||||
@inherits AssistantBaseCore
|
||||
|
||||
<MudExpansionPanels Class="mb-3">
|
||||
@for (var contextIndex = 0; contextIndex < this.codingContexts.Count; contextIndex++)
|
||||
{
|
||||
var codingContext = this.codingContexts[contextIndex];
|
||||
var index = contextIndex;
|
||||
<ExpansionPanel HeaderText="@codingContext.Id" HeaderIcon="@Icons.Material.Filled.Code" ShowEndButton="@true" EndButtonColor="Color.Error" EndButtonIcon="@Icons.Material.Filled.Delete" EndButtonTooltip="Delete context" EndButtonClickAsync="@(() => this.DeleteContext(index))">
|
||||
<CodingContextItem @bind-CodingContext="@codingContext"/>
|
||||
</ExpansionPanel>
|
||||
}
|
||||
</MudExpansionPanels>
|
||||
<MudButton Variant="Variant.Filled" OnClick="() => this.AddCodingContext()" Class="mb-3">
|
||||
Add context
|
||||
</MudButton>
|
||||
|
||||
<MudStack Row="@false" Class="mb-3">
|
||||
<MudTextSwitch Label="Do you want to provide compiler messages?" @bind-Value="@this.provideCompilerMessages" LabelOn="Yes, provide compiler messages" LabelOff="No, there are no compiler messages" />
|
||||
@if (this.provideCompilerMessages)
|
||||
{
|
||||
<MudTextField T="string" @bind-Text="@this.compilerMessages" Validation="@this.ValidatingCompilerMessages" AdornmentIcon="@Icons.Material.Filled.Error" Adornment="Adornment.Start" Label="Compiler messages" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
}
|
||||
</MudStack>
|
||||
|
||||
<MudTextField T="string" @bind-Text="@this.questions" Validation="@this.ValidateQuestions" AdornmentIcon="@Icons.Material.Filled.QuestionMark" Adornment="Adornment.Start" Label="Your question(s)" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||
@@ -0,0 +1,167 @@
|
||||
using System.Text;
|
||||
|
||||
namespace AIStudio.Assistants.Coding;
|
||||
|
||||
public partial class AssistantCoding : AssistantBaseCore
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.CODING_ASSISTANT;
|
||||
|
||||
protected override string Title => "Coding Assistant";
|
||||
|
||||
protected override string Description =>
|
||||
"""
|
||||
This coding assistant supports you in writing code. Provide some coding context by copying and pasting
|
||||
your code into the input fields. You might assign an ID to your code snippet to easily reference it later.
|
||||
When you have compiler messages, you can paste them into the input fields to get help with debugging as well.
|
||||
""";
|
||||
|
||||
protected override string SystemPrompt =>
|
||||
"""
|
||||
You are a friendly, helpful senior software developer with extensive experience in various programming languages
|
||||
and concepts. You are familiar with principles like DRY, KISS, YAGNI, and SOLID and can apply and explain them.
|
||||
You know object-oriented programming, as well as functional programming and procedural programming. You are also
|
||||
familiar with design patterns and can explain them. You are an expert of debugging and can help with compiler
|
||||
messages. You can also help with code refactoring and optimization.
|
||||
|
||||
When the user asks in a different language than English, you answer in the same language!
|
||||
""";
|
||||
|
||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||
|
||||
protected override string SubmitText => "Get Support";
|
||||
|
||||
protected override Func<Task> SubmitAction => this.GetSupport;
|
||||
|
||||
protected override void ResetFrom()
|
||||
{
|
||||
this.codingContexts.Clear();
|
||||
this.compilerMessages = string.Empty;
|
||||
this.questions = string.Empty;
|
||||
if (!this.MightPreselectValues())
|
||||
{
|
||||
this.provideCompilerMessages = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MightPreselectValues()
|
||||
{
|
||||
if (this.SettingsManager.ConfigurationData.Coding.PreselectOptions)
|
||||
{
|
||||
this.provideCompilerMessages = this.SettingsManager.ConfigurationData.Coding.PreselectCompilerMessages;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private readonly List<CodingContext> codingContexts = new();
|
||||
private bool provideCompilerMessages;
|
||||
private string compilerMessages = string.Empty;
|
||||
private string questions = string.Empty;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages<string>(Event.SEND_TO_CODING_ASSISTANT).FirstOrDefault();
|
||||
if (deferredContent is not null)
|
||||
this.questions = deferredContent;
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private string? ValidatingCompilerMessages(string checkCompilerMessages)
|
||||
{
|
||||
if(!this.provideCompilerMessages)
|
||||
return null;
|
||||
|
||||
if(string.IsNullOrWhiteSpace(checkCompilerMessages))
|
||||
return "Please provide the compiler messages.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateQuestions(string checkQuestions)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(checkQuestions))
|
||||
return "Please provide your questions.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void AddCodingContext()
|
||||
{
|
||||
this.codingContexts.Add(new()
|
||||
{
|
||||
Id = $"Context {this.codingContexts.Count + 1}",
|
||||
Language = this.SettingsManager.ConfigurationData.Coding.PreselectOptions ? this.SettingsManager.ConfigurationData.Coding.PreselectedProgrammingLanguage : default,
|
||||
OtherLanguage = this.SettingsManager.ConfigurationData.Coding.PreselectOptions ? this.SettingsManager.ConfigurationData.Coding.PreselectedOtherProgrammingLanguage : string.Empty,
|
||||
});
|
||||
}
|
||||
|
||||
private ValueTask DeleteContext(int index)
|
||||
{
|
||||
if(this.codingContexts.Count < index + 1)
|
||||
return ValueTask.CompletedTask;
|
||||
|
||||
this.codingContexts.RemoveAt(index);
|
||||
this.form?.ResetValidation();
|
||||
|
||||
this.StateHasChanged();
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task GetSupport()
|
||||
{
|
||||
await this.form!.Validate();
|
||||
if (!this.inputIsValid)
|
||||
return;
|
||||
|
||||
var sbContext = new StringBuilder();
|
||||
if (this.codingContexts.Count > 0)
|
||||
{
|
||||
sbContext.AppendLine("I have the following coding context:");
|
||||
sbContext.AppendLine();
|
||||
foreach (var codingContext in this.codingContexts)
|
||||
{
|
||||
sbContext.AppendLine($"ID: {codingContext.Id}");
|
||||
|
||||
if(codingContext.Language is not CommonCodingLanguages.OTHER)
|
||||
sbContext.AppendLine($"Language: {codingContext.Language.Name()}");
|
||||
else
|
||||
sbContext.AppendLine($"Language: {codingContext.OtherLanguage}");
|
||||
|
||||
sbContext.AppendLine("Content:");
|
||||
sbContext.AppendLine("```");
|
||||
sbContext.AppendLine(codingContext.Code);
|
||||
sbContext.AppendLine("```");
|
||||
sbContext.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
var sbCompilerMessages = new StringBuilder();
|
||||
if (this.provideCompilerMessages)
|
||||
{
|
||||
sbCompilerMessages.AppendLine("I have the following compiler messages:");
|
||||
sbCompilerMessages.AppendLine();
|
||||
sbCompilerMessages.AppendLine("```");
|
||||
sbCompilerMessages.AppendLine(this.compilerMessages);
|
||||
sbCompilerMessages.AppendLine("```");
|
||||
sbCompilerMessages.AppendLine();
|
||||
}
|
||||
|
||||
this.CreateChatThread();
|
||||
var time = this.AddUserRequest(
|
||||
$"""
|
||||
{sbContext}
|
||||
{sbCompilerMessages}
|
||||
|
||||
My questions are:
|
||||
{this.questions}
|
||||
""");
|
||||
|
||||
await this.AddAIResponseAsync(time);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace AIStudio.Assistants.Coding;
|
||||
|
||||
public sealed class CodingContext(string id, CommonCodingLanguages language, string otherLanguage, string code)
|
||||
{
|
||||
public CodingContext() : this(string.Empty, CommonCodingLanguages.NONE, string.Empty, string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
public string Id { get; set; } = id;
|
||||
|
||||
public CommonCodingLanguages Language { get; set; } = language;
|
||||
|
||||
public string OtherLanguage { get; set; } = otherLanguage;
|
||||
|
||||
public string Code { get; set; } = code;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<MudTextField T="string" @bind-Text="@this.CodingContext.Id" AdornmentIcon="@Icons.Material.Filled.Numbers" Adornment="Adornment.Start" Label="(Optional) Identifier" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||
<MudStack Row="@true" AlignItems="AlignItems.Center" Class="mb-3">
|
||||
<MudSelect T="CommonCodingLanguages" @bind-Value="@this.CodingContext.Language" AdornmentIcon="@Icons.Material.Filled.Code" Adornment="Adornment.Start" Label="Language" Variant="Variant.Outlined" Margin="Margin.Dense">
|
||||
@foreach (var language in Enum.GetValues<CommonCodingLanguages>())
|
||||
{
|
||||
<MudSelectItem Value="@language">@language.Name()</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
@if (this.CodingContext.Language is CommonCodingLanguages.OTHER)
|
||||
{
|
||||
<MudTextField T="string" @bind-Text="@this.CodingContext.OtherLanguage" Validation="@this.ValidatingOtherLanguage" Label="Other language" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
}
|
||||
</MudStack>
|
||||
<MudTextField T="string" @bind-Text="@this.CodingContext.Code" Validation="@this.ValidatingCode" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="Your code" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" />
|
||||
@@ -0,0 +1,50 @@
|
||||
using AIStudio.Settings;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Assistants.Coding;
|
||||
|
||||
public partial class CodingContextItem : ComponentBase
|
||||
{
|
||||
[Parameter]
|
||||
public CodingContext CodingContext { get; set; } = new();
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<CodingContext> CodingContextChanged { get; set; }
|
||||
|
||||
[Inject]
|
||||
protected SettingsManager SettingsManager { get; set; } = null!;
|
||||
|
||||
private static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
// Configure the spellchecking for the user input:
|
||||
this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES);
|
||||
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private string? ValidatingCode(string code)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(code))
|
||||
return $"{this.CodingContext.Id}: Please provide your input.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidatingOtherLanguage(string language)
|
||||
{
|
||||
if(this.CodingContext.Language != CommonCodingLanguages.OTHER)
|
||||
return null;
|
||||
|
||||
if(string.IsNullOrWhiteSpace(language))
|
||||
return "Please specify the language.";
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
namespace AIStudio.Assistants.Coding;
|
||||
|
||||
public static class CommonCodingLanguageExtensions
|
||||
{
|
||||
public static string Name(this CommonCodingLanguages language) => language switch
|
||||
{
|
||||
CommonCodingLanguages.NONE => "None",
|
||||
|
||||
CommonCodingLanguages.BASH => "Bash",
|
||||
CommonCodingLanguages.BLAZOR => ".NET Blazor",
|
||||
CommonCodingLanguages.C => "C",
|
||||
CommonCodingLanguages.CPP => "C++",
|
||||
CommonCodingLanguages.CSHARP => "C#",
|
||||
CommonCodingLanguages.CSS => "CSS",
|
||||
CommonCodingLanguages.FORTRAN => "Fortran",
|
||||
CommonCodingLanguages.GDSCRIPT => "GDScript",
|
||||
CommonCodingLanguages.GO => "Go",
|
||||
CommonCodingLanguages.HTML => "HTML",
|
||||
CommonCodingLanguages.JAVA => "Java",
|
||||
CommonCodingLanguages.JAVASCRIPT => "JavaScript",
|
||||
CommonCodingLanguages.JSON => "JSON",
|
||||
CommonCodingLanguages.JULIA => "Julia",
|
||||
CommonCodingLanguages.KOTLIN => "Kotlin",
|
||||
CommonCodingLanguages.LUA => "Lua",
|
||||
CommonCodingLanguages.MARKDOWN => "Markdown",
|
||||
CommonCodingLanguages.MATHEMATICA => "Mathematica",
|
||||
CommonCodingLanguages.MATLAB => "MATLAB",
|
||||
CommonCodingLanguages.PHP => "PHP",
|
||||
CommonCodingLanguages.POWERSHELL => "PowerShell",
|
||||
CommonCodingLanguages.PROLOG => "Prolog",
|
||||
CommonCodingLanguages.PYTHON => "Python",
|
||||
CommonCodingLanguages.R => "R",
|
||||
CommonCodingLanguages.RUBY => "Ruby",
|
||||
CommonCodingLanguages.RUST => "Rust",
|
||||
CommonCodingLanguages.SQL => "SQL",
|
||||
CommonCodingLanguages.SWIFT => "Swift",
|
||||
CommonCodingLanguages.TYPESCRIPT => "TypeScript",
|
||||
CommonCodingLanguages.XML => "XML",
|
||||
|
||||
CommonCodingLanguages.OTHER => "Other",
|
||||
_ => "Unknown"
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
namespace AIStudio.Assistants.Coding;
|
||||
|
||||
public enum CommonCodingLanguages
|
||||
{
|
||||
NONE,
|
||||
|
||||
BASH,
|
||||
BLAZOR,
|
||||
C,
|
||||
CPP,
|
||||
CSHARP,
|
||||
CSS,
|
||||
FORTRAN,
|
||||
GDSCRIPT,
|
||||
GO,
|
||||
HTML,
|
||||
JAVA,
|
||||
JAVASCRIPT,
|
||||
JSON,
|
||||
JULIA,
|
||||
KOTLIN,
|
||||
LUA,
|
||||
MARKDOWN,
|
||||
MATHEMATICA,
|
||||
MATLAB,
|
||||
PHP,
|
||||
POWERSHELL,
|
||||
PROLOG,
|
||||
PYTHON,
|
||||
R,
|
||||
RUBY,
|
||||
RUST,
|
||||
SQL,
|
||||
SWIFT,
|
||||
TYPESCRIPT,
|
||||
XML,
|
||||
|
||||
OTHER,
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
@attribute [Route(Routes.ASSISTANT_EMAIL)]
|
||||
@inherits AssistantBaseCore
|
||||
|
||||
<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)
|
||||
{
|
||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||
<MudTextField T="string" @bind-Text="@this.inputHistory" Validation="@this.ValidateHistory" Label="Previous conversation" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Provide the previous conversation, e.g., the last e-mail, the last chat, etc." Class="mb-3" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.DocumentScanner"/>
|
||||
</MudPaper>
|
||||
}
|
||||
|
||||
<MudTextField T="string" @bind-Text="@this.inputGreeting" Label="(Optional) The greeting phrase to use" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Person" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Placeholder="Dear Colleagues" Class="mb-3"/>
|
||||
<MudTextField T="string" @bind-Text="@this.inputBulletPoints" Validation="@this.ValidateBulletPoints" AdornmentIcon="@Icons.Material.Filled.ListAlt" Adornment="Adornment.Start" Label="Your bullet points" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Bullet list the content of the e-mail roughly. Use dashes (-) to separate the items." Immediate="@false" DebounceInterval="1_000" OnDebounceIntervalElapsed="@this.OnContentChanged" Placeholder="@PLACEHOLDER_BULLET_POINTS"/>
|
||||
<MudSelect T="string" Label="(Optional) Are any of your points particularly important?" MultiSelection="@true" @bind-SelectedValues="@this.selectedFoci" Variant="Variant.Outlined" Class="mb-3" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.ListAlt">
|
||||
@foreach (var contentLine in this.bulletPointsLines)
|
||||
{
|
||||
<MudSelectItem T="string" Value="@contentLine">@contentLine</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
<MudTextField T="string" @bind-Text="@this.inputName" Label="(Optional) Your name for the closing salutation" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Person" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Your name for the closing salutation of your e-mail." Class="mb-3"/>
|
||||
<EnumSelection T="WritingStyles" NameFunc="@(style => style.Name())" @bind-Value="@this.selectedWritingStyle" Icon="@Icons.Material.Filled.Edit" Label="Select the writing style" ValidateSelection="@this.ValidateWritingStyle"/>
|
||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelecting())" @bind-Value="@this.selectedTargetLanguage" ValidateSelection="@this.ValidateTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Target language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom target language" />
|
||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||
@@ -0,0 +1,249 @@
|
||||
using System.Text;
|
||||
|
||||
using AIStudio.Chat;
|
||||
|
||||
namespace AIStudio.Assistants.EMail;
|
||||
|
||||
public partial class AssistantEMail : AssistantBaseCore
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.EMAIL_ASSISTANT;
|
||||
|
||||
protected override string Title => "E-Mail";
|
||||
|
||||
protected override string Description =>
|
||||
"""
|
||||
Provide a list of bullet points and some basic information for an e-mail. The assistant will generate an e-mail based on that input.
|
||||
""";
|
||||
|
||||
protected override string SystemPrompt =>
|
||||
$"""
|
||||
You are an automated system that writes emails. {this.SystemPromptHistory()} The user provides you with bullet points on what
|
||||
he want to address in the response. Regarding the writing style of the email: {this.selectedWritingStyle.Prompt()}
|
||||
{this.SystemPromptGreeting()} {this.SystemPromptName()} You write the email in the following language: {this.SystemPromptLanguage()}.
|
||||
""";
|
||||
|
||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||
|
||||
protected override string SubmitText => "Create email";
|
||||
|
||||
protected override Func<Task> SubmitAction => this.CreateMail;
|
||||
|
||||
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||
{
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
};
|
||||
|
||||
protected override void ResetFrom()
|
||||
{
|
||||
this.inputBulletPoints = string.Empty;
|
||||
this.bulletPointsLines.Clear();
|
||||
this.selectedFoci = [];
|
||||
this.provideHistory = false;
|
||||
this.inputHistory = string.Empty;
|
||||
if (!this.MightPreselectValues())
|
||||
{
|
||||
this.inputName = string.Empty;
|
||||
this.selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
this.customTargetLanguage = string.Empty;
|
||||
this.selectedWritingStyle = WritingStyles.NONE;
|
||||
this.inputGreeting = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MightPreselectValues()
|
||||
{
|
||||
if (this.SettingsManager.ConfigurationData.EMail.PreselectOptions)
|
||||
{
|
||||
this.inputName = this.SettingsManager.ConfigurationData.EMail.SenderName;
|
||||
this.inputGreeting = this.SettingsManager.ConfigurationData.EMail.Greeting;
|
||||
this.selectedWritingStyle = this.SettingsManager.ConfigurationData.EMail.PreselectedWritingStyle;
|
||||
this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.EMail.PreselectedTargetLanguage;
|
||||
this.customTargetLanguage = this.SettingsManager.ConfigurationData.EMail.PreselectOtherLanguage;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private const string PLACEHOLDER_BULLET_POINTS = """
|
||||
- The last meeting was good
|
||||
- Thank you for feedback
|
||||
- Next is milestone 3
|
||||
- I need your input by next Wednesday
|
||||
""";
|
||||
|
||||
private WritingStyles selectedWritingStyle = WritingStyles.NONE;
|
||||
private string inputGreeting = string.Empty;
|
||||
private string inputBulletPoints = string.Empty;
|
||||
private readonly List<string> bulletPointsLines = [];
|
||||
private IEnumerable<string> selectedFoci = new HashSet<string>();
|
||||
private string inputName = string.Empty;
|
||||
private CommonLanguages selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
private string customTargetLanguage = string.Empty;
|
||||
private bool provideHistory;
|
||||
private string inputHistory = string.Empty;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages<string>(Event.SEND_TO_EMAIL_ASSISTANT).FirstOrDefault();
|
||||
if (deferredContent is not null)
|
||||
this.inputBulletPoints = deferredContent;
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private string? ValidateBulletPoints(string content)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(content))
|
||||
return "Please provide some content for the e-mail.";
|
||||
|
||||
var lines = content.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var line in lines)
|
||||
if(!line.TrimStart().StartsWith('-'))
|
||||
return "Please start each line of your content list with a dash (-) to create a bullet point list.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateTargetLanguage(CommonLanguages language)
|
||||
{
|
||||
if(language is CommonLanguages.AS_IS)
|
||||
return "Please select a target language for the e-mail.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateCustomLanguage(string language)
|
||||
{
|
||||
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||
return "Please provide a custom language.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateWritingStyle(WritingStyles style)
|
||||
{
|
||||
if(style == WritingStyles.NONE)
|
||||
return "Please select a writing style for the e-mail.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateHistory(string history)
|
||||
{
|
||||
if(this.provideHistory && string.IsNullOrWhiteSpace(history))
|
||||
return "Please provide some history for the e-mail.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void OnContentChanged(string content)
|
||||
{
|
||||
this.bulletPointsLines.Clear();
|
||||
var previousSelectedFoci = new HashSet<string>();
|
||||
foreach (var line in content.AsSpan().EnumerateLines())
|
||||
{
|
||||
var trimmedLine = line.Trim();
|
||||
if (trimmedLine.StartsWith("-"))
|
||||
trimmedLine = trimmedLine[1..].Trim();
|
||||
|
||||
if (trimmedLine.Length == 0)
|
||||
continue;
|
||||
|
||||
var finalLine = trimmedLine.ToString();
|
||||
if(this.selectedFoci.Any(x => x.StartsWith(finalLine, StringComparison.InvariantCultureIgnoreCase)))
|
||||
previousSelectedFoci.Add(finalLine);
|
||||
|
||||
this.bulletPointsLines.Add(finalLine);
|
||||
}
|
||||
|
||||
this.selectedFoci = previousSelectedFoci;
|
||||
}
|
||||
|
||||
private string SystemPromptHistory()
|
||||
{
|
||||
if (this.provideHistory)
|
||||
return "You receive the previous conversation as context.";
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private string SystemPromptGreeting()
|
||||
{
|
||||
if(!string.IsNullOrWhiteSpace(this.inputGreeting))
|
||||
return $"Your greeting should consider the following formulation: {this.inputGreeting}.";
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private string SystemPromptName()
|
||||
{
|
||||
if(!string.IsNullOrWhiteSpace(this.inputName))
|
||||
return $"For the closing phrase of the email, please use the following name: {this.inputName}.";
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private string SystemPromptLanguage()
|
||||
{
|
||||
if(this.selectedTargetLanguage is CommonLanguages.AS_IS)
|
||||
return "Use the same language as the input";
|
||||
|
||||
if(this.selectedTargetLanguage is CommonLanguages.OTHER)
|
||||
return this.customTargetLanguage;
|
||||
|
||||
return this.selectedTargetLanguage.Name();
|
||||
}
|
||||
|
||||
private string PromptFoci()
|
||||
{
|
||||
if(!this.selectedFoci.Any())
|
||||
return string.Empty;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("I want to amplify the following points:");
|
||||
foreach (var focus in this.selectedFoci)
|
||||
sb.AppendLine($"- {focus}");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private string PromptHistory()
|
||||
{
|
||||
if(!this.provideHistory)
|
||||
return string.Empty;
|
||||
|
||||
return $"""
|
||||
The previous conversation was:
|
||||
|
||||
```
|
||||
{this.inputHistory}
|
||||
```
|
||||
""";
|
||||
}
|
||||
|
||||
private async Task CreateMail()
|
||||
{
|
||||
await this.form!.Validate();
|
||||
if (!this.inputIsValid)
|
||||
return;
|
||||
|
||||
this.CreateChatThread();
|
||||
var time = this.AddUserRequest(
|
||||
$"""
|
||||
{this.PromptHistory()}
|
||||
|
||||
My bullet points for the e-mail are:
|
||||
|
||||
{this.inputBulletPoints}
|
||||
|
||||
{this.PromptFoci()}
|
||||
""");
|
||||
|
||||
await this.AddAIResponseAsync(time);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace AIStudio.Assistants.EMail;
|
||||
|
||||
public enum WritingStyles
|
||||
{
|
||||
NONE = 0,
|
||||
|
||||
BUSINESS_FORMAL,
|
||||
BUSINESS_INFORMAL,
|
||||
ACADEMIC,
|
||||
PERSONAL,
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace AIStudio.Assistants.EMail;
|
||||
|
||||
public static class WritingStylesExtensions
|
||||
{
|
||||
public static string Name(this WritingStyles style) => style switch
|
||||
{
|
||||
WritingStyles.ACADEMIC => "Academic",
|
||||
WritingStyles.PERSONAL => "Personal",
|
||||
WritingStyles.BUSINESS_FORMAL => "Business formal",
|
||||
WritingStyles.BUSINESS_INFORMAL => "Business informal",
|
||||
|
||||
_ => "Not specified",
|
||||
};
|
||||
|
||||
public static string Prompt(this WritingStyles style) => style switch
|
||||
{
|
||||
WritingStyles.ACADEMIC => "Use an academic style for communication in an academic context like between students and professors.",
|
||||
WritingStyles.PERSONAL => "Use a personal style for communication between friends and family.",
|
||||
WritingStyles.BUSINESS_FORMAL => "Use a formal business style for this e-mail.",
|
||||
WritingStyles.BUSINESS_INFORMAL => "Use an informal business style for this e-mail.",
|
||||
|
||||
_ => "Use a formal business style for this e-mail.",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
@attribute [Route(Routes.ASSISTANT_GRAMMAR_SPELLING)]
|
||||
@inherits AssistantBaseCore
|
||||
|
||||
<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" />
|
||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||
@@ -0,0 +1,136 @@
|
||||
using AIStudio.Chat;
|
||||
|
||||
namespace AIStudio.Assistants.GrammarSpelling;
|
||||
|
||||
public partial class AssistantGrammarSpelling : AssistantBaseCore
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.GRAMMAR_SPELLING_ASSISTANT;
|
||||
|
||||
protected override string Title => "Grammar & Spelling Checker";
|
||||
|
||||
protected override string Description =>
|
||||
"""
|
||||
Check the grammar and spelling of a text.
|
||||
""";
|
||||
|
||||
protected override string SystemPrompt =>
|
||||
$"""
|
||||
You are an expert in languages and their rules. For example, you know how US and UK English or German in
|
||||
Germany and German in Austria differ. You receive text as input. You check the spelling and grammar of
|
||||
this text according to the rules of {this.SystemPromptLanguage()}. You never add information. You
|
||||
never ask the user for additional information. You do not attempt to improve the wording of the text.
|
||||
Your response includes only the corrected text. Do not explain your changes. If no changes are needed,
|
||||
you return the text unchanged.
|
||||
""";
|
||||
|
||||
protected override bool AllowProfiles => false;
|
||||
|
||||
protected override bool ShowDedicatedProgress => true;
|
||||
|
||||
protected override Func<string> Result2Copy => () => this.correctedText;
|
||||
|
||||
protected override IReadOnlyList<IButtonData> FooterButtons =>
|
||||
[
|
||||
new SendToButton
|
||||
{
|
||||
Self = Tools.Components.GRAMMAR_SPELLING_ASSISTANT,
|
||||
UseResultingContentBlockData = false,
|
||||
GetText = () => string.IsNullOrWhiteSpace(this.correctedText) ? this.inputText : this.correctedText
|
||||
},
|
||||
];
|
||||
|
||||
protected override string SubmitText => "Proofread";
|
||||
|
||||
protected override Func<Task> SubmitAction => this.ProofreadText;
|
||||
|
||||
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||
{
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
};
|
||||
|
||||
protected override void ResetFrom()
|
||||
{
|
||||
this.inputText = string.Empty;
|
||||
this.correctedText = string.Empty;
|
||||
if (!this.MightPreselectValues())
|
||||
{
|
||||
this.selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
this.customTargetLanguage = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MightPreselectValues()
|
||||
{
|
||||
if (this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)
|
||||
{
|
||||
this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedTargetLanguage;
|
||||
this.customTargetLanguage = this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedOtherLanguage;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages<string>(Event.SEND_TO_GRAMMAR_SPELLING_ASSISTANT).FirstOrDefault();
|
||||
if (deferredContent is not null)
|
||||
this.inputText = deferredContent;
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private string inputText = string.Empty;
|
||||
private CommonLanguages selectedTargetLanguage;
|
||||
private string customTargetLanguage = string.Empty;
|
||||
private string correctedText = string.Empty;
|
||||
|
||||
private string? ValidateText(string text)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(text))
|
||||
return "Please provide a text as input. You might copy the desired text from a document or a website.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateCustomLanguage(string language)
|
||||
{
|
||||
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||
return "Please provide a custom language.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string SystemPromptLanguage()
|
||||
{
|
||||
var lang = this.selectedTargetLanguage switch
|
||||
{
|
||||
CommonLanguages.AS_IS => "the source language",
|
||||
CommonLanguages.OTHER => this.customTargetLanguage,
|
||||
|
||||
_ => $"{this.selectedTargetLanguage.Name()}",
|
||||
};
|
||||
|
||||
if (string.IsNullOrWhiteSpace(lang))
|
||||
return "the source language";
|
||||
|
||||
return lang;
|
||||
}
|
||||
|
||||
private async Task ProofreadText()
|
||||
{
|
||||
await this.form!.Validate();
|
||||
if (!this.inputIsValid)
|
||||
return;
|
||||
|
||||
this.CreateChatThread();
|
||||
var time = this.AddUserRequest(this.inputText);
|
||||
|
||||
this.correctedText = await this.AddAIResponseAsync(time);
|
||||
await this.JsRuntime.GenerateAndShowDiff(this.inputText, this.correctedText);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
@attribute [Route(Routes.ASSISTANT_ICON_FINDER)]
|
||||
@inherits AssistantBaseCore
|
||||
|
||||
<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"/>
|
||||
|
||||
<MudStack Row="@true" AlignItems="AlignItems.Center" Class="mb-3">
|
||||
<MudSelect T="IconSources" @bind-Value="@this.selectedIconSource" AdornmentIcon="@Icons.Material.Filled.Source" Adornment="Adornment.Start" Label="Your icon source" Variant="Variant.Outlined" Margin="Margin.Dense">
|
||||
@foreach (var source in Enum.GetValues<IconSources>())
|
||||
{
|
||||
<MudSelectItem Value="@source">@source.Name()</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
@if (this.selectedIconSource is not IconSources.GENERIC)
|
||||
{
|
||||
<MudButton Href="@this.selectedIconSource.URL()" Target="_blank" Variant="Variant.Filled" Size="Size.Medium">Open website</MudButton>
|
||||
}
|
||||
</MudStack>
|
||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||
@@ -0,0 +1,98 @@
|
||||
namespace AIStudio.Assistants.IconFinder;
|
||||
|
||||
public partial class AssistantIconFinder : AssistantBaseCore
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.ICON_FINDER_ASSISTANT;
|
||||
|
||||
protected override string Title => "Icon Finder";
|
||||
|
||||
protected override string Description =>
|
||||
"""
|
||||
Finding the right icon for a context, such as for a piece of text, is not easy. The first challenge:
|
||||
You need to extract a concept from your context, such as from a text. Let's take an example where
|
||||
your text contains statements about multiple departments. The sought-after concept could be "departments."
|
||||
The next challenge is that we need to anticipate the bias of the icon designers: under the search term
|
||||
"departments," there may be no relevant icons or only unsuitable ones. Depending on the icon source,
|
||||
it might be more effective to search for "buildings," for instance. LLMs assist you with both steps.
|
||||
""";
|
||||
|
||||
protected override string SystemPrompt =>
|
||||
"""
|
||||
I can search for icons using US English keywords. Please help me come up with the right search queries.
|
||||
I don't want you to translate my requests word-for-word into US English. Instead, you should provide keywords
|
||||
that are likely to yield suitable icons. For example, I might ask for an icon about departments, but icons
|
||||
related to the keyword "buildings" might be the best match. Provide your keywords in a Markdown list without
|
||||
quotation marks.
|
||||
""";
|
||||
|
||||
protected override bool AllowProfiles => false;
|
||||
|
||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||
|
||||
protected override string SubmitText => "Find Icon";
|
||||
|
||||
protected override Func<Task> SubmitAction => this.FindIcon;
|
||||
|
||||
protected override void ResetFrom()
|
||||
{
|
||||
this.inputContext = string.Empty;
|
||||
if (!this.MightPreselectValues())
|
||||
{
|
||||
this.selectedIconSource = IconSources.GENERIC;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MightPreselectValues()
|
||||
{
|
||||
if (this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)
|
||||
{
|
||||
this.selectedIconSource = this.SettingsManager.ConfigurationData.IconFinder.PreselectedSource;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private string inputContext = string.Empty;
|
||||
private IconSources selectedIconSource;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages<string>(Event.SEND_TO_ICON_FINDER_ASSISTANT).FirstOrDefault();
|
||||
if (deferredContent is not null)
|
||||
this.inputContext = deferredContent;
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private string? ValidatingContext(string context)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(context))
|
||||
return "Please provide a context. This will help the AI to find the right icon. You might type just a keyword or copy a sentence from your text, e.g., from a slide where you want to use the icon.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task FindIcon()
|
||||
{
|
||||
await this.form!.Validate();
|
||||
if (!this.inputIsValid)
|
||||
return;
|
||||
|
||||
this.CreateChatThread();
|
||||
var time = this.AddUserRequest(
|
||||
$"""
|
||||
{this.selectedIconSource.Prompt()} I search for an icon for the following context:
|
||||
|
||||
```
|
||||
{this.inputContext}
|
||||
```
|
||||
""");
|
||||
|
||||
await this.AddAIResponseAsync(time);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
namespace AIStudio.Assistants.IconFinder;
|
||||
|
||||
public static class IconSourceExtensions
|
||||
{
|
||||
public static string Name(this IconSources iconSource) => iconSource switch
|
||||
{
|
||||
IconSources.FLAT_ICON => "Flaticon",
|
||||
IconSources.FONT_AWESOME => "Font Awesome",
|
||||
IconSources.MATERIAL_ICONS => "Material Icons",
|
||||
IconSources.FEATHER_ICONS => "Feather Icons",
|
||||
IconSources.BOOTSTRAP_ICONS => "Bootstrap Icons",
|
||||
IconSources.ICONS8 => "Icons8",
|
||||
|
||||
_ => "Generic",
|
||||
};
|
||||
|
||||
public static string Prompt(this IconSources iconSource) => iconSource switch
|
||||
{
|
||||
IconSources.FLAT_ICON => "My icon source is Flaticon.",
|
||||
IconSources.FONT_AWESOME => "I look for an icon on Font Awesome. Please provide just valid icon names. Valid icon names are using the format `fa-icon-name`.",
|
||||
IconSources.MATERIAL_ICONS => "I look for a Material icon. Please provide just valid icon names. Valid icon names are using the format `IconName`.",
|
||||
IconSources.FEATHER_ICONS => "My icon source is Feather Icons. Please provide just valid icon names. Valid icon names usiing the format `icon-name`.",
|
||||
IconSources.BOOTSTRAP_ICONS => "I look for an icon for Bootstrap. Please provide just valid icon names. Valid icon names are using the format `bi-icon-name`.",
|
||||
IconSources.ICONS8 => "I look for an icon on Icons8.",
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
public static string URL(this IconSources iconSource) => iconSource switch
|
||||
{
|
||||
IconSources.FLAT_ICON => "https://www.flaticon.com/",
|
||||
IconSources.FONT_AWESOME => "https://fontawesome.com/",
|
||||
IconSources.MATERIAL_ICONS => "https://material.io/resources/icons/",
|
||||
IconSources.FEATHER_ICONS => "https://feathericons.com/",
|
||||
IconSources.BOOTSTRAP_ICONS => "https://icons.getbootstrap.com/",
|
||||
IconSources.ICONS8 => "https://icons8.com/",
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace AIStudio.Assistants.IconFinder;
|
||||
|
||||
public enum IconSources
|
||||
{
|
||||
GENERIC,
|
||||
|
||||
ICONS8,
|
||||
FLAT_ICON,
|
||||
FONT_AWESOME,
|
||||
MATERIAL_ICONS,
|
||||
FEATHER_ICONS,
|
||||
BOOTSTRAP_ICONS,
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
@attribute [Route(Routes.ASSISTANT_JOB_POSTING)]
|
||||
@inherits AssistantBaseCore
|
||||
|
||||
<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."/>
|
||||
<MudTextField T="string" @bind-Text="@this.inputMandatoryInformation" Label="(Optional) Provide mandatory information" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.TextSnippet" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Mandatory information that your company requires for all job postings. This can include the company description, etc." />
|
||||
<MudTextField T="string" @bind-Text="@this.inputJobDescription" Label="Job description" Validation="@this.ValidateJobDescription" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Settings" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Describe what the person is supposed to do in the company. This might be just short bullet points." />
|
||||
<MudTextField T="string" @bind-Text="@this.inputQualifications" Label="(Optional) Provide necessary job qualifications" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Settings" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Describe what the person should bring to the table. This might be just short bullet points." />
|
||||
<MudTextField T="string" @bind-Text="@this.inputResponsibilities" Label="(Optional) Provide job responsibilities" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Settings" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Describe the responsibilities the person should take on in the company." />
|
||||
<MudTextField T="string" @bind-Text="@this.inputWorkLocation" Label="(Optional) Provide the work location" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.MyLocation" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||
<MudTextField T="string" @bind-Text="@this.inputEntryDate" Label="(Optional) Provide the entry date" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.DateRange" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||
<MudTextField T="string" @bind-Text="@this.inputValidUntil" Label="(Optional) Provide the date until the job posting is valid" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.DateRange" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||
|
||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Target language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom target language" />
|
||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||
@@ -0,0 +1,290 @@
|
||||
using AIStudio.Chat;
|
||||
|
||||
namespace AIStudio.Assistants.JobPosting;
|
||||
|
||||
public partial class AssistantJobPostings : AssistantBaseCore
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.JOB_POSTING_ASSISTANT;
|
||||
|
||||
protected override string Title => "Job Posting";
|
||||
|
||||
protected override string Description =>
|
||||
"""
|
||||
Provide some key points about the job you want to post. The AI will then
|
||||
formulate a suggestion that you can finalize.
|
||||
""";
|
||||
|
||||
protected override string SystemPrompt =>
|
||||
$"""
|
||||
You are an experienced specialist in personnel matters. You write job postings.
|
||||
You follow the usual guidelines in the country of the posting. You ensure that
|
||||
no individuals are discriminated against or excluded by the job posting. You do
|
||||
not ask any questions and you do not repeat the task. You provide your response
|
||||
in a Markdown format. Missing information necessary for the job posting, you try
|
||||
to derive from the other details provided.
|
||||
|
||||
Structure of your response:
|
||||
---
|
||||
# <TITLE>
|
||||
<Description of the job and the company>
|
||||
|
||||
# <TASKS>
|
||||
<Describe what takes the job entails>
|
||||
|
||||
# <QUALIFICATIONS>
|
||||
<What qualifications are required>
|
||||
|
||||
# <RESPONSIBILITIES>
|
||||
<What responsibilities are associated with the job>
|
||||
|
||||
<When available, closing details such as the entry date etc.>
|
||||
|
||||
---
|
||||
|
||||
You write the job posting in the following language: {this.SystemPromptLanguage()}.
|
||||
""";
|
||||
|
||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||
|
||||
protected override string SubmitText => "Create the job posting";
|
||||
|
||||
protected override Func<Task> SubmitAction => this.CreateJobPosting;
|
||||
|
||||
protected override bool SubmitDisabled => false;
|
||||
|
||||
protected override bool AllowProfiles => false;
|
||||
|
||||
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||
{
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
};
|
||||
|
||||
protected override void ResetFrom()
|
||||
{
|
||||
this.inputEntryDate = string.Empty;
|
||||
this.inputValidUntil = string.Empty;
|
||||
if (!this.MightPreselectValues())
|
||||
{
|
||||
this.selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
this.customTargetLanguage = string.Empty;
|
||||
this.inputMandatoryInformation = string.Empty;
|
||||
this.inputJobDescription = string.Empty;
|
||||
this.inputQualifications = string.Empty;
|
||||
this.inputResponsibilities = string.Empty;
|
||||
this.inputCompanyName = string.Empty;
|
||||
this.inputWorkLocation = string.Empty;
|
||||
this.inputCountryLegalFramework = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MightPreselectValues()
|
||||
{
|
||||
if (this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)
|
||||
{
|
||||
this.inputMandatoryInformation = this.SettingsManager.ConfigurationData.JobPostings.PreselectedMandatoryInformation;
|
||||
if(string.IsNullOrWhiteSpace(this.inputJobDescription))
|
||||
this.inputJobDescription = this.SettingsManager.ConfigurationData.JobPostings.PreselectedJobDescription;
|
||||
this.inputQualifications = this.SettingsManager.ConfigurationData.JobPostings.PreselectedQualifications;
|
||||
this.inputResponsibilities = this.SettingsManager.ConfigurationData.JobPostings.PreselectedResponsibilities;
|
||||
this.inputCompanyName = this.SettingsManager.ConfigurationData.JobPostings.PreselectedCompanyName;
|
||||
this.inputWorkLocation = this.SettingsManager.ConfigurationData.JobPostings.PreselectedWorkLocation;
|
||||
this.inputCountryLegalFramework = this.SettingsManager.ConfigurationData.JobPostings.PreselectedCountryLegalFramework;
|
||||
this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.JobPostings.PreselectedTargetLanguage;
|
||||
this.customTargetLanguage = this.SettingsManager.ConfigurationData.JobPostings.PreselectOtherLanguage;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private string inputMandatoryInformation = string.Empty;
|
||||
private string inputJobDescription = string.Empty;
|
||||
private string inputQualifications = string.Empty;
|
||||
private string inputResponsibilities = string.Empty;
|
||||
private string inputCompanyName = string.Empty;
|
||||
private string inputEntryDate = string.Empty;
|
||||
private string inputValidUntil = string.Empty;
|
||||
private string inputWorkLocation = string.Empty;
|
||||
private string inputCountryLegalFramework = string.Empty;
|
||||
private CommonLanguages selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
private string customTargetLanguage = string.Empty;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages<string>(Event.SEND_TO_JOB_POSTING_ASSISTANT).FirstOrDefault();
|
||||
if (deferredContent is not null)
|
||||
this.inputJobDescription = deferredContent;
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private string? ValidateCustomLanguage(string language)
|
||||
{
|
||||
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||
return "Please provide a custom target language.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateJobDescription(string jobDescription)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(jobDescription))
|
||||
return "Please provide a job description.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateCountryLegalFramework(string countryLegalFramework)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(countryLegalFramework))
|
||||
return "Please provide the country where the job is posted (legal framework).";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string SystemPromptLanguage()
|
||||
{
|
||||
if(this.selectedTargetLanguage is CommonLanguages.AS_IS)
|
||||
return "Use the same language as the input";
|
||||
|
||||
if(this.selectedTargetLanguage is CommonLanguages.OTHER)
|
||||
return this.customTargetLanguage;
|
||||
|
||||
return this.selectedTargetLanguage.Name();
|
||||
}
|
||||
|
||||
private string UserPromptMandatoryInformation()
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(this.inputMandatoryInformation))
|
||||
return string.Empty;
|
||||
|
||||
return $"""
|
||||
# Mandatory Information
|
||||
{this.inputMandatoryInformation}
|
||||
|
||||
""";
|
||||
}
|
||||
|
||||
private string UserPromptJobDescription()
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(this.inputJobDescription))
|
||||
return string.Empty;
|
||||
|
||||
return $"""
|
||||
# Job Description
|
||||
{this.inputJobDescription}
|
||||
|
||||
""";
|
||||
}
|
||||
|
||||
private string UserPromptQualifications()
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(this.inputQualifications))
|
||||
return string.Empty;
|
||||
|
||||
return $"""
|
||||
# Qualifications
|
||||
{this.inputQualifications}
|
||||
|
||||
""";
|
||||
}
|
||||
|
||||
private string UserPromptResponsibilities()
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(this.inputResponsibilities))
|
||||
return string.Empty;
|
||||
|
||||
return $"""
|
||||
# Responsibilities
|
||||
{this.inputResponsibilities}
|
||||
|
||||
""";
|
||||
}
|
||||
|
||||
private string UserPromptCompanyName()
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(this.inputCompanyName))
|
||||
return string.Empty;
|
||||
|
||||
return $"""
|
||||
# Company Name
|
||||
{this.inputCompanyName}
|
||||
|
||||
""";
|
||||
}
|
||||
|
||||
private string UserPromptEntryDate()
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(this.inputEntryDate))
|
||||
return string.Empty;
|
||||
|
||||
return $"""
|
||||
# Entry Date
|
||||
{this.inputEntryDate}
|
||||
|
||||
""";
|
||||
}
|
||||
|
||||
private string UserPromptValidUntil()
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(this.inputValidUntil))
|
||||
return string.Empty;
|
||||
|
||||
return $"""
|
||||
# Job Posting Valid Until
|
||||
{this.inputValidUntil}
|
||||
|
||||
""";
|
||||
}
|
||||
|
||||
private string UserPromptWorkLocation()
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(this.inputWorkLocation))
|
||||
return string.Empty;
|
||||
|
||||
return $"""
|
||||
# Work Location
|
||||
{this.inputWorkLocation}
|
||||
|
||||
""";
|
||||
}
|
||||
|
||||
private string UserPromptCountryLegalFramework()
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(this.inputCountryLegalFramework))
|
||||
return string.Empty;
|
||||
|
||||
return $"""
|
||||
# Country where the job is posted (legal framework)
|
||||
{this.inputCountryLegalFramework}
|
||||
|
||||
""";
|
||||
}
|
||||
|
||||
private async Task CreateJobPosting()
|
||||
{
|
||||
await this.form!.Validate();
|
||||
if (!this.inputIsValid)
|
||||
return;
|
||||
|
||||
this.CreateChatThread();
|
||||
var time = this.AddUserRequest(
|
||||
$"""
|
||||
{this.UserPromptCompanyName()}
|
||||
{this.UserPromptCountryLegalFramework()}
|
||||
{this.UserPromptMandatoryInformation()}
|
||||
{this.UserPromptJobDescription()}
|
||||
{this.UserPromptQualifications()}
|
||||
{this.UserPromptResponsibilities()}
|
||||
{this.UserPromptWorkLocation()}
|
||||
{this.UserPromptEntryDate()}
|
||||
{this.UserPromptValidUntil()}
|
||||
""");
|
||||
|
||||
await this.AddAIResponseAsync(time);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
@attribute [Route(Routes.ASSISTANT_LEGAL_CHECK)]
|
||||
@inherits AssistantBaseCore
|
||||
|
||||
@if (!this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader)
|
||||
{
|
||||
<ReadWebContent @bind-Content="@this.inputLegalDocument" ProviderSettings="@this.providerSettings" @bind-AgentIsRunning="@this.isAgentRunning" Preselect="@(this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions && this.SettingsManager.ConfigurationData.LegalCheck.PreselectWebContentReader)" PreselectContentCleanerAgent="@(this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions && this.SettingsManager.ConfigurationData.LegalCheck.PreselectContentCleanerAgent)"/>
|
||||
}
|
||||
|
||||
<MudTextField T="string" Disabled="@this.isAgentRunning" @bind-Text="@this.inputLegalDocument" Validation="@this.ValidatingLegalDocument" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="Legal document" Variant="Variant.Outlined" Lines="12" AutoGrow="@true" MaxLines="24" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
<MudTextField T="string" Disabled="@this.isAgentRunning" @bind-Text="@this.inputQuestions" Validation="@this.ValidatingQuestions" AdornmentIcon="@Icons.Material.Filled.QuestionAnswer" Adornment="Adornment.Start" Label="Your questions" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||
@@ -0,0 +1,100 @@
|
||||
using AIStudio.Chat;
|
||||
|
||||
namespace AIStudio.Assistants.LegalCheck;
|
||||
|
||||
public partial class AssistantLegalCheck : AssistantBaseCore
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.LEGAL_CHECK_ASSISTANT;
|
||||
|
||||
protected override string Title => "Legal Check";
|
||||
|
||||
protected override string Description =>
|
||||
"""
|
||||
Provide a legal document and ask a question about it. This assistant does not
|
||||
replace legal advice. Consult a lawyer to get professional advice. Remember
|
||||
that LLMs can invent answers and facts. Please do not rely on this answers.
|
||||
""";
|
||||
|
||||
protected override string SystemPrompt =>
|
||||
"""
|
||||
You are an expert in legal matters. You have studied international law and are familiar with the law in
|
||||
various countries. You receive a legal document and answer the user's questions. You are a friendly and
|
||||
professional assistant. You respond to the user in the language in which the questions were asked. If
|
||||
you are unsure, unfamiliar with the legal area, or do not know the answers, you inform the user.
|
||||
Never invent facts!
|
||||
""";
|
||||
|
||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||
|
||||
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
|
||||
{
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
};
|
||||
|
||||
protected override void ResetFrom()
|
||||
{
|
||||
this.inputLegalDocument = string.Empty;
|
||||
this.inputQuestions = string.Empty;
|
||||
this.MightPreselectValues();
|
||||
}
|
||||
|
||||
protected override bool MightPreselectValues() => false;
|
||||
|
||||
private bool isAgentRunning;
|
||||
private string inputLegalDocument = string.Empty;
|
||||
private string inputQuestions = string.Empty;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages<string>(Event.SEND_TO_LEGAL_CHECK_ASSISTANT).FirstOrDefault();
|
||||
if (deferredContent is not null)
|
||||
this.inputQuestions = deferredContent;
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private string? ValidatingLegalDocument(string text)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(text))
|
||||
return "Please provide a legal document as input. You might copy the desired text from a document or a website.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidatingQuestions(string text)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(text))
|
||||
return "Please provide your questions as input.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task AksQuestions()
|
||||
{
|
||||
await this.form!.Validate();
|
||||
if (!this.inputIsValid)
|
||||
return;
|
||||
|
||||
this.CreateChatThread();
|
||||
var time = this.AddUserRequest(
|
||||
$"""
|
||||
# The legal document
|
||||
{this.inputLegalDocument}
|
||||
|
||||
# The questions
|
||||
{this.inputQuestions}
|
||||
""");
|
||||
|
||||
await this.AddAIResponseAsync(time);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
@attribute [Route(Routes.ASSISTANT_MY_TASKS)]
|
||||
@inherits AssistantBaseCore
|
||||
|
||||
<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"/>
|
||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Target language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom target language" />
|
||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||
@@ -0,0 +1,129 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Assistants.MyTasks;
|
||||
|
||||
public partial class AssistantMyTasks : AssistantBaseCore
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.MY_TASKS_ASSISTANT;
|
||||
|
||||
protected override string Title => "My Tasks";
|
||||
|
||||
protected override string Description =>
|
||||
"""
|
||||
You received a cryptic email that was sent to many recipients and you are now wondering
|
||||
if you need to do something? Copy the email into the input field. You also need to select
|
||||
a personal profile. In this profile, you should describe your role in the organization.
|
||||
The AI will then try to give you hints on what your tasks might be.
|
||||
""";
|
||||
|
||||
protected override string SystemPrompt =>
|
||||
$"""
|
||||
You are a friendly and professional business expert. You receive business emails, protocols,
|
||||
reports, etc. as input. Additionally, you know the user's role in the organization. The user
|
||||
wonders if any tasks arise for them in their role based on the text. You now try to give hints
|
||||
and advice on whether and what the user should do. When you believe there are no tasks for the
|
||||
user, you tell them this. You consider typical business etiquette in your advice.
|
||||
|
||||
You write your advice in the following language: {this.SystemPromptLanguage()}.
|
||||
""";
|
||||
|
||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||
|
||||
protected override string SubmitText => "Analyze text";
|
||||
|
||||
protected override Func<Task> SubmitAction => this.AnalyzeText;
|
||||
|
||||
protected override bool ShowProfileSelection => false;
|
||||
|
||||
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||
{
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
};
|
||||
|
||||
protected override void ResetFrom()
|
||||
{
|
||||
this.inputText = string.Empty;
|
||||
if (!this.MightPreselectValues())
|
||||
{
|
||||
this.selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
this.customTargetLanguage = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MightPreselectValues()
|
||||
{
|
||||
if (this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)
|
||||
{
|
||||
this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.MyTasks.PreselectedTargetLanguage;
|
||||
this.customTargetLanguage = this.SettingsManager.ConfigurationData.MyTasks.PreselectOtherLanguage;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private string inputText = string.Empty;
|
||||
private CommonLanguages selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
private string customTargetLanguage = string.Empty;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages<string>(Event.SEND_TO_MY_TASKS_ASSISTANT).FirstOrDefault();
|
||||
if (deferredContent is not null)
|
||||
this.inputText = deferredContent;
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private string? ValidatingText(string text)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(text))
|
||||
return "Please provide some text as input. For example, an email.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateProfile(Profile profile)
|
||||
{
|
||||
if(profile == default || profile == Profile.NO_PROFILE)
|
||||
return "Please select one of your profiles.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateCustomLanguage(string language)
|
||||
{
|
||||
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||
return "Please provide a custom language.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string SystemPromptLanguage()
|
||||
{
|
||||
if(this.selectedTargetLanguage is CommonLanguages.AS_IS)
|
||||
return "Use the same language as the input";
|
||||
|
||||
if(this.selectedTargetLanguage is CommonLanguages.OTHER)
|
||||
return this.customTargetLanguage;
|
||||
|
||||
return this.selectedTargetLanguage.Name();
|
||||
}
|
||||
|
||||
private async Task AnalyzeText()
|
||||
{
|
||||
await this.form!.Validate();
|
||||
if (!this.inputIsValid)
|
||||
return;
|
||||
|
||||
this.CreateChatThread();
|
||||
var time = this.AddUserRequest(this.inputText);
|
||||
|
||||
await this.AddAIResponseAsync(time);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
@attribute [Route(Routes.ASSISTANT_REWRITE)]
|
||||
@inherits AssistantBaseCore
|
||||
|
||||
<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" />
|
||||
<EnumSelection T="WritingStyles" NameFunc="@(style => style.Name())" @bind-Value="@this.selectedWritingStyle" Icon="@Icons.Material.Filled.Edit" Label="Writing style" AllowOther="@false" />
|
||||
<EnumSelection T="SentenceStructure" NameFunc="@(voice => voice.Name())" @bind-Value="@this.selectedSentenceStructure" Icon="@Icons.Material.Filled.Person4" Label="Sentence structure" />
|
||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||
@@ -0,0 +1,143 @@
|
||||
using AIStudio.Chat;
|
||||
|
||||
namespace AIStudio.Assistants.RewriteImprove;
|
||||
|
||||
public partial class AssistantRewriteImprove : AssistantBaseCore
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.REWRITE_ASSISTANT;
|
||||
|
||||
protected override string Title => "Rewrite & Improve Text";
|
||||
|
||||
protected override string Description =>
|
||||
"""
|
||||
Rewrite and improve your text. Please note, that the capabilities of the different LLM providers will vary.
|
||||
""";
|
||||
|
||||
protected override string SystemPrompt =>
|
||||
$"""
|
||||
You are an expert in language and style. You receive a text as input. First, you review the text. If no
|
||||
changes are needed, you return the text without modifications. If a change is necessary, you improve the
|
||||
text. You can also correct spelling and grammar issues. You never add additional information. You never
|
||||
ask the user for additional information. Your response only contains the improved text. You do not explain
|
||||
your changes. If no changes are needed, you return the text unchanged.
|
||||
The style of the text: {this.selectedWritingStyle.Prompt()}.{this.selectedSentenceStructure.Prompt()}
|
||||
You follow the rules according to {this.SystemPromptLanguage()} in all your changes.
|
||||
""";
|
||||
|
||||
protected override bool AllowProfiles => false;
|
||||
|
||||
protected override bool ShowDedicatedProgress => true;
|
||||
|
||||
protected override Func<string> Result2Copy => () => this.rewrittenText;
|
||||
|
||||
protected override IReadOnlyList<IButtonData> FooterButtons =>
|
||||
[
|
||||
new SendToButton
|
||||
{
|
||||
Self = Tools.Components.REWRITE_ASSISTANT,
|
||||
UseResultingContentBlockData = false,
|
||||
GetText = () => string.IsNullOrWhiteSpace(this.rewrittenText) ? this.inputText : this.rewrittenText,
|
||||
},
|
||||
];
|
||||
|
||||
protected override string SubmitText => "Improve";
|
||||
|
||||
protected override Func<Task> SubmitAction => this.RewriteText;
|
||||
|
||||
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||
{
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
};
|
||||
|
||||
protected override void ResetFrom()
|
||||
{
|
||||
this.inputText = string.Empty;
|
||||
this.rewrittenText = string.Empty;
|
||||
if (!this.MightPreselectValues())
|
||||
{
|
||||
this.selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
this.customTargetLanguage = string.Empty;
|
||||
this.selectedWritingStyle = WritingStyles.NOT_SPECIFIED;
|
||||
this.selectedSentenceStructure = SentenceStructure.NOT_SPECIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MightPreselectValues()
|
||||
{
|
||||
if (this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)
|
||||
{
|
||||
this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedTargetLanguage;
|
||||
this.customTargetLanguage = this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedOtherLanguage;
|
||||
this.selectedWritingStyle = this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedWritingStyle;
|
||||
this.selectedSentenceStructure = this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedSentenceStructure;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages<string>(Event.SEND_TO_REWRITE_ASSISTANT).FirstOrDefault();
|
||||
if (deferredContent is not null)
|
||||
this.inputText = deferredContent;
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private string inputText = string.Empty;
|
||||
private CommonLanguages selectedTargetLanguage;
|
||||
private string customTargetLanguage = string.Empty;
|
||||
private string rewrittenText = string.Empty;
|
||||
private WritingStyles selectedWritingStyle;
|
||||
private SentenceStructure selectedSentenceStructure;
|
||||
|
||||
private string? ValidateText(string text)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(text))
|
||||
return "Please provide a text as input. You might copy the desired text from a document or a website.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateCustomLanguage(string language)
|
||||
{
|
||||
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||
return "Please provide a custom language.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string SystemPromptLanguage()
|
||||
{
|
||||
var lang = this.selectedTargetLanguage switch
|
||||
{
|
||||
CommonLanguages.AS_IS => "the source language",
|
||||
CommonLanguages.OTHER => this.customTargetLanguage,
|
||||
|
||||
_ => $"{this.selectedTargetLanguage.Name()}",
|
||||
};
|
||||
|
||||
if (string.IsNullOrWhiteSpace(lang))
|
||||
return "the source language";
|
||||
|
||||
return lang;
|
||||
}
|
||||
|
||||
private async Task RewriteText()
|
||||
{
|
||||
await this.form!.Validate();
|
||||
if (!this.inputIsValid)
|
||||
return;
|
||||
|
||||
this.CreateChatThread();
|
||||
var time = this.AddUserRequest(this.inputText);
|
||||
|
||||
this.rewrittenText = await this.AddAIResponseAsync(time);
|
||||
await this.JsRuntime.GenerateAndShowDiff(this.inputText, this.rewrittenText);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace AIStudio.Assistants.RewriteImprove;
|
||||
|
||||
public enum SentenceStructure
|
||||
{
|
||||
NOT_SPECIFIED = 0,
|
||||
|
||||
ACTIVE,
|
||||
PASSIVE,
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace AIStudio.Assistants.RewriteImprove;
|
||||
|
||||
public static class SentenceStructureExtensions
|
||||
{
|
||||
public static string Name(this SentenceStructure sentenceStructure) => sentenceStructure switch
|
||||
{
|
||||
SentenceStructure.ACTIVE => "Active voice",
|
||||
SentenceStructure.PASSIVE => "Passive voice",
|
||||
|
||||
_ => "Not Specified",
|
||||
};
|
||||
|
||||
public static string Prompt(this SentenceStructure sentenceStructure) => sentenceStructure switch
|
||||
{
|
||||
SentenceStructure.ACTIVE => " Use an active voice for the sentence structure.",
|
||||
SentenceStructure.PASSIVE => " Use a passive voice for the sentence structure.",
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace AIStudio.Assistants.RewriteImprove;
|
||||
|
||||
public enum WritingStyles
|
||||
{
|
||||
NOT_SPECIFIED = 0,
|
||||
|
||||
EVERYDAY,
|
||||
BUSINESS,
|
||||
SCIENTIFIC,
|
||||
JOURNALISTIC,
|
||||
LITERARY,
|
||||
TECHNICAL,
|
||||
MARKETING,
|
||||
ACADEMIC,
|
||||
LEGAL,
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
namespace AIStudio.Assistants.RewriteImprove;
|
||||
|
||||
public static class WritingStylesExtensions
|
||||
{
|
||||
public static string Name(this WritingStyles style) => style switch
|
||||
{
|
||||
WritingStyles.EVERYDAY => "Everyday (personal texts, social media)",
|
||||
WritingStyles.BUSINESS => "Business (business emails, reports, presentations)",
|
||||
WritingStyles.SCIENTIFIC => "Scientific (scientific papers, research reports)",
|
||||
WritingStyles.JOURNALISTIC => "Journalistic (magazines, newspapers, news)",
|
||||
WritingStyles.LITERARY => "Literary (fiction, poetry)",
|
||||
WritingStyles.TECHNICAL => "Technical (manuals, documentation)",
|
||||
WritingStyles.MARKETING => "Marketing (advertisements, sales texts)",
|
||||
WritingStyles.ACADEMIC => "Academic (essays, seminar papers)",
|
||||
WritingStyles.LEGAL => "Legal (legal texts, contracts)",
|
||||
|
||||
_ => "Not specified",
|
||||
};
|
||||
|
||||
public static string Prompt(this WritingStyles style) => style switch
|
||||
{
|
||||
WritingStyles.EVERYDAY => "Use a everyday style like for personal texts, social media, and informal communication.",
|
||||
WritingStyles.BUSINESS => "Use a business style like for business emails, reports, and presentations. Most important is clarity and professionalism.",
|
||||
WritingStyles.SCIENTIFIC => "Use a scientific style like for scientific papers, research reports, and academic writing. Most important is precision and objectivity.",
|
||||
WritingStyles.JOURNALISTIC => "Use a journalistic style like for magazines, newspapers, and news. Most important is readability and engaging content.",
|
||||
WritingStyles.LITERARY => "Use a literary style like for fiction, poetry, and creative writing. Most important is creativity and emotional impact.",
|
||||
WritingStyles.TECHNICAL => "Use a technical style like for manuals, documentation, and technical writing. Most important is clarity and precision.",
|
||||
WritingStyles.MARKETING => "Use a marketing style like for advertisements, sales texts, and promotional content. Most important is persuasiveness and engagement.",
|
||||
WritingStyles.ACADEMIC => "Use a academic style like for essays, seminar papers, and academic writing. Most important is clarity and objectivity.",
|
||||
WritingStyles.LEGAL => "Use a legal style like for legal texts, contracts, and official documents. Most important is precision and legal correctness. Use formal legal language.",
|
||||
|
||||
_ => "Keep the style of the text as it is.",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
@attribute [Route(Routes.ASSISTANT_SYNONYMS)]
|
||||
@inherits AssistantBaseCore
|
||||
|
||||
<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"/>
|
||||
|
||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedLanguage" Icon="@Icons.Material.Filled.Translate" Label="Language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom target language" />
|
||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||
@@ -0,0 +1,170 @@
|
||||
using AIStudio.Chat;
|
||||
|
||||
namespace AIStudio.Assistants.Synonym;
|
||||
|
||||
public partial class AssistantSynonyms : AssistantBaseCore
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.SYNONYMS_ASSISTANT;
|
||||
|
||||
protected override string Title => "Synonyms";
|
||||
|
||||
protected override string Description =>
|
||||
"""
|
||||
Find synonyms for words or phrases.
|
||||
""";
|
||||
|
||||
protected override string SystemPrompt =>
|
||||
$"""
|
||||
You have a PhD in linguistics. Therefore, you are an expert in the {this.SystemPromptLanguage()} language.
|
||||
You receive a word or phrase as input. You might also receive some context. You then provide
|
||||
a list of synonyms as a Markdown list.
|
||||
|
||||
First, derive possible meanings from the word, phrase, and context, when available. Then, provide
|
||||
possible synonyms for each meaning.
|
||||
|
||||
Example for the word "learn" and the language English (US):
|
||||
|
||||
Derive possible meanings (*this list is not part of the output*):
|
||||
- Meaning "to learn"
|
||||
- Meaning "to retain"
|
||||
|
||||
Next, provide possible synonyms for each meaning, which is your output:
|
||||
|
||||
# Meaning "to learn"
|
||||
- absorb
|
||||
- study
|
||||
- acquire
|
||||
- advance
|
||||
- practice
|
||||
|
||||
# Meaning "to retain"
|
||||
- remember
|
||||
- note
|
||||
- realize
|
||||
|
||||
You do not ask follow-up questions and never repeat the task instructions. When you do not know
|
||||
any synonyms for the given word or phrase, you state this. Your output is always in
|
||||
the {this.SystemPromptLanguage()} language.
|
||||
""";
|
||||
|
||||
protected override bool AllowProfiles => false;
|
||||
|
||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||
|
||||
protected override string SubmitText => "Find synonyms";
|
||||
|
||||
protected override Func<Task> SubmitAction => this.FindSynonyms;
|
||||
|
||||
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||
{
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
};
|
||||
|
||||
protected override void ResetFrom()
|
||||
{
|
||||
this.inputText = string.Empty;
|
||||
this.inputContext = string.Empty;
|
||||
if (!this.MightPreselectValues())
|
||||
{
|
||||
this.selectedLanguage = CommonLanguages.AS_IS;
|
||||
this.customTargetLanguage = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MightPreselectValues()
|
||||
{
|
||||
if (this.SettingsManager.ConfigurationData.Synonyms.PreselectOptions)
|
||||
{
|
||||
this.selectedLanguage = this.SettingsManager.ConfigurationData.Synonyms.PreselectedLanguage;
|
||||
this.customTargetLanguage = this.SettingsManager.ConfigurationData.Synonyms.PreselectedOtherLanguage;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private string inputText = string.Empty;
|
||||
private string inputContext = string.Empty;
|
||||
private CommonLanguages selectedLanguage;
|
||||
private string customTargetLanguage = string.Empty;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages<string>(Event.SEND_TO_SYNONYMS_ASSISTANT).FirstOrDefault();
|
||||
if (deferredContent is not null)
|
||||
this.inputContext = deferredContent;
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private string? ValidatingText(string text)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(text))
|
||||
return "Please provide a word or phrase as input.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateCustomLanguage(string language)
|
||||
{
|
||||
if(this.selectedLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||
return "Please provide a custom language.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string SystemPromptLanguage()
|
||||
{
|
||||
var lang = this.selectedLanguage switch
|
||||
{
|
||||
CommonLanguages.AS_IS => "source",
|
||||
CommonLanguages.OTHER => this.customTargetLanguage,
|
||||
|
||||
_ => $"{this.selectedLanguage.Name()}",
|
||||
};
|
||||
|
||||
if (string.IsNullOrWhiteSpace(lang))
|
||||
return "source";
|
||||
|
||||
return lang;
|
||||
}
|
||||
|
||||
private string UserPromptContext()
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(this.inputContext))
|
||||
return string.Empty;
|
||||
|
||||
return $"""
|
||||
The given context is:
|
||||
|
||||
```
|
||||
{this.inputContext}
|
||||
```
|
||||
|
||||
""";
|
||||
}
|
||||
|
||||
private async Task FindSynonyms()
|
||||
{
|
||||
await this.form!.Validate();
|
||||
if (!this.inputIsValid)
|
||||
return;
|
||||
|
||||
this.CreateChatThread();
|
||||
var time = this.AddUserRequest(
|
||||
$"""
|
||||
{this.UserPromptContext()}
|
||||
The given word or phrase is:
|
||||
|
||||
```
|
||||
{this.inputText}
|
||||
```
|
||||
""");
|
||||
|
||||
await this.AddAIResponseAsync(time);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
@attribute [Route(Routes.ASSISTANT_SUMMARIZER)]
|
||||
@inherits AssistantBaseCore
|
||||
|
||||
@if (!this.SettingsManager.ConfigurationData.TextSummarizer.HideWebContentReader)
|
||||
{
|
||||
<ReadWebContent @bind-Content="@this.inputText" ProviderSettings="@this.providerSettings" @bind-AgentIsRunning="@this.isAgentRunning" Preselect="@(this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions && this.SettingsManager.ConfigurationData.TextSummarizer.PreselectWebContentReader)" PreselectContentCleanerAgent="@(this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions && this.SettingsManager.ConfigurationData.TextSummarizer.PreselectContentCleanerAgent)"/>
|
||||
}
|
||||
|
||||
<MudTextField T="string" Disabled="@this.isAgentRunning" @bind-Text="@this.inputText" Validation="@this.ValidatingText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="Your input" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.Name())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Target language" AllowOther="@true" @bind-OtherInput="@this.customTargetLanguage" OtherValue="CommonLanguages.OTHER" LabelOther="Custom target language" ValidateOther="@this.ValidateCustomLanguage" />
|
||||
<EnumSelection T="Complexity" NameFunc="@(complexity => complexity.Name())" @bind-Value="@this.selectedComplexity" Icon="@Icons.Material.Filled.Layers" Label="Target complexity" AllowOther="@true" @bind-OtherInput="@this.expertInField" OtherValue="Complexity.SCIENTIFIC_LANGUAGE_OTHER_EXPERTS" LabelOther="Your expertise" ValidateOther="@this.ValidateExpertInField" />
|
||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||
@@ -0,0 +1,134 @@
|
||||
using AIStudio.Chat;
|
||||
|
||||
namespace AIStudio.Assistants.TextSummarizer;
|
||||
|
||||
public partial class AssistantTextSummarizer : AssistantBaseCore
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.TEXT_SUMMARIZER_ASSISTANT;
|
||||
|
||||
protected override string Title => "Text Summarizer";
|
||||
|
||||
protected override string Description =>
|
||||
"""
|
||||
Summarize long text into a shorter version while retaining the main points.
|
||||
You might want to change the language of the summary to make it more readable.
|
||||
It is also possible to change the complexity of the summary to make it
|
||||
easy to understand.
|
||||
""";
|
||||
|
||||
protected override string SystemPrompt =>
|
||||
"""
|
||||
You get a long text as input. The user wants to get a summary of the text.
|
||||
The user might want to change the language of the summary. In this case,
|
||||
you should provide a summary in the requested language. Eventually, the user
|
||||
want to change the complexity of the text. In this case, you should provide
|
||||
a summary with the requested complexity. In any case, do not add any information.
|
||||
""";
|
||||
|
||||
protected override bool AllowProfiles => false;
|
||||
|
||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||
|
||||
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
|
||||
{
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
};
|
||||
|
||||
protected override void ResetFrom()
|
||||
{
|
||||
this.inputText = string.Empty;
|
||||
if(!this.MightPreselectValues())
|
||||
{
|
||||
this.selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
this.customTargetLanguage = string.Empty;
|
||||
this.selectedComplexity = Complexity.NO_CHANGE;
|
||||
this.expertInField = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MightPreselectValues()
|
||||
{
|
||||
if (this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions)
|
||||
{
|
||||
this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedTargetLanguage;
|
||||
this.customTargetLanguage = this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedOtherLanguage;
|
||||
this.selectedComplexity = this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedComplexity;
|
||||
this.expertInField = this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedExpertInField;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private string inputText = string.Empty;
|
||||
private bool isAgentRunning;
|
||||
private CommonLanguages selectedTargetLanguage;
|
||||
private string customTargetLanguage = string.Empty;
|
||||
private Complexity selectedComplexity;
|
||||
private string expertInField = string.Empty;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages<string>(Event.SEND_TO_TEXT_SUMMARIZER_ASSISTANT).FirstOrDefault();
|
||||
if (deferredContent is not null)
|
||||
this.inputText = deferredContent;
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private string? ValidatingText(string text)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(text))
|
||||
return "Please provide a text as input. You might copy the desired text from a document or a website.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateCustomLanguage(string language)
|
||||
{
|
||||
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||
return "Please provide a custom language.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateExpertInField(string field)
|
||||
{
|
||||
if(this.selectedComplexity == Complexity.SCIENTIFIC_LANGUAGE_OTHER_EXPERTS && string.IsNullOrWhiteSpace(field))
|
||||
return "Please provide your field of expertise.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task SummarizeText()
|
||||
{
|
||||
await this.form!.Validate();
|
||||
if (!this.inputIsValid)
|
||||
return;
|
||||
|
||||
this.CreateChatThread();
|
||||
var time = this.AddUserRequest(
|
||||
$"""
|
||||
{this.selectedTargetLanguage.PromptSummarizing(this.customTargetLanguage)}
|
||||
{this.selectedComplexity.Prompt(this.expertInField)}
|
||||
|
||||
Please summarize the following text:
|
||||
|
||||
```
|
||||
{this.inputText}
|
||||
```
|
||||
""");
|
||||
|
||||
await this.AddAIResponseAsync(time);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace AIStudio.Assistants.TextSummarizer;
|
||||
|
||||
public enum Complexity
|
||||
{
|
||||
NO_CHANGE,
|
||||
|
||||
SIMPLE_LANGUAGE,
|
||||
TEEN_LANGUAGE,
|
||||
EVERYDAY_LANGUAGE,
|
||||
POPULAR_SCIENCE_LANGUAGE,
|
||||
SCIENTIFIC_LANGUAGE_FIELD_EXPERTS,
|
||||
SCIENTIFIC_LANGUAGE_OTHER_EXPERTS,
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace AIStudio.Assistants.TextSummarizer;
|
||||
|
||||
public static class ComplexityExtensions
|
||||
{
|
||||
public static string Name(this Complexity complexity) => complexity switch
|
||||
{
|
||||
Complexity.NO_CHANGE => "No change in complexity",
|
||||
|
||||
Complexity.SIMPLE_LANGUAGE => "Simple language, e.g., for children",
|
||||
Complexity.TEEN_LANGUAGE => "Teen language, e.g., for teenagers",
|
||||
Complexity.EVERYDAY_LANGUAGE => "Everyday language, e.g., for adults",
|
||||
Complexity.POPULAR_SCIENCE_LANGUAGE => "Popular science language, e.g., for people interested in science",
|
||||
Complexity.SCIENTIFIC_LANGUAGE_FIELD_EXPERTS => "Scientific language for experts in this field",
|
||||
Complexity.SCIENTIFIC_LANGUAGE_OTHER_EXPERTS => "Scientific language for experts from other fields (interdisciplinary)",
|
||||
|
||||
_ => "No change in complexity",
|
||||
};
|
||||
|
||||
public static string Prompt(this Complexity complexity, string expertInField) => complexity switch
|
||||
{
|
||||
Complexity.NO_CHANGE => "Do not change the complexity of the text.",
|
||||
|
||||
Complexity.SIMPLE_LANGUAGE => "Simplify the language, e.g., for 8 to 12-year-old children. Might use short sentences and simple words. You could use analogies to explain complex terms.",
|
||||
Complexity.TEEN_LANGUAGE => "Use a language suitable for teenagers, e.g., 16 to 19 years old. Might use teenage slang and analogies to explain complex terms.",
|
||||
Complexity.EVERYDAY_LANGUAGE => "Use everyday language suitable for adults. Avoid specific scientific terms. Use everday analogies to explain complex terms.",
|
||||
Complexity.POPULAR_SCIENCE_LANGUAGE => "Use popular science language, e.g., for people interested in science. The text should be easy to understand, though. Use analogies to explain complex terms.",
|
||||
Complexity.SCIENTIFIC_LANGUAGE_FIELD_EXPERTS => "Use scientific language for experts in the field of the texts subject. Use specific terms for this field.",
|
||||
Complexity.SCIENTIFIC_LANGUAGE_OTHER_EXPERTS => $"The reader is an expert in {expertInField}. Change the language so that it is suitable. Explain specific terms, so that the reader within his field can understand the text. You might use analogies to explain complex terms.",
|
||||
|
||||
_ => "Do not change the complexity of the text.",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
@attribute [Route(Routes.ASSISTANT_TRANSLATION)]
|
||||
@inherits AssistantBaseCore
|
||||
|
||||
@if (!this.SettingsManager.ConfigurationData.Translation.HideWebContentReader)
|
||||
{
|
||||
<ReadWebContent @bind-Content="@this.inputText" ProviderSettings="@this.providerSettings" @bind-AgentIsRunning="@this.isAgentRunning" Preselect="@(this.SettingsManager.ConfigurationData.Translation.PreselectOptions && this.SettingsManager.ConfigurationData.Translation.PreselectWebContentReader)" PreselectContentCleanerAgent="@(this.SettingsManager.ConfigurationData.Translation.PreselectOptions && this.SettingsManager.ConfigurationData.Translation.PreselectContentCleanerAgent)"/>
|
||||
}
|
||||
|
||||
<MudTextSwitch Label="Live translation" @bind-Value="@this.liveTranslation" LabelOn="Live translation" LabelOff="No live translation"/>
|
||||
@if (this.liveTranslation)
|
||||
{
|
||||
<MudTextField T="string" Disabled="@this.isAgentRunning" @bind-Text="@this.inputText" Validation="@this.ValidatingText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="Your input" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" Immediate="@true" DebounceInterval="@this.SettingsManager.ConfigurationData.Translation.DebounceIntervalMilliseconds" OnDebounceIntervalElapsed="() => this.TranslateText(force: false)" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudTextField T="string" Disabled="@this.isAgentRunning" @bind-Text="@this.inputText" Validation="@this.ValidatingText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="Your input" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
}
|
||||
|
||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelecting())" @bind-Value="@this.selectedTargetLanguage" ValidateSelection="@this.ValidatingTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Target language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom target language" />
|
||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||
@@ -0,0 +1,131 @@
|
||||
using AIStudio.Chat;
|
||||
|
||||
namespace AIStudio.Assistants.Translation;
|
||||
|
||||
public partial class AssistantTranslation : AssistantBaseCore
|
||||
{
|
||||
public override Tools.Components Component => Tools.Components.TRANSLATION_ASSISTANT;
|
||||
|
||||
protected override string Title => "Translation";
|
||||
|
||||
protected override string Description =>
|
||||
"""
|
||||
Translate text from one language to another.
|
||||
""";
|
||||
|
||||
protected override string SystemPrompt =>
|
||||
"""
|
||||
You get text in a source language as input. The user wants to get the text translated into a target language.
|
||||
Provide the translation in the requested language. Do not add any information. Correct any spelling or grammar mistakes.
|
||||
Do not ask for additional information. Do not mirror the user's language. Do not mirror the task. When the target
|
||||
language requires, e.g., shorter sentences, you should split the text into shorter sentences.
|
||||
""";
|
||||
|
||||
protected override bool AllowProfiles => false;
|
||||
|
||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||
|
||||
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
|
||||
{
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
};
|
||||
|
||||
protected override void ResetFrom()
|
||||
{
|
||||
this.inputText = string.Empty;
|
||||
this.inputTextLastTranslation = string.Empty;
|
||||
if (!this.MightPreselectValues())
|
||||
{
|
||||
this.liveTranslation = false;
|
||||
this.selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
this.customTargetLanguage = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MightPreselectValues()
|
||||
{
|
||||
if (this.SettingsManager.ConfigurationData.Translation.PreselectOptions)
|
||||
{
|
||||
this.liveTranslation = this.SettingsManager.ConfigurationData.Translation.PreselectLiveTranslation;
|
||||
this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.Translation.PreselectedTargetLanguage;
|
||||
this.customTargetLanguage = this.SettingsManager.ConfigurationData.Translation.PreselectOtherLanguage;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool liveTranslation;
|
||||
private bool isAgentRunning;
|
||||
private string inputText = string.Empty;
|
||||
private string inputTextLastTranslation = string.Empty;
|
||||
private CommonLanguages selectedTargetLanguage;
|
||||
private string customTargetLanguage = string.Empty;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages<string>(Event.SEND_TO_TRANSLATION_ASSISTANT).FirstOrDefault();
|
||||
if (deferredContent is not null)
|
||||
this.inputText = deferredContent;
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private string? ValidatingText(string text)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(text))
|
||||
return "Please provide a text as input. You might copy the desired text from a document or a website.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidatingTargetLanguage(CommonLanguages language)
|
||||
{
|
||||
if(language == CommonLanguages.AS_IS)
|
||||
return "Please select a target language.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateCustomLanguage(string language)
|
||||
{
|
||||
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||
return "Please provide a custom language.";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task TranslateText(bool force)
|
||||
{
|
||||
await this.form!.Validate();
|
||||
if (!this.inputIsValid)
|
||||
return;
|
||||
|
||||
if(!force && this.inputText == this.inputTextLastTranslation)
|
||||
return;
|
||||
|
||||
this.inputTextLastTranslation = this.inputText;
|
||||
this.CreateChatThread();
|
||||
var time = this.AddUserRequest(
|
||||
$"""
|
||||
{this.selectedTargetLanguage.PromptTranslation(this.customTargetLanguage)}
|
||||
|
||||
The given text is:
|
||||
|
||||
---
|
||||
{this.inputText}
|
||||
""");
|
||||
|
||||
await this.AddAIResponseAsync(time);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@using AIStudio
|
||||
@using AIStudio.Tools
|
||||
@using AIStudio.Components
|
||||
@using MudBlazor
|
||||
@@ -11,6 +11,7 @@ public enum ChatRole
|
||||
SYSTEM,
|
||||
USER,
|
||||
AI,
|
||||
AGENT,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace AIStudio.Chat;
|
||||
/// <summary>
|
||||
/// Data structure for a chat thread.
|
||||
/// </summary>
|
||||
public sealed class ChatThread
|
||||
public sealed record ChatThread
|
||||
{
|
||||
/// <summary>
|
||||
/// The unique identifier of the chat thread.
|
||||
@@ -15,6 +15,11 @@ public sealed class ChatThread
|
||||
/// </summary>
|
||||
public Guid WorkspaceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the provider selected for the chat thread.
|
||||
/// </summary>
|
||||
public string SelectedProvider { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the chat thread. Usually generated by an AI model or manually edited by the user.
|
||||
/// </summary>
|
||||
|
||||
@@ -18,10 +18,15 @@ public class ContentBlock
|
||||
/// <summary>
|
||||
/// The content of the block.
|
||||
/// </summary>
|
||||
public IContent? Content { get; init; } = null;
|
||||
public IContent? Content { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The role of the content block in the chat thread, e.g., user, AI, etc.
|
||||
/// </summary>
|
||||
public ChatRole Role { get; init; } = ChatRole.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// Should the content block be hidden from the user?
|
||||
/// </summary>
|
||||
public bool HideFromUser { get; set; }
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
@using AIStudio.Tools
|
||||
@using MudBlazor
|
||||
|
||||
<MudCard Class="my-2 rounded-lg" Outlined="@true">
|
||||
<MudCard Class="@this.CardClasses" Outlined="@true">
|
||||
<MudCardHeader>
|
||||
<CardHeaderAvatar>
|
||||
<MudAvatar Color="@this.Role.ToColor()">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using AIStudio.Tools;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using RustService = AIStudio.Tools.RustService;
|
||||
|
||||
namespace AIStudio.Chat;
|
||||
|
||||
/// <summary>
|
||||
@@ -33,11 +33,14 @@ public partial class ContentBlockComponent : ComponentBase
|
||||
[Parameter]
|
||||
public DateTimeOffset Time { get; init; }
|
||||
|
||||
[Inject]
|
||||
private Rust Rust { get; init; } = null!;
|
||||
/// <summary>
|
||||
/// Optional CSS classes.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string Class { get; set; } = string.Empty;
|
||||
|
||||
[Inject]
|
||||
private IJSRuntime JsRuntime { get; init; } = null!;
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ISnackbar Snackbar { get; init; } = null!;
|
||||
@@ -61,7 +64,7 @@ public partial class ContentBlockComponent : ComponentBase
|
||||
private async Task AfterStreaming()
|
||||
{
|
||||
// Might be called from a different thread, so we need to invoke the UI thread:
|
||||
await this.InvokeAsync(() =>
|
||||
await this.InvokeAsync(async () =>
|
||||
{
|
||||
//
|
||||
// Issue we try to solve: When the content changes during streaming,
|
||||
@@ -80,6 +83,9 @@ public partial class ContentBlockComponent : ComponentBase
|
||||
|
||||
// Let Blazor update the UI, i.e., to see the render tree diff:
|
||||
this.StateHasChanged();
|
||||
|
||||
// Inform the chat that the streaming is done:
|
||||
await MessageBus.INSTANCE.SendMessage<bool>(this, Event.CHAT_STREAMING_DONE);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -94,7 +100,7 @@ public partial class ContentBlockComponent : ComponentBase
|
||||
{
|
||||
case ContentType.TEXT:
|
||||
var textContent = (ContentText) this.Content;
|
||||
await this.Rust.CopyText2Clipboard(this.JsRuntime, this.Snackbar, textContent.Text);
|
||||
await this.RustService.CopyText2Clipboard(this.Snackbar, textContent.Text);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -107,4 +113,6 @@ public partial class ContentBlockComponent : ComponentBase
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private string CardClasses => $"my-2 rounded-lg {this.Class}";
|
||||
}
|
||||
@@ -29,7 +29,7 @@ public sealed class ContentImage : IContent
|
||||
public Func<Task> StreamingEvent { get; set; } = () => Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task CreateFromProviderAsync(IProvider provider, IJSRuntime jsRuntime, SettingsManager settings, Model chatModel, ChatThread chatChatThread, CancellationToken token = default)
|
||||
public Task CreateFromProviderAsync(IProvider provider, SettingsManager settings, Model chatModel, ChatThread chatChatThread, CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -35,18 +35,18 @@ public sealed class ContentText : IContent
|
||||
public Func<Task> StreamingEvent { get; set; } = () => Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task CreateFromProviderAsync(IProvider provider, IJSRuntime jsRuntime, SettingsManager settings, Model chatModel, ChatThread? chatThread, CancellationToken token = default)
|
||||
public async Task CreateFromProviderAsync(IProvider provider, SettingsManager settings, Model chatModel, ChatThread? chatThread, CancellationToken token = default)
|
||||
{
|
||||
if(chatThread is null)
|
||||
return;
|
||||
|
||||
// Store the last time we got a response. We use this later,
|
||||
// Store the last time we got a response. We use this later
|
||||
// to determine whether we should notify the UI about the
|
||||
// new content or not. Depends on the energy saving mode
|
||||
// the user chose.
|
||||
var last = DateTimeOffset.Now;
|
||||
|
||||
// Start another thread by using a task, to uncouple
|
||||
// Start another thread by using a task to uncouple
|
||||
// the UI thread from the AI processing:
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
@@ -54,7 +54,7 @@ public sealed class ContentText : IContent
|
||||
this.InitialRemoteWait = true;
|
||||
|
||||
// Iterate over the responses from the AI:
|
||||
await foreach (var deltaText in provider.StreamChatCompletion(jsRuntime, settings, chatModel, chatThread, token))
|
||||
await foreach (var deltaText in provider.StreamChatCompletion(chatModel, chatThread, token))
|
||||
{
|
||||
// When the user cancels the request, we stop the loop:
|
||||
if (token.IsCancellationRequested)
|
||||
@@ -70,7 +70,7 @@ public sealed class ContentText : IContent
|
||||
// Notify the UI that the content has changed,
|
||||
// depending on the energy saving mode:
|
||||
var now = DateTimeOffset.Now;
|
||||
switch (settings.ConfigurationData.IsSavingEnergy)
|
||||
switch (settings.ConfigurationData.App.IsSavingEnergy)
|
||||
{
|
||||
// Energy saving mode is off. We notify the UI
|
||||
// as fast as possible -- no matter the odds:
|
||||
@@ -89,7 +89,7 @@ public sealed class ContentText : IContent
|
||||
}
|
||||
|
||||
// Stop the waiting animation (in case the loop
|
||||
// was stopped or no content was received):
|
||||
// was stopped, or no content was received):
|
||||
this.InitialRemoteWait = false;
|
||||
this.IsStreaming = false;
|
||||
}, token);
|
||||
|
||||
@@ -42,5 +42,5 @@ public interface IContent
|
||||
/// <summary>
|
||||
/// Uses the provider to create the content.
|
||||
/// </summary>
|
||||
public Task CreateFromProviderAsync(IProvider provider, IJSRuntime jsRuntime, SettingsManager settings, Model chatModel, ChatThread chatChatThread, CancellationToken token = default);
|
||||
public Task CreateFromProviderAsync(IProvider provider, SettingsManager settings, Model chatModel, ChatThread chatChatThread, CancellationToken token = default);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace AIStudio.Chat;
|
||||
|
||||
public static class SystemPrompts
|
||||
{
|
||||
public const string DEFAULT = "You are a helpful assistant!";
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
@* ReSharper disable Html.PathError *@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>MindWork AI Studio</title>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<base href="/"/>
|
||||
|
||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||
<link href="system/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
|
||||
<link href="system/MudBlazor.Markdown/MudBlazor.Markdown.min.css" rel="stylesheet" />
|
||||
<link href="app.css" rel="stylesheet" />
|
||||
<HeadOutlet/>
|
||||
</head>
|
||||
|
||||
<body style="overflow: hidden;">
|
||||
<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)"/>
|
||||
<script src="_framework/blazor.web.js" autostart="false"></script>
|
||||
<script src="boot.js"></script>
|
||||
<script src="system/MudBlazor/MudBlazor.min.js"></script>
|
||||
<script src="system/MudBlazor.Markdown/MudBlazor.Markdown.min.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,24 @@
|
||||
<MudCard Outlined="@true" Style="@this.BlockStyle">
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudStack AlignItems="AlignItems.Center" Row="@true">
|
||||
<MudIcon Icon="@this.Icon" Size="Size.Large" Color="Color.Primary"/>
|
||||
<MudText Typo="Typo.h6">
|
||||
@this.Name
|
||||
</MudText>
|
||||
</MudStack>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
<MudCardContent>
|
||||
<MudStack>
|
||||
<MudText>
|
||||
@this.Description
|
||||
</MudText>
|
||||
</MudStack>
|
||||
</MudCardContent>
|
||||
<MudCardActions>
|
||||
<MudButton Size="Size.Large" Variant="Variant.Filled" StartIcon="@this.Icon" Color="Color.Default" Href="@this.Link">
|
||||
@this.ButtonText
|
||||
</MudButton>
|
||||
</MudCardActions>
|
||||
</MudCard>
|
||||
@@ -0,0 +1,82 @@
|
||||
using AIStudio.Settings;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class AssistantBlock : ComponentBase, IMessageBusReceiver, IDisposable
|
||||
{
|
||||
[Parameter]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public string Icon { get; set; } = Icons.Material.Filled.DisabledByDefault;
|
||||
|
||||
[Parameter]
|
||||
public string ButtonText { get; set; } = "Start";
|
||||
|
||||
[Parameter]
|
||||
public string Link { get; set; } = string.Empty;
|
||||
|
||||
[Inject]
|
||||
private MudTheme ColorTheme { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private SettingsManager SettingsManager { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private MessageBus MessageBus { get; init; } = null!;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
this.MessageBus.RegisterComponent(this);
|
||||
this.MessageBus.ApplyFilters(this, [], [ Event.COLOR_THEME_CHANGED ]);
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IMessageBusReceiver
|
||||
|
||||
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
||||
{
|
||||
switch (triggeredEvent)
|
||||
{
|
||||
case Event.COLOR_THEME_CHANGED:
|
||||
this.StateHasChanged();
|
||||
break;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
|
||||
{
|
||||
return Task.FromResult<TResult?>(default);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private string BorderColor => this.SettingsManager.IsDarkMode switch
|
||||
{
|
||||
true => this.ColorTheme.GetCurrentPalette(this.SettingsManager).GrayLight,
|
||||
false => this.ColorTheme.GetCurrentPalette(this.SettingsManager).Primary.Value,
|
||||
};
|
||||
|
||||
private string BlockStyle => $"border-width: 2px; border-color: {this.BorderColor}; border-radius: 12px; border-style: solid; max-width: 20em;";
|
||||
|
||||
#region Implementation of IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.MessageBus.Unregister(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
namespace AIStudio.Components.Blocks;
|
||||
|
||||
public partial class Changelog
|
||||
{
|
||||
public readonly record struct Log(int Build, string Display, string Filename)
|
||||
{
|
||||
#region Overrides of ValueType
|
||||
|
||||
public override string ToString() => this.Display;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static readonly Log[] LOGS =
|
||||
[
|
||||
new (161, "v0.7.1, build 161 (2024-07-13 12:44 UTC)", "v0.7.1.md"),
|
||||
new (160, "v0.7.0, build 160 (2024-07-13 08:21 UTC)", "v0.7.0.md"),
|
||||
new (159, "v0.6.3, build 159 (2024-07-03 18:26 UTC)", "v0.6.3.md"),
|
||||
new (158, "v0.6.2, build 158 (2024-07-01 18:03 UTC)", "v0.6.2.md"),
|
||||
new (157, "v0.6.1, build 157 (2024-06-30 19:00 UTC)", "v0.6.1.md"),
|
||||
new (156, "v0.6.0, build 156 (2024-06-30 12:49 UTC)", "v0.6.0.md"),
|
||||
new (155, "v0.5.2, build 155 (2024-06-25 18:07 UTC)", "v0.5.2.md"),
|
||||
new (154, "v0.5.1, build 154 (2024-06-25 15:35 UTC)", "v0.5.1.md"),
|
||||
new (149, "v0.5.0, build 149 (2024-06-02 18:51 UTC)", "v0.5.0.md"),
|
||||
new (138, "v0.4.0, build 138 (2024-05-26 13:26 UTC)", "v0.4.0.md"),
|
||||
new (120, "v0.3.0, build 120 (2024-05-18 21:57 UTC)", "v0.3.0.md"),
|
||||
new (90, "v0.2.0, build 90 (2024-05-04 10:50 UTC)", "v0.2.0.md"),
|
||||
new (45, "v0.1.0, build 45 (2024-04-20 21:27 UTC)", "v0.1.0.md"),
|
||||
|
||||
];
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<MudExpansionPanel Class="border-solid border rounded-lg" IsInitiallyExpanded="@this.IsExpanded">
|
||||
<TitleContent>
|
||||
<div class="d-flex align-center">
|
||||
<MudIcon Icon="@this.HeaderIcon" Size="@this.IconSize" Color="@this.IconColor" class="mr-3"/>
|
||||
<MudText Typo="Typo.h6">@this.HeaderText</MudText>
|
||||
</div>
|
||||
</TitleContent>
|
||||
<ChildContent>
|
||||
@this.ChildContent
|
||||
</ChildContent>
|
||||
</MudExpansionPanel>
|
||||
@@ -1,3 +0,0 @@
|
||||
namespace AIStudio.Components.Blocks;
|
||||
|
||||
public interface ITreeItem;
|
||||
@@ -1,9 +0,0 @@
|
||||
@inherits AIStudio.Tools.MSGComponentBase
|
||||
|
||||
<div class="d-flex flex-column" style="@this.Height">
|
||||
<div class="flex-auto overflow-auto">
|
||||
@this.ChildContent
|
||||
</div>
|
||||
|
||||
@this.FooterContent
|
||||
</div>
|
||||
@@ -1,8 +0,0 @@
|
||||
<MudList Clickable="@this.Clickable" Class="@this.Classes">
|
||||
@foreach(var item in this.Items)
|
||||
{
|
||||
<MudListItem Icon="@this.Icon" Style="display: flex; align-items: flex-start;">
|
||||
<MudText Typo="Typo.body1" Style="text-align: justify; hyphens: auto;"><b>@item.Header:</b> @item.Text</MudText>
|
||||
</MudListItem>
|
||||
}
|
||||
</MudList>
|
||||
@@ -1,3 +0,0 @@
|
||||
namespace AIStudio.Components.Blocks;
|
||||
|
||||
public readonly record struct TreeDivider : ITreeItem;
|
||||
@@ -1,16 +0,0 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components.Blocks;
|
||||
|
||||
public partial class Vision : ComponentBase
|
||||
{
|
||||
private static readonly TextItem[] ITEMS_VISION =
|
||||
[
|
||||
new TextItem("Meet your needs", "Whatever your job or task is, MindWork AI Studio aims to meet your needs: whether you're a project manager, scientist, artist, author, software developer, or game developer."),
|
||||
new TextItem("One stop shop", "The app will strive to fulfill all your AI needs: text-generation AI (LLM), image-generation AI, audio-generation AI (text-to-speech, potentially even text-to-music), and audio input (transcription, dictation). When there's a provider and an API available, we'll try to integrate it."),
|
||||
new TextItem("Local file system", "When you wish, we aim to integrate your local system. Local documents could be incorporated using Retrieval-Augmented Generation (RAG), and we could directly save AI-generated content to your file system."),
|
||||
new TextItem("Local AI systems", "Want to use AI systems locally and offline? We aim to make that possible too."),
|
||||
new TextItem("Your AI systems", "Prefer to run your AI systems with providers like replicate.com? We plan to support that!"),
|
||||
new TextItem("Assistants", "We aim to integrate specialized user interfaces as assistants. For example, a UI specifically for writing emails, or one designed for translating and correcting text, and more."),
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class Changelog
|
||||
{
|
||||
public readonly record struct Log(int Build, string Display, string Filename)
|
||||
{
|
||||
#region Overrides of ValueType
|
||||
|
||||
public override string ToString() => this.Display;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static readonly Log[] LOGS =
|
||||
[
|
||||
new (197, "v0.9.22, build 197 (2024-12-04 10:58 UTC)", "v0.9.22.md"),
|
||||
new (196, "v0.9.21, build 196 (2024-11-23 12:22 UTC)", "v0.9.21.md"),
|
||||
new (195, "v0.9.20, build 195 (2024-11-16 20:44 UTC)", "v0.9.20.md"),
|
||||
new (194, "v0.9.19, build 194 (2024-11-14 05:58 UTC)", "v0.9.19.md"),
|
||||
new (193, "v0.9.18, build 193 (2024-11-09 21:10 UTC)", "v0.9.18.md"),
|
||||
new (192, "v0.9.17, build 192 (2024-11-03 11:11 UTC)", "v0.9.17.md"),
|
||||
new (191, "v0.9.16, build 191 (2024-11-02 22:04 UTC)", "v0.9.16.md"),
|
||||
new (190, "v0.9.15, build 190 (2024-10-28 15:04 UTC)", "v0.9.15.md"),
|
||||
new (189, "v0.9.14, build 189 (2024-10-18 08:48 UTC)", "v0.9.14.md"),
|
||||
new (188, "v0.9.13, build 188 (2024-10-07 11:18 UTC)", "v0.9.13.md"),
|
||||
new (187, "v0.9.12, build 187 (2024-09-15 20:49 UTC)", "v0.9.12.md"),
|
||||
new (186, "v0.9.11, build 186 (2024-09-15 10:33 UTC)", "v0.9.11.md"),
|
||||
new (185, "v0.9.10, build 185 (2024-09-12 20:52 UTC)", "v0.9.10.md"),
|
||||
new (184, "v0.9.9, build 184 (2024-09-11 21:10 UTC)", "v0.9.9.md"),
|
||||
new (183, "v0.9.8, build 183 (2024-09-09 13:10 UTC)", "v0.9.8.md"),
|
||||
new (182, "v0.9.7, build 182 (2024-09-08 20:55 UTC)", "v0.9.7.md"),
|
||||
new (181, "v0.9.6, build 181 (2024-09-08 09:30 UTC)", "v0.9.6.md"),
|
||||
new (180, "v0.9.5, build 180 (2024-09-07 16:33 UTC)", "v0.9.5.md"),
|
||||
new (179, "v0.9.4, build 179 (2024-09-06 20:11 UTC)", "v0.9.4.md"),
|
||||
new (178, "v0.9.3, build 178 (2024-09-06 11:06 UTC)", "v0.9.3.md"),
|
||||
new (177, "v0.9.2, build 177 (2024-09-05 09:19 UTC)", "v0.9.2.md"),
|
||||
new (176, "v0.9.1, build 176 (2024-09-04 13:48 UTC)", "v0.9.1.md"),
|
||||
new (175, "v0.9.0, build 175 (2024-09-03 16:00 UTC)", "v0.9.0.md"),
|
||||
new (174, "v0.8.12, build 174 (2024-08-24 08:30 UTC)", "v0.8.12.md"),
|
||||
new (173, "v0.8.11, build 173 (2024-08-21 07:03 UTC)", "v0.8.11.md"),
|
||||
new (172, "v0.8.10, build 172 (2024-08-18 19:44 UTC)", "v0.8.10.md"),
|
||||
new (171, "v0.8.9, build 171 (2024-08-18 10:35 UTC)", "v0.8.9.md"),
|
||||
new (170, "v0.8.8, build 170 (2024-08-14 06:30 UTC)", "v0.8.8.md"),
|
||||
new (169, "v0.8.7, build 169 (2024-08-01 19:08 UTC)", "v0.8.7.md"),
|
||||
new (168, "v0.8.6, build 168 (2024-08-01 19:50 UTC)", "v0.8.6.md"),
|
||||
new (167, "v0.8.5, build 167 (2024-07-28 16:44 UTC)", "v0.8.5.md"),
|
||||
new (166, "v0.8.4, build 166 (2024-07-26 06:53 UTC)", "v0.8.4.md"),
|
||||
new (165, "v0.8.3, build 165 (2024-07-25 13:25 UTC)", "v0.8.3.md"),
|
||||
new (164, "v0.8.2, build 164 (2024-07-16 18:03 UTC)", "v0.8.2.md"),
|
||||
new (163, "v0.8.1, build 163 (2024-07-16 08:32 UTC)", "v0.8.1.md"),
|
||||
new (162, "v0.8.0, build 162 (2024-07-14 19:39 UTC)", "v0.8.0.md"),
|
||||
new (161, "v0.7.1, build 161 (2024-07-13 11:42 UTC)", "v0.7.1.md"),
|
||||
new (160, "v0.7.0, build 160 (2024-07-13 08:21 UTC)", "v0.7.0.md"),
|
||||
new (159, "v0.6.3, build 159 (2024-07-03 18:26 UTC)", "v0.6.3.md"),
|
||||
new (158, "v0.6.2, build 158 (2024-07-01 18:03 UTC)", "v0.6.2.md"),
|
||||
new (157, "v0.6.1, build 157 (2024-06-30 19:00 UTC)", "v0.6.1.md"),
|
||||
new (156, "v0.6.0, build 156 (2024-06-30 12:49 UTC)", "v0.6.0.md"),
|
||||
new (155, "v0.5.2, build 155 (2024-06-25 18:07 UTC)", "v0.5.2.md"),
|
||||
new (154, "v0.5.1, build 154 (2024-06-25 15:35 UTC)", "v0.5.1.md"),
|
||||
new (149, "v0.5.0, build 149 (2024-06-02 18:51 UTC)", "v0.5.0.md"),
|
||||
new (138, "v0.4.0, build 138 (2024-05-26 13:26 UTC)", "v0.4.0.md"),
|
||||
new (120, "v0.3.0, build 120 (2024-05-18 21:57 UTC)", "v0.3.0.md"),
|
||||
new (90, "v0.2.0, build 90 (2024-05-04 10:50 UTC)", "v0.2.0.md"),
|
||||
new (45, "v0.1.0, build 45 (2024-04-20 21:27 UTC)", "v0.1.0.md"),
|
||||
|
||||
];
|
||||
}
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
@using AIStudio.Tools
|
||||
<MudSelect T="Log" @bind-Value="@this.SelectedLog" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Schedule" Margin="Margin.Dense" Label="Changelog" Class="mb-2 rounded-lg" Variant="Variant.Outlined" SelectedValuesChanged="() => this.ReadLogAsync()" OnKeyUp="() => this.ReadLogAsync()">
|
||||
@foreach (var log in LOGS)
|
||||
{
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components.Blocks;
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class Changelog : ComponentBase
|
||||
{
|
||||
@@ -0,0 +1,58 @@
|
||||
@using AIStudio.Provider
|
||||
<div class="d-flex">
|
||||
<MudTooltip Text="Shows and hides the confidence card with information about the selected LLM provider.">
|
||||
@if (this.Mode is ConfidenceInfoMode.ICON)
|
||||
{
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Security" Class="confidence-icon" Style="@this.LLMProvider.GetConfidence(this.SettingsManager).SetColorStyle(this.SettingsManager)" OnClick="@(() => this.ToggleConfidence())"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.Security" IconClass="confidence-icon" Style="@this.LLMProvider.GetConfidence(this.SettingsManager).SetColorStyle(this.SettingsManager)" OnClick="@(() => this.ToggleConfidence())">
|
||||
Confidence
|
||||
</MudButton>
|
||||
}
|
||||
</MudTooltip>
|
||||
|
||||
<MudPopover Open="@this.showConfidence" AnchorOrigin="Origin.TopLeft" TransformOrigin="Origin.BottomLeft" Style="@this.GetPopoverStyle()" DropShadow="@true" Class="border-solid border-4 rounded-lg">
|
||||
<MudCard>
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h5">Confidence Card</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
<MudCardContent>
|
||||
<MudText Typo="Typo.h6">Description</MudText>
|
||||
<MudMarkdown Value="@this.currentConfidence.Description"/>
|
||||
|
||||
@if (this.currentConfidence.Sources.Count > 0)
|
||||
{
|
||||
<MudText Typo="Typo.h6">Sources</MudText>
|
||||
<MudList T="@string">
|
||||
@foreach (var sourceTuple in this.GetConfidenceSources())
|
||||
{
|
||||
<MudListItem Icon="@Icons.Material.Filled.Link" Href="@sourceTuple.Source" Target="_blank" Text="@sourceTuple.Index"/>
|
||||
}
|
||||
</MudList>
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(this.currentConfidence.Region))
|
||||
{
|
||||
<MudText Typo="Typo.h6">Region</MudText>
|
||||
<MudText Typo="Typo.body1" Class="mb-3">
|
||||
<b>@this.currentConfidence.Region</b>
|
||||
</MudText>
|
||||
}
|
||||
|
||||
<MudText Typo="Typo.h6">Confidence Level</MudText>
|
||||
<MudText Typo="Typo.body1" Style="@this.GetCurrentConfidenceColor()">
|
||||
<b>@this.currentConfidence.Level.GetName()</b>
|
||||
</MudText>
|
||||
</MudCardContent>
|
||||
<MudCardActions>
|
||||
<MudButton Variant="Variant.Filled" OnClick="@(() => this.HideConfidence())">
|
||||
Close
|
||||
</MudButton>
|
||||
</MudCardActions>
|
||||
</MudCard>
|
||||
</MudPopover>
|
||||
</div>
|
||||
@@ -0,0 +1,94 @@
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class ConfidenceInfo : ComponentBase, IMessageBusReceiver, IDisposable
|
||||
{
|
||||
[Parameter]
|
||||
public ConfidenceInfoMode Mode { get; set; } = ConfidenceInfoMode.BUTTON;
|
||||
|
||||
[Parameter]
|
||||
public LLMProviders LLMProvider { get; set; }
|
||||
|
||||
[Inject]
|
||||
private SettingsManager SettingsManager { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private MessageBus MessageBus { get; init; } = null!;
|
||||
|
||||
private Confidence currentConfidence;
|
||||
private bool showConfidence;
|
||||
|
||||
public ConfidenceInfo()
|
||||
{
|
||||
this.currentConfidence = LLMProviders.NONE.GetConfidence(this.SettingsManager);
|
||||
}
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
this.MessageBus.RegisterComponent(this);
|
||||
this.MessageBus.ApplyFilters(this, [], [ Event.COLOR_THEME_CHANGED ]);
|
||||
|
||||
this.currentConfidence = this.LLMProvider.GetConfidence(this.SettingsManager);
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void ToggleConfidence()
|
||||
{
|
||||
this.showConfidence = !this.showConfidence;
|
||||
}
|
||||
|
||||
private void HideConfidence()
|
||||
{
|
||||
this.showConfidence = false;
|
||||
}
|
||||
|
||||
private IEnumerable<(string Index, string Source)> GetConfidenceSources()
|
||||
{
|
||||
var index = 0;
|
||||
foreach (var source in this.currentConfidence.Sources)
|
||||
yield return ($"Source {++index}", source);
|
||||
}
|
||||
|
||||
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);";
|
||||
|
||||
#region Implementation of IMessageBusReceiver
|
||||
|
||||
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
||||
{
|
||||
switch (triggeredEvent)
|
||||
{
|
||||
case Event.COLOR_THEME_CHANGED:
|
||||
this.showConfidence = false;
|
||||
this.StateHasChanged();
|
||||
break;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
|
||||
{
|
||||
return Task.FromResult<TResult?>(default);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.MessageBus.Unregister(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public enum ConfidenceInfoMode
|
||||
{
|
||||
BUTTON,
|
||||
ICON,
|
||||
}
|
||||
@@ -7,7 +7,7 @@ namespace AIStudio.Components;
|
||||
/// <summary>
|
||||
/// A base class for configuration options.
|
||||
/// </summary>
|
||||
public partial class ConfigurationBase : ComponentBase
|
||||
public partial class ConfigurationBase : ComponentBase, IMessageBusReceiver, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The description of the option, i.e., the name. Should be
|
||||
@@ -22,8 +22,66 @@ public partial class ConfigurationBase : ComponentBase
|
||||
[Parameter]
|
||||
public string OptionHelp { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Is the option disabled?
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Func<bool> Disabled { get; set; } = () => false;
|
||||
|
||||
[Inject]
|
||||
public SettingsManager SettingsManager { get; init; } = null!;
|
||||
protected SettingsManager SettingsManager { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected MessageBus MessageBus { get; init; } = null!;
|
||||
|
||||
protected const string MARGIN_CLASS = "mb-6";
|
||||
protected static readonly Dictionary<string, object?> SPELLCHECK_ATTRIBUTES = new();
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
// Configure the spellchecking for the instance name input:
|
||||
this.SettingsManager.InjectSpellchecking(SPELLCHECK_ATTRIBUTES);
|
||||
|
||||
// Register this component with the message bus:
|
||||
this.MessageBus.RegisterComponent(this);
|
||||
this.MessageBus.ApplyFilters(this, [], [ Event.CONFIGURATION_CHANGED ]);
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected async Task InformAboutChange() => await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||
|
||||
#region Implementation of IMessageBusReceiver
|
||||
|
||||
public Task ProcessMessage<TMsg>(ComponentBase? sendingComponent, Event triggeredEvent, TMsg? data)
|
||||
{
|
||||
switch (triggeredEvent)
|
||||
{
|
||||
case Event.CONFIGURATION_CHANGED:
|
||||
this.StateHasChanged();
|
||||
break;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
|
||||
{
|
||||
return Task.FromResult<TResult?>(default);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.MessageBus.Unregister(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
@using AIStudio.Settings
|
||||
<ConfigurationSelect Disabled="@this.Disabled" OptionDescription="Select a minimum confidence level" SelectedValue="@this.FilteredSelectedValue" Data="@ConfigurationSelectDataFactory.GetConfidenceLevelsData(this.SettingsManager, this.RestrictToGlobalMinimumConfidence)" SelectionUpdate="@this.SelectionUpdate" OptionHelp="Choose the minimum confidence level that all LLM providers must meet. This way, you can ensure that only trustworthy providers are used. You cannot use any provider that falls below this level."/>
|
||||
@@ -0,0 +1,51 @@
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class ConfigurationMinConfidenceSelection : ComponentBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The selected value.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Func<ConfidenceLevel> SelectedValue { get; set; } = () => default!;
|
||||
|
||||
/// <summary>
|
||||
/// An action that is called when the selection changes.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Action<ConfidenceLevel> SelectionUpdate { get; set; } = _ => { };
|
||||
|
||||
/// <summary>
|
||||
/// Is the selection component disabled?
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Func<bool> Disabled { get; set; } = () => false;
|
||||
|
||||
/// <summary>
|
||||
/// Boolean value indicating whether the selection is restricted to a global minimum confidence level.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool RestrictToGlobalMinimumConfidence { get; set; }
|
||||
|
||||
[Inject]
|
||||
private SettingsManager SettingsManager { get; init; } = null!;
|
||||
|
||||
private ConfidenceLevel FilteredSelectedValue()
|
||||
{
|
||||
if (this.SelectedValue() is ConfidenceLevel.NONE)
|
||||
return ConfidenceLevel.NONE;
|
||||
|
||||
if(this.RestrictToGlobalMinimumConfidence && this.SettingsManager.ConfigurationData.LLMProviders.EnforceGlobalMinimumConfidence)
|
||||
{
|
||||
var minimumLevel = this.SettingsManager.ConfigurationData.LLMProviders.GlobalMinimumConfidence;
|
||||
if(this.SelectedValue() < minimumLevel)
|
||||
return minimumLevel;
|
||||
}
|
||||
|
||||
return this.SelectedValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
@inherits ConfigurationBase
|
||||
@typeparam T
|
||||
|
||||
<MudSelectExtended
|
||||
T="T"
|
||||
MultiSelection="@true"
|
||||
MultiSelectionTextFunc="@this.GetMultiSelectionText"
|
||||
SelectedValues="@this.SelectedValues()"
|
||||
Strict="@true"
|
||||
Disabled="@this.Disabled()"
|
||||
Margin="Margin.Dense"
|
||||
Label="@this.OptionDescription"
|
||||
Class="@GetClass"
|
||||
Variant="Variant.Outlined"
|
||||
HelperText="@this.OptionHelp"
|
||||
SelectedValuesChanged="@this.OptionChanged">
|
||||
@foreach (var data in this.Data)
|
||||
{
|
||||
<MudSelectItemExtended Value="@data.Value">
|
||||
@data.Name
|
||||
</MudSelectItemExtended>
|
||||
}
|
||||
</MudSelectExtended>
|
||||
@@ -0,0 +1,54 @@
|
||||
using AIStudio.Settings;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration component for selecting many values from a list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the value to select.</typeparam>
|
||||
public partial class ConfigurationMultiSelect<T> : ConfigurationBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The data to select from.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public IEnumerable<ConfigurationSelectData<T>> Data { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The selected values.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Func<HashSet<T>> SelectedValues { get; set; } = () => [];
|
||||
|
||||
/// <summary>
|
||||
/// An action that is called when the selection changes.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Action<HashSet<T>> SelectionUpdate { get; set; } = _ => { };
|
||||
|
||||
private async Task OptionChanged(IEnumerable<T?>? updatedValues)
|
||||
{
|
||||
if(updatedValues is null)
|
||||
this.SelectionUpdate([]);
|
||||
else
|
||||
this.SelectionUpdate(updatedValues.Where(n => n is not null).ToHashSet()!);
|
||||
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.InformAboutChange();
|
||||
}
|
||||
|
||||
private static string GetClass => $"{MARGIN_CLASS} rounded-lg";
|
||||
|
||||
private string GetMultiSelectionText(List<T?>? selectedValues)
|
||||
{
|
||||
if(selectedValues is null || selectedValues.Count == 0)
|
||||
return "No preview features selected.";
|
||||
|
||||
if(selectedValues.Count == 1)
|
||||
return $"You have selected 1 preview feature.";
|
||||
|
||||
return $"You have selected {selectedValues.Count} preview features.";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
@inherits ConfigurationBase
|
||||
|
||||
<MudField Label="@this.OptionDescription" Variant="Variant.Outlined" HelperText="@this.OptionHelp" Class="@MARGIN_CLASS">
|
||||
<MudSwitch T="bool" Value="@this.State()" ValueChanged="@(updatedState => this.OptionChanged(updatedState))">
|
||||
<MudField Disabled="@this.Disabled()" Label="@this.OptionDescription" Variant="Variant.Outlined" HelperText="@this.OptionHelp" Class="@MARGIN_CLASS">
|
||||
<MudSwitch T="bool" Disabled="@this.Disabled()" Value="@this.State()" ValueChanged="@this.OptionChanged" Color="Color.Primary">
|
||||
@(this.State() ? this.LabelOn : this.LabelOff)
|
||||
</MudSwitch>
|
||||
</MudField>
|
||||
@@ -35,5 +35,6 @@ public partial class ConfigurationOption : ConfigurationBase
|
||||
{
|
||||
this.StateUpdate(updatedState);
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.InformAboutChange();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<ConfigurationSelect OptionDescription="Preselected provider" Disabled="@this.Disabled" OptionHelp="@this.HelpText()" Data="@this.FilteredData()" SelectedValue="@this.SelectedValue" SelectionUpdate="@this.SelectionUpdate"/>
|
||||
@@ -0,0 +1,100 @@
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class ConfigurationProviderSelection : ComponentBase, IMessageBusReceiver, IDisposable
|
||||
{
|
||||
[Parameter]
|
||||
public Func<string> SelectedValue { get; set; } = () => string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public Action<string> SelectionUpdate { get; set; } = _ => { };
|
||||
|
||||
[Parameter]
|
||||
public IEnumerable<ConfigurationSelectData<string>> Data { get; set; } = new List<ConfigurationSelectData<string>>();
|
||||
|
||||
/// <summary>
|
||||
/// Is the selection component disabled?
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Func<bool> Disabled { get; set; } = () => false;
|
||||
|
||||
[Parameter]
|
||||
public Func<string> HelpText { get; set; } = () => "Select a provider that is preselected.";
|
||||
|
||||
[Parameter]
|
||||
public Tools.Components Component { get; set; } = Tools.Components.NONE;
|
||||
|
||||
[Inject]
|
||||
private SettingsManager SettingsManager { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private MessageBus MessageBus { get; init; } = null!;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
// Register this component with the message bus:
|
||||
this.MessageBus.RegisterComponent(this);
|
||||
this.MessageBus.ApplyFilters(this, [], [ Event.CONFIGURATION_CHANGED ]);
|
||||
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private IEnumerable<ConfigurationSelectData<string>> FilteredData()
|
||||
{
|
||||
var minimumLevel = this.SettingsManager.GetMinimumConfidenceLevel(this.Component);
|
||||
foreach (var providerId in this.Data)
|
||||
{
|
||||
var provider = this.SettingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == providerId.Value);
|
||||
if (provider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level >= minimumLevel)
|
||||
yield return providerId;
|
||||
}
|
||||
}
|
||||
|
||||
#region Implementation of IMessageBusReceiver
|
||||
|
||||
public async Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
||||
{
|
||||
switch (triggeredEvent)
|
||||
{
|
||||
case Event.CONFIGURATION_CHANGED:
|
||||
|
||||
if(string.IsNullOrWhiteSpace(this.SelectedValue()))
|
||||
break;
|
||||
|
||||
// Check if the selected value is still valid:
|
||||
if (this.Data.All(x => x.Value != this.SelectedValue()))
|
||||
{
|
||||
this.SelectedValue = () => string.Empty;
|
||||
this.SelectionUpdate(string.Empty);
|
||||
await this.SettingsManager.StoreSettings();
|
||||
}
|
||||
|
||||
this.StateHasChanged();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
|
||||
{
|
||||
return Task.FromResult<TResult?>(default);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.MessageBus.Unregister(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
@inherits ConfigurationBase
|
||||
@typeparam T
|
||||
|
||||
<MudSelect T="T" Value="@this.SelectedValue()" Margin="Margin.Dense" Label="@this.OptionDescription" Class="@GetClass" Variant="Variant.Outlined" HelperText="@this.OptionHelp" ValueChanged="selectedValue => this.OptionChanged(selectedValue)">
|
||||
<MudSelect T="T" Value="@this.SelectedValue()" Strict="@true" ShrinkLabel="@true" Disabled="@this.Disabled()" Margin="Margin.Dense" Label="@this.OptionDescription" Class="@GetClass" Variant="Variant.Outlined" HelperText="@this.OptionHelp" ValueChanged="@this.OptionChanged">
|
||||
@foreach (var data in this.Data)
|
||||
{
|
||||
<MudSelectItem Value="@data.Value">
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using AIStudio.Settings;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
@@ -30,6 +32,7 @@ public partial class ConfigurationSelect<T> : ConfigurationBase
|
||||
{
|
||||
this.SelectionUpdate(updatedValue);
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.InformAboutChange();
|
||||
}
|
||||
|
||||
private static string GetClass => $"{MARGIN_CLASS} rounded-lg";
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
/// <summary>
|
||||
/// A data structure to map a name to a value.
|
||||
/// </summary>
|
||||
/// <param name="Name">The name of the value, to be displayed in the UI.</param>
|
||||
/// <param name="Value">The value to be stored.</param>
|
||||
/// <typeparam name="T">The type of the value to store.</typeparam>
|
||||
public readonly record struct ConfigurationSelectData<T>(string Name, T Value);
|
||||
|
||||
/// <summary>
|
||||
/// A static factory class to get the lists of selectable values.
|
||||
/// </summary>
|
||||
public static class ConfigurationSelectDataFactory
|
||||
{
|
||||
public static IEnumerable<ConfigurationSelectData<SendBehavior>> GetSendBehaviorData()
|
||||
{
|
||||
yield return new("No key is sending the input", SendBehavior.NO_KEY_IS_SENDING);
|
||||
yield return new("Modifier key + enter is sending the input", SendBehavior.MODIFER_ENTER_IS_SENDING);
|
||||
yield return new("Enter is sending the input", SendBehavior.ENTER_IS_SENDING);
|
||||
}
|
||||
|
||||
public static IEnumerable<ConfigurationSelectData<UpdateBehavior>> GetUpdateBehaviorData()
|
||||
{
|
||||
yield return new("No automatic update checks", UpdateBehavior.NO_CHECK);
|
||||
yield return new("Once at startup", UpdateBehavior.ONCE_STARTUP);
|
||||
yield return new("Check every hour", UpdateBehavior.HOURLY);
|
||||
yield return new("Check every day", UpdateBehavior.DAILY);
|
||||
yield return new ("Check every week", UpdateBehavior.WEEKLY);
|
||||
}
|
||||
|
||||
public static IEnumerable<ConfigurationSelectData<WorkspaceStorageBehavior>> GetWorkspaceStorageBehaviorData()
|
||||
{
|
||||
yield return new("Disable workspaces", WorkspaceStorageBehavior.DISABLE_WORKSPACES);
|
||||
yield return new("Store chats automatically", WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY);
|
||||
yield return new("Store chats manually", WorkspaceStorageBehavior.STORE_CHATS_MANUALLY);
|
||||
}
|
||||
|
||||
public static IEnumerable<ConfigurationSelectData<WorkspaceStorageTemporaryMaintenancePolicy>> GetWorkspaceStorageTemporaryMaintenancePolicyData()
|
||||
{
|
||||
yield return new("No automatic maintenance for temporary chats", WorkspaceStorageTemporaryMaintenancePolicy.NO_AUTOMATIC_MAINTENANCE);
|
||||
yield return new("Delete temporary chats older than 7 days", WorkspaceStorageTemporaryMaintenancePolicy.DELETE_OLDER_THAN_7_DAYS);
|
||||
yield return new("Delete temporary chats older than 30 days", WorkspaceStorageTemporaryMaintenancePolicy.DELETE_OLDER_THAN_30_DAYS);
|
||||
yield return new("Delete temporary chats older than 90 days", WorkspaceStorageTemporaryMaintenancePolicy.DELETE_OLDER_THAN_90_DAYS);
|
||||
yield return new("Delete temporary chats older than 180 days", WorkspaceStorageTemporaryMaintenancePolicy.DELETE_OLDER_THAN_180_DAYS);
|
||||
yield return new("Delete temporary chats older than 1 year", WorkspaceStorageTemporaryMaintenancePolicy.DELETE_OLDER_THAN_365_DAYS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
@typeparam T
|
||||
@inherits ConfigurationBase
|
||||
|
||||
<MudField Label="@this.OptionDescription" Variant="Variant.Outlined" Class="mb-3" Disabled="@this.Disabled()">
|
||||
<MudSlider T="@T" Size="Size.Medium" Value="@this.Value()" ValueChanged="@this.OptionChanged" Min="@this.Min" Max="@this.Max" Step="@this.Step" Immediate="@true" Disabled="@this.Disabled()">
|
||||
@this.Value() @this.Unit
|
||||
</MudSlider>
|
||||
</MudField>
|
||||
@@ -0,0 +1,76 @@
|
||||
using System.Numerics;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class ConfigurationSlider<T> : ConfigurationBase where T : struct, INumber<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// The minimum value for the slider.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public T Min { get; set; } = T.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum value for the slider.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public T Max { get; set; } = T.One;
|
||||
|
||||
/// <summary>
|
||||
/// The step size for the slider.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public T Step { get; set; } = T.One;
|
||||
|
||||
/// <summary>
|
||||
/// The unit to display next to the slider's value.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string Unit { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The value used for the slider.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Func<T> Value { get; set; } = () => T.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// An action which is called when the option is changed.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Action<T> ValueUpdate { get; set; } = _ => { };
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await this.EnsureMinMax();
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await this.EnsureMinMax();
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private async Task OptionChanged(T updatedValue)
|
||||
{
|
||||
this.ValueUpdate(updatedValue);
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.InformAboutChange();
|
||||
}
|
||||
|
||||
private async Task EnsureMinMax()
|
||||
{
|
||||
if (this.Value() < this.Min)
|
||||
await this.OptionChanged(this.Min);
|
||||
|
||||
else if(this.Value() > this.Max)
|
||||
await this.OptionChanged(this.Max);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
@inherits ConfigurationBase
|
||||
|
||||
<MudTextField
|
||||
T="string"
|
||||
Text="@this.Text()"
|
||||
TextChanged="@this.InternalUpdate"
|
||||
Label="@this.OptionDescription"
|
||||
Disabled="@this.Disabled()"
|
||||
Class="@MARGIN_CLASS"
|
||||
Adornment="Adornment.Start"
|
||||
AdornmentIcon="@this.Icon"
|
||||
AdornmentColor="@this.IconColor"
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
Lines="@this.NumLines"
|
||||
AutoGrow="@this.AutoGrow"
|
||||
MaxLines="@this.GetMaxLines"
|
||||
Immediate="@true"
|
||||
Variant="Variant.Outlined"
|
||||
/>
|
||||
@@ -0,0 +1,88 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class ConfigurationText : ConfigurationBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The text used for the textfield.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Func<string> Text { get; set; } = () => string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// An action which is called when the text was changed.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Action<string> TextUpdate { get; set; } = _ => { };
|
||||
|
||||
/// <summary>
|
||||
/// The icon to display next to the textfield.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string Icon { get; set; } = Icons.Material.Filled.Info;
|
||||
|
||||
/// <summary>
|
||||
/// The color of the icon to use.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Color IconColor { get; set; } = Color.Default;
|
||||
|
||||
/// <summary>
|
||||
/// How many lines should the textfield have?
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public int NumLines { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// What is the maximum number of lines?
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public int MaxLines { get; set; } = 12;
|
||||
|
||||
private string internalText = string.Empty;
|
||||
private Timer timer = new(TimeSpan.FromMilliseconds(500))
|
||||
{
|
||||
AutoReset = false
|
||||
};
|
||||
|
||||
#region Overrides of ConfigurationBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
this.timer.Elapsed += async (_, _) => await this.InvokeAsync(async () => await this.OptionChanged(this.internalText));
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
this.internalText = this.Text();
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
private bool AutoGrow => this.NumLines > 1;
|
||||
|
||||
private int GetMaxLines => this.AutoGrow ? this.MaxLines : 1;
|
||||
|
||||
private void InternalUpdate(string text)
|
||||
{
|
||||
this.timer.Stop();
|
||||
this.internalText = text;
|
||||
this.timer.Start();
|
||||
}
|
||||
|
||||
private async Task OptionChanged(string updatedText)
|
||||
{
|
||||
this.TextUpdate(updatedText);
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.InformAboutChange();
|
||||
}
|
||||
}
|
||||
Loaded 100 of 432 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user