mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-20 22:17:59 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
311092ec5e | ||
|
|
009bb33d83 | ||
|
|
8b0ee49ba9 | ||
|
|
45ac3eee2b | ||
|
|
0b6c2f1041 | ||
|
|
70fbc74a1d | ||
|
|
bac0b49dce | ||
|
|
cfb7fe403d | ||
|
|
164b07c629 | ||
|
|
473bfd068d | ||
|
|
120d5c808a | ||
|
|
9ef1a047f0 | ||
|
|
d6f5dc143d | ||
|
|
d90a82de64 | ||
|
|
9418b99275 | ||
|
|
37bd42e41f | ||
|
|
38103121c9 | ||
|
|
9587a07556 | ||
|
|
c3bbcacaed | ||
|
|
f00722e0a6 | ||
|
|
ce243913ed | ||
|
|
fc37882773 | ||
|
|
864bda1213 | ||
|
|
73b2f1fa6a | ||
|
|
d966929dca | ||
|
|
6a37ce6d25 | ||
|
|
70e64f2d14 | ||
|
|
38ec098430 | ||
|
|
4e167d58ea | ||
|
|
f61fe87181 | ||
|
|
2a01d3bb6e | ||
|
|
adf61a1384 | ||
|
|
f1c2a65450 | ||
|
|
e03289b680 | ||
|
|
99dc2606aa | ||
|
|
e931e28ab1 | ||
|
|
5d17dcc29b | ||
|
|
c1826426d9 | ||
|
|
be11efed67 | ||
|
|
7d10bb00f7 | ||
|
|
9ca2860079 | ||
|
|
28b470f40b | ||
|
|
6b61f34fff | ||
|
|
4197d5a0ee | ||
|
|
22aacff717 | ||
|
|
b75d90b13f | ||
|
|
fe2baa8c00 | ||
|
|
7d22b77d47 | ||
|
|
96c1c0653d | ||
|
|
035412f7ff | ||
|
|
6116c03f7c | ||
|
|
9c52812c09 | ||
|
|
be27fd84b8 | ||
|
|
61ffddf1ec | ||
|
|
6d1ecb7678 | ||
|
|
68f5bb1512 | ||
|
|
aaedf667fe | ||
|
|
7df0b3e6e0 | ||
|
|
1b8ed286e7 | ||
|
|
87bb04e3c0 | ||
|
|
181ba07d2d | ||
|
|
3587f547b5 | ||
|
|
cf17c643a0 | ||
|
|
7843426057 | ||
|
|
4db31fd9cf | ||
|
|
18b2e22725 | ||
|
|
b6169e5bfb | ||
|
|
4aeb51c9f1 | ||
|
|
7c4ddf1164 | ||
|
|
793a9cae0a | ||
|
|
368fc9ce39 | ||
|
|
2ef563e9ab | ||
|
|
32e1ab977d |
No files matched your search
@@ -362,7 +362,10 @@ jobs:
|
||||
|
||||
$PDFIUM_URL = "https://github.com/bblanchon/pdfium-binaries/releases/download/chromium%2F$($env:PDFIUM_VERSION)/pdfium-$PDFIUM_FILE"
|
||||
Write-Host "Download $PDFIUM_URL ..."
|
||||
$TMP = New-TemporaryFile | Split-Path
|
||||
|
||||
# Create a unique temporary directory (not just a file)
|
||||
$TMP = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName())
|
||||
New-Item -ItemType Directory -Path $TMP -Force | Out-Null
|
||||
$ARCHIVE = Join-Path $TMP "pdfium.tgz"
|
||||
|
||||
Invoke-WebRequest -Uri $PDFIUM_URL -OutFile $ARCHIVE
|
||||
@@ -380,8 +383,15 @@ jobs:
|
||||
Copy-Item -Path $SRC -Destination $DEST -Force
|
||||
|
||||
Write-Host "Cleaning up ..."
|
||||
Remove-Item $ARCHIVE -Force
|
||||
Remove-Item $TMP -Recurse -Force
|
||||
Remove-Item $ARCHIVE -Force -ErrorAction SilentlyContinue
|
||||
|
||||
# Try to remove the temporary directory, but ignore errors if files are still in use
|
||||
try {
|
||||
Remove-Item $TMP -Recurse -Force -ErrorAction Stop
|
||||
Write-Host "Successfully cleaned up temporary directory: $TMP"
|
||||
} catch {
|
||||
Write-Warning "Could not fully clean up temporary directory: $TMP. This is usually harmless as Windows will clean it up later. Error: $($_.Exception.Message)"
|
||||
}
|
||||
|
||||
- name: Build .NET project
|
||||
run: |
|
||||
|
||||
@@ -156,3 +156,6 @@ orleans.codegen.cs
|
||||
**/.idea/**/uiDesigner.xml
|
||||
**/.idea/**/dbnavigator.xml
|
||||
**/.vs
|
||||
|
||||
# Ignore AI plugin config files:
|
||||
/app/.idea/.idea.MindWork AI Studio/.idea/AugmentWebviewStateStore.xml
|
||||
@@ -0,0 +1,17 @@
|
||||
# Project Guidelines
|
||||
|
||||
## Repository Structure
|
||||
- The repository and the app consist of a Rust project in the `runtime` folder and a .NET solution in the `app` folder.
|
||||
- The .NET solution then contains 4 .NET projects:
|
||||
- `Build Script` is not required for running the app; instead, it contains the build script for creating new releases, for example.
|
||||
- `MindWork AI Studio` contains the actual app code.
|
||||
- `SharedTools` contains types that are needed in the build script and in the app, for example.
|
||||
- `SourceCodeRules` is a Roslyn analyzer project. It contains analyzers and code fixes that we use to enforce code style rules within the team.
|
||||
|
||||
## Changelogs
|
||||
- There is a changelog in Markdown format for each version.
|
||||
- All changelogs are located in the folder `app/MindWork AI Studio/wwwroot/changelog`.
|
||||
- These changelogs are intended for end users, not for developers.
|
||||
- Therefore, we don't mention all changes in the changelog: changes that end users wouldn't understand remain unmentioned. For complex refactorings, for example, we mention a generic point that the code quality has been improved to enhance future maintenance.
|
||||
- The changelog is always written in US English.
|
||||
- The changelog doesn't mention bug fixes if the bug was never shipped and users don't know about it.
|
||||
@@ -1,52 +1,86 @@
|
||||
# MindWork AI Studio
|
||||
<img src="app/MindWork%20AI%20Studio/wwwroot/svg/banner.svg" alt="MindWork AI Studio Banner"/>
|
||||
|
||||
Are you new here? [Read here](#what-is-ai-studio) what AI Studio is.
|
||||
|
||||
## News
|
||||
Things we are currently working on:
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style="display:inline-block">
|
||||
Things we are currently working on
|
||||
</h3>
|
||||
</summary>
|
||||
|
||||
- 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:
|
||||
<details>
|
||||
<summary>
|
||||
<h4 style="display:inline-block">
|
||||
RAG (Retrieval-Augmented Generation)
|
||||
</h4>
|
||||
</summary>
|
||||
|
||||
- [x] ~~Runtime: Restructuring the code into meaningful modules (PR [#192](https://github.com/MindWorkAI/AI-Studio/pull/192))~~
|
||||
- [x] ~~Define the [External Retrieval Interface (ERI)](https://github.com/MindWorkAI/ERI) as a contract for integrating arbitrary external data (PR [#1](https://github.com/MindWorkAI/ERI/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))~~
|
||||
- [x] ~~App: Implement an [ERI](https://github.com/MindWorkAI/ERI) server coding assistant (PR [#231](https://github.com/MindWorkAI/AI-Studio/pull/231))~~
|
||||
- [x] ~~App: Management of data sources (local & external data via [ERI](https://github.com/MindWorkAI/ERI)) (PR [#259](https://github.com/MindWorkAI/AI-Studio/pull/259), [#273](https://github.com/MindWorkAI/AI-Studio/pull/273))~~
|
||||
- [x] ~~Runtime: Extract data from txt / md / pdf / docx / xlsx files (PR [#374](https://github.com/MindWorkAI/AI-Studio/pull/374))~~
|
||||
- [ ] (*Optional*) Runtime: Implement internal embedding provider through [fastembed-rs](https://github.com/Anush008/fastembed-rs)
|
||||
- [x] ~~App: Implement dialog for checking & handling [pandoc](https://pandoc.org/) installation ([PR #393](https://github.com/MindWorkAI/AI-Studio/pull/393), [PR #487](https://github.com/MindWorkAI/AI-Studio/pull/487))~~
|
||||
- [ ] App: Implement external embedding providers
|
||||
- [ ] App: Implement 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
|
||||
- [x] ~~App: Define a common retrieval context interface for the integration of RAG processes in chats (PR [#281](https://github.com/MindWorkAI/AI-Studio/pull/281), [#284](https://github.com/MindWorkAI/AI-Studio/pull/284), [#286](https://github.com/MindWorkAI/AI-Studio/pull/286), [#287](https://github.com/MindWorkAI/AI-Studio/pull/287))~~
|
||||
- [x] ~~App: Define a common augmentation interface for the integration of RAG processes in chats (PR [#288](https://github.com/MindWorkAI/AI-Studio/pull/288), [#289](https://github.com/MindWorkAI/AI-Studio/pull/289))~~
|
||||
- [x] ~~App: Integrate data sources in chats (PR [#282](https://github.com/MindWorkAI/AI-Studio/pull/282))~~
|
||||
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 Retrieval Interface (ERI)](https://github.com/MindWorkAI/ERI) as a contract for integrating arbitrary external data (PR [#1](https://github.com/MindWorkAI/ERI/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))~~
|
||||
- [x] ~~App: Implement an [ERI](https://github.com/MindWorkAI/ERI) server coding assistant (PR [#231](https://github.com/MindWorkAI/AI-Studio/pull/231))~~
|
||||
- [x] ~~App: Management of data sources (local & external data via [ERI](https://github.com/MindWorkAI/ERI)) (PR [#259](https://github.com/MindWorkAI/AI-Studio/pull/259), [#273](https://github.com/MindWorkAI/AI-Studio/pull/273))~~
|
||||
- [x] ~~Runtime: Extract data from txt / md / pdf / docx / xlsx files (PR [#374](https://github.com/MindWorkAI/AI-Studio/pull/374))~~
|
||||
- [ ] (*Optional*) Runtime: Implement internal embedding provider through [fastembed-rs](https://github.com/Anush008/fastembed-rs)
|
||||
- [x] ~~App: Implement dialog for checking & handling [pandoc](https://pandoc.org/) installation ([PR #393](https://github.com/MindWorkAI/AI-Studio/pull/393), [PR #487](https://github.com/MindWorkAI/AI-Studio/pull/487))~~
|
||||
- [ ] App: Implement external embedding providers
|
||||
- [ ] App: Implement 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
|
||||
- [x] ~~App: Define a common retrieval context interface for the integration of RAG processes in chats (PR [#281](https://github.com/MindWorkAI/AI-Studio/pull/281), [#284](https://github.com/MindWorkAI/AI-Studio/pull/284), [#286](https://github.com/MindWorkAI/AI-Studio/pull/286), [#287](https://github.com/MindWorkAI/AI-Studio/pull/287))~~
|
||||
- [x] ~~App: Define a common augmentation interface for the integration of RAG processes in chats (PR [#288](https://github.com/MindWorkAI/AI-Studio/pull/288), [#289](https://github.com/MindWorkAI/AI-Studio/pull/289))~~
|
||||
- [x] ~~App: Integrate data sources in chats (PR [#282](https://github.com/MindWorkAI/AI-Studio/pull/282))~~
|
||||
|
||||
- Since September 2024: Experiments have been started on how we can work on long texts with AI Studio. Let's say you want to write a fantasy novel or create a complex project proposal and use LLM for support. The initial experiments were promising, but not yet satisfactory. We are testing further approaches until a satisfactory solution is found. The current state of our experiment is available as an experimental preview feature through your app configuration. Related PR: ~~[PR #167](https://github.com/MindWorkAI/AI-Studio/pull/167), [PR #226](https://github.com/MindWorkAI/AI-Studio/pull/226)~~, [PR #376](https://github.com/MindWorkAI/AI-Studio/pull/376).
|
||||
</details>
|
||||
|
||||
- Since March 2025: We have started developing the plugin system. There will be language plugins to offer AI Studio in other languages, configuration plugins to centrally manage certain providers and rules within an organization, and assistant plugins that allow anyone to develop their own assistants. We are using Lua as the plugin language:
|
||||
- [x] ~~Plan & implement the base plugin system ([PR #322](https://github.com/MindWorkAI/AI-Studio/pull/322))~~
|
||||
- [x] ~~Start the plugin system ([PR #372](https://github.com/MindWorkAI/AI-Studio/pull/372))~~
|
||||
- [x] ~~Added hot-reload support for plugins ([PR #377](https://github.com/MindWorkAI/AI-Studio/pull/377), [PR #391](https://github.com/MindWorkAI/AI-Studio/pull/391))~~
|
||||
- [x] ~~Add support for other languages (I18N) to AI Studio ([PR #381](https://github.com/MindWorkAI/AI-Studio/pull/381), [PR #400](https://github.com/MindWorkAI/AI-Studio/pull/400), [PR #404](https://github.com/MindWorkAI/AI-Studio/pull/404), [PR #429](https://github.com/MindWorkAI/AI-Studio/pull/429), [PR #446](https://github.com/MindWorkAI/AI-Studio/pull/446), [PR #451](https://github.com/MindWorkAI/AI-Studio/pull/451), [PR #455](https://github.com/MindWorkAI/AI-Studio/pull/455), [PR #458](https://github.com/MindWorkAI/AI-Studio/pull/458), [PR #462](https://github.com/MindWorkAI/AI-Studio/pull/462), [PR #469](https://github.com/MindWorkAI/AI-Studio/pull/469), [PR #486](https://github.com/MindWorkAI/AI-Studio/pull/486))~~
|
||||
- [x] ~~Add an I18N assistant to translate all AI Studio texts to a certain language & culture ([PR #422](https://github.com/MindWorkAI/AI-Studio/pull/422))~~
|
||||
- [x] ~~Provide MindWork AI Studio in German ([PR #430](https://github.com/MindWorkAI/AI-Studio/pull/430), [PR #446](https://github.com/MindWorkAI/AI-Studio/pull/446), [PR #451](https://github.com/MindWorkAI/AI-Studio/pull/451), [PR #455](https://github.com/MindWorkAI/AI-Studio/pull/455), [PR #458](https://github.com/MindWorkAI/AI-Studio/pull/458), [PR #462](https://github.com/MindWorkAI/AI-Studio/pull/462), [PR #469](https://github.com/MindWorkAI/AI-Studio/pull/469), [PR #486](https://github.com/MindWorkAI/AI-Studio/pull/486))~~
|
||||
- [x] ~~Add configuration plugins, which allow pre-defining some LLM providers in organizations ([PR #491](https://github.com/MindWorkAI/AI-Studio/pull/491), [PR #493](https://github.com/MindWorkAI/AI-Studio/pull/493), [PR #494](https://github.com/MindWorkAI/AI-Studio/pull/494))~~
|
||||
- [ ] Add an app store for plugins, showcasing community-contributed plugins from public GitHub and GitLab repositories. This will enable AI Studio users to discover, install, and update plugins directly within the platform.
|
||||
- [ ] Add assistant plugins
|
||||
<details>
|
||||
<summary>
|
||||
<h4 style="display:inline-block">
|
||||
Writer Mode
|
||||
</h4>
|
||||
</summary>
|
||||
|
||||
Other News:
|
||||
Since September 2024: Experiments have been started on how we can work on long texts with AI Studio. Let's say you want to write a fantasy novel or create a complex project proposal and use LLM for support. The initial experiments were promising, but not yet satisfactory. We are testing further approaches until a satisfactory solution is found. The current state of our experiment is available as an experimental preview feature through your app configuration. Related PR: ~~[PR #167](https://github.com/MindWorkAI/AI-Studio/pull/167), [PR #226](https://github.com/MindWorkAI/AI-Studio/pull/226)~~, [PR #376](https://github.com/MindWorkAI/AI-Studio/pull/376).
|
||||
|
||||
- April 2025: We have two active financial supporters: Peer `peerschuett` and Dominic `donework`. Thank you very much for your support. MindWork AI reinvests these donations by passing them on to our AI Studio dependencies ([see here](https://github.com/orgs/MindWorkAI/sponsoring)). In the event that we receive large donations, we will first sign the app ([#56](https://github.com/MindWorkAI/Planning/issues/56)). In case we receive more donations, we will look for and pay staff to develop features for AI Studio.
|
||||
</details>
|
||||
|
||||
- April 2025: The [German Aerospace Center (DLR)](https://en.wikipedia.org/wiki/German_Aerospace_Center) ([Website](https://www.dlr.de/en)) will use AI Studio at least within the scope of three projects and will also contribute to its further development. This is great news.
|
||||
<details>
|
||||
<summary>
|
||||
<h4 style="display:inline-block">
|
||||
Plugin System
|
||||
</h4>
|
||||
</summary>
|
||||
|
||||
Since March 2025: We have started developing the plugin system. There will be language plugins to offer AI Studio in other languages, configuration plugins to centrally manage certain providers and rules within an organization, and assistant plugins that allow anyone to develop their own assistants. We are using Lua as the plugin language:
|
||||
- [x] ~~Plan & implement the base plugin system ([PR #322](https://github.com/MindWorkAI/AI-Studio/pull/322))~~
|
||||
- [x] ~~Start the plugin system ([PR #372](https://github.com/MindWorkAI/AI-Studio/pull/372))~~
|
||||
- [x] ~~Added hot-reload support for plugins ([PR #377](https://github.com/MindWorkAI/AI-Studio/pull/377), [PR #391](https://github.com/MindWorkAI/AI-Studio/pull/391))~~
|
||||
- [x] ~~Add support for other languages (I18N) to AI Studio ([PR #381](https://github.com/MindWorkAI/AI-Studio/pull/381), [PR #400](https://github.com/MindWorkAI/AI-Studio/pull/400), [PR #404](https://github.com/MindWorkAI/AI-Studio/pull/404), [PR #429](https://github.com/MindWorkAI/AI-Studio/pull/429), [PR #446](https://github.com/MindWorkAI/AI-Studio/pull/446), [PR #451](https://github.com/MindWorkAI/AI-Studio/pull/451), [PR #455](https://github.com/MindWorkAI/AI-Studio/pull/455), [PR #458](https://github.com/MindWorkAI/AI-Studio/pull/458), [PR #462](https://github.com/MindWorkAI/AI-Studio/pull/462), [PR #469](https://github.com/MindWorkAI/AI-Studio/pull/469), [PR #486](https://github.com/MindWorkAI/AI-Studio/pull/486))~~
|
||||
- [x] ~~Add an I18N assistant to translate all AI Studio texts to a certain language & culture ([PR #422](https://github.com/MindWorkAI/AI-Studio/pull/422))~~
|
||||
- [x] ~~Provide MindWork AI Studio in German ([PR #430](https://github.com/MindWorkAI/AI-Studio/pull/430), [PR #446](https://github.com/MindWorkAI/AI-Studio/pull/446), [PR #451](https://github.com/MindWorkAI/AI-Studio/pull/451), [PR #455](https://github.com/MindWorkAI/AI-Studio/pull/455), [PR #458](https://github.com/MindWorkAI/AI-Studio/pull/458), [PR #462](https://github.com/MindWorkAI/AI-Studio/pull/462), [PR #469](https://github.com/MindWorkAI/AI-Studio/pull/469), [PR #486](https://github.com/MindWorkAI/AI-Studio/pull/486))~~
|
||||
- [x] ~~Add configuration plugins, which allow pre-defining some LLM providers in organizations ([PR #491](https://github.com/MindWorkAI/AI-Studio/pull/491), [PR #493](https://github.com/MindWorkAI/AI-Studio/pull/493), [PR #494](https://github.com/MindWorkAI/AI-Studio/pull/494), [PR #497](https://github.com/MindWorkAI/AI-Studio/pull/497))~~
|
||||
- [ ] Add an app store for plugins, showcasing community-contributed plugins from public GitHub and GitLab repositories. This will enable AI Studio users to discover, install, and update plugins directly within the platform.
|
||||
- [ ] Add assistant plugins
|
||||
|
||||
Features we have recently released:
|
||||
</details>
|
||||
</details>
|
||||
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 style="display:inline-block">
|
||||
Features we have recently released
|
||||
</h3>
|
||||
</summary>
|
||||
|
||||
- v0.9.51: Added support for [Perplexity](https://www.perplexity.ai/); citations added so that LLMs can provide source references (e.g., some OpenAI models, Perplexity); added support for OpenAI's Responses API so that all text LLMs from OpenAI now work in MindWork AI Studio, including Deep Research models; web searches are now possible (some OpenAI models, Perplexity).
|
||||
- v0.9.50: Added support for self-hosted LLMs using [vLLM](https://blog.vllm.ai/2023/06/20/vllm.html).
|
||||
- v0.9.46: Released our plugin system, a German language plugin, early support for enterprise environments, and configuration plugins. Additionally, we added the Pandoc integration for future data processing and file generation.
|
||||
- v0.9.45: Added chat templates to AI Studio, allowing you to create and use a library of system prompts for your chats.
|
||||
- v0.9.44: Added PDF import to the text summarizer, translation, and legal check assistants, allowing you to import PDF files and use them as input for the assistants.
|
||||
@@ -57,7 +91,8 @@ Features we have recently released:
|
||||
- v0.9.26+: Added RAG for external data sources using our [ERI interface](https://mindworkai.org/#eri---external-retrieval-interface) as a preview feature.
|
||||
- v0.9.25: Added [xAI](https://x.ai/) as a new provider. xAI provides their Grok models for generating content.
|
||||
- v0.9.23: Added support for OpenAI `o` models (`o1`, `o1-mini`, `o3`, etc.); added also an [ERI](https://github.com/MindWorkAI/ERI) server coding assistant as a preview feature behind the RAG feature flag. Your own ERI server can be used to gain access to, e.g., your enterprise data from within AI Studio.
|
||||
- v0.9.22: Added options for preview features; added embedding provider configuration for RAG (preview) and writer mode (experimental preview).
|
||||
|
||||
</details>
|
||||
|
||||
## What is AI Studio?
|
||||
|
||||
@@ -71,7 +106,8 @@ MindWork AI Studio is a free desktop app for macOS, Windows, and Linux. It provi
|
||||
**Key advantages:**
|
||||
- **Free of charge**: The app is free to use, both for personal and commercial purposes.
|
||||
- **Independence**: You are not tied to any single provider. Instead, you can choose the providers that best suit your needs. Right now, we support:
|
||||
- [OpenAI](https://openai.com/) (GPT4o, GPT4.1, o1, o3, o4, etc.)
|
||||
- [OpenAI](https://openai.com/) (GPT5, GPT4.1, o1, o3, o4, etc.)
|
||||
- [Perplexity](https://www.perplexity.ai/)
|
||||
- [Mistral](https://mistral.ai/)
|
||||
- [Anthropic](https://www.anthropic.com/) (Claude)
|
||||
- [Google Gemini](https://gemini.google.com)
|
||||
@@ -79,7 +115,7 @@ MindWork AI Studio is a free desktop app for macOS, Windows, and Linux. It provi
|
||||
- [DeepSeek](https://www.deepseek.com/en)
|
||||
- [Alibaba Cloud](https://www.alibabacloud.com) (Qwen)
|
||||
- [Hugging Face](https://huggingface.co/) using their [inference providers](https://huggingface.co/docs/inference-providers/index) such as Cerebras, Nebius, Sambanova, Novita, Hyperbolic, Together AI, Fireworks, Hugging Face
|
||||
- Self-hosted models using [llama.cpp](https://github.com/ggerganov/llama.cpp), [ollama](https://github.com/ollama/ollama), [LM Studio](https://lmstudio.ai/)
|
||||
- Self-hosted models using [llama.cpp](https://github.com/ggerganov/llama.cpp), [ollama](https://github.com/ollama/ollama), [LM Studio](https://lmstudio.ai/), and [vLLM](https://github.com/vllm-project/vllm)
|
||||
- [Groq](https://groq.com/)
|
||||
- [Fireworks](https://fireworks.ai/)
|
||||
- For scientists and employees of research institutions, we also support [Helmholtz](https://helmholtz.cloud/services/?serviceID=d7d5c597-a2f6-4bd1-b71e-4d6499d98570) and [GWDG](https://gwdg.de/services/application-services/ai-services/) AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
|
||||
@@ -92,7 +128,13 @@ MindWork AI Studio is a free desktop app for macOS, Windows, and Linux. It provi
|
||||
|
||||
## **Ready to get started 🤩?** [Download the appropriate setup for your operating system here](documentation/Setup.md).
|
||||
|
||||
## Support the Project
|
||||
<details>
|
||||
<summary>
|
||||
<h2 style="display:inline-block">
|
||||
Support the Project
|
||||
</h2>
|
||||
</summary>
|
||||
|
||||
Thank you for using MindWork AI Studio and considering supporting its development 😀. Your support helps keep the project alive and ensures continuous improvements and new features.
|
||||
|
||||
We offer various ways you can support the project:
|
||||
@@ -106,7 +148,15 @@ For companies, sponsoring MindWork AI Studio is not only a way to support innova
|
||||
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
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h2 style="display:inline-block">
|
||||
Planned Features
|
||||
</h2>
|
||||
</summary>
|
||||
|
||||
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'll be able to integrate your data into AI Studio, like your PDF or Office files, or your Markdown notes.
|
||||
- **Integration of enterprise data:** It will soon be possible to integrate data from the corporate network using a specified interface ([External Retrieval Interface](https://github.com/MindWorkAI/ERI), ERI for short). This will likely require development work by the organization in question.
|
||||
@@ -120,14 +170,45 @@ Here's an exciting look at some of the features we're planning to add to AI Stud
|
||||
|
||||
Stay tuned for more updates and enhancements to make MindWork AI Studio even more powerful and versatile 🤩.
|
||||
|
||||
## Building
|
||||
If you're interested in learning more about future plans, check out our [roadmap](https://github.com/orgs/MindWorkAI/projects/2/views/3) and our [planning issues](https://github.com/MindWorkAI/Planning/issues).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h2 style="display:inline-block">
|
||||
Building
|
||||
</h2>
|
||||
</summary>
|
||||
|
||||
You want to know how to build MindWork AI Studio from source? [Check out the instructions here](documentation/Build.md).
|
||||
|
||||
## License
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h2 style="display:inline-block">
|
||||
Enterprise IT
|
||||
</h2>
|
||||
</summary>
|
||||
|
||||
Do you want to manage AI Studio centrally from your IT department? Yes, that’s possible. [Here’s how it works.](documentation/Enterprise%20IT.md)
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h2 style="display:inline-block">
|
||||
License
|
||||
</h2>
|
||||
</summary>
|
||||
|
||||
MindWork AI Studio is licensed under the `FSL-1.1-MIT` license (functional source license). Here’s a simple rundown of what that means for you:
|
||||
- **Permitted Use**: Feel free to use, copy, modify, and share the software for your own projects, educational purposes, research, or even in professional services. The key is to use it in a way that doesn't compete with our offerings.
|
||||
- **Competing Use**: Our only request is that you don't create commercial products or services that replace or compete with MindWork AI Studio or any of our other offerings.
|
||||
- **No Warranties**: The software is provided "as is", without any promises from us about it working perfectly for your needs. While we strive to make it great, we can't guarantee it will be free of bugs or issues.
|
||||
- **Future License**: Good news! The license for each release of MindWork AI Studio will automatically convert to an MIT license two years from its release date. This makes it even easier for you to use the software in the future.
|
||||
|
||||
For more details, refer to the [LICENSE](LICENSE.md) file. This license structure ensures you have plenty of freedom to use and enjoy the software while protecting our work.
|
||||
For more details, refer to the [LICENSE](LICENSE.md) file. This license structure ensures you have plenty of freedom to use and enjoy the software while protecting our work.
|
||||
|
||||
</details>
|
||||
@@ -336,8 +336,9 @@ public sealed partial class UpdateMetadataCommands
|
||||
await File.WriteAllLinesAsync(pathMetadata, lines, Environment.UTF8_NO_BOM);
|
||||
Console.WriteLine(" done.");
|
||||
}
|
||||
|
||||
private async Task UpdateProjectCommitHash()
|
||||
|
||||
[Command("update-project-hash", Description = "Update the project commit hash")]
|
||||
public async Task UpdateProjectCommitHash()
|
||||
{
|
||||
const int COMMIT_HASH_INDEX = 8;
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ public sealed class UpdateWebAssetsCommand
|
||||
var rid = Environment.GetCurrentRid();
|
||||
var cwd = Environment.GetAIStudioDirectory();
|
||||
var contentPath = Path.Join(cwd, "bin", "release", Environment.DOTNET_VERSION, rid.AsMicrosoftRid(), "publish", "wwwroot", "_content");
|
||||
|
||||
var isMudBlazorDirectoryPresent = Directory.Exists(Path.Join(contentPath, "MudBlazor"));
|
||||
if (!isMudBlazorDirectoryPresent)
|
||||
{
|
||||
@@ -28,14 +29,21 @@ public sealed class UpdateWebAssetsCommand
|
||||
return;
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(Path.Join(cwd, "wwwroot", "system"));
|
||||
var destinationPath = Path.Join(cwd, "wwwroot", "system");
|
||||
if(Directory.Exists(destinationPath))
|
||||
Directory.Delete(destinationPath, true);
|
||||
|
||||
Directory.CreateDirectory(destinationPath);
|
||||
|
||||
var sourcePaths = Directory.EnumerateFiles(contentPath, "*", SearchOption.AllDirectories);
|
||||
var counter = 0;
|
||||
foreach(var sourcePath in sourcePaths)
|
||||
{
|
||||
counter++;
|
||||
var relativePath = Path.GetRelativePath(cwd, sourcePath);
|
||||
var targetPath = Path.Join(cwd, "wwwroot", relativePath);
|
||||
var relativePath = sourcePath
|
||||
.Replace(contentPath, "")
|
||||
.TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
var targetPath = Path.Join(cwd, "wwwroot", "system", relativePath);
|
||||
var targetDirectory = Path.GetDirectoryName(targetPath);
|
||||
if (targetDirectory != null)
|
||||
Directory.CreateDirectory(targetDirectory);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FNV/@EntryIndexedValue">FNV</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GWDG/@EntryIndexedValue">GWDG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HF/@EntryIndexedValue">HF</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IERI/@EntryIndexedValue">IERI</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>
|
||||
@@ -20,6 +21,7 @@
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=groq/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=gwdg/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=huggingface/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=ieri/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=mwais/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=ollama/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=tauri_0027s/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@@ -54,7 +54,7 @@ public abstract class AgentBase(ILogger<AgentBase> logger, SettingsManager setti
|
||||
|
||||
#region Implementation of IAgent
|
||||
|
||||
public abstract AIStudio.Settings.Provider? ProviderSettings { get; set; }
|
||||
public abstract AIStudio.Settings.Provider ProviderSettings { get; set; }
|
||||
|
||||
public abstract Task<ChatThread> ProcessContext(ChatThread chatThread, IDictionary<string, string> additionalData);
|
||||
|
||||
@@ -73,7 +73,6 @@ public abstract class AgentBase(ILogger<AgentBase> logger, SettingsManager setti
|
||||
WorkspaceId = Guid.Empty,
|
||||
ChatId = Guid.NewGuid(),
|
||||
Name = string.Empty,
|
||||
Seed = this.RNG.Next(),
|
||||
SystemPrompt = systemPrompt,
|
||||
Blocks = [],
|
||||
};
|
||||
@@ -103,10 +102,9 @@ public abstract class AgentBase(ILogger<AgentBase> logger, SettingsManager setti
|
||||
|
||||
protected async Task AddAIResponseAsync(ChatThread thread, IContent lastUserPrompt, DateTimeOffset time)
|
||||
{
|
||||
if(this.ProviderSettings is null)
|
||||
if(this.ProviderSettings == Settings.Provider.NONE)
|
||||
return;
|
||||
|
||||
var providerSettings = this.ProviderSettings.Value;
|
||||
var aiText = new ContentText
|
||||
{
|
||||
// We have to wait for the remote
|
||||
@@ -127,6 +125,6 @@ public abstract class AgentBase(ILogger<AgentBase> logger, SettingsManager setti
|
||||
// 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), providerSettings.Model, lastUserPrompt, thread);
|
||||
await aiText.CreateFromProviderAsync(this.ProviderSettings.CreateProvider(), this.ProviderSettings.Model, lastUserPrompt, thread);
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ public sealed class AgentDataSourceSelection (ILogger<AgentDataSourceSelection>
|
||||
""";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Settings.Provider? ProviderSettings { get; set; }
|
||||
public override Settings.Provider ProviderSettings { get; set; } = Settings.Provider.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// The data source selection agent does not work with context. Use
|
||||
@@ -131,7 +131,7 @@ public sealed class AgentDataSourceSelection (ILogger<AgentDataSourceSelection>
|
||||
|
||||
#endregion
|
||||
|
||||
public async Task<List<SelectedDataSource>> PerformSelectionAsync(IProvider provider, IContent lastPrompt, ChatThread chatThread, AllowedSelectedDataSources dataSources, CancellationToken token = default)
|
||||
public async Task<List<SelectedDataSource>> PerformSelectionAsync(IProvider provider, IContent lastUserPrompt, ChatThread chatThread, AllowedSelectedDataSources dataSources, CancellationToken token = default)
|
||||
{
|
||||
logger.LogInformation("The AI should select the appropriate data sources.");
|
||||
|
||||
@@ -141,6 +141,11 @@ public sealed class AgentDataSourceSelection (ILogger<AgentDataSourceSelection>
|
||||
|
||||
// We start with the provider currently selected by the user:
|
||||
var agentProvider = this.SettingsManager.GetPreselectedProvider(Tools.Components.AGENT_DATA_SOURCE_SELECTION, provider.Id, true);
|
||||
if (agentProvider == Settings.Provider.NONE)
|
||||
{
|
||||
logger.LogWarning("No provider is selected for the agent. The agent cannot select data sources.");
|
||||
return [];
|
||||
}
|
||||
|
||||
// Assign the provider settings to the agent:
|
||||
logger.LogInformation($"The agent for the data source selection uses the provider '{agentProvider.InstanceName}' ({agentProvider.UsedLLMProvider.ToName()}, confidence={agentProvider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level.GetName()}).");
|
||||
@@ -149,7 +154,7 @@ public sealed class AgentDataSourceSelection (ILogger<AgentDataSourceSelection>
|
||||
//
|
||||
// 2. Prepare the current system and user prompts as input for the agent:
|
||||
//
|
||||
var lastPromptContent = lastPrompt switch
|
||||
var lastPromptContent = lastUserPrompt switch
|
||||
{
|
||||
ContentText text => text.Text,
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ public sealed class AgentRetrievalContextValidation (ILogger<AgentRetrievalConte
|
||||
""";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Settings.Provider? ProviderSettings { get; set; }
|
||||
public override Settings.Provider ProviderSettings { get; set; } = Settings.Provider.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// The retrieval context validation agent does not work with context. Use
|
||||
@@ -133,6 +133,11 @@ public sealed class AgentRetrievalContextValidation (ILogger<AgentRetrievalConte
|
||||
{
|
||||
// We start with the provider currently selected by the user:
|
||||
var agentProvider = this.SettingsManager.GetPreselectedProvider(Tools.Components.AGENT_RETRIEVAL_CONTEXT_VALIDATION, provider.Id, true);
|
||||
if (agentProvider == Settings.Provider.NONE)
|
||||
{
|
||||
logger.LogWarning("No provider is selected for the agent.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Assign the provider settings to the agent:
|
||||
logger.LogInformation($"The agent for the retrieval context validation uses the provider '{agentProvider.InstanceName}' ({agentProvider.UsedLLMProvider.ToName()}, confidence={agentProvider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level.GetName()}).");
|
||||
@@ -142,12 +147,12 @@ public sealed class AgentRetrievalContextValidation (ILogger<AgentRetrievalConte
|
||||
/// <summary>
|
||||
/// Validate all retrieval contexts against the last user and the system prompt.
|
||||
/// </summary>
|
||||
/// <param name="lastPrompt">The last user prompt.</param>
|
||||
/// <param name="lastUserPrompt">The last user prompt.</param>
|
||||
/// <param name="chatThread">The chat thread.</param>
|
||||
/// <param name="retrievalContexts">All retrieval contexts to validate.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The validation results.</returns>
|
||||
public async Task<IReadOnlyList<RetrievalContextValidationResult>> ValidateRetrievalContextsAsync(IContent lastPrompt, ChatThread chatThread, IReadOnlyList<IRetrievalContext> retrievalContexts, CancellationToken token = default)
|
||||
public async Task<IReadOnlyList<RetrievalContextValidationResult>> ValidateRetrievalContextsAsync(IContent lastUserPrompt, ChatThread chatThread, IReadOnlyList<IRetrievalContext> retrievalContexts, CancellationToken token = default)
|
||||
{
|
||||
// Check if the retrieval context validation is enabled:
|
||||
if (!this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.EnableRetrievalContextValidation)
|
||||
@@ -173,7 +178,7 @@ public sealed class AgentRetrievalContextValidation (ILogger<AgentRetrievalConte
|
||||
await semaphore.WaitAsync(token);
|
||||
|
||||
// Start the next validation task:
|
||||
validationTasks.Add(this.ValidateRetrievalContextAsync(lastPrompt, chatThread, retrievalContext, token, semaphore));
|
||||
validationTasks.Add(this.ValidateRetrievalContextAsync(lastUserPrompt, chatThread, retrievalContext, token, semaphore));
|
||||
}
|
||||
|
||||
// Wait for all validation tasks to complete:
|
||||
@@ -188,13 +193,13 @@ public sealed class AgentRetrievalContextValidation (ILogger<AgentRetrievalConte
|
||||
/// can call this method in parallel for each retrieval context. You might use
|
||||
/// the ValidateRetrievalContextsAsync method to validate all retrieval contexts.
|
||||
/// </remarks>
|
||||
/// <param name="lastPrompt">The last user prompt.</param>
|
||||
/// <param name="lastUserPrompt">The last user prompt.</param>
|
||||
/// <param name="chatThread">The chat thread.</param>
|
||||
/// <param name="retrievalContext">The retrieval context to validate.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <param name="semaphore">The optional semaphore to limit the number of parallel validations.</param>
|
||||
/// <returns>The validation result.</returns>
|
||||
public async Task<RetrievalContextValidationResult> ValidateRetrievalContextAsync(IContent lastPrompt, ChatThread chatThread, IRetrievalContext retrievalContext, CancellationToken token = default, SemaphoreSlim? semaphore = null)
|
||||
public async Task<RetrievalContextValidationResult> ValidateRetrievalContextAsync(IContent lastUserPrompt, ChatThread chatThread, IRetrievalContext retrievalContext, CancellationToken token = default, SemaphoreSlim? semaphore = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -209,7 +214,7 @@ public sealed class AgentRetrievalContextValidation (ILogger<AgentRetrievalConte
|
||||
//
|
||||
// 1. Prepare the current system and user prompts as input for the agent:
|
||||
//
|
||||
var lastPromptContent = lastPrompt switch
|
||||
var lastPromptContent = lastUserPrompt switch
|
||||
{
|
||||
ContentText text => text.Text,
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ public sealed class AgentTextContentCleaner(ILogger<AgentBase> logger, SettingsM
|
||||
|
||||
#region Overrides of AgentBase
|
||||
|
||||
public override AIStudio.Settings.Provider? ProviderSettings { get; set; }
|
||||
public override AIStudio.Settings.Provider ProviderSettings { get; set; } = AIStudio.Settings.Provider.NONE;
|
||||
|
||||
protected override Type Type => Type.SYSTEM;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ public interface IAgent
|
||||
/// <summary>
|
||||
/// The provider to use for this agent.
|
||||
/// </summary>
|
||||
public AIStudio.Settings.Provider? ProviderSettings { get; set; }
|
||||
public Settings.Provider ProviderSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Processes a chat thread (i.e., context) and returns the updated thread.
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Class="mb-2 mr-3" StretchItems="StretchItems.Start">
|
||||
<MudText Typo="Typo.h3">
|
||||
@(this.Title)
|
||||
@this.Title
|
||||
</MudText>
|
||||
|
||||
<MudIconButton Variant="Variant.Text" Icon="@Icons.Material.Filled.Settings" OnClick="() => this.OpenSettingsDialog()"/>
|
||||
<MudIconButton Variant="Variant.Text" Icon="@Icons.Material.Filled.Settings" OnClick="@(async () => await this.OpenSettingsDialog())"/>
|
||||
</MudStack>
|
||||
|
||||
<InnerScrolling>
|
||||
@@ -26,13 +26,13 @@
|
||||
</CascadingValue>
|
||||
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" StretchItems="StretchItems.Start" Class="mb-3">
|
||||
<MudButton Disabled="@this.SubmitDisabled" Variant="Variant.Filled" OnClick="async () => await this.Start()" Style="@this.SubmitButtonStyle">
|
||||
<MudButton Disabled="@this.SubmitDisabled" Variant="Variant.Filled" OnClick="@(async () => await this.Start())" Style="@this.SubmitButtonStyle">
|
||||
@this.SubmitText
|
||||
</MudButton>
|
||||
@if (this.isProcessing && this.cancellationTokenSource is not null)
|
||||
{
|
||||
<MudTooltip Text="@TB("Stop generation")">
|
||||
<MudIconButton Variant="Variant.Filled" Icon="@Icons.Material.Filled.Stop" Color="Color.Error" OnClick="() => this.CancelStreaming()"/>
|
||||
<MudIconButton Variant="Variant.Filled" Icon="@Icons.Material.Filled.Stop" Color="Color.Error" OnClick="@(async () => await this.CancelStreaming())"/>
|
||||
</MudTooltip>
|
||||
}
|
||||
</MudStack>
|
||||
@@ -71,7 +71,7 @@
|
||||
</div>
|
||||
</ChildContent>
|
||||
<FooterContent>
|
||||
<MudStack Row="@true" Wrap="Wrap.Wrap" Class="ma-1">
|
||||
<MudStack Row="@true" Wrap="Wrap.Wrap" AlignItems="AlignItems.Center" StretchItems="StretchItems.None" Class="ma-1">
|
||||
|
||||
@if (!this.FooterButtons.Any(x => x.Type is ButtonTypes.SEND_TO))
|
||||
{
|
||||
@@ -80,7 +80,7 @@
|
||||
<MudMenu AnchorOrigin="Origin.TopLeft" TransformOrigin="Origin.BottomLeft" StartIcon="@Icons.Material.Filled.Apps" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="@TB("Send to ...")" Variant="Variant.Filled" Style="@this.GetSendToColor()" Class="rounded">
|
||||
@foreach (var assistant in Enum.GetValues<Components>().Where(n => n.AllowSendTo()).OrderBy(n => n.Name().Length))
|
||||
{
|
||||
<MudMenuItem OnClick="() => this.SendToAssistant(assistant, new())">
|
||||
<MudMenuItem OnClick="@(async () => await this.SendToAssistant(assistant, new()))">
|
||||
@assistant.Name()
|
||||
</MudMenuItem>
|
||||
}
|
||||
@@ -94,14 +94,14 @@
|
||||
{
|
||||
case ButtonData buttonData when !string.IsNullOrWhiteSpace(buttonData.Tooltip):
|
||||
<MudTooltip Text="@buttonData.Tooltip">
|
||||
<MudButton Variant="Variant.Filled" Color="@buttonData.Color" Disabled="@buttonData.DisabledAction()" StartIcon="@GetButtonIcon(buttonData.Icon)" OnClick="async () => await buttonData.AsyncAction()">
|
||||
<MudButton Variant="Variant.Filled" Color="@buttonData.Color" Disabled="@buttonData.DisabledAction()" StartIcon="@GetButtonIcon(buttonData.Icon)" OnClick="@(async () => await buttonData.AsyncAction())">
|
||||
@buttonData.Text
|
||||
</MudButton>
|
||||
</MudTooltip>
|
||||
break;
|
||||
|
||||
case ButtonData buttonData:
|
||||
<MudButton Variant="Variant.Filled" Color="@buttonData.Color" Disabled="@buttonData.DisabledAction()" StartIcon="@GetButtonIcon(buttonData.Icon)" OnClick="async () => await buttonData.AsyncAction()">
|
||||
<MudButton Variant="Variant.Filled" Color="@buttonData.Color" Disabled="@buttonData.DisabledAction()" StartIcon="@GetButtonIcon(buttonData.Icon)" OnClick="@(async () => await buttonData.AsyncAction())">
|
||||
@buttonData.Text
|
||||
</MudButton>
|
||||
break;
|
||||
@@ -110,7 +110,7 @@
|
||||
<MudMenu AnchorOrigin="Origin.TopLeft" TransformOrigin="Origin.BottomLeft" StartIcon="@Icons.Material.Filled.Apps" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="@TB("Send to ...")" Variant="Variant.Filled" Style="@this.GetSendToColor()" Class="rounded">
|
||||
@foreach (var assistant in Enum.GetValues<Components>().Where(n => n.AllowSendTo()).OrderBy(n => n.Name().Length))
|
||||
{
|
||||
<MudMenuItem OnClick="() => this.SendToAssistant(assistant, sendToButton)">
|
||||
<MudMenuItem OnClick="@(async () => await this.SendToAssistant(assistant, sendToButton))">
|
||||
@assistant.Name()
|
||||
</MudMenuItem>
|
||||
}
|
||||
@@ -121,14 +121,14 @@
|
||||
|
||||
@if (this.ShowCopyResult)
|
||||
{
|
||||
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.ContentCopy" OnClick="() => this.CopyToClipboard()">
|
||||
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.ContentCopy" OnClick="@(async () => await this.CopyToClipboard())">
|
||||
@TB("Copy result")
|
||||
</MudButton>
|
||||
}
|
||||
|
||||
@if (this.ShowReset)
|
||||
{
|
||||
<MudButton Variant="Variant.Filled" Style="@this.GetResetColor()" StartIcon="@Icons.Material.Filled.Refresh" OnClick="() => this.InnerResetForm()">
|
||||
<MudButton Variant="Variant.Filled" Style="@this.GetResetColor()" StartIcon="@Icons.Material.Filled.Refresh" OnClick="@(async () => await this.InnerResetForm())">
|
||||
@TB("Reset")
|
||||
</MudButton>
|
||||
}
|
||||
|
||||
@@ -20,9 +20,6 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
|
||||
[Inject]
|
||||
protected IJSRuntime JsRuntime { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected ThreadSafeRandom RNG { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected ISnackbar Snackbar { get; init; } = null!;
|
||||
@@ -85,7 +82,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
|
||||
protected virtual IReadOnlyList<IButtonData> FooterButtons => [];
|
||||
|
||||
protected AIStudio.Settings.Provider providerSettings;
|
||||
protected AIStudio.Settings.Provider providerSettings = Settings.Provider.NONE;
|
||||
protected MudForm? form;
|
||||
protected bool inputIsValid;
|
||||
protected Profile currentProfile = Profile.NO_PROFILE;
|
||||
@@ -199,7 +196,6 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
WorkspaceId = Guid.Empty,
|
||||
ChatId = Guid.NewGuid(),
|
||||
Name = string.Format(this.TB("Assistant - {0}"), this.Title),
|
||||
Seed = this.RNG.Next(),
|
||||
Blocks = [],
|
||||
};
|
||||
}
|
||||
@@ -215,7 +211,6 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
WorkspaceId = workspaceId,
|
||||
ChatId = chatId,
|
||||
Name = name,
|
||||
Seed = this.RNG.Next(),
|
||||
Blocks = [],
|
||||
};
|
||||
|
||||
@@ -275,7 +270,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
// Use the selected provider to get the AI response.
|
||||
// By awaiting this line, we wait for the entire
|
||||
// content to be streamed.
|
||||
this.chatThread = await aiText.CreateFromProviderAsync(this.providerSettings.CreateProvider(this.Logger), this.providerSettings.Model, this.lastUserPrompt, this.chatThread, this.cancellationTokenSource!.Token);
|
||||
this.chatThread = await aiText.CreateFromProviderAsync(this.providerSettings.CreateProvider(), this.providerSettings.Model, this.lastUserPrompt, this.chatThread, this.cancellationTokenSource!.Token);
|
||||
|
||||
this.isProcessing = false;
|
||||
this.StateHasChanged();
|
||||
@@ -352,13 +347,15 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
private async Task InnerResetForm()
|
||||
{
|
||||
this.resultingContentBlock = null;
|
||||
this.providerSettings = default;
|
||||
this.providerSettings = Settings.Provider.NONE;
|
||||
|
||||
await this.JsRuntime.ClearDiv(RESULT_DIV_ID);
|
||||
await this.JsRuntime.ClearDiv(AFTER_RESULT_DIV_ID);
|
||||
|
||||
this.ResetForm();
|
||||
this.providerSettings = this.SettingsManager.GetPreselectedProvider(this.Component);
|
||||
this.currentProfile = this.SettingsManager.GetPreselectedProfile(this.Component);
|
||||
this.currentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(this.Component);
|
||||
|
||||
this.inputIsValid = false;
|
||||
this.inputIssues = [];
|
||||
@@ -384,7 +381,17 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
this.formChangeTimer.Dispose();
|
||||
try
|
||||
{
|
||||
this.formChangeTimer.Stop();
|
||||
this.formChangeTimer.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
base.DisposeResources();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
@attribute [Route(Routes.ASSISTANT_DOCUMENT_ANALYSIS)]
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogDocumentAnalysis>
|
||||
|
||||
@using AIStudio.Settings.DataModel
|
||||
|
||||
<PreviewPrototype/>
|
||||
<div class="mb-6"></div>
|
||||
|
||||
<MudText Typo="Typo.h4" Class="mb-3">
|
||||
@T("Document analysis policies")
|
||||
</MudText>
|
||||
|
||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||
@T("Here you have the option to save different policies for various document analysis assistants and switch between them.")
|
||||
</MudJustifiedText>
|
||||
|
||||
@if(this.SettingsManager.ConfigurationData.DocumentAnalysis.Policies.Count is 0)
|
||||
{
|
||||
<MudText Typo="Typo.body1" Class="mb-3">
|
||||
@T("You have not yet added any document analysis policies.")
|
||||
</MudText>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudList T="DataDocumentAnalysisPolicy" Class="mb-1" SelectedValue="@this.selectedPolicy" SelectedValueChanged="@this.SelectedPolicyChanged">
|
||||
@foreach (var policy in this.SettingsManager.ConfigurationData.DocumentAnalysis.Policies)
|
||||
{
|
||||
<MudListItem T="DataDocumentAnalysisPolicy" Icon="@Icons.Material.Filled.Policy" Value="@policy">
|
||||
@policy.PolicyName
|
||||
</MudListItem>
|
||||
}
|
||||
</MudList>
|
||||
}
|
||||
|
||||
<MudStack Row="@true" Class="mt-1">
|
||||
<MudButton OnClick="@this.AddPolicy" Variant="Variant.Filled" Color="Color.Primary">
|
||||
@T("Add policy")
|
||||
</MudButton>
|
||||
<MudButton OnClick="@this.RemovePolicy" Disabled="@(this.selectedPolicy?.IsProtected ?? true)" Variant="Variant.Filled" Color="Color.Error">
|
||||
@T("Delete this policy")
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
|
||||
<MudExpansionPanels Class="mb-3 mt-6" MultiExpansion="@false">
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Policy" HeaderText="@(T("Policy definition") + $": {this.selectedPolicy?.PolicyName}")" IsExpanded="@(!this.selectedPolicy?.IsProtected ?? true)">
|
||||
<MudText Typo="Typo.h4" Class="mb-1">
|
||||
@T("Common settings")
|
||||
</MudText>
|
||||
|
||||
<MudTextField T="string" Disabled="@this.IsNoPolicySelectedOrProtected" @bind-Text="@this.policyName" Validation="@this.ValidatePolicyName" Immediate="@true" Label="@T("Policy name")" HelperText="@T("Please give your policy a name that provides information about the intended purpose. The name will be displayed to users in AI Studio.")" Counter="60" MaxLength="60" Variant="Variant.Outlined" Margin="Margin.Normal" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3" OnKeyUp="() => this.PolicyNameWasChanged()"/>
|
||||
|
||||
<MudTextField T="string" Disabled="@this.IsNoPolicySelectedOrProtected" @bind-Text="@this.policyDescription" Validation="@this.ValidatePolicyDescription" Immediate="@true" Label="@T("Policy description")" HelperText="@T("Please provide a brief description of your policy. Describe or explain what your policy does. This description will be shown to users in AI Studio.")" Counter="512" MaxLength="512" Variant="Variant.Outlined" Margin="Margin.Normal" Lines="3" AutoGrow="@true" MaxLines="6" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||
|
||||
<MudTextSwitch Disabled="@(this.IsNoPolicySelected || (this.selectedPolicy?.IsManaged ?? true))" Label="@T("Would you like to protect this policy so that you cannot accidentally edit or delete it?")" Value="@this.policyIsProtected" ValueChanged="async state => await this.PolicyProtectionWasChanged(state)" LabelOn="@T("Yes, protect this policy")" LabelOff="@T("No, the policy can be edited")" />
|
||||
|
||||
<MudText Typo="Typo.h4" Class="mt-6 mb-1">
|
||||
@T("Input and output rules")
|
||||
</MudText>
|
||||
|
||||
<MudTextField T="string" Disabled="@this.IsNoPolicySelectedOrProtected" @bind-Text="@this.policyAnalysisRules" Validation="@this.ValidateAnalysisRules" Immediate="@true" Label="@T("Analysis rules")" HelperText="@T("Please provide a description of your analysis rules. This rules will be used to instruct the AI on how to analyze the documents.")" Variant="Variant.Outlined" Margin="Margin.Normal" Lines="5" AutoGrow="@true" MaxLines="26" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||
|
||||
<ReadFileContent Text="@T("Load analysis rules from document")" @bind-FileContent="@this.policyAnalysisRules"/>
|
||||
|
||||
<MudTextField T="string" Disabled="@this.IsNoPolicySelectedOrProtected" @bind-Text="@this.policyOutputRules" Validation="@this.ValidateOutputRules" Immediate="@true" Label="@T("Output rules")" HelperText="@T("Please provide a description of your output rules. This rules will be used to instruct the AI on how to format the output of the analysis.")" Variant="Variant.Outlined" Margin="Margin.Normal" Lines="5" AutoGrow="@true" MaxLines="26" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||
|
||||
<ReadFileContent Text="@T("Load output rules from document")" @bind-FileContent="@this.policyOutputRules"/>
|
||||
|
||||
<MudText Typo="Typo.h4" Class="mt-6 mb-1">
|
||||
@T("Preparation for enterprise distribution")
|
||||
</MudText>
|
||||
|
||||
<MudTooltip Text="@T("Not implemented yet.")">
|
||||
<MudButton StartIcon="@Icons.Material.Filled.FileDownload" Disabled="true" Variant="Variant.Filled" Color="Color.Primary" OnClick="@this.ExportPolicyAsConfiguration">
|
||||
@T("Export policy as configuration section")
|
||||
</MudButton>
|
||||
</MudTooltip>
|
||||
</ExpansionPanel>
|
||||
|
||||
<MudDivider Style="height: 0.25ch; margin: 1rem 0;" Class="mt-6" />
|
||||
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.DocumentScanner" HeaderText="@(T("Document analysis") + $": {this.selectedPolicy?.PolicyName}")" IsExpanded="@(this.selectedPolicy?.IsProtected ?? false)">
|
||||
<MudText Typo="Typo.h4" Class="mb-1">
|
||||
@T("Description")
|
||||
</MudText>
|
||||
|
||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||
@this.selectedPolicy?.PolicyDescription
|
||||
</MudJustifiedText>
|
||||
|
||||
<MudText Typo="Typo.h4" Class="mb-1 mt-6">
|
||||
@T("Documents for the analysis")
|
||||
</MudText>
|
||||
|
||||
<AttachDocuments Name="Document Analysis Files Drop" @bind-DocumentPaths="@this.loadedDocumentPaths" CatchAllDocuments="true"/>
|
||||
|
||||
</ExpansionPanel>
|
||||
</MudExpansionPanels>
|
||||
|
||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||
+366
@@ -0,0 +1,366 @@
|
||||
using System.Text;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
namespace AIStudio.Assistants.DocumentAnalysis;
|
||||
|
||||
public partial class DocumentAnalysisAssistant : AssistantBaseCore<SettingsDialogDocumentAnalysis>
|
||||
{
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
public override Tools.Components Component => Tools.Components.DOCUMENT_ANALYSIS_ASSISTANT;
|
||||
|
||||
protected override string Title => T("Document Analysis Assistant");
|
||||
|
||||
protected override string Description => T("The document analysis assistant helps you to analyze and extract information from documents based on predefined policies. You can create, edit, and manage document analysis policies that define how documents should be processed and what information should be extracted. Some policies might be protected by your organization and cannot be modified or deleted.");
|
||||
|
||||
protected override string SystemPrompt
|
||||
{
|
||||
get
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.Append("# Task description");
|
||||
sb.AppendLine();
|
||||
|
||||
if (this.loadedDocumentPaths.Count > 1)
|
||||
{
|
||||
sb.Append($"Your task is to analyse {this.loadedDocumentPaths.Count} documents.");
|
||||
sb.Append("Different Documents are divided by a horizontal rule in markdown formatting followed by the name of the document.");
|
||||
sb.AppendLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append("Your task is to analyse a single document.");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
var taskDescription = """
|
||||
The analysis should be done using the policy analysis rules.
|
||||
The output should be formatted according to the policy output rules.
|
||||
The rule sets should be followed strictly.
|
||||
Only use information given in the documents or in the policy.
|
||||
Never add any information of your own to it.
|
||||
Keep your answers precise, professional and factual.
|
||||
Only answer with the correctly formatted analysis result and do not add any opening or closing remarks.
|
||||
Answer in the language that is used by the policy or is stated in the output rules.
|
||||
""";
|
||||
|
||||
sb.Append(taskDescription);
|
||||
sb.AppendLine();
|
||||
|
||||
sb.Append(this.PromptGetActivePolicy());
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||
|
||||
protected override bool ShowEntireChatThread => true;
|
||||
|
||||
protected override bool ShowSendTo => true;
|
||||
|
||||
protected override string SubmitText => T("Analyze documents");
|
||||
|
||||
protected override Func<Task> SubmitAction => this.Analyze;
|
||||
|
||||
protected override bool SubmitDisabled => (this.IsNoPolicySelected || this.loadedDocumentPaths.Count==0);
|
||||
|
||||
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||
{
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
};
|
||||
|
||||
protected override void ResetForm()
|
||||
{
|
||||
if (!this.MightPreselectValues())
|
||||
{
|
||||
this.policyName = string.Empty;
|
||||
this.policyDescription = string.Empty;
|
||||
this.policyIsProtected = false;
|
||||
this.policyAnalysisRules = string.Empty;
|
||||
this.policyOutputRules = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MightPreselectValues()
|
||||
{
|
||||
if (this.selectedPolicy is not null)
|
||||
{
|
||||
this.policyName = this.selectedPolicy.PolicyName;
|
||||
this.policyDescription = this.selectedPolicy.PolicyDescription;
|
||||
this.policyIsProtected = this.selectedPolicy.IsProtected;
|
||||
this.policyAnalysisRules = this.selectedPolicy.AnalysisRules;
|
||||
this.policyOutputRules = this.selectedPolicy.OutputRules;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override async Task OnFormChange()
|
||||
{
|
||||
await this.AutoSave();
|
||||
}
|
||||
|
||||
#region Overrides of AssistantBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
this.selectedPolicy = this.SettingsManager.ConfigurationData.DocumentAnalysis.Policies.FirstOrDefault();
|
||||
if(this.selectedPolicy is null)
|
||||
{
|
||||
await this.AddPolicy();
|
||||
this.selectedPolicy = this.SettingsManager.ConfigurationData.DocumentAnalysis.Policies.First();
|
||||
}
|
||||
|
||||
var receivedDeferredContent = MessageBus.INSTANCE.CheckDeferredMessages<string>(Event.SEND_TO_DOCUMENT_ANALYSIS_ASSISTANT).FirstOrDefault();
|
||||
if (receivedDeferredContent is not null)
|
||||
this.deferredContent = receivedDeferredContent;
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private async Task AutoSave(bool force = false)
|
||||
{
|
||||
if(this.selectedPolicy is null)
|
||||
return;
|
||||
|
||||
if(!force && this.selectedPolicy.IsProtected)
|
||||
return;
|
||||
|
||||
if(!force && this.policyIsProtected)
|
||||
return;
|
||||
|
||||
this.selectedPolicy.PreselectedProvider = this.providerSettings.Id;
|
||||
|
||||
this.selectedPolicy.PolicyName = this.policyName;
|
||||
this.selectedPolicy.PolicyDescription = this.policyDescription;
|
||||
this.selectedPolicy.IsProtected = this.policyIsProtected;
|
||||
this.selectedPolicy.AnalysisRules = this.policyAnalysisRules;
|
||||
this.selectedPolicy.OutputRules = this.policyOutputRules;
|
||||
|
||||
await this.SettingsManager.StoreSettings();
|
||||
}
|
||||
|
||||
private DataDocumentAnalysisPolicy? selectedPolicy;
|
||||
private bool policyIsProtected;
|
||||
private string policyName = string.Empty;
|
||||
private string policyDescription = string.Empty;
|
||||
private string policyAnalysisRules = string.Empty;
|
||||
private string policyOutputRules = string.Empty;
|
||||
#warning Use deferred content for document analysis
|
||||
private string deferredContent = string.Empty;
|
||||
private HashSet<string> loadedDocumentPaths = [];
|
||||
|
||||
private bool IsNoPolicySelectedOrProtected => this.selectedPolicy is null || this.selectedPolicy.IsProtected;
|
||||
|
||||
private bool IsNoPolicySelected => this.selectedPolicy is null;
|
||||
|
||||
private void SelectedPolicyChanged(DataDocumentAnalysisPolicy? policy)
|
||||
{
|
||||
this.selectedPolicy = policy;
|
||||
this.ResetForm();
|
||||
}
|
||||
|
||||
private async Task AddPolicy()
|
||||
{
|
||||
this.SettingsManager.ConfigurationData.DocumentAnalysis.Policies.Add(new ()
|
||||
{
|
||||
PolicyName = string.Format(T("Policy {0}"), DateTimeOffset.UtcNow),
|
||||
});
|
||||
|
||||
await this.SettingsManager.StoreSettings();
|
||||
}
|
||||
|
||||
private async Task RemovePolicy()
|
||||
{
|
||||
if(this.selectedPolicy is null)
|
||||
return;
|
||||
|
||||
if(this.selectedPolicy.IsProtected)
|
||||
return;
|
||||
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ x => x.Message, string.Format(T("Are you sure you want to delete the document analysis policy '{0}'?"), this.selectedPolicy.PolicyName) },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete document analysis policy"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
var dialogResult = await dialogReference.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled)
|
||||
return;
|
||||
|
||||
this.SettingsManager.ConfigurationData.DocumentAnalysis.Policies.Remove(this.selectedPolicy);
|
||||
this.selectedPolicy = null;
|
||||
this.ResetForm();
|
||||
|
||||
await this.SettingsManager.StoreSettings();
|
||||
this.form?.ResetValidation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets called when the policy name was changed by typing.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is used to update the policy name in the selected policy.
|
||||
/// Otherwise, the users would be confused when they change the name and the changes are not reflected in the UI.
|
||||
/// </remarks>
|
||||
private void PolicyNameWasChanged()
|
||||
{
|
||||
if(this.selectedPolicy is null)
|
||||
return;
|
||||
|
||||
if(this.selectedPolicy.IsProtected)
|
||||
return;
|
||||
|
||||
this.selectedPolicy.PolicyName = this.policyName;
|
||||
}
|
||||
|
||||
private async Task PolicyProtectionWasChanged(bool state)
|
||||
{
|
||||
if(this.selectedPolicy is null)
|
||||
return;
|
||||
|
||||
this.policyIsProtected = state;
|
||||
this.selectedPolicy.IsProtected = state;
|
||||
await this.AutoSave(true);
|
||||
}
|
||||
|
||||
private string? ValidatePolicyName(string name)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(name))
|
||||
return T("Please provide a name for your policy. This name will be used to identify the policy in AI Studio.");
|
||||
|
||||
if(name.Length is > 60 or < 6)
|
||||
return T("The name of your policy must be between 6 and 60 characters long.");
|
||||
|
||||
if(this.SettingsManager.ConfigurationData.DocumentAnalysis.Policies.Where(n => n != this.selectedPolicy).Any(n => n.PolicyName == name))
|
||||
return T("A policy with this name already exists. Please choose a different name.");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidatePolicyDescription(string description)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(description))
|
||||
return T("Please provide a description for your policy. This description will be used to inform users about the purpose of your document analysis policy.");
|
||||
|
||||
if(description.Length is < 32 or > 512)
|
||||
return T("The description of your policy must be between 32 and 512 characters long.");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateAnalysisRules(string analysisRules)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(analysisRules))
|
||||
return T("Please provide a description of your analysis rules. This rules will be used to instruct the AI on how to analyze the documents.");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? ValidateOutputRules(string outputRules)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(outputRules))
|
||||
return T("Please provide a description of your output rules. This rules will be used to instruct the AI on how to format the output of the analysis.");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string PromptGetActivePolicy()
|
||||
{
|
||||
return $"""
|
||||
# Policy
|
||||
The policy is defined as follows:
|
||||
|
||||
## Policy name
|
||||
{this.policyName}
|
||||
|
||||
## Policy description
|
||||
{this.policyDescription}
|
||||
|
||||
## Policy analysis rules
|
||||
{this.policyAnalysisRules}
|
||||
|
||||
## Policy output rules
|
||||
{this.policyOutputRules}
|
||||
""";
|
||||
}
|
||||
|
||||
private async Task<string> PromptLoadDocumentsContent()
|
||||
{
|
||||
if (this.loadedDocumentPaths.Count == 0)
|
||||
return string.Empty;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
var count = 1;
|
||||
foreach(var documentPath in this.loadedDocumentPaths)
|
||||
{
|
||||
sb.Append("---");
|
||||
sb.AppendLine();
|
||||
sb.Append($"Document {count} file path: {documentPath}");
|
||||
sb.AppendLine();
|
||||
sb.Append($"Document {count} content:");
|
||||
sb.AppendLine();
|
||||
|
||||
var fileContent = await this.RustService.ReadArbitraryFileData(documentPath, int.MaxValue);
|
||||
sb.Append($"""
|
||||
```
|
||||
{fileContent}
|
||||
```
|
||||
""");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine();
|
||||
count += 1;
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private async Task Analyze()
|
||||
{
|
||||
// if (this.IsNoPolicySelectedOrProtected)
|
||||
// return;
|
||||
|
||||
await this.AutoSave();
|
||||
await this.form!.Validate();
|
||||
if (!this.inputIsValid)
|
||||
return;
|
||||
|
||||
this.CreateChatThread();
|
||||
|
||||
var userRequest = this.AddUserRequest(
|
||||
$"""
|
||||
{await this.PromptLoadDocumentsContent()}
|
||||
""", hideContentFromUser:true);
|
||||
|
||||
await this.AddAIResponseAsync(userRequest);
|
||||
}
|
||||
|
||||
private async Task ExportPolicyAsConfiguration()
|
||||
{
|
||||
return;
|
||||
|
||||
# warning Implement the export function
|
||||
// do not allow the export of a protected policy
|
||||
if (this.IsNoPolicySelectedOrProtected)
|
||||
return;
|
||||
|
||||
await this.AutoSave();
|
||||
await this.form!.Validate();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
@attribute [Route(Routes.ASSISTANT_ERI)]
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogERIServer>
|
||||
|
||||
@using AIStudio.Settings.DataModel
|
||||
@using MudExtensions
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogERIServer>
|
||||
|
||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||
@T("You can imagine it like this: Hypothetically, when Wikipedia implemented the ERI, it would vectorize all pages using an embedding method. All of Wikipedia’s data would remain with Wikipedia, including the vector database (decentralized approach). Then, any AI Studio user could add Wikipedia as a data source to significantly reduce the hallucination of the LLM in knowledge questions.")
|
||||
|
||||
@@ -473,9 +473,9 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
||||
if(this.selectedERIServer is null)
|
||||
return;
|
||||
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Are you sure you want to delete the ERI server preset '{0}'?"), this.selectedERIServer.ServerName) },
|
||||
{ x => x.Message, string.Format(T("Are you sure you want to delete the ERI server preset '{0}'?"), this.selectedERIServer.ServerName) },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete ERI server preset"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -827,9 +827,9 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
||||
? string.Format(T("The embedding '{0}' is used in one or more retrieval processes. Are you sure you want to delete it?"), embeddingInfo.EmbeddingName)
|
||||
: string.Format(T("Are you sure you want to delete the embedding '{0}'?"), embeddingInfo.EmbeddingName);
|
||||
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", message },
|
||||
{ x => x.Message, message },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Embedding"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -890,9 +890,9 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
||||
|
||||
private async Task DeleteRetrievalProcess(RetrievalInfo retrievalInfo)
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Are you sure you want to delete the retrieval process '{0}'?"), retrievalInfo.Name) },
|
||||
{ x => x.Message, string.Format(T("Are you sure you want to delete the retrieval process '{0}'?"), retrievalInfo.Name) },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Retrieval Process"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
|
||||
@@ -382,6 +382,120 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::CODING::COMMONCODINGLANGUAGEEXTENSIONS::T
|
||||
-- None
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::CODING::COMMONCODINGLANGUAGEEXTENSIONS::T810547195"] = "None"
|
||||
|
||||
-- Please provide a brief description of your policy. Describe or explain what your policy does. This description will be shown to users in AI Studio.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T1182372158"] = "Please provide a brief description of your policy. Describe or explain what your policy does. This description will be shown to users in AI Studio."
|
||||
|
||||
-- No, the policy can be edited
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T1286595725"] = "No, the policy can be edited"
|
||||
|
||||
-- Please provide a description of your analysis rules. This rules will be used to instruct the AI on how to analyze the documents.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T1291179736"] = "Please provide a description of your analysis rules. This rules will be used to instruct the AI on how to analyze the documents."
|
||||
|
||||
-- Not implemented yet.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T1568777658"] = "Not implemented yet."
|
||||
|
||||
-- Input and output rules
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T1714738288"] = "Input and output rules"
|
||||
|
||||
-- Description
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T1725856265"] = "Description"
|
||||
|
||||
-- Yes, protect this policy
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T1762380857"] = "Yes, protect this policy"
|
||||
|
||||
-- Please give your policy a name that provides information about the intended purpose. The name will be displayed to users in AI Studio.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T1786783201"] = "Please give your policy a name that provides information about the intended purpose. The name will be displayed to users in AI Studio."
|
||||
|
||||
-- Please provide a description for your policy. This description will be used to inform users about the purpose of your document analysis policy.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T1837166236"] = "Please provide a description for your policy. This description will be used to inform users about the purpose of your document analysis policy."
|
||||
|
||||
-- Common settings
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T1963959073"] = "Common settings"
|
||||
|
||||
-- The document analysis assistant helps you to analyze and extract information from documents based on predefined policies. You can create, edit, and manage document analysis policies that define how documents should be processed and what information should be extracted. Some policies might be protected by your organization and cannot be modified or deleted.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T206207667"] = "The document analysis assistant helps you to analyze and extract information from documents based on predefined policies. You can create, edit, and manage document analysis policies that define how documents should be processed and what information should be extracted. Some policies might be protected by your organization and cannot be modified or deleted."
|
||||
|
||||
-- Document analysis policies
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T2064879144"] = "Document analysis policies"
|
||||
|
||||
-- Load output rules from document
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T2168201568"] = "Load output rules from document"
|
||||
|
||||
-- The name of your policy must be between 6 and 60 characters long.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T2435013256"] = "The name of your policy must be between 6 and 60 characters long."
|
||||
|
||||
-- Policy definition
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T2491168104"] = "Policy definition"
|
||||
|
||||
-- Export policy as configuration section
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T2556564432"] = "Export policy as configuration section"
|
||||
|
||||
-- Are you sure you want to delete the document analysis policy '{0}'?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T2582525917"] = "Are you sure you want to delete the document analysis policy '{0}'?"
|
||||
|
||||
-- Document analysis
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T2708005534"] = "Document analysis"
|
||||
|
||||
-- Policy name
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T2879019438"] = "Policy name"
|
||||
|
||||
-- Analyze documents
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T2894951609"] = "Analyze documents"
|
||||
|
||||
-- Documents for the analysis
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T3030664641"] = "Documents for the analysis"
|
||||
|
||||
-- Analysis rules
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T3108719748"] = "Analysis rules"
|
||||
|
||||
-- Delete this policy
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T3119086260"] = "Delete this policy"
|
||||
|
||||
-- Policy {0}
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T3157740273"] = "Policy {0}"
|
||||
|
||||
-- The description of your policy must be between 32 and 512 characters long.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T3285636934"] = "The description of your policy must be between 32 and 512 characters long."
|
||||
|
||||
-- Add policy
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T3357792182"] = "Add policy"
|
||||
|
||||
-- You have not yet added any document analysis policies.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T3394850216"] = "You have not yet added any document analysis policies."
|
||||
|
||||
-- Document Analysis Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T348883878"] = "Document Analysis Assistant"
|
||||
|
||||
-- A policy with this name already exists. Please choose a different name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T3584374593"] = "A policy with this name already exists. Please choose a different name."
|
||||
|
||||
-- Load analysis rules from document
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T3813558135"] = "Load analysis rules from document"
|
||||
|
||||
-- Output rules
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T3918193587"] = "Output rules"
|
||||
|
||||
-- Preparation for enterprise distribution
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T39557294"] = "Preparation for enterprise distribution"
|
||||
|
||||
-- Please provide a name for your policy. This name will be used to identify the policy in AI Studio.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T4040507702"] = "Please provide a name for your policy. This name will be used to identify the policy in AI Studio."
|
||||
|
||||
-- Delete document analysis policy
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T4293094335"] = "Delete document analysis policy"
|
||||
|
||||
-- Please provide a description of your output rules. This rules will be used to instruct the AI on how to format the output of the analysis.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T652187065"] = "Please provide a description of your output rules. This rules will be used to instruct the AI on how to format the output of the analysis."
|
||||
|
||||
-- Policy description
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T748735777"] = "Policy description"
|
||||
|
||||
-- Would you like to protect this policy so that you cannot accidentally edit or delete it?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T80597472"] = "Would you like to protect this policy so that you cannot accidentally edit or delete it?"
|
||||
|
||||
-- Here you have the option to save different policies for various document analysis assistants and switch between them.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::DOCUMENTANALYSIS::DOCUMENTANALYSISASSISTANT::T848153710"] = "Here you have the option to save different policies for various document analysis assistants and switch between them."
|
||||
|
||||
-- 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.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::EMAIL::ASSISTANTEMAIL::T1143222914"] = "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."
|
||||
|
||||
@@ -1222,12 +1336,18 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::
|
||||
-- Target language
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T237828418"] = "Target language"
|
||||
|
||||
-- (Optional) Important Aspects
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T24391765"] = "(Optional) Important Aspects"
|
||||
|
||||
-- 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.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T359929871"] = "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."
|
||||
|
||||
-- Please provide your field of expertise.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T3610378685"] = "Please provide your field of expertise."
|
||||
|
||||
-- (Optional) Specify aspects for the LLM to focus on when generating a summary, such as summary length or specific topics to emphasize.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T3830285347"] = "(Optional) Specify aspects for the LLM to focus on when generating a summary, such as summary length or specific topics to emphasize."
|
||||
|
||||
-- Custom target language
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T3848935911"] = "Custom target language"
|
||||
|
||||
@@ -1315,9 +1435,6 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CHATROLEEXTENSIONS::T601166687"] = "AI"
|
||||
-- Edit Message
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1183581066"] = "Edit Message"
|
||||
|
||||
-- Copies the content to the clipboard
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T12948066"] = "Copies the content to the clipboard"
|
||||
|
||||
-- Do you really want to remove this message?
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1347427447"] = "Do you really want to remove this message?"
|
||||
|
||||
@@ -1330,6 +1447,9 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1603883875"] = "Yes, re
|
||||
-- Yes, remove it
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1820166585"] = "Yes, remove it"
|
||||
|
||||
-- Number of sources
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1848978959"] = "Number of sources"
|
||||
|
||||
-- Do you really want to edit this message? In order to edit this message, the AI response will be deleted.
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T2018431076"] = "Do you really want to edit this message? In order to edit this message, the AI response will be deleted."
|
||||
|
||||
@@ -1351,18 +1471,39 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3587744975"] = "Regener
|
||||
-- Do you really want to regenerate this message?
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3878878761"] = "Do you really want to regenerate this message?"
|
||||
|
||||
-- Cannot copy this content type to clipboard!
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4021525742"] = "Cannot copy this content type to clipboard!"
|
||||
|
||||
-- Remove Message
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4070211974"] = "Remove Message"
|
||||
|
||||
-- No, keep it
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4188329028"] = "No, keep it"
|
||||
|
||||
-- Export Chat to Microsoft Word
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T861873672"] = "Export Chat to Microsoft Word"
|
||||
|
||||
-- Open Settings
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T1172211894"] = "Open Settings"
|
||||
|
||||
-- Drag and drop files here or click to attach documents.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T1647829151"] = "Drag and drop files here or click to attach documents."
|
||||
|
||||
-- Pandoc Load Document Preview
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T2686523471"] = "Pandoc Load Document Preview"
|
||||
|
||||
-- Videos are not supported yet
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T2928927510"] = "Videos are not supported yet"
|
||||
|
||||
-- Images are not supported yet
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T298062956"] = "Images are not supported yet"
|
||||
|
||||
-- Clear file list
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T3759696136"] = "Clear file list"
|
||||
|
||||
-- Executables are not allowed
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T4167762413"] = "Executables are not allowed"
|
||||
|
||||
-- Select a file to attach
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T595772870"] = "Select a file to attach"
|
||||
|
||||
-- Changelog
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHANGELOG::T3017574265"] = "Changelog"
|
||||
|
||||
@@ -1414,6 +1555,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T636393754"] = "Move the c
|
||||
-- Show your workspaces
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T733672375"] = "Show your workspaces"
|
||||
|
||||
-- Create template from current chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATTEMPLATESELECTION::T1112722156"] = "Create template from current chat"
|
||||
|
||||
-- Start a new chat with a chat template
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATTEMPLATESELECTION::T1333844707"] = "Start a new chat with a chat template"
|
||||
|
||||
@@ -1447,6 +1591,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CONFIDENCEINFO::T3243388657"] = "Confiden
|
||||
-- Shows and hides the confidence card with information about the selected LLM provider.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CONFIDENCEINFO::T847071819"] = "Shows and hides the confidence card with information about the selected LLM provider."
|
||||
|
||||
-- This feature is managed by your organization and has therefore been disabled.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CONFIGURATIONBASE::T1416426626"] = "This feature is managed by your organization and has therefore been disabled."
|
||||
|
||||
-- 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.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CONFIGURATIONMINCONFIDENCESELECTION::T2526727283"] = "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."
|
||||
|
||||
@@ -1585,6 +1732,12 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::MOTIVATION::T372007989"] = "Relying on we
|
||||
-- Cross-Platform and Modern Development
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::MOTIVATION::T843057510"] = "Cross-Platform and Modern Development"
|
||||
|
||||
-- Copies the content to the clipboard
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::MUDCOPYCLIPBOARDBUTTON::T12948066"] = "Copies the content to the clipboard"
|
||||
|
||||
-- Cannot copy this content type to clipboard.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::MUDCOPYCLIPBOARDBUTTON::T3937637647"] = "Cannot copy this content type to clipboard."
|
||||
|
||||
-- Alpha phase means that we are working on the last details before the beta phase.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PREVIEWALPHA::T166807685"] = "Alpha phase means that we are working on the last details before the beta phase."
|
||||
|
||||
@@ -1648,11 +1801,26 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROFILESELECTION::T918741365"] = "You can
|
||||
-- Provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T900237532"] = "Provider"
|
||||
|
||||
-- Use PDF content as input
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READPDFCONTENT::T2849276709"] = "Use PDF content as input"
|
||||
-- Pandoc Installation
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T185447014"] = "Pandoc Installation"
|
||||
|
||||
-- Select PDF file
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READPDFCONTENT::T63272795"] = "Select PDF file"
|
||||
-- Pandoc may be required for importing files.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T2596465560"] = "Pandoc may be required for importing files."
|
||||
|
||||
-- Videos are not supported yet
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T2928927510"] = "Videos are not supported yet"
|
||||
|
||||
-- Images are not supported yet
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T298062956"] = "Images are not supported yet"
|
||||
|
||||
-- Use file content as input
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T3499386973"] = "Use file content as input"
|
||||
|
||||
-- Select file to read its content
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T354817589"] = "Select file to read its content"
|
||||
|
||||
-- Executables are not allowed
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T4167762413"] = "Executables are not allowed"
|
||||
|
||||
-- The content is cleaned using an LLM agent: the main content is extracted, advertisements and other irrelevant things are attempted to be removed; relative links are attempted to be converted into absolute links so that they can be used.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENT::T1164201762"] = "The content is cleaned using an LLM agent: the main content is extracted, advertisements and other irrelevant things are attempted to be removed; relative links are attempted to be converted into absolute links so that they can be used."
|
||||
@@ -1693,6 +1861,24 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENT::T3825586228"] = "Please p
|
||||
-- Show web content options
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENT::T4249712357"] = "Show web content options"
|
||||
|
||||
-- Loading
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T1404011351"] = "Loading"
|
||||
|
||||
-- Start
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T182978943"] = "Start"
|
||||
|
||||
-- Done
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T2379421585"] = "Done"
|
||||
|
||||
-- Parsing
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T3151033983"] = "Parsing"
|
||||
|
||||
-- Cleaning
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T3420573362"] = "Cleaning"
|
||||
|
||||
-- n/a
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T907272257"] = "n/a"
|
||||
|
||||
-- Hide content
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SECRETINPUTFIELD::T1273315904"] = "Hide content"
|
||||
|
||||
@@ -1711,6 +1897,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTCONTENTCLEANE
|
||||
-- Use Case: this agent is used to clean up text content. It extracts the main content, removes advertisements and other irrelevant things, and attempts to convert relative links into absolute links so that they can be used.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTCONTENTCLEANER::T1299550589"] = "Use Case: this agent is used to clean up text content. It extracts the main content, removes advertisements and other irrelevant things, and attempts to convert relative links into absolute links so that they can be used."
|
||||
|
||||
-- Agent: Text Content Cleaner Options
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTCONTENTCLEANER::T3517268866"] = "Agent: Text Content Cleaner Options"
|
||||
|
||||
-- No options are preselected
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTCONTENTCLEANER::T354528094"] = "No options are preselected"
|
||||
|
||||
@@ -1813,6 +2002,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1907446663"]
|
||||
-- Language behavior
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T2341504363"] = "Language behavior"
|
||||
|
||||
-- Update installation method
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T237706157"] = "Update installation method"
|
||||
|
||||
-- Language
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T2591284123"] = "Language"
|
||||
|
||||
@@ -1843,6 +2035,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T602293588"]
|
||||
-- Choose the color theme that best suits for you.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T654667432"] = "Choose the color theme that best suits for you."
|
||||
|
||||
-- Should updates be installed automatically or manually?
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T707880477"] = "Should updates be installed automatically or manually?"
|
||||
|
||||
-- Energy saving is enabled
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T71162186"] = "Energy saving is enabled"
|
||||
|
||||
@@ -2098,6 +2293,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1016188706"] = "Are you sure
|
||||
-- Move chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1133040906"] = "Move chat"
|
||||
|
||||
-- Unnamed workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1307384014"] = "Unnamed workspace"
|
||||
|
||||
-- Delete
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1469573738"] = "Delete"
|
||||
|
||||
@@ -2131,6 +2329,12 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2237618267"] = "Are you sure
|
||||
-- Delete Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2244038752"] = "Delete Chat"
|
||||
|
||||
-- Please enter a chat name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2301651387"] = "Please enter a chat name."
|
||||
|
||||
-- Workspace Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2446263209"] = "Workspace Name"
|
||||
|
||||
-- Move to workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2509305748"] = "Move to workspace"
|
||||
|
||||
@@ -2143,6 +2347,12 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3045856778"] = "Move Chat to
|
||||
-- Please enter a new or edit the name for your workspace '{0}':
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T323280982"] = "Please enter a new or edit the name for your workspace '{0}':"
|
||||
|
||||
-- Please enter a workspace name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3288132732"] = "Please enter a workspace name."
|
||||
|
||||
-- Unnamed chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3310482275"] = "Unnamed chat"
|
||||
|
||||
-- Rename
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3355849203"] = "Rename"
|
||||
|
||||
@@ -2155,6 +2365,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3555709365"] = "Load Chat"
|
||||
-- Add Workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3672981145"] = "Add Workspace"
|
||||
|
||||
-- Chat Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3891063690"] = "Chat Name"
|
||||
|
||||
-- Empty chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T4019509364"] = "Empty chat"
|
||||
|
||||
@@ -2188,18 +2401,30 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T1847791252"] = "Update"
|
||||
-- The chat template name must not exceed 40 characters.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T204496403"] = "The chat template name must not exceed 40 characters."
|
||||
|
||||
-- Profile Usage
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2147062613"] = "Profile Usage"
|
||||
|
||||
-- Add messages of an example conversation (user prompt followed by assistant prompt) to demonstrate the desired interaction pattern. These examples help the AI understand your expectations by showing it the correct format, style, and content of responses before it receives actual user inputs.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2292424657"] = "Add messages of an example conversation (user prompt followed by assistant prompt) to demonstrate the desired interaction pattern. These examples help the AI understand your expectations by showing it the correct format, style, and content of responses before it receives actual user inputs."
|
||||
|
||||
-- Role
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2418769465"] = "Role"
|
||||
|
||||
-- What predefined user input do you want to use?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2501284417"] = "What predefined user input do you want to use?"
|
||||
|
||||
-- Tell the AI your system prompt.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2552381261"] = "Tell the AI your system prompt."
|
||||
|
||||
-- No, prohibit profile use for this template
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2579080722"] = "No, prohibit profile use for this template"
|
||||
|
||||
-- You might want to predefine a first message that will be copied into the user prompt, when you use this chat template. This message could for example be a blueprint for a structured message that this chat template is defined to work with.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2579208700"] = "You might want to predefine a first message that will be copied into the user prompt, when you use this chat template. This message could for example be a blueprint for a structured message that this chat template is defined to work with."
|
||||
|
||||
-- Predefined User Input
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2607897066"] = "Predefined User Input"
|
||||
|
||||
-- Entry
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2629823795"] = "Entry"
|
||||
|
||||
@@ -2254,6 +2479,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3893704289"] = "Message
|
||||
-- Use the default system prompt
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T4051106111"] = "Use the default system prompt"
|
||||
|
||||
-- Tell the AI your predefined user input.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T4052406705"] = "Tell the AI your predefined user input."
|
||||
|
||||
-- Create your custom chat template to tailor the LLM's behavior for specific tasks or domains. Define a custom system prompt and provide an example conversation to design an AI experience perfectly suited to your requirements.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T4199560726"] = "Create your custom chat template to tailor the LLM's behavior for specific tasks or domains. Define a custom system prompt and provide an example conversation to design an AI experience perfectly suited to your requirements."
|
||||
|
||||
@@ -2890,9 +3118,27 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PANDOCDIALOG::T504404155"] = "Accept the ter
|
||||
-- Pandoc is distributed under the GNU General Public License v2 (GPL). By clicking "Accept GPL and archive," you agree to the terms of the GPL license. Software under GPL is free of charge and free to use.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PANDOCDIALOG::T523908375"] = "Pandoc is distributed under the GNU General Public License v2 (GPL). By clicking \"Accept GPL and archive,\" you agree to the terms of the GPL license. Software under GPL is free of charge and free to use."
|
||||
|
||||
-- Test how Pandoc loads your document. See the raw content it produces before further processing.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PANDOCDOCUMENTCHECKDIALOG::T1481857352"] = "Test how Pandoc loads your document. See the raw content it produces before further processing."
|
||||
|
||||
-- Content Loaded by Pandoc
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PANDOCDOCUMENTCHECKDIALOG::T2147198279"] = "Content Loaded by Pandoc"
|
||||
|
||||
-- This is the content Pandoc loaded from your document — including headings, lists, and formatting. Use this to verify your document loads as expected.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PANDOCDOCUMENTCHECKDIALOG::T2156541074"] = "This is the content Pandoc loaded from your document — including headings, lists, and formatting. Use this to verify your document loads as expected."
|
||||
|
||||
-- Load document
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PANDOCDOCUMENTCHECKDIALOG::T2394358670"] = "Load document"
|
||||
|
||||
-- Cancel
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PANDOCDOCUMENTCHECKDIALOG::T900713019"] = "Cancel"
|
||||
|
||||
-- Tell the AI what you want it to do for you. What are your goals or are you trying to achieve? Like having the AI address you informally.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T1458195391"] = "Tell the AI what you want it to do for you. What are your goals or are you trying to achieve? Like having the AI address you informally."
|
||||
|
||||
-- Please be aware that your profile info becomes part of the system prompt. This means it uses up context space — the “memory” the LLM uses to understand and respond to your request. If your profile is extremely long, the LLM may struggle to focus on your actual task.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T1717545317"] = "Please be aware that your profile info becomes part of the system prompt. This means it uses up context space — the “memory” the LLM uses to understand and respond to your request. If your profile is extremely long, the LLM may struggle to focus on your actual task."
|
||||
|
||||
-- Update
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T1847791252"] = "Update"
|
||||
|
||||
@@ -2905,18 +3151,12 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T2261456575"] = "What should
|
||||
-- Please enter a profile name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T2386844536"] = "Please enter a profile name."
|
||||
|
||||
-- The text must not exceed 256 characters.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T2560188276"] = "The text must not exceed 256 characters."
|
||||
|
||||
-- Add
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T2646845972"] = "Add"
|
||||
|
||||
-- The profile name must not exceed 40 characters.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3243902394"] = "The profile name must not exceed 40 characters."
|
||||
|
||||
-- The text must not exceed 444 characters.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3253349421"] = "The text must not exceed 444 characters."
|
||||
|
||||
-- Profile Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3392578705"] = "Profile Name"
|
||||
|
||||
@@ -2941,9 +3181,15 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T900713019"] = "Cancel"
|
||||
-- The profile name must be unique; the chosen name is already in use.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T911748898"] = "The profile name must be unique; the chosen name is already in use."
|
||||
|
||||
-- Please be aware: This section is for experts only. You are responsible for verifying the correctness of the additional parameters you provide to the API call. By default, AI Studio uses the OpenAI-compatible chat completions API, when that it is supported by the underlying service and model.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1017509792"] = "Please be aware: This section is for experts only. You are responsible for verifying the correctness of the additional parameters you provide to the API call. By default, AI Studio uses the OpenAI-compatible chat completions API, when that it is supported by the underlying service and model."
|
||||
|
||||
-- Hugging Face Inference Provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1085481431"] = "Hugging Face Inference Provider"
|
||||
|
||||
-- Hide Expert Settings
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1108876344"] = "Hide Expert Settings"
|
||||
|
||||
-- Failed to store the API key in the operating system. The message was: {0}. Please try again.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1122745046"] = "Failed to store the API key in the operating system. The message was: {0}. Please try again."
|
||||
|
||||
@@ -2956,6 +3202,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1356621346"] = "Create acco
|
||||
-- Load models
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T15352225"] = "Load models"
|
||||
|
||||
-- Add the parameters in proper JSON formatting, e.g., "temperature": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1689135032"] = "Add the parameters in proper JSON formatting, e.g., \"temperature\": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though."
|
||||
|
||||
-- Hostname
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1727440780"] = "Hostname"
|
||||
|
||||
@@ -2977,12 +3226,18 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2331453405"] = "(Optional)
|
||||
-- Add
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2646845972"] = "Add"
|
||||
|
||||
-- Additional API parameters
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2728244552"] = "Additional API parameters"
|
||||
|
||||
-- No models loaded or available.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2810182573"] = "No models loaded or available."
|
||||
|
||||
-- Instance Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2842060373"] = "Instance Name"
|
||||
|
||||
-- Show Expert Settings
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3361153305"] = "Show Expert Settings"
|
||||
|
||||
-- Show available models
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3763891899"] = "Show available models"
|
||||
|
||||
@@ -3385,6 +3640,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T32678
|
||||
-- Close
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3448155331"] = "Close"
|
||||
|
||||
-- This template is managed by your organization.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3576775249"] = "This template is managed by your organization."
|
||||
|
||||
-- Edit Chat Template
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3596030597"] = "Edit Chat Template"
|
||||
|
||||
@@ -3520,6 +3778,33 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDATASOURCES::T774473
|
||||
-- Local Directory
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDATASOURCES::T926703547"] = "Local Directory"
|
||||
|
||||
-- Assistant: Document Analysis
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDOCUMENTANALYSIS::T1372406750"] = "Assistant: Document Analysis"
|
||||
|
||||
-- Most document analysis options can be customized and saved directly in the assistant. For this, the assistant has an auto-save function.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDOCUMENTANALYSIS::T1870328357"] = "Most document analysis options can be customized and saved directly in the assistant. For this, the assistant has an auto-save function."
|
||||
|
||||
-- Would you like to preselect one of your profiles?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDOCUMENTANALYSIS::T2221665527"] = "Would you like to preselect one of your profiles?"
|
||||
|
||||
-- Preselect document analysis options?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDOCUMENTANALYSIS::T2230062650"] = "Preselect document analysis options?"
|
||||
|
||||
-- When enabled, you can preselect some document analysis options.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDOCUMENTANALYSIS::T2301091111"] = "When enabled, you can preselect some document analysis options."
|
||||
|
||||
-- No document analysis options are preselected
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDOCUMENTANALYSIS::T3317802895"] = "No document analysis options are preselected"
|
||||
|
||||
-- Close
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDOCUMENTANALYSIS::T3448155331"] = "Close"
|
||||
|
||||
-- Document analysis options are preselected
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDOCUMENTANALYSIS::T3945756386"] = "Document analysis options are preselected"
|
||||
|
||||
-- Preselect one of your profiles?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGDOCUMENTANALYSIS::T4004501229"] = "Preselect one of your profiles?"
|
||||
|
||||
-- When enabled, you can preselect some ERI server options.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGERISERVER::T1280666275"] = "When enabled, you can preselect some ERI server options."
|
||||
|
||||
@@ -3790,6 +4075,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T380451542
|
||||
-- Actions
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T3865031940"] = "Actions"
|
||||
|
||||
-- This profile is managed by your organization.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4058414654"] = "This profile is managed by your organization."
|
||||
|
||||
-- Store personal data about yourself in various profiles so that the AIs know your personal context. This saves you from having to explain your context each time, for example, in every chat. When you have different roles, you can create a profile for each role.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4125557797"] = "Store personal data about yourself in various profiles so that the AIs know your personal context. This saves you from having to explain your context each time, for example, in every chat. When you have different roles, you can create a profile for each role."
|
||||
|
||||
@@ -3928,12 +4216,18 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T364
|
||||
-- When enabled, the content cleaner agent is preselected. This is might be useful when you prefer to clean up the content before summarize it.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T3660434400"] = "When enabled, the content cleaner agent is preselected. This is might be useful when you prefer to clean up the content before summarize it."
|
||||
|
||||
-- Preselect important aspects
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T3705987833"] = "Preselect important aspects"
|
||||
|
||||
-- When enabled, you can preselect the text summarizer options. This is might be useful when you prefer a specific language, complexity, or LLM.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T3820844575"] = "When enabled, you can preselect the text summarizer options. This is might be useful when you prefer a specific language, complexity, or LLM."
|
||||
|
||||
-- Which summarizer complexity should be preselected?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T408530182"] = "Which summarizer complexity should be preselected?"
|
||||
|
||||
-- Preselect aspects for the LLM to focus on when generating a summary, such as summary length or specific topics to emphasize.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T414420518"] = "Preselect aspects for the LLM to focus on when generating a summary, such as summary length or specific topics to emphasize."
|
||||
|
||||
-- Preselect your expertise
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T51139714"] = "Preselect your expertise"
|
||||
|
||||
@@ -4087,8 +4381,11 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGWRITINGEMAILS::T3832
|
||||
-- Preselect one of your profiles?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGWRITINGEMAILS::T4004501229"] = "Preselect one of your profiles?"
|
||||
|
||||
-- Chat name
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T1746586282"] = "Chat name"
|
||||
-- Please enter a value.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T3576780391"] = "Please enter a value."
|
||||
|
||||
-- Your Input
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T4030229154"] = "Your Input"
|
||||
|
||||
-- Cancel
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T900713019"] = "Cancel"
|
||||
@@ -4156,8 +4453,11 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1020427799"] = "About MindWork AI Stud
|
||||
-- Browse AI Studio's source code on GitHub — we welcome your contributions.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1107156991"] = "Browse AI Studio's source code on GitHub — we welcome your contributions."
|
||||
|
||||
-- AI Studio runs with an enterprise configuration id '{0}' and configuration server URL '{1}'. The configuration plugin is not yet available.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1297057566"] = "AI Studio runs with an enterprise configuration id '{0}' and configuration server URL '{1}'. The configuration plugin is not yet available."
|
||||
-- This is a private AI Studio installation. It runs without an enterprise configuration.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1209549230"] = "This is a private AI Studio installation. It runs without an enterprise configuration."
|
||||
|
||||
-- AI Studio runs with an enterprise configuration and a configuration server. The configuration plugin is not yet available.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1282228996"] = "AI Studio runs with an enterprise configuration and a configuration server. The configuration plugin is not yet available."
|
||||
|
||||
-- This library is used to read PDF files. This is necessary, e.g., for using PDFs as a data source for a chat.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1388816916"] = "This library is used to read PDF files. This is necessary, e.g., for using PDFs as a data source for a chat."
|
||||
@@ -4165,12 +4465,6 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1388816916"] = "This library is used t
|
||||
-- This library is used to extend the MudBlazor library. It provides additional components that are not part of the MudBlazor library.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1421513382"] = "This library is used to extend the MudBlazor library. It provides additional components that are not part of the MudBlazor library."
|
||||
|
||||
-- AI Studio runs with an enterprise configuration id '{0}' and configuration server URL '{1}'. The configuration plugin is active.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1454889560"] = "AI Studio runs with an enterprise configuration id '{0}' and configuration server URL '{1}'. The configuration plugin is active."
|
||||
|
||||
-- AI Studio runs with an enterprise configuration using the configuration plugin '{0}', without central configuration management.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1530477579"] = "AI Studio runs with an enterprise configuration using the configuration plugin '{0}', without central configuration management."
|
||||
|
||||
-- We use Lua as the language for plugins. Lua-CSharp lets Lua scripts communicate with AI Studio and vice versa. Thank you, Yusuke Nakada, for this great library.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T162898512"] = "We use Lua as the language for plugins. Lua-CSharp lets Lua scripts communicate with AI Studio and vice versa. Thank you, Yusuke Nakada, for this great library."
|
||||
|
||||
@@ -4189,6 +4483,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1806897624"] = "By clicking on the res
|
||||
-- Pandoc Installation
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T185447014"] = "Pandoc Installation"
|
||||
|
||||
-- Copies the configuration plugin ID to the clipboard
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1859295819"] = "Copies the configuration plugin ID to the clipboard"
|
||||
|
||||
-- Check for updates
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1890416390"] = "Check for updates"
|
||||
|
||||
@@ -4204,21 +4501,30 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1924365263"] = "This library is used t
|
||||
-- We use Rocket to implement the runtime API. This is necessary because the runtime must be able to communicate with the user interface (IPC). Rocket is a great framework for implementing web APIs in Rust.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1943216839"] = "We use Rocket to implement the runtime API. This is necessary because the runtime must be able to communicate with the user interface (IPC). Rocket is a great framework for implementing web APIs in Rust."
|
||||
|
||||
-- Copies the server URL to the clipboard
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2037899437"] = "Copies the server URL to the clipboard"
|
||||
|
||||
-- This library is used to determine the file type of a file. This is necessary, e.g., when we want to stream a file.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2173617769"] = "This library is used to determine the file type of a file. This is necessary, e.g., when we want to stream a file."
|
||||
|
||||
-- For the secure communication between the user interface and the runtime, we need to create certificates. This Rust library is great for this purpose.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2174764529"] = "For the secure communication between the user interface and the runtime, we need to create certificates. This Rust library is great for this purpose."
|
||||
|
||||
-- AI Studio runs without an enterprise configuration.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2244723851"] = "AI Studio runs without an enterprise configuration."
|
||||
|
||||
-- OK
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2246359087"] = "OK"
|
||||
|
||||
-- Configuration server:
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2272122662"] = "Configuration server:"
|
||||
|
||||
-- We must generate random numbers, e.g., for securing the interprocess communication between the user interface and the runtime. The rand library is great for this purpose.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2273492381"] = "We must generate random numbers, e.g., for securing the interprocess communication between the user interface and the runtime. The rand library is great for this purpose."
|
||||
|
||||
-- AI Studio runs with an enterprise configuration using a configuration plugin, without central configuration management.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2280402765"] = "AI Studio runs with an enterprise configuration using a configuration plugin, without central configuration management."
|
||||
|
||||
-- Configuration plugin ID:
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2301484629"] = "Configuration plugin ID:"
|
||||
|
||||
-- The C# language is used for the implementation of the user interface and the backend. To implement the user interface with C#, the Blazor technology from ASP.NET Core is used. All these technologies are integrated into the .NET SDK.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2329884315"] = "The C# language is used for the implementation of the user interface and the backend. To implement the user interface with C#, the Blazor technology from ASP.NET Core is used. All these technologies are integrated into the .NET SDK."
|
||||
|
||||
@@ -4258,6 +4564,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2765814390"] = "Determine Pandoc versi
|
||||
-- Code in the Rust language can be specified as synchronous or asynchronous. Unlike .NET and the C# language, Rust cannot execute asynchronous code by itself. Rust requires support in the form of an executor for this. Tokio is one such executor.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2777988282"] = "Code in the Rust language can be specified as synchronous or asynchronous. Unlike .NET and the C# language, Rust cannot execute asynchronous code by itself. Rust requires support in the form of an executor for this. Tokio is one such executor."
|
||||
|
||||
-- Show Details
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T27924674"] = "Show Details"
|
||||
|
||||
-- View our project roadmap and help shape AI Studio's future development.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2829971158"] = "View our project roadmap and help shape AI Studio's future development."
|
||||
|
||||
@@ -4273,12 +4582,18 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2868174483"] = "The .NET backend canno
|
||||
-- Changelog
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3017574265"] = "Changelog"
|
||||
|
||||
-- Enterprise configuration ID:
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3092349641"] = "Enterprise configuration ID:"
|
||||
|
||||
-- Connect AI Studio to your organization's data with our External Retrieval Interface (ERI).
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T313276297"] = "Connect AI Studio to your organization's data with our External Retrieval Interface (ERI)."
|
||||
|
||||
-- Have feature ideas? Submit suggestions for future AI Studio enhancements.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3178730036"] = "Have feature ideas? Submit suggestions for future AI Studio enhancements."
|
||||
|
||||
-- Hide Details
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3183837919"] = "Hide Details"
|
||||
|
||||
-- Update Pandoc
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3249965383"] = "Update Pandoc"
|
||||
|
||||
@@ -4303,6 +4618,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3563271893"] = "Motivation"
|
||||
-- This library is used to read Excel and OpenDocument spreadsheet files. This is necessary, e.g., for using spreadsheets as a data source for a chat.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3722989559"] = "This library is used to read Excel and OpenDocument spreadsheet files. This is necessary, e.g., for using spreadsheets as a data source for a chat."
|
||||
|
||||
-- AI Studio runs with an enterprise configuration and a configuration server. The configuration plugin is active.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3741877842"] = "AI Studio runs with an enterprise configuration and a configuration server. The configuration plugin is active."
|
||||
|
||||
-- this version does not met the requirements
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3813932670"] = "this version does not met the requirements"
|
||||
|
||||
@@ -4345,9 +4663,15 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T639371534"] = "Did you find a bug or a
|
||||
-- This Rust library is used to output the app's messages to the terminal. This is helpful during development and troubleshooting. This feature is initially invisible; when the app is started via the terminal, the messages become visible.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T64689067"] = "This Rust library is used to output the app's messages to the terminal. This is helpful during development and troubleshooting. This feature is initially invisible; when the app is started via the terminal, the messages become visible."
|
||||
|
||||
-- Copies the config ID to the clipboard
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T788846912"] = "Copies the config ID to the clipboard"
|
||||
|
||||
-- installed by AI Studio
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T833849470"] = "installed by AI Studio"
|
||||
|
||||
-- We use this library to be able to read PowerPoint files. This allows us to insert content from slides into prompts and take PowerPoint files into account in RAG processes. We thank Nils Kruthoff for his work on this Rust crate.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T855925638"] = "We use this library to be able to read PowerPoint files. This allows us to insert content from slides into prompts and take PowerPoint files into account in RAG processes. We thank Nils Kruthoff for his work on this Rust crate."
|
||||
|
||||
-- For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T870640199"] = "For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose."
|
||||
|
||||
@@ -4357,6 +4681,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T986578435"] = "Install Pandoc"
|
||||
-- Get coding and debugging support from an LLM.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T1243850917"] = "Get coding and debugging support from an LLM."
|
||||
|
||||
-- Analyze a document regarding defined rules and extract key information.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T1271524187"] = "Analyze a document regarding defined rules and extract key information."
|
||||
|
||||
-- Business
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T131837803"] = "Business"
|
||||
|
||||
@@ -4402,6 +4729,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T2547582747"] = "Synonyms"
|
||||
-- Find synonyms for a given word or phrase.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T2712131461"] = "Find synonyms for a given word or phrase."
|
||||
|
||||
-- Document Analysis
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T2770149758"] = "Document Analysis"
|
||||
|
||||
-- AI Studio Development
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T2830810750"] = "AI Studio Development"
|
||||
|
||||
@@ -4516,8 +4846,8 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T1702902297"] = "Introduction"
|
||||
-- Vision
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T1892426825"] = "Vision"
|
||||
|
||||
-- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT4o, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2217921237"] = "You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT4o, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities."
|
||||
-- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT5, o1, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using vLLM, llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2183503084"] = "You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT5, o1, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using vLLM, llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities."
|
||||
|
||||
-- Let's get started
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2331588413"] = "Let's get started"
|
||||
@@ -4687,8 +5017,8 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T1674355816"] = "Tried to com
|
||||
-- Tried to stream the LLM provider '{0}' answer. Was not able to read the stream. The message is: '{1}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T1856278860"] = "Tried to stream the LLM provider '{0}' answer. Was not able to read the stream. The message is: '{1}'"
|
||||
|
||||
-- Tried to communicate with the LLM provider '{0}'. Even after {1} retries, there were some problems with the request. The provider message is: '{2}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2249520705"] = "Tried to communicate with the LLM provider '{0}'. Even after {1} retries, there were some problems with the request. The provider message is: '{2}'"
|
||||
-- Tried to communicate with the LLM provider '{0}'. Even after {1} retries, there were some problems with the request. The provider message is: '{2}'.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2181034173"] = "Tried to communicate with the LLM provider '{0}'. Even after {1} retries, there were some problems with the request. The provider message is: '{2}'."
|
||||
|
||||
-- Tried to communicate with the LLM provider '{0}'. Something was not found. The provider message is: '{1}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2780552614"] = "Tried to communicate with the LLM provider '{0}'. Something was not found. The provider message is: '{1}'"
|
||||
@@ -4810,6 +5140,9 @@ UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T2128088682
|
||||
-- Navigation expands on mouse hover
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T2195945406"] = "Navigation expands on mouse hover"
|
||||
|
||||
-- Install updates manually
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T220653235"] = "Install updates manually"
|
||||
|
||||
-- Also show features ready for release; these should be stable
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T2301448762"] = "Also show features ready for release; these should be stable"
|
||||
|
||||
@@ -4849,6 +5182,9 @@ UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3137986690
|
||||
-- Delete disappearing chats older than 180 days
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3491430707"] = "Delete disappearing chats older than 180 days"
|
||||
|
||||
-- Install updates automatically
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3569059463"] = "Install updates automatically"
|
||||
|
||||
-- Disable workspaces
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3612390107"] = "Disable workspaces"
|
||||
|
||||
@@ -4918,6 +5254,9 @@ UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::PREVIEWFEATURESEXTENSIONS::T1587
|
||||
-- Read PDF: Preview of our PDF reading system where you can read and extract text from PDF files
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::PREVIEWFEATURESEXTENSIONS::T1847148141"] = "Read PDF: Preview of our PDF reading system where you can read and extract text from PDF files"
|
||||
|
||||
-- Document Analysis: Preview of our document analysis system where you can analyze and extract information from documents
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::PREVIEWFEATURESEXTENSIONS::T1848209619"] = "Document Analysis: Preview of our document analysis system where you can analyze and extract information from documents"
|
||||
|
||||
-- Plugins: Preview of our plugin system where you can extend the functionality of the app
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::PREVIEWFEATURESEXTENSIONS::T2056842933"] = "Plugins: Preview of our plugin system where you can extend the functionality of the app"
|
||||
|
||||
@@ -5038,6 +5377,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T2684676843"] = "Text Su
|
||||
-- Synonym Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T2921123194"] = "Synonym Assistant"
|
||||
|
||||
-- Document Analysis Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T348883878"] = "Document Analysis Assistant"
|
||||
|
||||
-- Translation Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T3887962308"] = "Translation Assistant"
|
||||
|
||||
@@ -5224,6 +5566,18 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOC::T567205144"] = "It seems that Pandoc i
|
||||
-- The latest Pandoc version was not found, installing version {0} instead.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOC::T726914939"] = "The latest Pandoc version was not found, installing version {0} instead."
|
||||
|
||||
-- Pandoc is required for Microsoft Word export.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T1473115556"] = "Pandoc is required for Microsoft Word export."
|
||||
|
||||
-- Pandoc Installation
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T185447014"] = "Pandoc Installation"
|
||||
|
||||
-- Error during Microsoft Word export
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T3290596792"] = "Error during Microsoft Word export"
|
||||
|
||||
-- Microsoft Word export successful
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T4256043333"] = "Microsoft Word export successful"
|
||||
|
||||
-- The table AUTHORS does not exist or is using an invalid syntax.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINBASE::T1068328139"] = "The table AUTHORS does not exist or is using an invalid syntax."
|
||||
|
||||
@@ -5383,9 +5737,6 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T1148682011
|
||||
-- The CONFIG table does not exist or is not a valid table.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T3331620576"] = "The CONFIG table does not exist or is not a valid table."
|
||||
|
||||
-- The LLM_PROVIDERS table does not exist or is not a valid table.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T806592324"] = "The LLM_PROVIDERS table does not exist or is not a valid table."
|
||||
|
||||
-- The field IETF_TAG does not exist or is not a valid string.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINLANGUAGE::T1796010240"] = "The field IETF_TAG does not exist or is not a valid string."
|
||||
|
||||
@@ -5476,6 +5827,12 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::RAG::RAGPROCESSES::AISRCSELWITHRETCTXVAL::T304
|
||||
-- AI source selection with AI retrieval context validation
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RAG::RAGPROCESSES::AISRCSELWITHRETCTXVAL::T3775725978"] = "AI source selection with AI retrieval context validation"
|
||||
|
||||
-- Executable Files
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPEFILTER::T2217313358"] = "Executable Files"
|
||||
|
||||
-- All Video Files
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPEFILTER::T2850789856"] = "All Video Files"
|
||||
|
||||
-- PDF Files
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPEFILTER::T3108466742"] = "PDF Files"
|
||||
|
||||
@@ -5509,6 +5866,15 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::SECRETS::T4007657575"]
|
||||
-- No update found.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::UPDATESERVICE::T1015418291"] = "No update found."
|
||||
|
||||
-- Failed to install update automatically. Please try again manually.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::UPDATESERVICE::T3709709946"] = "Failed to install update automatically. Please try again manually."
|
||||
|
||||
-- Sources provided by the data providers
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SOURCEEXTENSIONS::T4174900468"] = "Sources provided by the data providers"
|
||||
|
||||
-- Sources provided by the AI
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SOURCEEXTENSIONS::T4261248356"] = "Sources provided by the AI"
|
||||
|
||||
-- The hostname is not a valid HTTP(S) URL.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::DATASOURCEVALIDATION::T1013354736"] = "The hostname is not a valid HTTP(S) URL."
|
||||
|
||||
@@ -5611,5 +5977,8 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::PROVIDERVALIDATION::T497939286"] =
|
||||
-- Please select a model.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::PROVIDERVALIDATION::T818893091"] = "Please select a model."
|
||||
|
||||
-- Unnamed workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T1307384014"] = "Unnamed workspace"
|
||||
|
||||
-- Delete Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T2244038752"] = "Delete Chat"
|
||||
@@ -3,10 +3,10 @@
|
||||
|
||||
@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)"/>
|
||||
<ReadWebContent @bind-Content="@this.inputLegalDocument" ProviderSettings="@this.providerSettings" @bind-AgentIsRunning="@this.isAgentRunning" @bind-Preselect="@this.showWebContentReader" @bind-PreselectContentCleanerAgent="@this.useContentCleanerAgent"/>
|
||||
}
|
||||
|
||||
<ReadPDFContent @bind-PDFContent="@this.inputLegalDocument"/>
|
||||
<ReadFileContent @bind-FileContent="@this.inputLegalDocument"/>
|
||||
<MudTextField T="string" Disabled="@this.isAgentRunning" @bind-Text="@this.inputLegalDocument" Validation="@this.ValidatingLegalDocument" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="@T("Legal document")" Variant="Variant.Outlined" Lines="12" AutoGrow="@true" MaxLines="24" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
<MudTextField T="string" Disabled="@this.isAgentRunning" @bind-Text="@this.inputQuestions" Validation="@this.ValidatingQuestions" AdornmentIcon="@Icons.Material.Filled.QuestionAnswer" Adornment="Adornment.Start" Label="@T("Your questions")" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||
@@ -37,11 +37,27 @@ public partial class AssistantLegalCheck : AssistantBaseCore<SettingsDialogLegal
|
||||
{
|
||||
this.inputLegalDocument = string.Empty;
|
||||
this.inputQuestions = string.Empty;
|
||||
this.MightPreselectValues();
|
||||
if (!this.MightPreselectValues())
|
||||
{
|
||||
this.showWebContentReader = false;
|
||||
this.useContentCleanerAgent = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MightPreselectValues() => false;
|
||||
|
||||
protected override bool MightPreselectValues()
|
||||
{
|
||||
if (this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)
|
||||
{
|
||||
this.showWebContentReader = this.SettingsManager.ConfigurationData.LegalCheck.PreselectWebContentReader;
|
||||
this.useContentCleanerAgent = this.SettingsManager.ConfigurationData.LegalCheck.PreselectContentCleanerAgent;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool showWebContentReader;
|
||||
private bool useContentCleanerAgent;
|
||||
private bool isAgentRunning;
|
||||
private string inputLegalDocument = string.Empty;
|
||||
private string inputQuestions = string.Empty;
|
||||
|
||||
@@ -85,7 +85,7 @@ public partial class AssistantMyTasks : AssistantBaseCore<SettingsDialogMyTasks>
|
||||
|
||||
private string? ValidateProfile(Profile profile)
|
||||
{
|
||||
if(profile == default || profile == Profile.NO_PROFILE)
|
||||
if(profile == Profile.NO_PROFILE)
|
||||
return T("Please select one of your profiles.");
|
||||
|
||||
return null;
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
|
||||
@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)"/>
|
||||
<ReadWebContent @bind-Content="@this.inputText" ProviderSettings="@this.providerSettings" @bind-AgentIsRunning="@this.isAgentRunning" @bind-Preselect="@this.showWebContentReader" @bind-PreselectContentCleanerAgent="@this.useContentCleanerAgent"/>
|
||||
}
|
||||
|
||||
<ReadPDFContent @bind-PDFContent="@this.inputText"/>
|
||||
<ReadFileContent @bind-FileContent="@this.inputText"/>
|
||||
<MudTextField T="string" Disabled="@this.isAgentRunning" @bind-Text="@this.inputText" Validation="@this.ValidatingText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="@T("Your input")" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.Name())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="@T("Target language")" AllowOther="@true" @bind-OtherInput="@this.customTargetLanguage" OtherValue="CommonLanguages.OTHER" LabelOther="@T("Custom target language")" ValidateOther="@this.ValidateCustomLanguage" />
|
||||
<EnumSelection T="Complexity" NameFunc="@(complexity => complexity.Name())" @bind-Value="@this.selectedComplexity" Icon="@Icons.Material.Filled.Layers" Label="@T("Target complexity")" AllowOther="@true" @bind-OtherInput="@this.expertInField" OtherValue="Complexity.SCIENTIFIC_LANGUAGE_OTHER_EXPERTS" LabelOther="@T("Your expertise")" ValidateOther="@this.ValidateExpertInField" />
|
||||
<MudTextField T="string" AutoGrow="true" Lines="2" @bind-Text="@this.importantAspects" class="mb-3" Label="@T("(Optional) Important Aspects")" HelperText="@T("(Optional) Specify aspects for the LLM to focus on when generating a summary, such as summary length or specific topics to emphasize.")" ShrinkLabel="true" Variant="Variant.Outlined" AdornmentIcon="@Icons.Material.Filled.List" Adornment="Adornment.Start"/>
|
||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||
@@ -12,13 +12,13 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
|
||||
protected override string Description => T("Summarize long text into a shorter version while retaining the main points. You might want to change the language of the summary to make it more readable. It is also possible to change the complexity of the summary to make it easy to understand.");
|
||||
|
||||
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.
|
||||
""";
|
||||
$"""
|
||||
You get a long text as input. The text is marked with ```. The user wants to get a summary of the text.
|
||||
{this.selectedTargetLanguage.PromptSummarizing(this.customTargetLanguage)}
|
||||
{this.selectedComplexity.Prompt(this.expertInField)}
|
||||
{this.PromptImportantAspects()}
|
||||
In any case, only use information that is provided in the text for the summary.
|
||||
""";
|
||||
|
||||
protected override bool AllowProfiles => false;
|
||||
|
||||
@@ -40,10 +40,13 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
|
||||
this.inputText = string.Empty;
|
||||
if(!this.MightPreselectValues())
|
||||
{
|
||||
this.showWebContentReader = false;
|
||||
this.useContentCleanerAgent = false;
|
||||
this.selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
this.customTargetLanguage = string.Empty;
|
||||
this.selectedComplexity = Complexity.NO_CHANGE;
|
||||
this.expertInField = string.Empty;
|
||||
this.importantAspects = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,22 +54,28 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
|
||||
{
|
||||
if (this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions)
|
||||
{
|
||||
this.showWebContentReader = this.SettingsManager.ConfigurationData.TextSummarizer.PreselectWebContentReader;
|
||||
this.useContentCleanerAgent = this.SettingsManager.ConfigurationData.TextSummarizer.PreselectContentCleanerAgent;
|
||||
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;
|
||||
this.importantAspects = this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedImportantAspects;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool showWebContentReader;
|
||||
private bool useContentCleanerAgent;
|
||||
private string inputText = string.Empty;
|
||||
private bool isAgentRunning;
|
||||
private CommonLanguages selectedTargetLanguage;
|
||||
private string customTargetLanguage = string.Empty;
|
||||
private Complexity selectedComplexity;
|
||||
private string expertInField = string.Empty;
|
||||
private string importantAspects = string.Empty;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
@@ -105,6 +114,17 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
|
||||
return null;
|
||||
}
|
||||
|
||||
private string PromptImportantAspects()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(this.importantAspects))
|
||||
return string.Empty;
|
||||
|
||||
return $"""
|
||||
Emphasize the following aspects in your summary:
|
||||
{this.importantAspects}
|
||||
""";
|
||||
}
|
||||
|
||||
private async Task SummarizeText()
|
||||
{
|
||||
await this.form!.Validate();
|
||||
@@ -114,11 +134,7 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
|
||||
this.CreateChatThread();
|
||||
var time = this.AddUserRequest(
|
||||
$"""
|
||||
{this.selectedTargetLanguage.PromptSummarizing(this.customTargetLanguage)}
|
||||
{this.selectedComplexity.Prompt(this.expertInField)}
|
||||
|
||||
Please summarize the following text:
|
||||
|
||||
```
|
||||
{this.inputText}
|
||||
```
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
|
||||
@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)"/>
|
||||
<ReadWebContent @bind-Content="@this.inputText" ProviderSettings="@this.providerSettings" @bind-AgentIsRunning="@this.isAgentRunning" @bind-Preselect="@this.showWebContentReader" @bind-PreselectContentCleanerAgent="@this.useContentCleanerAgent"/>
|
||||
}
|
||||
|
||||
<ReadPDFContent @bind-PDFContent="@this.inputText"/>
|
||||
<ReadFileContent @bind-FileContent="@this.inputText"/>
|
||||
|
||||
<MudTextSwitch Label="@T("Live translation")" @bind-Value="@this.liveTranslation" LabelOn="@T("Live translation")" LabelOff="@T("No live translation")"/>
|
||||
@if (this.liveTranslation)
|
||||
{
|
||||
|
||||
@@ -40,6 +40,8 @@ public partial class AssistantTranslation : AssistantBaseCore<SettingsDialogTran
|
||||
this.inputTextLastTranslation = string.Empty;
|
||||
if (!this.MightPreselectValues())
|
||||
{
|
||||
this.showWebContentReader = false;
|
||||
this.useContentCleanerAgent = false;
|
||||
this.liveTranslation = false;
|
||||
this.selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
this.customTargetLanguage = string.Empty;
|
||||
@@ -50,6 +52,8 @@ public partial class AssistantTranslation : AssistantBaseCore<SettingsDialogTran
|
||||
{
|
||||
if (this.SettingsManager.ConfigurationData.Translation.PreselectOptions)
|
||||
{
|
||||
this.showWebContentReader = this.SettingsManager.ConfigurationData.Translation.PreselectWebContentReader;
|
||||
this.useContentCleanerAgent = this.SettingsManager.ConfigurationData.Translation.PreselectContentCleanerAgent;
|
||||
this.liveTranslation = this.SettingsManager.ConfigurationData.Translation.PreselectLiveTranslation;
|
||||
this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.Translation.PreselectedTargetLanguage;
|
||||
this.customTargetLanguage = this.SettingsManager.ConfigurationData.Translation.PreselectOtherLanguage;
|
||||
@@ -59,6 +63,8 @@ public partial class AssistantTranslation : AssistantBaseCore<SettingsDialogTran
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool showWebContentReader;
|
||||
private bool useContentCleanerAgent;
|
||||
private bool liveTranslation;
|
||||
private bool isAgentRunning;
|
||||
private string inputText = string.Empty;
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace AIStudio.Chat;
|
||||
/// </summary>
|
||||
public sealed record ChatThread
|
||||
{
|
||||
private static readonly ILogger<ChatThread> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ChatThread>();
|
||||
|
||||
/// <summary>
|
||||
/// The unique identifier of the chat thread.
|
||||
/// </summary>
|
||||
@@ -59,11 +61,6 @@ public sealed record ChatThread
|
||||
/// The name of the chat thread. Usually generated by an AI model or manually edited by the user.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The seed for the chat thread. Some providers use this to generate deterministic results.
|
||||
/// </summary>
|
||||
public int Seed { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The current system prompt for the chat thread.
|
||||
@@ -87,9 +84,8 @@ public sealed record ChatThread
|
||||
/// </remarks>
|
||||
/// <param name="settingsManager">The settings manager instance to use.</param>
|
||||
/// <param name="chatThread">The chat thread to prepare the system prompt for.</param>
|
||||
/// <param name="logger">The logger instance to use.</param>
|
||||
/// <returns>The prepared system prompt.</returns>
|
||||
public string PrepareSystemPrompt(SettingsManager settingsManager, ChatThread chatThread, ILogger logger)
|
||||
public string PrepareSystemPrompt(SettingsManager settingsManager, ChatThread chatThread)
|
||||
{
|
||||
//
|
||||
// Use the information from the chat template, if provided. Otherwise, use the default system prompt
|
||||
@@ -100,16 +96,16 @@ public sealed record ChatThread
|
||||
systemPromptTextWithChatTemplate = chatThread.SystemPrompt;
|
||||
else
|
||||
{
|
||||
if(!Guid.TryParse(chatThread.SelectedChatTemplate, out var chatTeamplateId))
|
||||
if(!Guid.TryParse(chatThread.SelectedChatTemplate, out var chatTemplateId))
|
||||
systemPromptTextWithChatTemplate = chatThread.SystemPrompt;
|
||||
else
|
||||
{
|
||||
if(chatThread.SelectedChatTemplate == ChatTemplate.NO_CHAT_TEMPLATE.Id || chatTeamplateId == Guid.Empty)
|
||||
if(chatThread.SelectedChatTemplate == ChatTemplate.NO_CHAT_TEMPLATE.Id || chatTemplateId == Guid.Empty)
|
||||
systemPromptTextWithChatTemplate = chatThread.SystemPrompt;
|
||||
else
|
||||
{
|
||||
var chatTemplate = settingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatThread.SelectedChatTemplate);
|
||||
if(chatTemplate == default)
|
||||
if(chatTemplate == null)
|
||||
systemPromptTextWithChatTemplate = chatThread.SystemPrompt;
|
||||
else
|
||||
{
|
||||
@@ -121,7 +117,12 @@ public sealed record ChatThread
|
||||
}
|
||||
}
|
||||
|
||||
logger.LogInformation(logMessage);
|
||||
// We need a way to save the changed system prompt in our chat thread.
|
||||
// Otherwise, the chat thread will always tell us that it is using the
|
||||
// default system prompt:
|
||||
chatThread = chatThread with { SystemPrompt = systemPromptTextWithChatTemplate };
|
||||
|
||||
LOGGER.LogInformation(logMessage);
|
||||
|
||||
//
|
||||
// Add augmented data, if available:
|
||||
@@ -139,9 +140,9 @@ public sealed record ChatThread
|
||||
};
|
||||
|
||||
if(isAugmentedDataAvailable)
|
||||
logger.LogInformation("Augmented data is available for the chat thread.");
|
||||
LOGGER.LogInformation("Augmented data is available for the chat thread.");
|
||||
else
|
||||
logger.LogInformation("No augmented data is available for the chat thread.");
|
||||
LOGGER.LogInformation("No augmented data is available for the chat thread.");
|
||||
|
||||
|
||||
//
|
||||
@@ -177,7 +178,7 @@ public sealed record ChatThread
|
||||
}
|
||||
}
|
||||
|
||||
logger.LogInformation(logMessage);
|
||||
LOGGER.LogInformation(logMessage);
|
||||
return systemPromptText;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,12 +30,12 @@ public class ContentBlock
|
||||
/// </summary>
|
||||
public bool HideFromUser { get; init; }
|
||||
|
||||
public ContentBlock DeepClone() => new()
|
||||
public ContentBlock DeepClone(bool changeHideState = false, bool hideFromUser = true) => new()
|
||||
{
|
||||
Time = this.Time,
|
||||
ContentType = this.ContentType,
|
||||
Content = this.Content?.DeepClone(),
|
||||
Role = this.Role,
|
||||
HideFromUser = this.HideFromUser,
|
||||
HideFromUser = changeHideState ? hideFromUser : this.HideFromUser,
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
@using AIStudio.Tools
|
||||
@using MudBlazor
|
||||
@using AIStudio.Components
|
||||
@inherits AIStudio.Components.MSGComponentBase
|
||||
|
||||
<MudCard Class="@this.CardClasses" Outlined="@true">
|
||||
<MudCardHeader>
|
||||
<CardHeaderAvatar>
|
||||
@@ -10,10 +12,18 @@
|
||||
</CardHeaderAvatar>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.body1">
|
||||
@this.Role.ToName() (@this.Time)
|
||||
@this.Role.ToName() (@this.Time.LocalDateTime)
|
||||
</MudText>
|
||||
</CardHeaderContent>
|
||||
<CardHeaderActions>
|
||||
@if (this.Content.Sources.Count > 0)
|
||||
{
|
||||
<MudTooltip Text="@T("Number of sources")" Placement="Placement.Bottom">
|
||||
<MudBadge Content="@this.Content.Sources.Count" Color="Color.Primary" Overlap="true" BadgeClass="sources-card-header">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Link" />
|
||||
</MudBadge>
|
||||
</MudTooltip>
|
||||
}
|
||||
@if (this.IsSecondToLastBlock && this.Role is ChatRole.USER && this.EditLastUserBlockFunc is not null)
|
||||
{
|
||||
<MudTooltip Text="@T("Edit")" Placement="Placement.Bottom">
|
||||
@@ -38,9 +48,14 @@
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error" OnClick="@this.RemoveBlock"/>
|
||||
</MudTooltip>
|
||||
}
|
||||
<MudTooltip Text="@T("Copies the content to the clipboard")" Placement="Placement.Bottom">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.ContentCopy" Color="Color.Default" OnClick="@this.CopyToClipboard"/>
|
||||
</MudTooltip>
|
||||
|
||||
@if (this.Role is ChatRole.AI)
|
||||
{
|
||||
<MudTooltip Text="@T("Export Chat to Microsoft Word")" Placement="Placement.Bottom">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Save" OnClick="@this.ExportToWord"/>
|
||||
</MudTooltip>
|
||||
}
|
||||
<MudCopyClipboardButton Content="@this.Content" Type="@this.Type" Size="Size.Medium"/>
|
||||
</CardHeaderActions>
|
||||
</MudCardHeader>
|
||||
<MudCardContent>
|
||||
@@ -67,12 +82,16 @@
|
||||
@if (this.Content.IsStreaming)
|
||||
{
|
||||
<MudText Typo="Typo.body1" Style="white-space: pre-wrap;">
|
||||
@textContent.Text
|
||||
@textContent.Text.RemoveThinkTags()
|
||||
</MudText>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudMarkdown Value="@textContent.Text" OverrideHeaderTypo="@Markdown.OverrideHeaderTypo" CodeBlockTheme="@this.CodeColorPalette"/>
|
||||
<MudMarkdown Value="@textContent.Text.RemoveThinkTags().Trim()" Props="Markdown.DefaultConfig" Styling="@this.MarkdownStyling" />
|
||||
@if (textContent.Sources.Count > 0)
|
||||
{
|
||||
<MudMarkdown Value="@textContent.Sources.ToMarkdown()" Props="Markdown.DefaultConfig" Styling="@this.MarkdownStyling" />
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using AIStudio.Components;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Chat;
|
||||
@@ -61,15 +60,12 @@ public partial class ContentBlockComponent : MSGComponentBase
|
||||
[Parameter]
|
||||
public Func<bool> RegenerateEnabled { get; set; } = () => false;
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ISnackbar Snackbar { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
private bool HideContent { get; set; }
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
@@ -115,33 +111,15 @@ public partial class ContentBlockComponent : MSGComponentBase
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Copy this block's content to the clipboard.
|
||||
/// </summary>
|
||||
private async Task CopyToClipboard()
|
||||
{
|
||||
switch (this.Type)
|
||||
{
|
||||
case ContentType.TEXT:
|
||||
var textContent = (ContentText) this.Content;
|
||||
await this.RustService.CopyText2Clipboard(this.Snackbar, textContent.Text);
|
||||
break;
|
||||
|
||||
default:
|
||||
this.Snackbar.Add(T("Cannot copy this content type to clipboard!"), Severity.Error, config =>
|
||||
{
|
||||
config.Icon = Icons.Material.Filled.ContentCopy;
|
||||
config.IconSize = Size.Large;
|
||||
config.IconColor = Color.Error;
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private string CardClasses => $"my-2 rounded-lg {this.Class}";
|
||||
|
||||
private CodeBlockTheme CodeColorPalette => this.SettingsManager.IsDarkMode ? CodeBlockTheme.Dark : CodeBlockTheme.Default;
|
||||
|
||||
private MudMarkdownStyling MarkdownStyling => new()
|
||||
{
|
||||
CodeBlock = { Theme = this.CodeColorPalette },
|
||||
};
|
||||
|
||||
private async Task RemoveBlock()
|
||||
{
|
||||
@@ -158,6 +136,11 @@ public partial class ContentBlockComponent : MSGComponentBase
|
||||
await this.RemoveBlockFunc(this.Content);
|
||||
}
|
||||
|
||||
private async Task ExportToWord()
|
||||
{
|
||||
await PandocExport.ToMicrosoftWord(this.RustService, this.DialogService, T("Export Chat to Microsoft Word"), this.Content);
|
||||
}
|
||||
|
||||
private async Task RegenerateBlock()
|
||||
{
|
||||
if (this.RegenerateFunc is null)
|
||||
@@ -204,4 +187,5 @@ public partial class ContentBlockComponent : MSGComponentBase
|
||||
if (edit.HasValue && edit.Value)
|
||||
await this.EditLastUserBlockFunc(this.Content);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,7 +28,10 @@ public sealed class ContentImage : IContent, IImageSource
|
||||
public Func<Task> StreamingEvent { get; set; } = () => Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastPrompt, ChatThread? chatChatThread, CancellationToken token = default)
|
||||
public List<Source> Sources { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastUserPrompt, ChatThread? chatChatThread, CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace AIStudio.Chat;
|
||||
/// </summary>
|
||||
public sealed class ContentText : IContent
|
||||
{
|
||||
private static readonly ILogger<ContentText> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ContentText>();
|
||||
|
||||
/// <summary>
|
||||
/// The minimum time between two streaming events, when the user
|
||||
/// enables the energy saving mode.
|
||||
@@ -24,7 +26,7 @@ public sealed class ContentText : IContent
|
||||
public bool InitialRemoteWait { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
// [JsonIgnore]
|
||||
[JsonIgnore]
|
||||
public bool IsStreaming { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -36,30 +38,31 @@ public sealed class ContentText : IContent
|
||||
public Func<Task> StreamingEvent { get; set; } = () => Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastPrompt, ChatThread? chatThread, CancellationToken token = default)
|
||||
public List<Source> Sources { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastUserPrompt, ChatThread? chatThread, CancellationToken token = default)
|
||||
{
|
||||
if(chatThread is null)
|
||||
return new();
|
||||
|
||||
if(!chatThread.IsLLMProviderAllowed(provider))
|
||||
{
|
||||
var logger = Program.SERVICE_PROVIDER.GetService<ILogger<ContentText>>()!;
|
||||
logger.LogError("The provider is not allowed for this chat thread due to data security reasons. Skipping the AI process.");
|
||||
LOGGER.LogError("The provider is not allowed for this chat thread due to data security reasons. Skipping the AI process.");
|
||||
return chatThread;
|
||||
}
|
||||
|
||||
// Call the RAG process. Right now, we only have one RAG process:
|
||||
if (lastPrompt is not null)
|
||||
if (lastUserPrompt is not null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var rag = new AISrcSelWithRetCtxVal();
|
||||
chatThread = await rag.ProcessAsync(provider, lastPrompt, chatThread, token);
|
||||
chatThread = await rag.ProcessAsync(provider, lastUserPrompt, chatThread, token);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var logger = Program.SERVICE_PROVIDER.GetService<ILogger<ContentText>>()!;
|
||||
logger.LogError(e, "Skipping the RAG process due to an error.");
|
||||
LOGGER.LogError(e, "Skipping the RAG process due to an error.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +83,7 @@ public sealed class ContentText : IContent
|
||||
this.InitialRemoteWait = true;
|
||||
|
||||
// Iterate over the responses from the AI:
|
||||
await foreach (var deltaText in provider.StreamChatCompletion(chatModel, chatThread, settings, token))
|
||||
await foreach (var contentStreamChunk in provider.StreamChatCompletion(chatModel, chatThread, settings, token))
|
||||
{
|
||||
// When the user cancels the request, we stop the loop:
|
||||
if (token.IsCancellationRequested)
|
||||
@@ -91,7 +94,10 @@ public sealed class ContentText : IContent
|
||||
this.IsStreaming = true;
|
||||
|
||||
// Add the response to the text:
|
||||
this.Text += deltaText;
|
||||
this.Text += contentStreamChunk;
|
||||
|
||||
// Merge the sources:
|
||||
this.Sources.MergeSources(contentStreamChunk.Sources);
|
||||
|
||||
// Notify the UI that the content has changed,
|
||||
// depending on the energy saving mode:
|
||||
@@ -119,6 +125,8 @@ public sealed class ContentText : IContent
|
||||
this.InitialRemoteWait = false;
|
||||
this.IsStreaming = false;
|
||||
}, token);
|
||||
|
||||
this.Text = this.Text.RemoveThinkTags().Trim();
|
||||
|
||||
// Inform the UI that the streaming is done:
|
||||
await this.StreamingDone();
|
||||
|
||||
@@ -37,11 +37,21 @@ public interface IContent
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public Func<Task> StreamingDone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The provided sources, if any.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We cannot use ISource here because System.Text.Json does not support
|
||||
/// interface serialization. So we have to use a concrete class.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public List<Source> Sources { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Uses the provider to create the content.
|
||||
/// </summary>
|
||||
public Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastPrompt, ChatThread? chatChatThread, CancellationToken token = default);
|
||||
public Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastUserPrompt, ChatThread? chatChatThread, CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep copy
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace AIStudio.Chat;
|
||||
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static string RemoveThinkTags(this string input)
|
||||
{
|
||||
const string OPEN_TAG = "<think>";
|
||||
const string CLOSE_TAG = "</think>";
|
||||
if (string.IsNullOrWhiteSpace(input) || !input.StartsWith(OPEN_TAG, StringComparison.Ordinal))
|
||||
return input;
|
||||
|
||||
var endIndex = input.IndexOf(CLOSE_TAG, StringComparison.Ordinal);
|
||||
if (endIndex == -1)
|
||||
return string.Empty;
|
||||
|
||||
return input[(endIndex + CLOSE_TAG.Length)..];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
@inherits MSGComponentBase
|
||||
|
||||
<div @onmouseenter="@this.OnMouseEnter" @onmouseleave="@this.OnMouseLeave">
|
||||
<MudLink OnClick="@(() => this.AddFilesManually())" Style="text-decoration: none;">
|
||||
<MudPaper Height="20em" Outlined="true" Class="@this.dragClass" Style="overflow-y: auto;">
|
||||
<MudText Typo="Typo.h6">
|
||||
@T("Drag and drop files here or click to attach documents.")
|
||||
</MudText>
|
||||
@foreach (var fileInfo in this.DocumentPaths.Select(file => new FileInfo(file)))
|
||||
{
|
||||
<MudBadge Origin="Origin.TopCenter" Icon="@Icons.Material.Filled.Search" Color="Color.Primary" Overlap="true" Bordered="true" OnClick="@(() => this.InvestigateFile(@fileInfo))">
|
||||
<MudChip T="string" Color="Color.Dark" Text="@fileInfo.Name" tabindex="-1" Icon="@Icons.Material.Filled.Search" OnClick="@(() => this.InvestigateFile(@fileInfo))" OnClose="@(() => this.RemoveDocumentPathFromDocumentPaths(@fileInfo))"/>
|
||||
</MudBadge>
|
||||
}
|
||||
</MudPaper>
|
||||
</MudLink>
|
||||
<MudButton OnClick="@(async () => await this.ClearAllFiles())" Variant="Variant.Filled" Color="Color.Info" Class="mt-2" StartIcon="@Icons.Material.Filled.Delete">
|
||||
@T("Clear file list")
|
||||
</MudButton>
|
||||
</div>
|
||||
@@ -0,0 +1,185 @@
|
||||
using AIStudio.Dialogs;
|
||||
using AIStudio.Tools.Rust;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
public partial class AttachDocuments : MSGComponentBase
|
||||
{
|
||||
[Parameter]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public HashSet<string> DocumentPaths { get; set; } = [];
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<HashSet<string>> DocumentPathsChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<HashSet<string>, Task> OnChange { get; set; } = _ => Task.CompletedTask;
|
||||
|
||||
/// <summary>
|
||||
/// Catch all documents that are hovered over the AI Studio window and not only over the drop zone.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool CatchAllDocuments { get; set; }
|
||||
|
||||
[Inject]
|
||||
private ILogger<AttachDocuments> Logger { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
#region Overrides of MSGComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
this.ApplyFilters([], [ Event.TAURI_EVENT_RECEIVED ]);
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
protected override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||
{
|
||||
switch (triggeredEvent)
|
||||
{
|
||||
case Event.TAURI_EVENT_RECEIVED when data is TauriEvent { EventType: TauriEventType.FILE_DROP_HOVERED }:
|
||||
if(!this.isComponentHovered && !this.CatchAllDocuments)
|
||||
{
|
||||
this.Logger.LogDebug("Attach documents component '{Name}' is not hovered, ignoring file drop hovered event.", this.Name);
|
||||
return;
|
||||
}
|
||||
|
||||
this.SetDragClass();
|
||||
this.StateHasChanged();
|
||||
break;
|
||||
|
||||
case Event.TAURI_EVENT_RECEIVED when data is TauriEvent { EventType: TauriEventType.FILE_DROP_DROPPED, Payload: var paths }:
|
||||
if(!this.isComponentHovered && !this.CatchAllDocuments)
|
||||
{
|
||||
this.Logger.LogDebug("Attach documents component '{Name}' is not hovered, ignoring file drop dropped event.", this.Name);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var path in paths)
|
||||
{
|
||||
if(!await this.IsFileExtensionValid(path))
|
||||
continue;
|
||||
|
||||
this.DocumentPaths.Add(path);
|
||||
}
|
||||
|
||||
await this.DocumentPathsChanged.InvokeAsync(this.DocumentPaths);
|
||||
await this.OnChange(this.DocumentPaths);
|
||||
this.StateHasChanged();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private const string DEFAULT_DRAG_CLASS = "relative rounded-lg border-2 border-dashed pa-4 mt-4 mud-width-full mud-height-full";
|
||||
|
||||
private string dragClass = DEFAULT_DRAG_CLASS;
|
||||
|
||||
private bool isComponentHovered;
|
||||
|
||||
private async Task AddFilesManually()
|
||||
{
|
||||
var selectedFile = await this.RustService.SelectFile(T("Select a file to attach"));
|
||||
if (selectedFile.UserCancelled)
|
||||
return;
|
||||
|
||||
if (!File.Exists(selectedFile.SelectedFilePath))
|
||||
return;
|
||||
|
||||
if (!await this.IsFileExtensionValid(selectedFile.SelectedFilePath))
|
||||
return;
|
||||
|
||||
this.DocumentPaths.Add(selectedFile.SelectedFilePath);
|
||||
await this.DocumentPathsChanged.InvokeAsync(this.DocumentPaths);
|
||||
await this.OnChange(this.DocumentPaths);
|
||||
}
|
||||
|
||||
private async Task<bool> IsFileExtensionValid(string selectedFile)
|
||||
{
|
||||
var ext = Path.GetExtension(selectedFile).TrimStart('.');
|
||||
if (Array.Exists(FileTypeFilter.Executables.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.AppBlocking, this.T("Executables are not allowed")));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Array.Exists(FileTypeFilter.AllImages.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.ImageNotSupported, this.T("Images are not supported yet")));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Array.Exists(FileTypeFilter.AllVideos.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.FeaturedVideo, this.T("Videos are not supported yet")));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task ClearAllFiles()
|
||||
{
|
||||
this.DocumentPaths.Clear();
|
||||
await this.DocumentPathsChanged.InvokeAsync(this.DocumentPaths);
|
||||
await this.OnChange(this.DocumentPaths);
|
||||
}
|
||||
|
||||
private void SetDragClass() => this.dragClass = $"{DEFAULT_DRAG_CLASS} mud-border-primary border-4";
|
||||
|
||||
private void ClearDragClass() => this.dragClass = DEFAULT_DRAG_CLASS;
|
||||
|
||||
private void OnMouseEnter(EventArgs _)
|
||||
{
|
||||
this.Logger.LogDebug("Attach documents component '{Name}' is hovered.", this.Name);
|
||||
this.isComponentHovered = true;
|
||||
this.SetDragClass();
|
||||
this.StateHasChanged();
|
||||
}
|
||||
|
||||
private void OnMouseLeave(EventArgs _)
|
||||
{
|
||||
this.Logger.LogDebug("Attach documents component '{Name}' is no longer hovered.", this.Name);
|
||||
this.isComponentHovered = false;
|
||||
this.ClearDragClass();
|
||||
this.StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task RemoveDocumentPathFromDocumentPaths(FileInfo file)
|
||||
{
|
||||
this.DocumentPaths.Remove(file.ToString());
|
||||
|
||||
await this.DocumentPathsChanged.InvokeAsync(this.DocumentPaths);
|
||||
await this.OnChange(this.DocumentPaths);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The user might want to check what the Pandoc integration actually extracts from his file and therefore gives the LLM as input.
|
||||
/// </summary>
|
||||
/// <param name="file">The file to check.</param>
|
||||
private async Task InvestigateFile(FileInfo file)
|
||||
{
|
||||
# warning Implement Investigation of file
|
||||
|
||||
var dialogParameters = new DialogParameters<PandocDocumentCheckDialog>{};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<PandocDocumentCheckDialog>(T("Pandoc Load Document Preview"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
var dialogResult = await dialogReference.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled)
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,13 @@ public partial class Changelog
|
||||
|
||||
public static readonly Log[] LOGS =
|
||||
[
|
||||
new (229, "v0.9.54, build 229 (2025-11-24 18:28 UTC)", "v0.9.54.md"),
|
||||
new (228, "v0.9.53, build 228 (2025-11-14 13:14 UTC)", "v0.9.53.md"),
|
||||
new (227, "v0.9.52, build 227 (2025-10-24 06:00 UTC)", "v0.9.52.md"),
|
||||
new (226, "v0.9.51, build 226 (2025-09-04 18:02 UTC)", "v0.9.51.md"),
|
||||
new (225, "v0.9.50, build 225 (2025-08-10 16:40 UTC)", "v0.9.50.md"),
|
||||
new (224, "v0.9.49, build 224 (2025-07-02 12:12 UTC)", "v0.9.49.md"),
|
||||
new (223, "v0.9.48, build 223 (2025-06-10 13:15 UTC)", "v0.9.48.md"),
|
||||
new (222, "v0.9.47, build 222 (2025-06-02 18:25 UTC)", "v0.9.47.md"),
|
||||
new (221, "v0.9.46, build 221 (2025-06-01 19:19 UTC)", "v0.9.46.md"),
|
||||
new (220, "v0.9.45, build 220 (2025-05-25 13:56 UTC)", "v0.9.45.md"),
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
}
|
||||
</MudSelect>
|
||||
|
||||
<MudMarkdown Value="@this.LogContent" OverrideHeaderTypo="@Markdown.OverrideHeaderTypo"/>
|
||||
<MudMarkdown Value="@this.LogContent" Props="Markdown.DefaultConfig"/>
|
||||
@@ -81,7 +81,8 @@
|
||||
</MudTooltip>
|
||||
}
|
||||
|
||||
<ChatTemplateSelection CurrentChatTemplate="@this.currentChatTemplate" CurrentChatTemplateChanged="@this.ChatTemplateWasChanged"/>
|
||||
<ChatTemplateSelection CanChatThreadBeUsedForTemplate="@this.CanThreadBeSaved" CurrentChatThread="@this.ChatThread" CurrentChatTemplate="@this.currentChatTemplate" CurrentChatTemplateChanged="@this.ChatTemplateWasChanged"/>
|
||||
|
||||
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY)
|
||||
{
|
||||
<MudTooltip Text="@T("Delete this chat & start a new one.")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||
|
||||
@@ -18,9 +18,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<ChatThread?> ChatThreadChanged { get; set; }
|
||||
|
||||
|
||||
[Parameter]
|
||||
public AIStudio.Settings.Provider Provider { get; set; }
|
||||
public AIStudio.Settings.Provider Provider { get; set; } = AIStudio.Settings.Provider.NONE;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<AIStudio.Settings.Provider> ProviderChanged { get; set; }
|
||||
@@ -34,9 +34,6 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
[Inject]
|
||||
private ILogger<ChatComponent> Logger { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ThreadSafeRandom RNG { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
@@ -80,6 +77,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
|
||||
// Get the preselected chat template:
|
||||
this.currentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(Tools.Components.CHAT);
|
||||
this.userInput = this.currentChatTemplate.PredefinedUserPrompt;
|
||||
|
||||
//
|
||||
// Check for deferred messages of the kind 'SEND_TO_CHAT',
|
||||
@@ -272,7 +270,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanThreadBeSaved => this.ChatThread is not null && this.ChatThread.Blocks.Count > 0;
|
||||
private bool CanThreadBeSaved => this.ChatThread is not null && this.ChatThread.Blocks.Any(b => !b.HideFromUser);
|
||||
|
||||
private string TooltipAddChatToWorkspace => string.Format(T("Start new chat in workspace '{0}'"), this.currentWorkspaceName);
|
||||
|
||||
@@ -326,6 +324,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
private async Task ChatTemplateWasChanged(ChatTemplate chatTemplate)
|
||||
{
|
||||
this.currentChatTemplate = chatTemplate;
|
||||
if(!string.IsNullOrWhiteSpace(this.currentChatTemplate.PredefinedUserPrompt))
|
||||
this.userInput = this.currentChatTemplate.PredefinedUserPrompt;
|
||||
|
||||
if(this.ChatThread is null)
|
||||
return;
|
||||
|
||||
@@ -432,8 +433,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
ChatId = Guid.NewGuid(),
|
||||
DataSourceOptions = this.earlyDataSourceOptions,
|
||||
Name = this.ExtractThreadName(this.userInput),
|
||||
Seed = this.RNG.Next(),
|
||||
Blocks = this.currentChatTemplate == default ? [] : this.currentChatTemplate.ExampleConversation.Select(x => x.DeepClone()).ToList(),
|
||||
Blocks = this.currentChatTemplate == ChatTemplate.NO_CHAT_TEMPLATE ? [] : this.currentChatTemplate.ExampleConversation.Select(x => x.DeepClone()).ToList(),
|
||||
};
|
||||
|
||||
await this.ChatThreadChanged.InvokeAsync(this.ChatThread);
|
||||
@@ -505,7 +505,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
});
|
||||
|
||||
// Clear the input field:
|
||||
await this.inputField.FocusAsync();
|
||||
this.userInput = string.Empty;
|
||||
await this.inputField.BlurAsync();
|
||||
|
||||
// Enable the stream state for the chat component:
|
||||
this.isStreaming = true;
|
||||
@@ -526,7 +528,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
// Use the selected provider to get the AI response.
|
||||
// By awaiting this line, we wait for the entire
|
||||
// content to be streamed.
|
||||
this.ChatThread = await aiText.CreateFromProviderAsync(this.Provider.CreateProvider(this.Logger), this.Provider.Model, lastUserPrompt, this.ChatThread, this.cancellationTokenSource.Token);
|
||||
this.ChatThread = await aiText.CreateFromProviderAsync(this.Provider.CreateProvider(), this.Provider.Model, lastUserPrompt, this.ChatThread, this.cancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
this.cancellationTokenSource = null;
|
||||
@@ -581,9 +583,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
//
|
||||
if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_MANUALLY && this.hasUnsavedChanges)
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", "Are you sure you want to start a new chat? All unsaved changes will be lost." },
|
||||
{ x => x.Message, "Are you sure you want to start a new chat? All unsaved changes will be lost." },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Delete Chat", dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -628,7 +630,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
|
||||
default:
|
||||
case AddChatProviderBehavior.ADDED_CHATS_USE_LATEST_PROVIDER:
|
||||
if(this.Provider == default)
|
||||
if(this.Provider == AIStudio.Settings.Provider.NONE)
|
||||
{
|
||||
this.Provider = this.SettingsManager.GetPreselectedProvider(Tools.Components.CHAT);
|
||||
await this.ProviderChanged.InvokeAsync(this.Provider);
|
||||
@@ -668,11 +670,12 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
WorkspaceId = this.currentWorkspaceId,
|
||||
ChatId = Guid.NewGuid(),
|
||||
Name = string.Empty,
|
||||
Seed = this.RNG.Next(),
|
||||
Blocks = this.currentChatTemplate == default ? [] : this.currentChatTemplate.ExampleConversation.Select(x => x.DeepClone()).ToList(),
|
||||
Blocks = this.currentChatTemplate == ChatTemplate.NO_CHAT_TEMPLATE ? [] : this.currentChatTemplate.ExampleConversation.Select(x => x.DeepClone()).ToList(),
|
||||
};
|
||||
}
|
||||
|
||||
this.userInput = this.currentChatTemplate.PredefinedUserPrompt;
|
||||
|
||||
// Now, we have to reset the data source options as well:
|
||||
this.ApplyStandardDataSourceOptions();
|
||||
|
||||
@@ -687,9 +690,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
|
||||
if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_MANUALLY && this.hasUnsavedChanges)
|
||||
{
|
||||
var confirmationDialogParameters = new DialogParameters
|
||||
var confirmationDialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", T("Are you sure you want to move this chat? All unsaved changes will be lost.") },
|
||||
{ x => x.Message, T("Are you sure you want to move this chat? All unsaved changes will be lost.") },
|
||||
};
|
||||
|
||||
var confirmationDialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Unsaved Changes", confirmationDialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -698,11 +701,11 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<WorkspaceSelectionDialog>
|
||||
{
|
||||
{ "Message", T("Please select the workspace where you want to move the chat to.") },
|
||||
{ "SelectedWorkspace", this.ChatThread?.WorkspaceId },
|
||||
{ "ConfirmText", T("Move chat") },
|
||||
{ x => x.Message, T("Please select the workspace where you want to move the chat to.") },
|
||||
{ x => x.SelectedWorkspace, this.ChatThread?.WorkspaceId ?? Guid.Empty },
|
||||
{ x => x.ConfirmText, T("Move chat") },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<WorkspaceSelectionDialog>(T("Move Chat to Workspace"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -789,7 +792,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
break;
|
||||
|
||||
case LoadingChatProviderBehavior.ALWAYS_USE_LATEST_CHAT_PROVIDER:
|
||||
if(this.Provider == default)
|
||||
if(this.Provider == AIStudio.Settings.Provider.NONE)
|
||||
this.Provider = this.SettingsManager.GetPreselectedProvider(Tools.Components.CHAT);
|
||||
break;
|
||||
}
|
||||
@@ -798,18 +801,13 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
|
||||
// Try to select the profile:
|
||||
if (!string.IsNullOrWhiteSpace(chatProfile))
|
||||
{
|
||||
this.currentProfile = this.SettingsManager.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == chatProfile);
|
||||
if(this.currentProfile == default)
|
||||
this.currentProfile = Profile.NO_PROFILE;
|
||||
}
|
||||
this.currentProfile = this.SettingsManager.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == chatProfile) ?? Profile.NO_PROFILE;
|
||||
|
||||
// Try to select the chat template:
|
||||
if (!string.IsNullOrWhiteSpace(chatChatTemplate))
|
||||
{
|
||||
this.currentChatTemplate = this.SettingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatChatTemplate);
|
||||
if(this.currentChatTemplate == default)
|
||||
this.currentChatTemplate = ChatTemplate.NO_CHAT_TEMPLATE;
|
||||
var selectedTemplate = this.SettingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatChatTemplate);
|
||||
this.currentChatTemplate = selectedTemplate ?? ChatTemplate.NO_CHAT_TEMPLATE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
<ChildContent>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Settings" Label="@T("Manage your templates")" OnClick="async () => await this.OpenSettingsDialog()" />
|
||||
<MudDivider/>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.AddComment" Label="@T("Create template from current chat")" OnClick="async () => await this.CreateNewChatTemplateFromChat()" Disabled="@(!this.CanChatThreadBeUsedForTemplate)"/>
|
||||
<MudDivider/>
|
||||
@foreach (var chatTemplate in this.SettingsManager.ConfigurationData.ChatTemplates.GetAllChatTemplates())
|
||||
{
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.RateReview" OnClick="async () => await this.SelectionChanged(chatTemplate)">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Settings;
|
||||
|
||||
@@ -11,6 +12,12 @@ public partial class ChatTemplateSelection : MSGComponentBase
|
||||
[Parameter]
|
||||
public ChatTemplate CurrentChatTemplate { get; set; } = ChatTemplate.NO_CHAT_TEMPLATE;
|
||||
|
||||
[Parameter]
|
||||
public bool CanChatThreadBeUsedForTemplate { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public ChatThread? CurrentChatThread { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<ChatTemplate> CurrentChatTemplateChanged { get; set; }
|
||||
|
||||
@@ -36,4 +43,14 @@ public partial class ChatTemplateSelection : MSGComponentBase
|
||||
var dialogParameters = new DialogParameters();
|
||||
await this.DialogService.ShowAsync<SettingsDialogChatTemplate>(T("Open Chat Template Options"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
}
|
||||
|
||||
private async Task CreateNewChatTemplateFromChat()
|
||||
{
|
||||
var dialogParameters = new DialogParameters<SettingsDialogChatTemplate>
|
||||
{
|
||||
{ x => x.CreateTemplateFromExistingChatThread, true },
|
||||
{ x => x.ExistingChatThread, this.CurrentChatThread }
|
||||
};
|
||||
await this.DialogService.ShowAsync<SettingsDialogChatTemplate>(T("Open Chat Template Options"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
}
|
||||
}
|
||||
@@ -1 +1,23 @@
|
||||
@inherits MSGComponentBase
|
||||
@inherits MSGComponentBase
|
||||
|
||||
@if (this.Body is not null)
|
||||
{
|
||||
@if (!this.Disabled() && this.IsLocked())
|
||||
{
|
||||
<MudField Label="@this.Label" Variant="@this.Variant" Underline="false" HelperText="@this.OptionHelp" Class="@this.Classes" InnerPadding="false">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.FlexStart" Wrap="Wrap.NoWrap" StretchItems="this.StretchItems">
|
||||
@* MudTooltip.RootStyle is set as a workaround for issue -> https://github.com/MudBlazor/MudBlazor/issues/10882 *@
|
||||
<MudTooltip Text="@TB("This feature is managed by your organization and has therefore been disabled.")" Arrow="true" Placement="Placement.Right" RootStyle="display:inline-flex;">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Lock" Color="Color.Error" Size="Size.Small" Class="mr-1"/>
|
||||
</MudTooltip>
|
||||
@this.Body
|
||||
</MudStack>
|
||||
</MudField>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudField Label="@this.Label" Variant="@this.Variant" Underline="false" HelperText="@this.OptionHelp" Class="@this.Classes" InnerPadding="false">
|
||||
@this.Body
|
||||
</MudField>
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ namespace AIStudio.Components;
|
||||
/// <summary>
|
||||
/// A base class for configuration options.
|
||||
/// </summary>
|
||||
public partial class ConfigurationBase : MSGComponentBase
|
||||
public abstract partial class ConfigurationBase : MSGComponentBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The description of the option, i.e., the name. Should be
|
||||
@@ -26,7 +26,42 @@ public partial class ConfigurationBase : MSGComponentBase
|
||||
[Parameter]
|
||||
public Func<bool> Disabled { get; set; } = () => false;
|
||||
|
||||
protected const string MARGIN_CLASS = "mb-6";
|
||||
/// <summary>
|
||||
/// Is the option locked by a configuration plugin?
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Func<bool> IsLocked { get; set; } = () => false;
|
||||
|
||||
/// <summary>
|
||||
/// Should the option be stretched to fill the available space?
|
||||
/// </summary>
|
||||
protected abstract bool Stretch { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The CSS class to apply to the component.
|
||||
/// </summary>
|
||||
protected virtual string GetClassForBase => string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The visual variant of the option.
|
||||
/// </summary>
|
||||
protected virtual Variant Variant => Variant.Text;
|
||||
|
||||
/// <summary>
|
||||
/// The label to display for the option.
|
||||
/// </summary>
|
||||
protected virtual string Label => string.Empty;
|
||||
|
||||
private StretchItems StretchItems => this.Stretch ? StretchItems.End : StretchItems.None;
|
||||
|
||||
protected bool IsDisabled => this.Disabled() || this.IsLocked();
|
||||
|
||||
private string Classes => $"{this.GetClassForBase} {MARGIN_CLASS}";
|
||||
|
||||
private protected virtual RenderFragment? Body => null;
|
||||
|
||||
private const string MARGIN_CLASS = "mb-6";
|
||||
|
||||
protected static readonly Dictionary<string, object?> SPELLCHECK_ATTRIBUTES = new();
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
@@ -39,7 +74,9 @@ public partial class ConfigurationBase : MSGComponentBase
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
private string TB(string fallbackEN) => this.T(fallbackEN, typeof(ConfigurationBase).Namespace, nameof(ConfigurationBase));
|
||||
|
||||
protected async Task InformAboutChange() => await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||
|
||||
#region Overrides of MSGComponentBase
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Rendering;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public abstract class ConfigurationBaseCore : ConfigurationBase
|
||||
{
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
@using AIStudio.Settings
|
||||
@inherits MSGComponentBase
|
||||
<ConfigurationSelect Disabled="@this.Disabled" OptionDescription="@T("Select a minimum confidence level")" SelectedValue="@this.FilteredSelectedValue" Data="@ConfigurationSelectDataFactory.GetConfidenceLevelsData(this.SettingsManager, this.RestrictToGlobalMinimumConfidence)" SelectionUpdate="@this.SelectionUpdate" OptionHelp="@T("Choose the minimum confidence level that all LLM providers must meet. This way, you can ensure that only trustworthy providers are used. You cannot use any provider that falls below this level.")"/>
|
||||
<ConfigurationSelect IsLocked="this.IsLocked" Disabled="this.Disabled" OptionDescription="@T("Select a minimum confidence level")" SelectedValue="@this.FilteredSelectedValue" Data="@ConfigurationSelectDataFactory.GetConfidenceLevelsData(this.SettingsManager, this.RestrictToGlobalMinimumConfidence)" SelectionUpdate="@this.SelectionUpdate" OptionHelp="@T("Choose the minimum confidence level that all LLM providers must meet. This way, you can ensure that only trustworthy providers are used. You cannot use any provider that falls below this level.")"/>
|
||||
@@ -18,17 +18,17 @@ public partial class ConfigurationMinConfidenceSelection : MSGComponentBase
|
||||
[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; }
|
||||
|
||||
[Parameter]
|
||||
public Func<bool> Disabled { get; set; } = () => false;
|
||||
|
||||
[Parameter]
|
||||
public Func<bool> IsLocked { get; set; } = () => false;
|
||||
|
||||
private ConfidenceLevel FilteredSelectedValue()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@inherits ConfigurationBase
|
||||
@inherits ConfigurationBaseCore
|
||||
@typeparam TData
|
||||
|
||||
<MudSelectExtended
|
||||
@@ -7,12 +7,10 @@
|
||||
MultiSelectionTextFunc="@this.GetMultiSelectionText"
|
||||
SelectedValues="@this.SelectedValues()"
|
||||
Strict="@true"
|
||||
Disabled="@this.Disabled()"
|
||||
Disabled="@this.IsDisabled"
|
||||
Margin="Margin.Dense"
|
||||
Label="@this.OptionDescription"
|
||||
Class="@GetClass"
|
||||
Variant="Variant.Outlined"
|
||||
HelperText="@this.OptionHelp"
|
||||
Class="rounded-lg"
|
||||
Underline="false"
|
||||
SelectedValuesChanged="@this.OptionChanged">
|
||||
@foreach (var data in this.Data)
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace AIStudio.Components;
|
||||
/// Configuration component for selecting many values from a list.
|
||||
/// </summary>
|
||||
/// <typeparam name="TData">The type of the value to select.</typeparam>
|
||||
public partial class ConfigurationMultiSelect<TData> : ConfigurationBase
|
||||
public partial class ConfigurationMultiSelect<TData> : ConfigurationBaseCore
|
||||
{
|
||||
/// <summary>
|
||||
/// The data to select from.
|
||||
@@ -28,6 +28,19 @@ public partial class ConfigurationMultiSelect<TData> : ConfigurationBase
|
||||
[Parameter]
|
||||
public Action<HashSet<TData>> SelectionUpdate { get; set; } = _ => { };
|
||||
|
||||
#region Overrides of ConfigurationBase
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool Stretch => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Variant Variant => Variant.Outlined;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override string Label => this.OptionDescription;
|
||||
|
||||
#endregion
|
||||
|
||||
private async Task OptionChanged(IEnumerable<TData?>? updatedValues)
|
||||
{
|
||||
if(updatedValues is null)
|
||||
@@ -39,8 +52,6 @@ public partial class ConfigurationMultiSelect<TData> : ConfigurationBase
|
||||
await this.InformAboutChange();
|
||||
}
|
||||
|
||||
private static string GetClass => $"{MARGIN_CLASS} rounded-lg";
|
||||
|
||||
private string GetMultiSelectionText(List<TData?>? selectedValues)
|
||||
{
|
||||
if(selectedValues is null || selectedValues.Count == 0)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
@inherits ConfigurationBase
|
||||
@inherits ConfigurationBaseCore
|
||||
|
||||
<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>
|
||||
<MudSwitch T="bool" Disabled="@this.IsDisabled" Value="@this.State()" ValueChanged="@this.OptionChanged" Color="Color.Primary">
|
||||
@(this.State() ? this.LabelOn : this.LabelOff)
|
||||
</MudSwitch>
|
||||
@@ -5,7 +5,7 @@ namespace AIStudio.Components;
|
||||
/// <summary>
|
||||
/// Configuration component for any boolean option.
|
||||
/// </summary>
|
||||
public partial class ConfigurationOption : ConfigurationBase
|
||||
public partial class ConfigurationOption : ConfigurationBaseCore
|
||||
{
|
||||
/// <summary>
|
||||
/// Text to display when the option is true.
|
||||
@@ -31,6 +31,19 @@ public partial class ConfigurationOption : ConfigurationBase
|
||||
[Parameter]
|
||||
public Action<bool> StateUpdate { get; set; } = _ => { };
|
||||
|
||||
#region Overrides of ConfigurationBase
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool Stretch => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Variant Variant => Variant.Outlined;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override string Label => this.OptionDescription;
|
||||
|
||||
#endregion
|
||||
|
||||
private async Task OptionChanged(bool updatedState)
|
||||
{
|
||||
this.StateUpdate(updatedState);
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
@inherits MSGComponentBase
|
||||
<ConfigurationSelect OptionDescription="@T("Preselected provider")" Disabled="@this.Disabled" OptionHelp="@this.HelpText()" Data="@this.FilteredData()" SelectedValue="@this.SelectedValue" SelectionUpdate="@this.SelectionUpdate"/>
|
||||
<ConfigurationSelect IsLocked="@this.IsLocked" OptionDescription="@T("Preselected provider")" Disabled="@(() => this.Disabled())" OptionHelp="@this.HelpText()" Data="@this.FilteredData()" SelectedValue="@this.SelectedValue" SelectionUpdate="@this.SelectionUpdate"/>
|
||||
@@ -20,27 +20,17 @@ public partial class ConfigurationProviderSelection : MSGComponentBase
|
||||
[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; } = () => TB("Select a provider that is preselected.");
|
||||
|
||||
[Parameter]
|
||||
public Tools.Components Component { get; set; } = Tools.Components.NONE;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
this.ApplyFilters([], [ Event.CONFIGURATION_CHANGED ]);
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
[Parameter]
|
||||
public Func<bool> Disabled { get; set; } = () => false;
|
||||
|
||||
[Parameter]
|
||||
public Func<bool> IsLocked { get; set; } = () => false;
|
||||
|
||||
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
|
||||
private IEnumerable<ConfigurationSelectData<string>> FilteredData()
|
||||
@@ -52,6 +42,9 @@ public partial class ConfigurationProviderSelection : MSGComponentBase
|
||||
foreach (var providerId in this.Data)
|
||||
{
|
||||
var provider = this.SettingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == providerId.Value);
|
||||
if (provider is null)
|
||||
continue;
|
||||
|
||||
if (provider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level >= minimumLevel)
|
||||
yield return providerId;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@inherits ConfigurationBase
|
||||
@typeparam T
|
||||
@inherits ConfigurationBaseCore
|
||||
@typeparam TConfig
|
||||
|
||||
<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">
|
||||
<MudSelect T="TConfig" Value="@this.SelectedValue()" Strict="@true" ShrinkLabel="@true" Disabled="@this.IsDisabled" Margin="Margin.Dense" Class="rounded-lg mb-0" Underline="false" ValueChanged="@this.OptionChanged">
|
||||
@foreach (var data in this.Data)
|
||||
{
|
||||
<MudSelectItem Value="@data.Value">
|
||||
|
||||
@@ -7,33 +7,44 @@ namespace AIStudio.Components;
|
||||
/// <summary>
|
||||
/// Configuration component for selecting a value from a list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the value to select.</typeparam>
|
||||
public partial class ConfigurationSelect<T> : ConfigurationBase
|
||||
/// <typeparam name="TConfig">The type of the value to select.</typeparam>
|
||||
public partial class ConfigurationSelect<TConfig> : ConfigurationBaseCore
|
||||
{
|
||||
/// <summary>
|
||||
/// The data to select from.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public IEnumerable<ConfigurationSelectData<T>> Data { get; set; } = [];
|
||||
public IEnumerable<ConfigurationSelectData<TConfig>> Data { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The selected value.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Func<T> SelectedValue { get; set; } = () => default!;
|
||||
public Func<TConfig> SelectedValue { get; set; } = () => default!;
|
||||
|
||||
/// <summary>
|
||||
/// An action that is called when the selection changes.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Action<T> SelectionUpdate { get; set; } = _ => { };
|
||||
public Action<TConfig> SelectionUpdate { get; set; } = _ => { };
|
||||
|
||||
private async Task OptionChanged(T updatedValue)
|
||||
#region Overrides of ConfigurationBase
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool Stretch => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override string Label => this.OptionDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Variant Variant => Variant.Outlined;
|
||||
|
||||
#endregion
|
||||
|
||||
private async Task OptionChanged(TConfig updatedValue)
|
||||
{
|
||||
this.SelectionUpdate(updatedValue);
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.InformAboutChange();
|
||||
}
|
||||
|
||||
private static string GetClass => $"{MARGIN_CLASS} rounded-lg";
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
@typeparam T
|
||||
@inherits ConfigurationBase
|
||||
@inherits ConfigurationBaseCore
|
||||
|
||||
<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>
|
||||
<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.IsDisabled" Class="mb-1">
|
||||
@this.Value() @this.Unit
|
||||
</MudSlider>
|
||||
@@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class ConfigurationSlider<T> : ConfigurationBase where T : struct, INumber<T>
|
||||
public partial class ConfigurationSlider<T> : ConfigurationBaseCore where T : struct, INumber<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// The minimum value for the slider.
|
||||
@@ -42,6 +42,18 @@ public partial class ConfigurationSlider<T> : ConfigurationBase where T : struct
|
||||
[Parameter]
|
||||
public Action<T> ValueUpdate { get; set; } = _ => { };
|
||||
|
||||
#region Overrides of ConfigurationBase
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool Stretch => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Variant Variant => Variant.Outlined;
|
||||
|
||||
protected override string Label => this.OptionDescription;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
@inherits ConfigurationBase
|
||||
@inherits ConfigurationBaseCore
|
||||
|
||||
<MudTextField
|
||||
T="string"
|
||||
Text="@this.Text()"
|
||||
TextChanged="@this.InternalUpdate"
|
||||
Label="@this.OptionDescription"
|
||||
Disabled="@this.Disabled()"
|
||||
Class="@MARGIN_CLASS"
|
||||
Disabled="@this.IsDisabled"
|
||||
Adornment="Adornment.Start"
|
||||
AdornmentIcon="@this.Icon"
|
||||
AdornmentColor="@this.IconColor"
|
||||
@@ -15,5 +13,5 @@
|
||||
AutoGrow="@this.AutoGrow"
|
||||
MaxLines="@this.GetMaxLines"
|
||||
Immediate="@true"
|
||||
Variant="Variant.Outlined"
|
||||
Underline="false"
|
||||
/>
|
||||
@@ -4,7 +4,7 @@ using Timer = System.Timers.Timer;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class ConfigurationText : ConfigurationBase
|
||||
public partial class ConfigurationText : ConfigurationBaseCore
|
||||
{
|
||||
/// <summary>
|
||||
/// The text used for the textfield.
|
||||
@@ -43,10 +43,21 @@ public partial class ConfigurationText : ConfigurationBase
|
||||
public int MaxLines { get; set; } = 12;
|
||||
|
||||
private string internalText = string.Empty;
|
||||
private Timer timer = new(TimeSpan.FromMilliseconds(500))
|
||||
private readonly Timer timer = new(TimeSpan.FromMilliseconds(500))
|
||||
{
|
||||
AutoReset = false
|
||||
};
|
||||
|
||||
#region Overrides of ConfigurationBase
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool Stretch => true;
|
||||
|
||||
protected override Variant Variant => Variant.Outlined;
|
||||
|
||||
protected override string Label => this.OptionDescription;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides of ConfigurationBase
|
||||
|
||||
@@ -56,8 +67,6 @@ public partial class ConfigurationText : ConfigurationBase
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
this.internalText = this.Text();
|
||||
@@ -66,8 +75,6 @@ public partial class ConfigurationText : ConfigurationBase
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
private bool AutoGrow => this.NumLines > 1;
|
||||
|
||||
private int GetMaxLines => this.AutoGrow ? this.MaxLines : 1;
|
||||
@@ -85,4 +92,23 @@ public partial class ConfigurationText : ConfigurationBase
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.InformAboutChange();
|
||||
}
|
||||
|
||||
#region Overrides of MSGComponentBase
|
||||
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.timer.Stop();
|
||||
this.timer.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
base.DisposeResources();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using Timer = System.Timers.Timer;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class DebouncedTextField : MudComponentBase
|
||||
public partial class DebouncedTextField : MudComponentBase, IDisposable
|
||||
{
|
||||
[Parameter]
|
||||
public string Label { get; set; } = string.Empty;
|
||||
@@ -50,12 +50,15 @@ public partial class DebouncedTextField : MudComponentBase
|
||||
|
||||
private readonly Timer debounceTimer = new();
|
||||
private string text = string.Empty;
|
||||
private string lastParameterText = string.Empty;
|
||||
private bool isInitialized;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
this.text = this.Text;
|
||||
this.lastParameterText = this.Text;
|
||||
this.debounceTimer.AutoReset = false;
|
||||
this.debounceTimer.Interval = this.DebounceTime.TotalMilliseconds;
|
||||
this.debounceTimer.Elapsed += (_, _) =>
|
||||
@@ -66,8 +69,31 @@ public partial class DebouncedTextField : MudComponentBase
|
||||
this.InvokeAsync(() => this.WhenTextCanged(this.text));
|
||||
};
|
||||
|
||||
this.isInitialized = true;
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
// Ensure the timer uses the latest debouncing interval:
|
||||
if (!this.isInitialized)
|
||||
return;
|
||||
|
||||
if(Math.Abs(this.debounceTimer.Interval - this.DebounceTime.TotalMilliseconds) > 1)
|
||||
this.debounceTimer.Interval = this.DebounceTime.TotalMilliseconds;
|
||||
|
||||
// Only sync when the parent's parameter actually changed since the last change:
|
||||
if (this.Text != this.lastParameterText)
|
||||
{
|
||||
this.text = this.Text;
|
||||
this.lastParameterText = this.Text;
|
||||
}
|
||||
|
||||
this.debounceTimer.Stop();
|
||||
this.debounceTimer.Start();
|
||||
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -77,4 +103,21 @@ public partial class DebouncedTextField : MudComponentBase
|
||||
this.debounceTimer.Stop();
|
||||
this.debounceTimer.Start();
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.debounceTimer.Stop();
|
||||
this.debounceTimer.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
@inherits ConfigurationBaseCore
|
||||
|
||||
<MudButton Variant="Variant.Filled" Color="@Color.Primary" StartIcon="@this.Icon" Disabled="@this.IsDisabled" OnClick="@(async () => await this.ClickAsync())">
|
||||
@this.Text
|
||||
</MudButton>
|
||||
@@ -0,0 +1,39 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class LockableButton : ConfigurationBaseCore
|
||||
{
|
||||
[Parameter]
|
||||
public string Icon { get; set; } = Icons.Material.Filled.Info;
|
||||
|
||||
[Parameter]
|
||||
public Func<Task> OnClickAsync { get; set; } = () => Task.CompletedTask;
|
||||
|
||||
[Parameter]
|
||||
public Action OnClick { get; set; } = () => { };
|
||||
|
||||
[Parameter]
|
||||
public string Text { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public string Class { get; set; } = string.Empty;
|
||||
|
||||
#region Overrides of ConfigurationBase
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool Stretch => false;
|
||||
|
||||
protected override string GetClassForBase => this.Class;
|
||||
|
||||
#endregion
|
||||
|
||||
private async Task ClickAsync()
|
||||
{
|
||||
if (this.IsLocked() || this.Disabled())
|
||||
return;
|
||||
|
||||
await this.OnClickAsync();
|
||||
this.OnClick();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<MudTooltip Text="@this.TooltipMessage" Placement="Placement.Bottom">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.ContentCopy" Size="@this.Size" OnClick="@(() => this.HandleCopyClick())"/>
|
||||
</MudTooltip>
|
||||
@@ -0,0 +1,90 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
using AIStudio.Tools.Services;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class MudCopyClipboardButton : ComponentBase
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(MudCopyClipboardButton).Namespace, nameof(MudCopyClipboardButton));
|
||||
|
||||
/// <summary>
|
||||
/// The string, if you want to copy a string.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string StringContent { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The content, if you want to copy content.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public IContent? Content { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The content type, if you want to copy Content.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public ContentType Type { get; init; } = ContentType.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// The tooltip that should be shown to the user.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string TooltipMessage { get; set; } = TB("Copies the content to the clipboard");
|
||||
|
||||
/// <summary>
|
||||
/// The size of the button. The default size is small.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Size Size { get; set; } = Size.Small;
|
||||
|
||||
[Inject]
|
||||
private ISnackbar Snackbar { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
private async Task HandleCopyClick()
|
||||
{
|
||||
if (this.Type is ContentType.NONE)
|
||||
await this.CopyToClipboard(this.StringContent);
|
||||
else
|
||||
await this.CopyToClipboard(this.Content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy this the string to the clipboard.
|
||||
/// </summary>
|
||||
private async Task CopyToClipboard(string textContent)
|
||||
{
|
||||
await this.RustService.CopyText2Clipboard(this.Snackbar, textContent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy this block's content to the clipboard.
|
||||
/// </summary>
|
||||
private async Task CopyToClipboard(IContent? contentToCopy)
|
||||
{
|
||||
if (contentToCopy is null)
|
||||
return;
|
||||
|
||||
switch (this.Type)
|
||||
{
|
||||
case ContentType.TEXT:
|
||||
var textContent = (ContentText) contentToCopy;
|
||||
await this.RustService.CopyText2Clipboard(this.Snackbar, textContent.Text);
|
||||
break;
|
||||
|
||||
default:
|
||||
this.Snackbar.Add(TB("Cannot copy this content type to clipboard."), Severity.Error, config =>
|
||||
{
|
||||
config.Icon = Icons.Material.Filled.ContentCopy;
|
||||
config.IconSize = Size.Large;
|
||||
config.IconColor = Color.Error;
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Person4" />
|
||||
<MudIconButton Size="Size.Small" Icon="@Icons.Material.Filled.Person4" />
|
||||
}
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
@@ -19,7 +19,7 @@
|
||||
<MudDivider/>
|
||||
@foreach (var profile in this.SettingsManager.ConfigurationData.Profiles.GetAllProfiles())
|
||||
{
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Person4" OnClick="() => this.SelectionChanged(profile)">
|
||||
<MudMenuItem Icon="@this.ProfileIcon(profile)" OnClick="() => this.SelectionChanged(profile)">
|
||||
@profile.Name
|
||||
</MudMenuItem>
|
||||
}
|
||||
|
||||
@@ -38,6 +38,14 @@ public partial class ProfileSelection : MSGComponentBase
|
||||
|
||||
private string MarginClass => $"{this.MarginLeft} {this.MarginRight}";
|
||||
|
||||
private string ProfileIcon(Profile profile)
|
||||
{
|
||||
if (profile.IsEnterpriseConfiguration)
|
||||
return Icons.Material.Filled.Business;
|
||||
|
||||
return Icons.Material.Filled.Person4;
|
||||
}
|
||||
|
||||
private async Task SelectionChanged(Profile profile)
|
||||
{
|
||||
this.CurrentProfile = profile;
|
||||
|
||||
@@ -11,9 +11,9 @@ public partial class ProviderSelection : MSGComponentBase
|
||||
{
|
||||
[CascadingParameter]
|
||||
public AssistantBase<NoComponent>? AssistantBase { get; set; }
|
||||
|
||||
|
||||
[Parameter]
|
||||
public AIStudio.Settings.Provider ProviderSettings { get; set; }
|
||||
public AIStudio.Settings.Provider ProviderSettings { get; set; } = AIStudio.Settings.Provider.NONE;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<AIStudio.Settings.Provider> ProviderSettingsChanged { get; set; }
|
||||
@@ -32,7 +32,8 @@ public partial class ProviderSelection : MSGComponentBase
|
||||
{
|
||||
var minimumLevel = this.SettingsManager.GetMinimumConfidenceLevel(this.AssistantBase?.Component ?? Tools.Components.NONE);
|
||||
foreach (var provider in this.SettingsManager.ConfigurationData.Providers)
|
||||
if (provider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level >= minimumLevel)
|
||||
yield return provider;
|
||||
if (provider.UsedLLMProvider != LLMProviders.NONE)
|
||||
if (provider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level >= minimumLevel)
|
||||
yield return provider;
|
||||
}
|
||||
}
|
||||
+8
-1
@@ -1,4 +1,11 @@
|
||||
@inherits MSGComponentBase
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Description" OnClick="async () => await this.SelectFile()" Variant="Variant.Filled" Class="mb-3">
|
||||
@T("Use PDF content as input")
|
||||
@if (string.IsNullOrWhiteSpace(this.Text))
|
||||
{
|
||||
@T("Use file content as input")
|
||||
}
|
||||
else
|
||||
{
|
||||
@this.Text
|
||||
}
|
||||
</MudButton>
|
||||
@@ -0,0 +1,82 @@
|
||||
using AIStudio.Dialogs;
|
||||
using AIStudio.Tools.Rust;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class ReadFileContent : MSGComponentBase
|
||||
{
|
||||
[Parameter]
|
||||
public string Text { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public string FileContent { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<string> FileContentChanged { get; set; }
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ILogger<ReadFileContent> Logger { get; init; } = null!;
|
||||
|
||||
private async Task SelectFile()
|
||||
{
|
||||
var selectedFile = await this.RustService.SelectFile(T("Select file to read its content"));
|
||||
if (selectedFile.UserCancelled)
|
||||
return;
|
||||
|
||||
if(!File.Exists(selectedFile.SelectedFilePath))
|
||||
return;
|
||||
|
||||
var ext = Path.GetExtension(selectedFile.SelectedFilePath).TrimStart('.');
|
||||
if (Array.Exists(FileTypeFilter.Executables.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.AppBlocking, T("Executables are not allowed")));
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.Exists(FileTypeFilter.AllImages.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.ImageNotSupported, T("Images are not supported yet")));
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.Exists(FileTypeFilter.AllVideos.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.FeaturedVideo, this.T("Videos are not supported yet")));
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure that Pandoc is installed and ready:
|
||||
var pandocState = await Pandoc.CheckAvailabilityAsync(this.RustService, showSuccessMessage: false);
|
||||
if (!pandocState.IsAvailable)
|
||||
{
|
||||
var dialogParameters = new DialogParameters<PandocDialog>
|
||||
{
|
||||
{ x => x.ShowInitialResultInSnackbar, false },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<PandocDialog>(T("Pandoc Installation"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
await dialogReference.Result;
|
||||
|
||||
pandocState = await Pandoc.CheckAvailabilityAsync(this.RustService, showSuccessMessage: true);
|
||||
if (!pandocState.IsAvailable)
|
||||
{
|
||||
this.Logger.LogError("Pandoc is not available after installation attempt.");
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Cancel, T("Pandoc may be required for importing files.")));
|
||||
}
|
||||
}
|
||||
|
||||
var fileContent = await this.RustService.ReadArbitraryFileData(selectedFile.SelectedFilePath, int.MaxValue);
|
||||
await this.FileContentChanged.InvokeAsync(fileContent);
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using AIStudio.Tools.Rust;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class ReadPDFContent : MSGComponentBase
|
||||
{
|
||||
[Parameter]
|
||||
public string PDFContent { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<string> PDFContentChanged { get; set; }
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
private async Task SelectFile()
|
||||
{
|
||||
var pdfFile = await this.RustService.SelectFile(T("Select PDF file"), FileTypeFilter.PDF);
|
||||
if (pdfFile.UserCancelled)
|
||||
return;
|
||||
|
||||
if(!File.Exists(pdfFile.SelectedFilePath))
|
||||
return;
|
||||
|
||||
var pdfText = await this.RustService.GetPDFText(pdfFile.SelectedFilePath);
|
||||
await this.PDFContentChanged.InvokeAsync(pdfText);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
@inherits MSGComponentBase
|
||||
<MudPaper Class="pa-3 mb-3 border-dashed border rounded-lg">
|
||||
<MudTextSwitch Label="@T("Read content from web?")" Disabled="@this.AgentIsRunning" @bind-Value="@this.showWebContentReader" LabelOn="@T("Show web content options")" LabelOff="@T("Hide web content options")" />
|
||||
@if (this.showWebContentReader)
|
||||
<MudTextSwitch Label="@T("Read content from web?")" Disabled="@this.AgentIsRunning" Value="@this.Preselect" ValueChanged="@this.ShowWebContentReaderChanged" LabelOn="@T("Show web content options")" LabelOff="@T("Hide web content options")" />
|
||||
@if (this.Preselect)
|
||||
{
|
||||
<MudTextSwitch Label="@T("Cleanup content by using an LLM agent?")" @bind-Value="@this.useContentCleanerAgent" Validation="@this.ValidateProvider" Disabled="@this.AgentIsRunning" LabelOn="@T("The content is cleaned using an LLM agent: the main content is extracted, advertisements and other irrelevant things are attempted to be removed; relative links are attempted to be converted into absolute links so that they can be used.")" LabelOff="@T("No content cleaning")" />
|
||||
<MudTextSwitch Label="@T("Cleanup content by using an LLM agent?")" Value="@this.PreselectContentCleanerAgent" ValueChanged="@this.UseContentCleanerAgentChanged" Validation="@this.ValidateProvider" Disabled="@this.AgentIsRunning" LabelOn="@T("The content is cleaned using an LLM agent: the main content is extracted, advertisements and other irrelevant things are attempted to be removed; relative links are attempted to be converted into absolute links so that they can be used.")" LabelOff="@T("No content cleaning")" />
|
||||
<MudStack Row="@true" AlignItems="@AlignItems.Baseline" Class="mb-3">
|
||||
<MudTextField T="string" Label="@T("URL from which to load the content")" @bind-Value="@this.providedURL" Validation="@this.ValidateURL" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Link" Placeholder="https://..." HelperText="@T("Loads the content from your URL. Does not work when the content is hidden behind a paywall.")" Variant="Variant.Outlined" Immediate="@true" Disabled="@this.AgentIsRunning"/>
|
||||
<MudButton Disabled="@(!this.IsReady || this.AgentIsRunning)" Variant="Variant.Filled" Size="Size.Large" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Download" OnClick="() => this.LoadFromWeb()">
|
||||
|
||||
@@ -20,7 +20,7 @@ public partial class ReadWebContent : MSGComponentBase
|
||||
public EventCallback<string> ContentChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public AIStudio.Settings.Provider ProviderSettings { get; set; }
|
||||
public AIStudio.Settings.Provider ProviderSettings { get; set; } = AIStudio.Settings.Provider.NONE;
|
||||
|
||||
[Parameter]
|
||||
public bool AgentIsRunning { get; set; }
|
||||
@@ -31,31 +31,32 @@ public partial class ReadWebContent : MSGComponentBase
|
||||
[Parameter]
|
||||
public bool Preselect { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> PreselectChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool PreselectContentCleanerAgent { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> PreselectContentCleanerAgentChanged { get; set; }
|
||||
|
||||
private Process<ReadWebContentSteps> process = Process<ReadWebContentSteps>.INSTANCE;
|
||||
private readonly Process<ReadWebContentSteps> process = Process<ReadWebContentSteps>.INSTANCE;
|
||||
private ProcessStepValue processStep;
|
||||
|
||||
private bool showWebContentReader;
|
||||
private bool useContentCleanerAgent;
|
||||
private string providedURL = string.Empty;
|
||||
private bool urlIsValid;
|
||||
private bool isProviderValid;
|
||||
|
||||
private AIStudio.Settings.Provider providerSettings;
|
||||
private AIStudio.Settings.Provider providerSettings = AIStudio.Settings.Provider.NONE;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if(this.Preselect)
|
||||
this.showWebContentReader = true;
|
||||
|
||||
if(this.PreselectContentCleanerAgent)
|
||||
this.useContentCleanerAgent = true;
|
||||
|
||||
this.ProviderSettings = this.SettingsManager.GetPreselectedProvider(Tools.Components.AGENT_TEXT_CONTENT_CLEANER, this.ProviderSettings.Id, true);
|
||||
this.providerSettings = this.ProviderSettings;
|
||||
this.ValidateProvider(this.PreselectContentCleanerAgent);
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
@@ -64,6 +65,7 @@ public partial class ReadWebContent : MSGComponentBase
|
||||
if (!this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions)
|
||||
this.providerSettings = this.ProviderSettings;
|
||||
|
||||
this.ValidateProvider(this.PreselectContentCleanerAgent);
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
@@ -86,7 +88,7 @@ public partial class ReadWebContent : MSGComponentBase
|
||||
this.StateHasChanged();
|
||||
markdown = this.HTMLParser.ParseToMarkdown(html);
|
||||
|
||||
if (this.useContentCleanerAgent)
|
||||
if (this.PreselectContentCleanerAgent && this.providerSettings != AIStudio.Settings.Provider.NONE)
|
||||
{
|
||||
this.AgentTextContentCleaner.ProviderSettings = this.providerSettings;
|
||||
var additionalData = new Dictionary<string, string>
|
||||
@@ -140,16 +142,26 @@ public partial class ReadWebContent : MSGComponentBase
|
||||
if(!this.urlIsValid)
|
||||
return false;
|
||||
|
||||
if(this.useContentCleanerAgent && !this.isProviderValid)
|
||||
if(this.PreselectContentCleanerAgent && !this.isProviderValid)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ShowWebContentReaderChanged(bool state)
|
||||
{
|
||||
await this.PreselectChanged.InvokeAsync(state);
|
||||
}
|
||||
|
||||
private async Task UseContentCleanerAgentChanged(bool state)
|
||||
{
|
||||
await this.PreselectContentCleanerAgentChanged.InvokeAsync(state);
|
||||
}
|
||||
|
||||
private string? ValidateProvider(bool shouldUseAgent)
|
||||
{
|
||||
if(shouldUseAgent && this.providerSettings == default)
|
||||
if(shouldUseAgent && this.providerSettings == AIStudio.Settings.Provider.NONE)
|
||||
{
|
||||
this.isProviderValid = false;
|
||||
return T("Please select a provider to use the cleanup agent.");
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public static class ReadWebContentStepsExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ReadWebContentStepsExtensions).Namespace, nameof(ReadWebContentStepsExtensions));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text representation of a given ReadWebContentSteps enum value.
|
||||
/// </summary>
|
||||
/// <param name="step">The ReadWebContentSteps enum value.</param>
|
||||
/// <returns>>The text representation of the process step.</returns>
|
||||
public static string GetText(this ReadWebContentSteps step) => step switch
|
||||
{
|
||||
ReadWebContentSteps.START => TB("Start"),
|
||||
ReadWebContentSteps.LOADING => TB("Loading"),
|
||||
ReadWebContentSteps.PARSING => TB("Parsing"),
|
||||
ReadWebContentSteps.CLEANING => TB("Cleaning"),
|
||||
ReadWebContentSteps.DONE => TB("Done"),
|
||||
|
||||
_ => TB("n/a")
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
@inherits SettingsPanelBase
|
||||
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.TextFields" HeaderText="Agent: Text Content Cleaner Options">
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.TextFields" HeaderText="@T("Agent: Text Content Cleaner Options")">
|
||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||
<MudText Typo="Typo.body1" Class="mb-3">
|
||||
@T("Use Case: this agent is used to clean up text content. It extracts the main content, removes advertisements and other irrelevant things, and attempts to convert relative links into absolute links so that they can be used.")
|
||||
|
||||
@@ -13,19 +13,20 @@
|
||||
<ConfigurationSelect OptionDescription="@T("Color theme")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreferredTheme)" Data="@ConfigurationSelectDataFactory.GetThemesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreferredTheme = selectedValue)" OptionHelp="@T("Choose the color theme that best suits for you.")"/>
|
||||
<ConfigurationOption OptionDescription="@T("Save energy?")" LabelOn="@T("Energy saving is enabled")" LabelOff="@T("Energy saving is disabled")" State="@(() => this.SettingsManager.ConfigurationData.App.IsSavingEnergy)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.IsSavingEnergy = updatedState)" OptionHelp="@T("When enabled, streamed content from the AI is updated once every third second. When disabled, streamed content will be updated as soon as it is available.")"/>
|
||||
<ConfigurationOption OptionDescription="@T("Enable spellchecking?")" LabelOn="@T("Spellchecking is enabled")" LabelOff="@T("Spellchecking is disabled")" State="@(() => this.SettingsManager.ConfigurationData.App.EnableSpellchecking)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.EnableSpellchecking = updatedState)" OptionHelp="@T("When enabled, spellchecking will be active in all input fields. Depending on your operating system, errors may not be visually highlighted, but right-clicking may still offer possible corrections.")"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Check for updates")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.UpdateBehavior)" Data="@ConfigurationSelectDataFactory.GetUpdateBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UpdateBehavior = selectedValue)" OptionHelp="@T("How often should we check for app updates?")" Disabled="() => this.SettingsLocker.IsLocked<DataApp>(x => x.UpdateBehavior)"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Check for updates")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.UpdateInterval)" Data="@ConfigurationSelectDataFactory.GetUpdateIntervalData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UpdateInterval = selectedValue)" OptionHelp="@T("How often should we check for app updates?")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.UpdateInterval, out var meta) && meta.IsLocked"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Update installation method")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.UpdateInstallation)" Data="@ConfigurationSelectDataFactory.GetUpdateBehaviourData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UpdateInstallation = selectedValue)" OptionHelp="@T("Should updates be installed automatically or manually?")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.UpdateInstallation, out var meta) && meta.IsLocked"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Navigation bar behavior")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.NavigationBehavior)" Data="@ConfigurationSelectDataFactory.GetNavBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.NavigationBehavior = selectedValue)" OptionHelp="@T("Select the desired behavior for the navigation bar.")"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Preview feature visibility")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreviewVisibility)" Data="@ConfigurationSelectDataFactory.GetPreviewVisibility()" SelectionUpdate="@this.UpdatePreviewFeatures" OptionHelp="@T("Do you want to show preview features in the app?")"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Preview feature visibility")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreviewVisibility)" Data="@ConfigurationSelectDataFactory.GetPreviewVisibility()" SelectionUpdate="@this.UpdatePreviewFeatures" OptionHelp="@T("Do you want to show preview features in the app?")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.PreviewVisibility, out var meta) && meta.IsLocked"/>
|
||||
|
||||
@if (this.SettingsManager.ConfigurationData.App.PreviewVisibility > PreviewVisibility.NONE)
|
||||
{
|
||||
var availablePreviewFeatures = ConfigurationSelectDataFactory.GetPreviewFeaturesData(this.SettingsManager).ToList();
|
||||
if (availablePreviewFeatures.Count > 0)
|
||||
{
|
||||
<ConfigurationMultiSelect OptionDescription="@T("Select preview features")" SelectedValues="@(() => this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures.Where(x => !x.IsReleased()).ToHashSet())" Data="@availablePreviewFeatures" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures = selectedValue)" OptionHelp="@T("Which preview features would you like to enable?")"/>
|
||||
<ConfigurationMultiSelect OptionDescription="@T("Select preview features")" SelectedValues="@(() => this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures.Where(x => !x.IsReleased()).ToHashSet())" Data="@availablePreviewFeatures" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures = selectedValue)" OptionHelp="@T("Which preview features would you like to enable?")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.EnabledPreviewFeatures, out var meta) && meta.IsLocked"/>
|
||||
}
|
||||
}
|
||||
|
||||
<ConfigurationProviderSelection Component="Components.APP_SETTINGS" Data="@this.AvailableLLMProvidersFunc()" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProvider = selectedValue)" HelpText="@(() => T("Would you like to set one provider as the default for the entire app? When you configure a different provider for an assistant, it will always take precedence."))"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Preselect one of your profiles?")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProfile = selectedValue)" OptionHelp="@T("Would you like to set one of your profiles as the default for the entire app? When you configure a different profile for an assistant, it will always take precedence.")"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Preselect one of your profiles?")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProfile = selectedValue)" OptionHelp="@T("Would you like to set one of your profiles as the default for the entire app? When you configure a different profile for an assistant, it will always take precedence.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.PreselectedProfile, out var meta) && meta.IsLocked"/>
|
||||
</ExpansionPanel>
|
||||
@@ -15,7 +15,4 @@ public abstract class SettingsPanelBase : MSGComponentBase
|
||||
|
||||
[Inject]
|
||||
protected RustService RustService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected SettingsLocker SettingsLocker { get; init; } = null!;
|
||||
}
|
||||
@@ -90,9 +90,9 @@ public partial class SettingsPanelEmbeddings : SettingsPanelBase
|
||||
|
||||
private async Task DeleteEmbeddingProvider(EmbeddingProvider provider)
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Are you sure you want to delete the embedding provider '{0}'?"), provider.Name) },
|
||||
{ x => x.Message, string.Format(T("Are you sure you want to delete the embedding provider '{0}'?"), provider.Name) },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Embedding Provider"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
|
||||
@@ -74,10 +74,8 @@
|
||||
@T("No providers configured yet.")
|
||||
</MudText>
|
||||
}
|
||||
|
||||
<MudButton Variant="Variant.Filled" Color="@Color.Primary" StartIcon="@Icons.Material.Filled.AddRoad" Class="mt-3 mb-6" OnClick="@this.AddLLMProvider">
|
||||
@T("Add Provider")
|
||||
</MudButton>
|
||||
|
||||
<LockableButton Text="@T("Add Provider")" IsLocked="@(() => !this.SettingsManager.ConfigurationData.App.AllowUserToAddProvider)" Icon="@Icons.Material.Filled.AddRoad" OnClickAsync="@this.AddLLMProvider" Class="mt-3" />
|
||||
|
||||
<MudText Typo="Typo.h4" Class="mb-3">
|
||||
@T("LLM Provider Confidence")
|
||||
|
||||
@@ -54,6 +54,12 @@ public partial class SettingsPanelProviders : SettingsPanelBase
|
||||
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
|
||||
private async Task EditLLMProvider(AIStudio.Settings.Provider provider)
|
||||
{
|
||||
if(provider == AIStudio.Settings.Provider.NONE)
|
||||
return;
|
||||
|
||||
if (provider.IsEnterpriseConfiguration)
|
||||
return;
|
||||
|
||||
var dialogParameters = new DialogParameters<ProviderDialog>
|
||||
{
|
||||
{ x => x.DataNum, provider.Num },
|
||||
@@ -66,6 +72,7 @@ public partial class SettingsPanelProviders : SettingsPanelBase
|
||||
{ x => x.IsEditing, true },
|
||||
{ x => x.DataHost, provider.Host },
|
||||
{ x => x.HFInferenceProviderId, provider.HFInferenceProvider },
|
||||
{ x => x.AdditionalJsonApiParameters, provider.AdditionalJsonApiParameters },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ProviderDialog>(T("Edit LLM Provider"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -90,9 +97,9 @@ public partial class SettingsPanelProviders : SettingsPanelBase
|
||||
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
|
||||
private async Task DeleteLLMProvider(AIStudio.Settings.Provider provider)
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Are you sure you want to delete the provider '{0}'?"), provider.InstanceName) },
|
||||
{ x => x.Message, string.Format(T("Are you sure you want to delete the provider '{0}'?"), provider.InstanceName) },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete LLM Provider"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -108,9 +115,9 @@ public partial class SettingsPanelProviders : SettingsPanelBase
|
||||
}
|
||||
else
|
||||
{
|
||||
var issueDialogParameters = new DialogParameters
|
||||
var issueDialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Couldn't delete the provider '{0}'. The issue: {1}. We can ignore this issue and delete the provider anyway. Do you want to ignore it and delete this provider?"), provider.InstanceName, deleteSecretResponse.Issue) },
|
||||
{ x => x.Message, string.Format(T("Couldn't delete the provider '{0}'. The issue: {1}. We can ignore this issue and delete the provider anyway. Do you want to ignore it and delete this provider?"), provider.InstanceName, deleteSecretResponse.Issue) },
|
||||
};
|
||||
|
||||
var issueDialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete LLM Provider"), issueDialogParameters, DialogOptions.FULLSCREEN);
|
||||
|
||||
@@ -16,9 +16,6 @@ public partial class Workspaces : MSGComponentBase
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ThreadSafeRandom RNG { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ILogger<Workspaces> Logger { get; init; } = null!;
|
||||
|
||||
@@ -47,14 +44,17 @@ public partial class Workspaces : MSGComponentBase
|
||||
// - Those initial tree items cannot have children
|
||||
// - When assigning the tree items to the MudTreeViewItem component, we must set the Value property to the value of the item
|
||||
//
|
||||
await this.LoadTreeItems();
|
||||
// We won't await the loading of the tree items here,
|
||||
// to avoid blocking the UI thread:
|
||||
_ = this.LoadTreeItems();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private async Task LoadTreeItems()
|
||||
{
|
||||
var workspacesNode = new TreeItemData<ITreeItem>
|
||||
this.treeItems.Clear();
|
||||
this.treeItems.Add(new TreeItemData<ITreeItem>
|
||||
{
|
||||
Expanded = this.ExpandRootNodes,
|
||||
Expandable = true,
|
||||
@@ -68,9 +68,16 @@ public partial class Workspaces : MSGComponentBase
|
||||
Path = "root",
|
||||
Children = await this.LoadWorkspaces(),
|
||||
},
|
||||
};
|
||||
|
||||
var tempChatNode = new TreeItemData<ITreeItem>
|
||||
});
|
||||
|
||||
this.treeItems.Add(new TreeItemData<ITreeItem>
|
||||
{
|
||||
Expandable = false,
|
||||
Value = new TreeDivider(),
|
||||
});
|
||||
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
this.treeItems.Add(new TreeItemData<ITreeItem>
|
||||
{
|
||||
Expanded = this.ExpandRootNodes,
|
||||
Expandable = true,
|
||||
@@ -84,16 +91,9 @@ public partial class Workspaces : MSGComponentBase
|
||||
Path = "temp",
|
||||
Children = await this.LoadTemporaryChats(),
|
||||
},
|
||||
};
|
||||
|
||||
this.treeItems.Clear();
|
||||
this.treeItems.Add(workspacesNode);
|
||||
this.treeItems.Add(new TreeItemData<ITreeItem>
|
||||
{
|
||||
Expandable = false,
|
||||
Value = new TreeDivider(),
|
||||
});
|
||||
this.treeItems.Add(tempChatNode);
|
||||
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyCollection<TreeItemData<ITreeItem>>> LoadTemporaryChats()
|
||||
@@ -109,9 +109,30 @@ public partial class Workspaces : MSGComponentBase
|
||||
// Enumerate the chat directories:
|
||||
foreach (var tempChatDirPath in Directory.EnumerateDirectories(temporaryDirectories))
|
||||
{
|
||||
// Read the `name` file:
|
||||
// Read or create the `name` file (self-heal):
|
||||
var chatNamePath = Path.Join(tempChatDirPath, "name");
|
||||
var chatName = await File.ReadAllTextAsync(chatNamePath, Encoding.UTF8);
|
||||
string chatName;
|
||||
try
|
||||
{
|
||||
if (!File.Exists(chatNamePath))
|
||||
{
|
||||
chatName = T("Unnamed chat");
|
||||
await File.WriteAllTextAsync(chatNamePath, chatName, Encoding.UTF8);
|
||||
}
|
||||
else
|
||||
{
|
||||
chatName = await File.ReadAllTextAsync(chatNamePath, Encoding.UTF8);
|
||||
if (string.IsNullOrWhiteSpace(chatName))
|
||||
{
|
||||
chatName = T("Unnamed chat");
|
||||
await File.WriteAllTextAsync(chatNamePath, chatName, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
chatName = T("Unnamed chat");
|
||||
}
|
||||
|
||||
// Read the last change time of the chat:
|
||||
var chatThreadPath = Path.Join(tempChatDirPath, "thread.json");
|
||||
@@ -155,9 +176,30 @@ public partial class Workspaces : MSGComponentBase
|
||||
// Enumerate the workspace directories:
|
||||
foreach (var workspaceDirPath in Directory.EnumerateDirectories(workspaceDirectories))
|
||||
{
|
||||
// Read the `name` file:
|
||||
// Read or create the `name` file (self-heal):
|
||||
var workspaceNamePath = Path.Join(workspaceDirPath, "name");
|
||||
var workspaceName = await File.ReadAllTextAsync(workspaceNamePath, Encoding.UTF8);
|
||||
string workspaceName;
|
||||
try
|
||||
{
|
||||
if (!File.Exists(workspaceNamePath))
|
||||
{
|
||||
workspaceName = T("Unnamed workspace");
|
||||
await File.WriteAllTextAsync(workspaceNamePath, workspaceName, Encoding.UTF8);
|
||||
}
|
||||
else
|
||||
{
|
||||
workspaceName = await File.ReadAllTextAsync(workspaceNamePath, Encoding.UTF8);
|
||||
if (string.IsNullOrWhiteSpace(workspaceName))
|
||||
{
|
||||
workspaceName = T("Unnamed workspace");
|
||||
await File.WriteAllTextAsync(workspaceNamePath, workspaceName, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
workspaceName = T("Unnamed workspace");
|
||||
}
|
||||
|
||||
workspaces.Add(new TreeItemData<ITreeItem>
|
||||
{
|
||||
@@ -191,9 +233,30 @@ public partial class Workspaces : MSGComponentBase
|
||||
// Enumerate the workspace directory:
|
||||
foreach (var chatPath in Directory.EnumerateDirectories(workspacePath))
|
||||
{
|
||||
// Read the `name` file:
|
||||
// Read or create the `name` file (self-heal):
|
||||
var chatNamePath = Path.Join(chatPath, "name");
|
||||
var chatName = await File.ReadAllTextAsync(chatNamePath, Encoding.UTF8);
|
||||
string chatName;
|
||||
try
|
||||
{
|
||||
if (!File.Exists(chatNamePath))
|
||||
{
|
||||
chatName = T("Unnamed chat");
|
||||
await File.WriteAllTextAsync(chatNamePath, chatName, Encoding.UTF8);
|
||||
}
|
||||
else
|
||||
{
|
||||
chatName = await File.ReadAllTextAsync(chatNamePath, Encoding.UTF8);
|
||||
if (string.IsNullOrWhiteSpace(chatName))
|
||||
{
|
||||
chatName = T("Unnamed chat");
|
||||
await File.WriteAllTextAsync(chatNamePath, chatName, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
chatName = T("Unnamed chat");
|
||||
}
|
||||
|
||||
// Read the last change time of the chat:
|
||||
var chatThreadPath = Path.Join(chatPath, "thread.json");
|
||||
@@ -249,9 +312,9 @@ public partial class Workspaces : MSGComponentBase
|
||||
// Check if the chat has unsaved changes:
|
||||
if (switchToChat && await MessageBus.INSTANCE.SendMessageUseFirstResult<bool, bool>(this, Event.HAS_CHAT_UNSAVED_CHANGES))
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", T("Are you sure you want to load another chat? All unsaved changes will be lost.") },
|
||||
{ x => x.Message, T("Are you sure you want to load another chat? All unsaved changes will be lost.") },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Load Chat"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -290,10 +353,10 @@ public partial class Workspaces : MSGComponentBase
|
||||
if (askForConfirmation)
|
||||
{
|
||||
var workspaceName = await WorkspaceBehaviour.LoadWorkspaceName(chat.WorkspaceId);
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{
|
||||
"Message", (chat.WorkspaceId == Guid.Empty) switch
|
||||
x => x.Message, (chat.WorkspaceId == Guid.Empty) switch
|
||||
{
|
||||
true => string.Format(T("Are you sure you want to delete the temporary chat '{0}'?"), chat.Name),
|
||||
false => string.Format(T("Are you sure you want to delete the chat '{0}' in the workspace '{1}'?"), chat.Name, workspaceName),
|
||||
@@ -330,12 +393,15 @@ public partial class Workspaces : MSGComponentBase
|
||||
if (chat is null)
|
||||
return;
|
||||
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<SingleInputDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Please enter a new or edit the name for your chat '{0}':"), chat.Name) },
|
||||
{ "UserInput", chat.Name },
|
||||
{ "ConfirmText", T("Rename") },
|
||||
{ "ConfirmColor", Color.Info },
|
||||
{ x => x.Message, string.Format(T("Please enter a new or edit the name for your chat '{0}':"), chat.Name) },
|
||||
{ x => x.InputHeaderText, T("Chat Name") },
|
||||
{ x => x.UserInput, chat.Name },
|
||||
{ x => x.ConfirmText, T("Rename") },
|
||||
{ x => x.ConfirmColor, Color.Info },
|
||||
{ x => x.AllowEmptyInput, false },
|
||||
{ x => x.EmptyInputErrorMessage, T("Please enter a chat name.") },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<SingleInputDialog>(T("Rename Chat"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -362,12 +428,15 @@ public partial class Workspaces : MSGComponentBase
|
||||
|
||||
var workspaceId = Guid.Parse(Path.GetFileName(workspacePath));
|
||||
var workspaceName = await WorkspaceBehaviour.LoadWorkspaceName(workspaceId);
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<SingleInputDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Please enter a new or edit the name for your workspace '{0}':"), workspaceName) },
|
||||
{ "UserInput", workspaceName },
|
||||
{ "ConfirmText", T("Rename") },
|
||||
{ "ConfirmColor", Color.Info },
|
||||
{ x => x.Message, string.Format(T("Please enter a new or edit the name for your workspace '{0}':"), workspaceName) },
|
||||
{ x => x.InputHeaderText, T("Workspace Name") },
|
||||
{ x => x.UserInput, workspaceName },
|
||||
{ x => x.ConfirmText, T("Rename") },
|
||||
{ x => x.ConfirmColor, Color.Info },
|
||||
{ x => x.AllowEmptyInput, false },
|
||||
{ x => x.EmptyInputErrorMessage, T("Please enter a workspace name.") },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<SingleInputDialog>(T("Rename Workspace"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -383,12 +452,15 @@ public partial class Workspaces : MSGComponentBase
|
||||
|
||||
private async Task AddWorkspace()
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<SingleInputDialog>
|
||||
{
|
||||
{ "Message", T("Please name your workspace:") },
|
||||
{ "UserInput", string.Empty },
|
||||
{ "ConfirmText", T("Add workspace") },
|
||||
{ "ConfirmColor", Color.Info },
|
||||
{ x => x.Message, T("Please name your workspace:") },
|
||||
{ x => x.InputHeaderText, T("Workspace Name") },
|
||||
{ x => x.UserInput, string.Empty },
|
||||
{ x => x.ConfirmText, T("Add workspace") },
|
||||
{ x => x.ConfirmColor, Color.Info },
|
||||
{ x => x.AllowEmptyInput, false },
|
||||
{ x => x.EmptyInputErrorMessage, T("Please enter a workspace name.") },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<SingleInputDialog>(T("Add Workspace"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -417,9 +489,9 @@ public partial class Workspaces : MSGComponentBase
|
||||
// Determine how many chats are in the workspace:
|
||||
var chatCount = Directory.EnumerateDirectories(workspacePath).Count();
|
||||
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Are you sure you want to delete the workspace '{0}'? This will also delete {1} chat(s) in this workspace."), workspaceName, chatCount) },
|
||||
{ x => x.Message, string.Format(T("Are you sure you want to delete the workspace '{0}'? This will also delete {1} chat(s) in this workspace."), workspaceName, chatCount) },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Workspace"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -437,11 +509,11 @@ public partial class Workspaces : MSGComponentBase
|
||||
if (chat is null)
|
||||
return;
|
||||
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<WorkspaceSelectionDialog>
|
||||
{
|
||||
{ "Message", T("Please select the workspace where you want to move the chat to.") },
|
||||
{ "SelectedWorkspace", chat.WorkspaceId },
|
||||
{ "ConfirmText", T("Move chat") },
|
||||
{ x => x.Message, T("Please select the workspace where you want to move the chat to.") },
|
||||
{ x => x.SelectedWorkspace, chat.WorkspaceId },
|
||||
{ x => x.ConfirmText, T("Move chat") },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<WorkspaceSelectionDialog>(T("Move Chat to Workspace"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -484,9 +556,9 @@ public partial class Workspaces : MSGComponentBase
|
||||
// Check if the chat has unsaved changes:
|
||||
if (await MessageBus.INSTANCE.SendMessageUseFirstResult<bool, bool>(this, Event.HAS_CHAT_UNSAVED_CHANGES))
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", T("Are you sure you want to create a another chat? All unsaved changes will be lost.") },
|
||||
{ x => x.Message, T("Are you sure you want to create a another chat? All unsaved changes will be lost.") },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Create Chat"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -501,7 +573,6 @@ public partial class Workspaces : MSGComponentBase
|
||||
WorkspaceId = workspaceId,
|
||||
ChatId = Guid.NewGuid(),
|
||||
Name = string.Empty,
|
||||
Seed = this.RNG.Next(),
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
Blocks = [],
|
||||
};
|
||||
|
||||
@@ -56,7 +56,32 @@
|
||||
@T("Use the default system prompt")
|
||||
</MudButton>
|
||||
|
||||
<MudJustifiedText Class="mt-6" Typo="Typo.body1">
|
||||
<MudText Typo="Typo.h6" Class="mb-3 mt-6">
|
||||
@T("Predefined User Input")
|
||||
</MudText>
|
||||
<MudJustifiedText Class="mb-3" Typo="Typo.body1">
|
||||
@T("You might want to predefine a first message that will be copied into the user prompt, when you use this chat template. This message could for example be a blueprint for a structured message that this chat template is defined to work with.")
|
||||
</MudJustifiedText>
|
||||
<MudTextField
|
||||
T="string"
|
||||
@bind-Text="@this.PredefinedUserPrompt"
|
||||
AdornmentIcon="@Icons.Material.Filled.ListAlt"
|
||||
Adornment="Adornment.Start"
|
||||
Immediate="@true"
|
||||
Label="@T("What predefined user input do you want to use?")"
|
||||
Variant="Variant.Outlined"
|
||||
Lines="4"
|
||||
MaxLines="12"
|
||||
AutoGrow="@true"
|
||||
Class="mb-3"
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
HelperText="@T("Tell the AI your predefined user input.")"
|
||||
/>
|
||||
|
||||
<MudText Typo="Typo.h6" Class="mb-3 mt-6">
|
||||
@T("Profile Usage")
|
||||
</MudText>
|
||||
<MudJustifiedText Class="mb-3" Typo="Typo.body1">
|
||||
@T("Using some chat templates in tandem with profiles might cause issues. Therefore, you might prohibit the usage of profiles here.")
|
||||
</MudJustifiedText>
|
||||
<MudTextSwitch @bind-Value="@this.AllowProfileUsage" Color="Color.Primary" Label="@T("Allow the use of profiles together with this chat template?")" LabelOn="@T("Yes, allow profiles when using this template")" LabelOff="@T("No, prohibit profile use for this template")" />
|
||||
@@ -64,11 +89,9 @@
|
||||
<MudText Typo="Typo.h6" Class="mb-3 mt-6">
|
||||
@T("Example Conversation")
|
||||
</MudText>
|
||||
|
||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||
@T("Add messages of an example conversation (user prompt followed by assistant prompt) to demonstrate the desired interaction pattern. These examples help the AI understand your expectations by showing it the correct format, style, and content of responses before it receives actual user inputs.")
|
||||
</MudJustifiedText>
|
||||
|
||||
<MudTable Items="@this.dataExampleConversation" FixedHeader="true" Hover="true" Class="mt-3 mb-3" CanCancelEdit="true" CancelEditTooltip="@T("Cancel")" CommitEditTooltip="@T("Commit Changes")" Outlined="true" RowEditCancel="@this.ResetItem" RowEditPreview="@this.BackupItem" EditTrigger="TableEditTrigger.RowClick" IsEditRowSwitchingBlocked="false" RowEditCommit="@this.CommitInlineEdit">
|
||||
<ColGroup>
|
||||
<col style="width: 10em;" />
|
||||
|
||||
@@ -35,6 +35,12 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
[Parameter]
|
||||
public string DataSystemPrompt { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// What is the predefined user prompt?
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string PredefinedUserPrompt { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Should the dialog be in editing mode?
|
||||
/// </summary>
|
||||
@@ -47,6 +53,12 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
[Parameter]
|
||||
public bool AllowProfileUsage { get; set; } = true;
|
||||
|
||||
[Parameter]
|
||||
public bool CreateFromExistingChatThread { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public ChatThread? ExistingChatThread { get; set; }
|
||||
|
||||
[Inject]
|
||||
private ILogger<ChatTemplateDialog> Logger { get; init; } = null!;
|
||||
|
||||
@@ -84,6 +96,13 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
this.dataEditingPreviousName = this.DataName.ToLowerInvariant();
|
||||
this.dataExampleConversation = this.ExampleConversation.Select(n => n.DeepClone()).ToList();
|
||||
}
|
||||
|
||||
if (this.CreateFromExistingChatThread && this.ExistingChatThread is not null)
|
||||
{
|
||||
this.DataSystemPrompt = this.ExistingChatThread.SystemPrompt;
|
||||
this.dataExampleConversation = this.ExistingChatThread.Blocks.Select(n => n.DeepClone(true)).ToList();
|
||||
this.DataName = this.ExistingChatThread.Name;
|
||||
}
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
@@ -107,8 +126,12 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
|
||||
Name = this.DataName,
|
||||
SystemPrompt = this.DataSystemPrompt,
|
||||
PredefinedUserPrompt = this.PredefinedUserPrompt,
|
||||
ExampleConversation = this.dataExampleConversation,
|
||||
AllowProfileUsage = this.AllowProfileUsage,
|
||||
|
||||
EnterpriseConfigurationPluginId = Guid.Empty,
|
||||
IsEnterpriseConfiguration = false,
|
||||
};
|
||||
|
||||
private void RemoveMessage(ContentBlock item)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
@inherits MSGComponentBase
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
<MudText Typo="Typo.body1">
|
||||
<MudJustifiedText Typo="Typo.body1">
|
||||
@this.Message
|
||||
</MudText>
|
||||
</MudJustifiedText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">
|
||||
|
||||
@@ -106,6 +106,12 @@ public partial class DataSourceERI_V1Dialog : MSGComponentBase, ISecretId
|
||||
this.dataUsername = this.DataSource.Username;
|
||||
this.dataSecurityPolicy = this.DataSource.SecurityPolicy;
|
||||
this.dataMaxMatches = this.DataSource.MaxMatches;
|
||||
|
||||
// We cannot load the retrieval processes now, since we have
|
||||
// to load the data first. But while doing so, we can need to
|
||||
// restore the selected retrieval process id. That's why we
|
||||
// assign the selected retrieval id to the default retrieval process:
|
||||
this.dataSelectedRetrievalProcess = this.dataSelectedRetrievalProcess with { Id = this.DataSource.SelectedRetrievalId };
|
||||
|
||||
if (this.dataAuthMethod is AuthMethod.TOKEN or AuthMethod.USERNAME_PASSWORD)
|
||||
{
|
||||
|
||||
@@ -69,9 +69,6 @@ public partial class EmbeddingProviderDialog : MSGComponentBase, ISecretId
|
||||
[Parameter]
|
||||
public bool IsEditing { get; init; }
|
||||
|
||||
[Inject]
|
||||
private ILogger<ProviderDialog> Logger { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
@@ -244,7 +241,7 @@ public partial class EmbeddingProviderDialog : MSGComponentBase, ISecretId
|
||||
private async Task ReloadModels()
|
||||
{
|
||||
var currentEmbeddingProviderSettings = this.CreateEmbeddingProviderSettings();
|
||||
var provider = currentEmbeddingProviderSettings.CreateProvider(this.Logger);
|
||||
var provider = currentEmbeddingProviderSettings.CreateProvider();
|
||||
if(provider is NoProvider)
|
||||
return;
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(this.licenseText))
|
||||
{
|
||||
<MudMarkdown Value="@this.licenseText" OverrideHeaderTypo="@Markdown.OverrideHeaderTypo"/>
|
||||
<MudMarkdown Value="@this.licenseText" Props="Markdown.DefaultConfig"/>
|
||||
}
|
||||
</ExpansionPanel>
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ public partial class PandocDialog : MSGComponentBase
|
||||
[CascadingParameter]
|
||||
private IMudDialogInstance MudDialog { get; set; } = null!;
|
||||
|
||||
private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger("PandocDialog");
|
||||
private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger(nameof(PandocDialog));
|
||||
private static readonly string LICENCE_URI = "https://raw.githubusercontent.com/jgm/pandoc/refs/heads/main/COPYING.md";
|
||||
private static string LATEST_PANDOC_VERSION = string.Empty;
|
||||
|
||||
@@ -83,9 +83,9 @@ public partial class PandocDialog : MSGComponentBase
|
||||
private async Task RejectLicense()
|
||||
{
|
||||
var message = T("Pandoc is open-source and free, but if you reject its license, you can't install it and some MindWork AI Studio features will be limited (like the integration of Office files) or unavailable (like the generation of Office files). You can change your decision anytime. Are you sure you want to reject the license?");
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", message },
|
||||
{ x => x.Message, message },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Reject Pandoc's Licence"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
@inherits MSGComponentBase
|
||||
|
||||
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
<PreviewPrototype/>
|
||||
|
||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||
@T("Test how Pandoc loads your document. See the raw content it produces before further processing.")
|
||||
</MudJustifiedText>
|
||||
|
||||
<ReadFileContent Text="@T("Load document")" @bind-FileContent="@this.documentContent"/>
|
||||
|
||||
<MudTextField
|
||||
T="string"
|
||||
@bind-Text="@this.documentContent"
|
||||
AdornmentIcon="@Icons.Material.Filled.Article"
|
||||
Adornment="Adornment.Start"
|
||||
Immediate="@true"
|
||||
Label="@T("Content Loaded by Pandoc")"
|
||||
Variant="Variant.Outlined"
|
||||
Lines="6"
|
||||
AutoGrow="@true"
|
||||
MaxLines="25"
|
||||
Class="mt-10"
|
||||
HelperText="@T("This is the content Pandoc loaded from your document — including headings, lists, and formatting. Use this to verify your document loads as expected.")"
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">
|
||||
@T("Cancel")
|
||||
</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Formats.Asn1;
|
||||
using AIStudio.Components;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Dialogs;
|
||||
|
||||
/// <summary>
|
||||
/// Check how your file will be loaded by Pandoc.
|
||||
/// </summary>
|
||||
public partial class PandocDocumentCheckDialog : MSGComponentBase
|
||||
{
|
||||
[CascadingParameter]
|
||||
private IMudDialogInstance MudDialog { get; set; } = null!;
|
||||
|
||||
|
||||
private string documentContent = string.Empty;
|
||||
|
||||
private void Cancel() => this.MudDialog.Cancel();
|
||||
}
|
||||
@@ -42,12 +42,10 @@
|
||||
Lines="6"
|
||||
AutoGrow="@true"
|
||||
MaxLines="12"
|
||||
MaxLength="444"
|
||||
Counter="444"
|
||||
Class="mb-3"
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
HelperText="@T("Tell the AI something about yourself. What is your profession? How experienced are you in this profession? Which technologies do you like?")"
|
||||
/>
|
||||
<ReadFileContent @bind-FileContent="@this.DataNeedToKnow"/>
|
||||
|
||||
<MudTextField
|
||||
T="string"
|
||||
@@ -61,13 +59,16 @@
|
||||
Lines="6"
|
||||
AutoGrow="@true"
|
||||
MaxLines="12"
|
||||
MaxLength="256"
|
||||
Counter="256"
|
||||
Class="mb-3"
|
||||
Class="mt-10"
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
HelperText="@T("Tell the AI what you want it to do for you. What are your goals or are you trying to achieve? Like having the AI address you informally.")"
|
||||
/>
|
||||
<ReadFileContent @bind-FileContent="@this.DataActions"/>
|
||||
|
||||
<MudJustifiedText Typo="Typo.body2" Class="mb-3 mt-3">
|
||||
@T("Please be aware that your profile info becomes part of the system prompt. This means it uses up context space — the “memory” the LLM uses to understand and respond to your request. If your profile is extremely long, the LLM may struggle to focus on your actual task.")
|
||||
</MudJustifiedText>
|
||||
|
||||
</MudForm>
|
||||
<Issues IssuesData="@this.dataIssues"/>
|
||||
</DialogContent>
|
||||
|
||||
@@ -67,10 +67,12 @@ public partial class ProfileDialog : MSGComponentBase
|
||||
{
|
||||
Num = this.DataNum,
|
||||
Id = this.DataId,
|
||||
|
||||
Name = this.DataName,
|
||||
NeedToKnow = this.DataNeedToKnow,
|
||||
Actions = this.DataActions,
|
||||
|
||||
EnterpriseConfigurationPluginId = Guid.Empty,
|
||||
IsEnterpriseConfiguration = false,
|
||||
};
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
@@ -129,9 +131,6 @@ public partial class ProfileDialog : MSGComponentBase
|
||||
if (string.IsNullOrWhiteSpace(this.DataNeedToKnow) && string.IsNullOrWhiteSpace(this.DataActions))
|
||||
return T("Please enter what the LLM should know about you and/or what actions it should take.");
|
||||
|
||||
if(text.Length > 444)
|
||||
return T("The text must not exceed 444 characters.");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -140,9 +139,6 @@ public partial class ProfileDialog : MSGComponentBase
|
||||
if (string.IsNullOrWhiteSpace(this.DataNeedToKnow) && string.IsNullOrWhiteSpace(this.DataActions))
|
||||
return T("Please enter what the LLM should know about you and/or what actions it should take.");
|
||||
|
||||
if(text.Length > 256)
|
||||
return T("The text must not exceed 256 characters.");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -130,6 +130,18 @@
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
/>
|
||||
|
||||
<MudStack>
|
||||
<MudButton OnClick="@this.ToggleExpertSettings">
|
||||
@(this.showExpertSettings ? T("Hide Expert Settings") : T("Show Expert Settings"))
|
||||
</MudButton>
|
||||
<MudDivider />
|
||||
<MudCollapse Expanded="@this.showExpertSettings" Class="@this.GetExpertStyles">
|
||||
<MudJustifiedText Class="mb-5">
|
||||
@T("Please be aware: This section is for experts only. You are responsible for verifying the correctness of the additional parameters you provide to the API call. By default, AI Studio uses the OpenAI-compatible chat completions API, when that it is supported by the underlying service and model.")
|
||||
</MudJustifiedText>
|
||||
<MudTextField T="string" Label=@T("Additional API parameters") Variant="Variant.Outlined" Lines="4" AutoGrow="true" MaxLines="10" HelperText=@T("""Add the parameters in proper JSON formatting, e.g., "temperature": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though.""") Placeholder="@GetPlaceholderExpertSettings" @bind-Value="@this.AdditionalJsonApiParameters" OnBlur="@this.OnInputChangeExpertSettings"/>
|
||||
</MudCollapse>
|
||||
</MudStack>
|
||||
</MudForm>
|
||||
<Issues IssuesData="@this.dataIssues"/>
|
||||
</DialogContent>
|
||||
|
||||
@@ -78,8 +78,8 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
[Parameter]
|
||||
public bool IsEditing { get; init; }
|
||||
|
||||
[Inject]
|
||||
private ILogger<ProviderDialog> Logger { get; init; } = null!;
|
||||
[Parameter]
|
||||
public string AdditionalJsonApiParameters { get; set; } = string.Empty;
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
@@ -97,6 +97,7 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
private string dataManuallyModel = string.Empty;
|
||||
private string dataAPIKeyStorageIssue = string.Empty;
|
||||
private string dataEditingPreviousInstanceName = string.Empty;
|
||||
private bool showExpertSettings;
|
||||
|
||||
// We get the form reference from Blazor code to validate it manually:
|
||||
private MudForm form = null!;
|
||||
@@ -126,17 +127,19 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
Id = this.DataId,
|
||||
InstanceName = this.DataInstanceName,
|
||||
UsedLLMProvider = this.DataLLMProvider,
|
||||
|
||||
Model = this.DataLLMProvider switch
|
||||
{
|
||||
LLMProviders.FIREWORKS => new Model(this.dataManuallyModel, null),
|
||||
LLMProviders.HUGGINGFACE => new Model(this.dataManuallyModel, null),
|
||||
LLMProviders.FIREWORKS or LLMProviders.HUGGINGFACE => new Model(this.dataManuallyModel, null),
|
||||
_ => this.DataModel
|
||||
},
|
||||
|
||||
IsSelfHosted = this.DataLLMProvider is LLMProviders.SELF_HOSTED,
|
||||
IsEnterpriseConfiguration = false,
|
||||
Hostname = cleanedHostname.EndsWith('/') ? cleanedHostname[..^1] : cleanedHostname,
|
||||
Host = this.DataHost,
|
||||
HFInferenceProvider = this.HFInferenceProviderId,
|
||||
AdditionalJsonApiParameters = this.AdditionalJsonApiParameters,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -151,6 +154,8 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
#pragma warning disable MWAIS0001
|
||||
this.UsedInstanceNames = this.SettingsManager.ConfigurationData.Providers.Select(x => x.InstanceName.ToLowerInvariant()).ToList();
|
||||
#pragma warning restore MWAIS0001
|
||||
|
||||
this.showExpertSettings = !string.IsNullOrWhiteSpace(this.AdditionalJsonApiParameters);
|
||||
|
||||
// When editing, we need to load the data:
|
||||
if(this.IsEditing)
|
||||
@@ -158,13 +163,13 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
this.dataEditingPreviousInstanceName = this.DataInstanceName.ToLowerInvariant();
|
||||
|
||||
// When using Fireworks or Hugging Face, we must copy the model name:
|
||||
if (this.DataLLMProvider is LLMProviders.FIREWORKS or LLMProviders.HUGGINGFACE)
|
||||
if (this.DataLLMProvider.IsLLMModelProvidedManually())
|
||||
this.dataManuallyModel = this.DataModel.Id;
|
||||
|
||||
//
|
||||
// We cannot load the API key for self-hosted providers:
|
||||
//
|
||||
if (this.DataLLMProvider is LLMProviders.SELF_HOSTED && this.DataHost is not Host.OLLAMA)
|
||||
if (this.DataLLMProvider is LLMProviders.SELF_HOSTED && this.DataHost is not Host.OLLAMA && this.DataHost is not Host.VLLM)
|
||||
{
|
||||
await this.ReloadModels();
|
||||
await base.OnInitializedAsync();
|
||||
@@ -241,7 +246,7 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
|
||||
private string? ValidateManuallyModel(string manuallyModel)
|
||||
{
|
||||
if ((this.DataLLMProvider is LLMProviders.FIREWORKS or LLMProviders.HUGGINGFACE) && string.IsNullOrWhiteSpace(manuallyModel))
|
||||
if (this.DataLLMProvider.IsLLMModelProvidedManually() && string.IsNullOrWhiteSpace(manuallyModel))
|
||||
return T("Please enter a model name.");
|
||||
|
||||
return null;
|
||||
@@ -252,7 +257,7 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
private async Task ReloadModels()
|
||||
{
|
||||
var currentProviderSettings = this.CreateProviderSettings();
|
||||
var provider = currentProviderSettings.CreateProvider(this.Logger);
|
||||
var provider = currentProviderSettings.CreateProvider();
|
||||
if(provider is NoProvider)
|
||||
return;
|
||||
|
||||
@@ -270,4 +275,20 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
LLMProviders.SELF_HOSTED => T("(Optional) API Key"),
|
||||
_ => T("API Key"),
|
||||
};
|
||||
|
||||
private void ToggleExpertSettings() => this.showExpertSettings = !this.showExpertSettings;
|
||||
|
||||
private void OnInputChangeExpertSettings()
|
||||
{
|
||||
this.AdditionalJsonApiParameters = this.AdditionalJsonApiParameters.Trim().TrimEnd(',', ' ');
|
||||
}
|
||||
|
||||
private string GetExpertStyles => this.showExpertSettings ? "border-2 border-dashed rounded pa-2" : string.Empty;
|
||||
|
||||
private static string GetPlaceholderExpertSettings =>
|
||||
"""
|
||||
"temperature": 0.5,
|
||||
"top_p": 0.9,
|
||||
"frequency_penalty": 0.0
|
||||
""";
|
||||
}
|
||||
@@ -8,9 +8,9 @@ public partial class SettingsDialogAssistantBias : SettingsDialogBase
|
||||
|
||||
private async Task ResetBiasOfTheDayHistory()
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", T("Are you sure you want to reset your bias-of-the-day statistics? The system will no longer remember which biases you already know. As a result, biases you are already familiar with may be addressed again.") },
|
||||
{ x => x.Message, T("Are you sure you want to reset your bias-of-the-day statistics? The system will no longer remember which biases you already know. As a result, biases you are already familiar with may be addressed again.") },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Reset your bias-of-the-day statistics"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
|
||||
@@ -31,14 +31,23 @@
|
||||
<MudTd>@context.Num</MudTd>
|
||||
<MudTd>@context.Name</MudTd>
|
||||
<MudTd>
|
||||
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
|
||||
<MudTooltip Text="@T("Edit")">
|
||||
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Edit" OnClick="() => this.EditChatTemplate(context)"/>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="@T("Delete")">
|
||||
<MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteChatTemplate(context)"/>
|
||||
</MudTooltip>
|
||||
</MudStack>
|
||||
@if (context.IsEnterpriseConfiguration)
|
||||
{
|
||||
<MudTooltip Text="@T("This template is managed by your organization.")">
|
||||
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Business" Disabled="true"/>
|
||||
</MudTooltip>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
|
||||
<MudTooltip Text="@T("Edit")">
|
||||
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Edit" OnClick="() => this.EditChatTemplate(context)"/>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="@T("Delete")">
|
||||
<MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteChatTemplate(context)"/>
|
||||
</MudTooltip>
|
||||
</MudStack>
|
||||
}
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
|
||||
@@ -1,16 +1,42 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Settings;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Dialogs.Settings;
|
||||
|
||||
public partial class SettingsDialogChatTemplate : SettingsDialogBase
|
||||
{
|
||||
[Parameter]
|
||||
public bool CreateTemplateFromExistingChatThread { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public ChatThread? ExistingChatThread { get; set; }
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
if (this.CreateTemplateFromExistingChatThread)
|
||||
await this.AddChatTemplate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private async Task AddChatTemplate()
|
||||
{
|
||||
var dialogParameters = new DialogParameters<ChatTemplateDialog>
|
||||
{
|
||||
{ x => x.IsEditing, false },
|
||||
};
|
||||
|
||||
|
||||
if (this.CreateTemplateFromExistingChatThread)
|
||||
{
|
||||
dialogParameters.Add(x => x.CreateFromExistingChatThread, this.CreateTemplateFromExistingChatThread);
|
||||
dialogParameters.Add(x => x.ExistingChatThread, this.ExistingChatThread);
|
||||
}
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ChatTemplateDialog>(T("Add Chat Template"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
var dialogResult = await dialogReference.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled)
|
||||
@@ -27,12 +53,16 @@ public partial class SettingsDialogChatTemplate : SettingsDialogBase
|
||||
|
||||
private async Task EditChatTemplate(ChatTemplate chatTemplate)
|
||||
{
|
||||
if (chatTemplate == ChatTemplate.NO_CHAT_TEMPLATE || chatTemplate.IsEnterpriseConfiguration)
|
||||
return;
|
||||
|
||||
var dialogParameters = new DialogParameters<ChatTemplateDialog>
|
||||
{
|
||||
{ x => x.DataNum, chatTemplate.Num },
|
||||
{ x => x.DataId, chatTemplate.Id },
|
||||
{ x => x.DataName, chatTemplate.Name },
|
||||
{ x => x.DataSystemPrompt, chatTemplate.SystemPrompt },
|
||||
{ x => x.PredefinedUserPrompt, chatTemplate.PredefinedUserPrompt },
|
||||
{ x => x.IsEditing, true },
|
||||
{ x => x.ExampleConversation, chatTemplate.ExampleConversation },
|
||||
{ x => x.AllowProfileUsage, chatTemplate.AllowProfileUsage },
|
||||
@@ -52,9 +82,9 @@ public partial class SettingsDialogChatTemplate : SettingsDialogBase
|
||||
|
||||
private async Task DeleteChatTemplate(ChatTemplate chatTemplate)
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Are you sure you want to delete the chat template '{0}'?"), chatTemplate.Name) },
|
||||
{ x => x.Message, string.Format(T("Are you sure you want to delete the chat template '{0}'?"), chatTemplate.Name) },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Chat Template"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
|
||||
@@ -151,9 +151,9 @@ public partial class SettingsDialogDataSources : SettingsDialogBase
|
||||
|
||||
private async Task DeleteDataSource(IDataSource dataSource)
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Are you sure you want to delete the data source '{0}' of type {1}?"), dataSource.Name, dataSource.Type.GetDisplayName()) },
|
||||
{ x => x.Message, string.Format(T("Are you sure you want to delete the data source '{0}' of type {1}?"), dataSource.Name, dataSource.Type.GetDisplayName()) },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Data Source"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
@using AIStudio.Settings
|
||||
@inherits SettingsDialogBase
|
||||
|
||||
<MudDialog>
|
||||
<TitleContent>
|
||||
<PreviewPrototype/>
|
||||
<MudText Typo="Typo.h6" Class="d-flex align-center">
|
||||
<MudIcon Icon="@Icons.Material.Filled.DocumentScanner" Class="mr-2"/>
|
||||
@T("Assistant: Document Analysis")
|
||||
</MudText>
|
||||
</TitleContent>
|
||||
<DialogContent>
|
||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||
<ConfigurationOption OptionDescription="@T("Preselect document analysis options?")" LabelOn="@T("Document analysis options are preselected")" LabelOff="@T("No document analysis options are preselected")" State="@(() => this.SettingsManager.ConfigurationData.ERI.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.ERI.PreselectOptions = updatedState)" OptionHelp="@T("When enabled, you can preselect some document analysis options.")"/>
|
||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.ERI.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.ERI.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.ERI.MinimumProviderConfidence = selectedValue)"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Preselect one of your profiles?")" Disabled="@(() => !this.SettingsManager.ConfigurationData.ERI.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.ERI.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.ERI.PreselectedProfile = selectedValue)" OptionHelp="@T("Would you like to preselect one of your profiles?")"/>
|
||||
|
||||
<MudText Typo="Typo.body1" Class="mb-3">
|
||||
@T("Most document analysis options can be customized and saved directly in the assistant. For this, the assistant has an auto-save function.")
|
||||
</MudText>
|
||||
</MudPaper>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
||||
@T("Close")
|
||||
</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace AIStudio.Dialogs.Settings;
|
||||
|
||||
public partial class SettingsDialogDocumentAnalysis : SettingsDialogBase;
|
||||
@@ -30,14 +30,23 @@
|
||||
<MudTd>@context.Num</MudTd>
|
||||
<MudTd>@context.Name</MudTd>
|
||||
<MudTd>
|
||||
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
|
||||
<MudTooltip Text="@T("Edit")">
|
||||
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Edit" OnClick="() => this.EditProfile(context)"/>
|
||||
@if (context.IsEnterpriseConfiguration)
|
||||
{
|
||||
<MudTooltip Text="@T("This profile is managed by your organization.")">
|
||||
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Business" Disabled="true"/>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="@T("Delete")">
|
||||
<MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteProfile(context)"/>
|
||||
</MudTooltip>
|
||||
</MudStack>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
|
||||
<MudTooltip Text="@T("Edit")">
|
||||
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Edit" OnClick="() => this.EditProfile(context)"/>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="@T("Delete")">
|
||||
<MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteProfile(context)"/>
|
||||
</MudTooltip>
|
||||
</MudStack>
|
||||
}
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
|
||||
Loaded 100 of 326 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user