mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2025-08-21 01:52:57 +00:00
Compare commits
38 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
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 | ||
|
7a498d524e | ||
|
ce915292dc | ||
|
6979a73e5c |
16
.github/workflows/build-and-release.yml
vendored
16
.github/workflows/build-and-release.yml
vendored
@ -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: |
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -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
|
||||
|
17
.junie/guidelines.md
Normal file
17
.junie/guidelines.md
Normal file
@ -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.
|
13
README.md
13
README.md
@ -33,8 +33,8 @@ Things we are currently working on:
|
||||
- [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)~~)
|
||||
- [ ] Add configuration plugins, which allow pre-defining some LLM providers in organizations (~~[PR #491](https://github.com/MindWorkAI/AI-Studio/pull/491)~~)
|
||||
- [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
|
||||
|
||||
@ -47,6 +47,7 @@ Other News:
|
||||
|
||||
Features we have recently released:
|
||||
|
||||
- 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 +58,6 @@ 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).
|
||||
|
||||
## What is AI Studio?
|
||||
|
||||
@ -71,7 +71,7 @@ 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.)
|
||||
- [Mistral](https://mistral.ai/)
|
||||
- [Anthropic](https://www.anthropic.com/) (Claude)
|
||||
- [Google Gemini](https://gemini.google.com)
|
||||
@ -79,7 +79,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.
|
||||
@ -123,6 +123,9 @@ Stay tuned for more updates and enhancements to make MindWork AI Studio even mor
|
||||
## Building
|
||||
You want to know how to build MindWork AI Studio from source? [Check out the instructions here](documentation/Build.md).
|
||||
|
||||
## Enterprise IT
|
||||
Do you want to manage AI Studio centrally from your IT department? Yes, that’s possible. [Here’s how it works.](documentation/Enterprise%20IT.md)
|
||||
|
||||
## License
|
||||
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.
|
||||
|
@ -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>
|
@ -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>
|
||||
}
|
||||
|
@ -384,7 +384,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
|
||||
|
@ -1315,9 +1315,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?"
|
||||
|
||||
@ -1351,9 +1348,6 @@ 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"
|
||||
|
||||
@ -1414,6 +1408,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 +1444,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 +1585,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 +1654,17 @@ 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"
|
||||
-- Images are not supported yet
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T298062956"] = "Images are not supported yet"
|
||||
|
||||
-- Select PDF file
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READPDFCONTENT::T63272795"] = "Select PDF file"
|
||||
-- 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."
|
||||
@ -1711,6 +1723,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"
|
||||
|
||||
@ -2188,18 +2203,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 +2281,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."
|
||||
|
||||
@ -3385,6 +3415,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"
|
||||
|
||||
@ -4156,8 +4189,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 +4201,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 +4219,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 +4237,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 +4300,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 +4318,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 +4354,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 +4399,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."
|
||||
|
||||
@ -4516,9 +4576,6 @@ 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."
|
||||
|
||||
-- Let's get started
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2331588413"] = "Let's get started"
|
||||
|
||||
@ -4528,6 +4585,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2348849647"] = "Last Changelog"
|
||||
-- Choose the provider and model best suited for your current task.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2588488920"] = "Choose the provider and model best suited for your current task."
|
||||
|
||||
-- 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 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::T2900280782"] = "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 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."
|
||||
|
||||
-- Quick Start Guide
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3002014720"] = "Quick Start Guide"
|
||||
|
||||
@ -5476,6 +5536,9 @@ 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"
|
||||
|
||||
-- PDF Files
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPEFILTER::T3108466742"] = "PDF Files"
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
<ReadWebContent @bind-Content="@this.inputLegalDocument" ProviderSettings="@this.providerSettings" @bind-AgentIsRunning="@this.isAgentRunning" Preselect="@(this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions && this.SettingsManager.ConfigurationData.LegalCheck.PreselectWebContentReader)" PreselectContentCleanerAgent="@(this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions && this.SettingsManager.ConfigurationData.LegalCheck.PreselectContentCleanerAgent)"/>
|
||||
}
|
||||
|
||||
<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"/>
|
@ -6,7 +6,7 @@
|
||||
<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)"/>
|
||||
}
|
||||
|
||||
<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" />
|
||||
|
@ -6,7 +6,8 @@
|
||||
<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)"/>
|
||||
}
|
||||
|
||||
<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)
|
||||
{
|
||||
|
@ -100,16 +100,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,6 +121,11 @@ public sealed record ChatThread
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
//
|
||||
|
@ -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,5 +1,6 @@
|
||||
@using AIStudio.Tools
|
||||
@using MudBlazor
|
||||
@using AIStudio.Components
|
||||
@inherits AIStudio.Components.MSGComponentBase
|
||||
<MudCard Class="@this.CardClasses" Outlined="@true">
|
||||
<MudCardHeader>
|
||||
@ -10,7 +11,7 @@
|
||||
</CardHeaderAvatar>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.body1">
|
||||
@this.Role.ToName() (@this.Time)
|
||||
@this.Role.ToName() (@this.Time.LocalDateTime)
|
||||
</MudText>
|
||||
</CardHeaderContent>
|
||||
<CardHeaderActions>
|
||||
@ -38,9 +39,7 @@
|
||||
<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>
|
||||
<MudCopyClipboardButton Content="@this.Content" Type="@this.Type" Size="Size.Medium"/>
|
||||
</CardHeaderActions>
|
||||
</MudCardHeader>
|
||||
<MudCardContent>
|
||||
@ -67,12 +66,12 @@
|
||||
@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" />
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
using AIStudio.Components;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
@ -61,12 +60,6 @@ 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!;
|
||||
|
||||
@ -115,33 +108,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()
|
||||
{
|
||||
|
@ -119,6 +119,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();
|
||||
|
18
app/MindWork AI Studio/Chat/StringExtension.cs
Normal file
18
app/MindWork AI Studio/Chat/StringExtension.cs
Normal file
@ -0,0 +1,18 @@
|
||||
namespace AIStudio.Chat;
|
||||
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static string RemoveThinkTags(this string input)
|
||||
{
|
||||
const string OPEN_TAG = "<think>";
|
||||
const string CLOSE_TAG = "</think>";
|
||||
if (string.IsNullOrWhiteSpace(input) || !input.StartsWith(OPEN_TAG, StringComparison.Ordinal))
|
||||
return input;
|
||||
|
||||
var endIndex = input.IndexOf(CLOSE_TAG, StringComparison.Ordinal);
|
||||
if (endIndex == -1)
|
||||
return string.Empty;
|
||||
|
||||
return input[(endIndex + CLOSE_TAG.Length)..];
|
||||
}
|
||||
}
|
@ -13,6 +13,10 @@ public partial class Changelog
|
||||
|
||||
public static readonly Log[] LOGS =
|
||||
[
|
||||
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"),
|
||||
new (219, "v0.9.44, build 219 (2025-05-18 19:33 UTC)", "v0.9.44.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">
|
||||
|
@ -80,6 +80,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 +273,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 +327,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;
|
||||
|
||||
@ -433,7 +437,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
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 +509,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;
|
||||
@ -669,10 +675,12 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
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();
|
||||
|
||||
@ -807,9 +815,8 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
// 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
|
||||
|
15
app/MindWork AI Studio/Components/ConfigurationBaseCore.cs
Normal file
15
app/MindWork AI Studio/Components/ConfigurationBaseCore.cs
Normal file
@ -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,17 @@ public partial class ConfigurationMultiSelect<TData> : ConfigurationBase
|
||||
[Parameter]
|
||||
public Action<HashSet<TData>> SelectionUpdate { get; set; } = _ => { };
|
||||
|
||||
#region Overrides of ConfigurationBase
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool Stretch => true;
|
||||
|
||||
protected override Variant Variant => Variant.Outlined;
|
||||
|
||||
protected override string Label => this.OptionDescription;
|
||||
|
||||
#endregion
|
||||
|
||||
private async Task OptionChanged(IEnumerable<TData?>? updatedValues)
|
||||
{
|
||||
if(updatedValues is null)
|
||||
@ -39,8 +50,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()
|
||||
|
@ -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,43 @@ 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;
|
||||
|
||||
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
|
||||
}
|
5
app/MindWork AI Studio/Components/LockableButton.razor
Normal file
5
app/MindWork AI Studio/Components/LockableButton.razor
Normal file
@ -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>
|
39
app/MindWork AI Studio/Components/LockableButton.razor.cs
Normal file
39
app/MindWork AI Studio/Components/LockableButton.razor.cs
Normal file
@ -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();
|
||||
}
|
||||
}
|
@ -13,10 +13,6 @@ public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBus
|
||||
[Inject]
|
||||
protected MessageBus MessageBus { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
||||
private ILogger<MSGComponentBase> Logger { get; init; } = null!;
|
||||
|
||||
private ILanguagePlugin Lang { get; set; } = PluginFactory.BaseLanguage;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
@ -45,19 +41,22 @@ public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBus
|
||||
|
||||
public async Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
||||
{
|
||||
switch (triggeredEvent)
|
||||
await this.InvokeAsync(async () =>
|
||||
{
|
||||
case Event.COLOR_THEME_CHANGED:
|
||||
this.StateHasChanged();
|
||||
break;
|
||||
switch (triggeredEvent)
|
||||
{
|
||||
case Event.COLOR_THEME_CHANGED:
|
||||
this.StateHasChanged();
|
||||
break;
|
||||
|
||||
case Event.PLUGINS_RELOADED:
|
||||
this.Lang = await this.SettingsManager.GetActiveLanguagePlugin();
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
break;
|
||||
}
|
||||
case Event.PLUGINS_RELOADED:
|
||||
this.Lang = await this.SettingsManager.GetActiveLanguagePlugin();
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
break;
|
||||
}
|
||||
|
||||
await this.ProcessIncomingMessage(sendingComponent, triggeredEvent, data);
|
||||
await this.ProcessIncomingMessage(sendingComponent, triggeredEvent, data);
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
|
||||
|
@ -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>
|
||||
|
@ -1,4 +1,4 @@
|
||||
@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")
|
||||
@T("Use file content as input")
|
||||
</MudButton>
|
44
app/MindWork AI Studio/Components/ReadFileContent.razor.cs
Normal file
44
app/MindWork AI Studio/Components/ReadFileContent.razor.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using AIStudio.Tools.Rust;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class ReadFileContent : MSGComponentBase
|
||||
{
|
||||
[Parameter]
|
||||
public string FileContent { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<string> FileContentChanged { get; set; }
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { 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;
|
||||
}
|
||||
|
||||
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,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,7 +13,7 @@
|
||||
<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.UpdateBehavior)" Data="@ConfigurationSelectDataFactory.GetUpdateBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UpdateBehavior = selectedValue)" OptionHelp="@T("How often should we check for app updates?")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.UpdateBehavior, 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?")"/>
|
||||
|
||||
|
@ -15,7 +15,4 @@ public abstract class SettingsPanelBase : MSGComponentBase
|
||||
|
||||
[Inject]
|
||||
protected RustService RustService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected SettingsLocker SettingsLocker { get; init; } = null!;
|
||||
}
|
@ -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,9 @@ 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.IsEnterpriseConfiguration)
|
||||
return;
|
||||
|
||||
var dialogParameters = new DialogParameters<ProviderDialog>
|
||||
{
|
||||
{ x => x.DataNum, provider.Num },
|
||||
|
@ -47,14 +47,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 +71,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 +94,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()
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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>
|
||||
|
||||
|
@ -164,7 +164,7 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
//
|
||||
// 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();
|
||||
|
@ -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 },
|
||||
|
@ -5,7 +5,7 @@
|
||||
<MudIcon Icon="@Icons.Material.Filled.Update" Size="Size.Large" Class="mr-3"/>
|
||||
@this.HeaderText
|
||||
</MudText>
|
||||
<MudMarkdown Value="@this.UpdateResponse.Changelog" OverrideHeaderTypo="@Markdown.OverrideHeaderTypo"/>
|
||||
<MudMarkdown Value="@this.UpdateResponse.Changelog" Props="Markdown.DefaultConfig"/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">
|
||||
|
@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Routing;
|
||||
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
using EnterpriseEnvironment = AIStudio.Tools.EnterpriseEnvironment;
|
||||
|
||||
namespace AIStudio.Layout;
|
||||
|
||||
@ -108,7 +109,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
// Send a message to start the plugin system:
|
||||
await this.MessageBus.SendMessage<bool>(this, Event.STARTUP_PLUGIN_SYSTEM);
|
||||
|
||||
await this.themeProvider.WatchSystemPreference(this.SystemeThemeChanged);
|
||||
await this.themeProvider.WatchSystemDarkModeAsync(this.SystemeThemeChanged);
|
||||
await this.UpdateThemeConfiguration();
|
||||
this.LoadNavItems();
|
||||
|
||||
@ -138,90 +139,104 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
|
||||
public async Task ProcessMessage<TMessage>(ComponentBase? sendingComponent, Event triggeredEvent, TMessage? data)
|
||||
{
|
||||
switch (triggeredEvent)
|
||||
await this.InvokeAsync(async () =>
|
||||
{
|
||||
case Event.UPDATE_AVAILABLE:
|
||||
if (data is UpdateResponse updateResponse)
|
||||
{
|
||||
this.currentUpdateResponse = updateResponse;
|
||||
var message = string.Format(T("An update to version {0} is available."), updateResponse.NewVersion);
|
||||
this.Snackbar.Add(message, Severity.Info, config =>
|
||||
switch (triggeredEvent)
|
||||
{
|
||||
case Event.UPDATE_AVAILABLE:
|
||||
if (data is UpdateResponse updateResponse)
|
||||
{
|
||||
config.Icon = Icons.Material.Filled.Update;
|
||||
config.IconSize = Size.Large;
|
||||
config.HideTransitionDuration = 600;
|
||||
config.VisibleStateDuration = 32_000;
|
||||
config.OnClick = async _ =>
|
||||
this.currentUpdateResponse = updateResponse;
|
||||
var message = string.Format(T("An update to version {0} is available."), updateResponse.NewVersion);
|
||||
this.Snackbar.Add(message, Severity.Info, config =>
|
||||
{
|
||||
await this.ShowUpdateDialog();
|
||||
};
|
||||
config.Action = T("Show details");
|
||||
config.ActionVariant = Variant.Filled;
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Event.CONFIGURATION_CHANGED:
|
||||
if(this.SettingsManager.ConfigurationData.App.NavigationBehavior is NavBehavior.ALWAYS_EXPAND)
|
||||
this.navBarOpen = true;
|
||||
else
|
||||
this.navBarOpen = false;
|
||||
|
||||
await this.UpdateThemeConfiguration();
|
||||
this.LoadNavItems();
|
||||
this.StateHasChanged();
|
||||
break;
|
||||
|
||||
case Event.COLOR_THEME_CHANGED:
|
||||
this.StateHasChanged();
|
||||
break;
|
||||
|
||||
case Event.SHOW_SUCCESS:
|
||||
if (data is DataSuccessMessage success)
|
||||
success.Show(this.Snackbar);
|
||||
|
||||
break;
|
||||
|
||||
case Event.SHOW_ERROR:
|
||||
if (data is DataErrorMessage error)
|
||||
error.Show(this.Snackbar);
|
||||
|
||||
break;
|
||||
|
||||
case Event.SHOW_WARNING:
|
||||
if (data is DataWarningMessage warning)
|
||||
warning.Show(this.Snackbar);
|
||||
|
||||
break;
|
||||
|
||||
case Event.STARTUP_PLUGIN_SYSTEM:
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
// Set up the plugin system:
|
||||
if (PluginFactory.Setup())
|
||||
{
|
||||
// Ensure that all internal plugins are present:
|
||||
await PluginFactory.EnsureInternalPlugins();
|
||||
|
||||
// Load (but not start) all plugins, without waiting for them:
|
||||
var pluginLoadingTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||
await PluginFactory.LoadAll(pluginLoadingTimeout.Token);
|
||||
|
||||
// Set up hot reloading for plugins:
|
||||
PluginFactory.SetUpHotReloading();
|
||||
config.Icon = Icons.Material.Filled.Update;
|
||||
config.IconSize = Size.Large;
|
||||
config.HideTransitionDuration = 600;
|
||||
config.VisibleStateDuration = 32_000;
|
||||
config.OnClick = async _ =>
|
||||
{
|
||||
await this.ShowUpdateDialog();
|
||||
};
|
||||
config.Action = T("Show details");
|
||||
config.ActionVariant = Variant.Filled;
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case Event.PLUGINS_RELOADED:
|
||||
this.Lang = await this.SettingsManager.GetActiveLanguagePlugin();
|
||||
I18N.Init(this.Lang);
|
||||
this.LoadNavItems();
|
||||
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Event.CONFIGURATION_CHANGED:
|
||||
if (this.SettingsManager.ConfigurationData.App.NavigationBehavior is NavBehavior.ALWAYS_EXPAND)
|
||||
this.navBarOpen = true;
|
||||
else
|
||||
this.navBarOpen = false;
|
||||
|
||||
await this.UpdateThemeConfiguration();
|
||||
this.LoadNavItems();
|
||||
this.StateHasChanged();
|
||||
break;
|
||||
|
||||
case Event.COLOR_THEME_CHANGED:
|
||||
this.StateHasChanged();
|
||||
break;
|
||||
|
||||
case Event.SHOW_SUCCESS:
|
||||
if (data is DataSuccessMessage success)
|
||||
success.Show(this.Snackbar);
|
||||
|
||||
break;
|
||||
|
||||
case Event.SHOW_ERROR:
|
||||
if (data is DataErrorMessage error)
|
||||
error.Show(this.Snackbar);
|
||||
|
||||
break;
|
||||
|
||||
case Event.SHOW_WARNING:
|
||||
if (data is DataWarningMessage warning)
|
||||
warning.Show(this.Snackbar);
|
||||
|
||||
break;
|
||||
|
||||
case Event.STARTUP_PLUGIN_SYSTEM:
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
// Set up the plugin system:
|
||||
if (PluginFactory.Setup())
|
||||
{
|
||||
// Ensure that all internal plugins are present:
|
||||
await PluginFactory.EnsureInternalPlugins();
|
||||
|
||||
//
|
||||
// Check if there is an enterprise configuration plugin to download:
|
||||
//
|
||||
var enterpriseEnvironment = this.MessageBus.CheckDeferredMessages<EnterpriseEnvironment>(Event.STARTUP_ENTERPRISE_ENVIRONMENT).FirstOrDefault();
|
||||
if (enterpriseEnvironment != default)
|
||||
await PluginFactory.TryDownloadingConfigPluginAsync(enterpriseEnvironment.ConfigurationId, enterpriseEnvironment.ConfigurationServerUrl);
|
||||
|
||||
// Load (but not start) all plugins without waiting for them:
|
||||
#if DEBUG
|
||||
var pluginLoadingTimeout = new CancellationTokenSource();
|
||||
#else
|
||||
var pluginLoadingTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||
#endif
|
||||
await PluginFactory.LoadAll(pluginLoadingTimeout.Token);
|
||||
|
||||
// Set up hot reloading for plugins:
|
||||
PluginFactory.SetUpHotReloading();
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case Event.PLUGINS_RELOADED:
|
||||
this.Lang = await this.SettingsManager.GetActiveLanguagePlugin();
|
||||
I18N.Init(this.Lang);
|
||||
this.LoadNavItems();
|
||||
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
|
||||
@ -313,7 +328,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
private async Task UpdateThemeConfiguration()
|
||||
{
|
||||
if (this.SettingsManager.ConfigurationData.App.PreferredTheme is Themes.SYSTEM)
|
||||
this.useDarkMode = await this.themeProvider.GetSystemPreference();
|
||||
this.useDarkMode = await this.themeProvider.GetSystemDarkModeAsync();
|
||||
else
|
||||
this.useDarkMode = this.SettingsManager.ConfigurationData.App.PreferredTheme == Themes.DARK;
|
||||
|
||||
|
@ -47,12 +47,12 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CodeBeam.MudBlazor.Extensions" Version="8.2.1" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.12.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.5" />
|
||||
<PackageReference Include="MudBlazor" Version="8.6.0" />
|
||||
<PackageReference Include="MudBlazor.Markdown" Version="8.6.0" />
|
||||
<PackageReference Include="ReverseMarkdown" Version="4.6.0" />
|
||||
<PackageReference Include="CodeBeam.MudBlazor.Extensions" Version="8.2.4" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.12.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.8" />
|
||||
<PackageReference Include="MudBlazor" Version="8.11.0" />
|
||||
<PackageReference Include="MudBlazor.Markdown" Version="8.11.0" />
|
||||
<PackageReference Include="ReverseMarkdown" Version="4.7.0" />
|
||||
<PackageReference Include="LuaCSharp" Version="0.4.2" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
@attribute [Route(Routes.ABOUT)]
|
||||
@using AIStudio.Tools.Services
|
||||
@inherits MSGComponentBase
|
||||
|
||||
<div class="inner-scrolling-context">
|
||||
@ -14,16 +15,104 @@
|
||||
</MudText>
|
||||
<MudList T="string" Class="mb-3">
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Chat" Text="@VersionApp"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Timer" Text="@BuildTime"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Build" Text="@VersionDotnetSdk"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Memory" Text="@VersionDotnetRuntime"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Build" Text="@VersionRust"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.DocumentScanner" Text="@VersionPdfium"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Timer" Text="@this.BuildTime"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Build" Text="@this.VersionDotnetSdk"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Memory" Text="@this.VersionDotnetRuntime"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Build" Text="@this.VersionRust"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.DocumentScanner" Text="@this.VersionPdfium"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Article" Text="@this.versionPandoc"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Widgets" Text="@MudBlazorVersion"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Memory" Text="@TauriVersion"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Translate" Text="@this.OSLanguage"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Business" Text="@this.GetEnterpriseEnvironment()"/>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Business">
|
||||
@switch (EnterpriseEnvironmentService.CURRENT_ENVIRONMENT.IsActive)
|
||||
{
|
||||
case false when this.configPlug is null:
|
||||
<MudText Typo="Typo.body1">
|
||||
@T("This is a private AI Studio installation. It runs without an enterprise configuration.")
|
||||
</MudText>
|
||||
break;
|
||||
|
||||
case false:
|
||||
<MudText Typo="Typo.body1">
|
||||
@T("AI Studio runs with an enterprise configuration using a configuration plugin, without central configuration management.")
|
||||
</MudText>
|
||||
<MudCollapse Expanded="@this.showEnterpriseConfigDetails">
|
||||
<MudText Typo="Typo.body1" Class="mt-2 mb-2">
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<MudIcon Icon="@Icons.Material.Filled.ArrowRightAlt"/>
|
||||
<span>@T("Configuration plugin ID:") @this.configPlug!.Id</span>
|
||||
<MudCopyClipboardButton TooltipMessage="@T("Copies the configuration plugin ID to the clipboard")" StringContent=@this.configPlug!.Id.ToString()/>
|
||||
</div>
|
||||
</MudText>
|
||||
</MudCollapse>
|
||||
break;
|
||||
|
||||
case true when this.configPlug is null:
|
||||
<MudText Typo="Typo.body1">
|
||||
@T("AI Studio runs with an enterprise configuration and a configuration server. The configuration plugin is not yet available.")
|
||||
</MudText>
|
||||
<MudCollapse Expanded="@this.showEnterpriseConfigDetails">
|
||||
<MudText Typo="Typo.body1" Class="mt-2 mb-2">
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<MudIcon Icon="@Icons.Material.Filled.ArrowRightAlt"/>
|
||||
<span>@T("Enterprise configuration ID:") @EnterpriseEnvironmentService.CURRENT_ENVIRONMENT.ConfigurationId</span>
|
||||
<MudCopyClipboardButton TooltipMessage="@T("Copies the config ID to the clipboard")" StringContent=@EnterpriseEnvironmentService.CURRENT_ENVIRONMENT.ConfigurationId.ToString()/>
|
||||
</div>
|
||||
</MudText>
|
||||
|
||||
<MudText Typo="Typo.body1" Class="mt-2 mb-2">
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<MudIcon Icon="@Icons.Material.Filled.ArrowRightAlt"/>
|
||||
<span>@T("Configuration server:") @EnterpriseEnvironmentService.CURRENT_ENVIRONMENT.ConfigurationServerUrl</span>
|
||||
<MudCopyClipboardButton TooltipMessage="@T("Copies the server URL to the clipboard")" StringContent=@EnterpriseEnvironmentService.CURRENT_ENVIRONMENT.ConfigurationServerUrl/>
|
||||
</div>
|
||||
</MudText>
|
||||
</MudCollapse>
|
||||
break;
|
||||
|
||||
case true:
|
||||
<MudText Typo="Typo.body1">
|
||||
@T("AI Studio runs with an enterprise configuration and a configuration server. The configuration plugin is active.")
|
||||
</MudText>
|
||||
<MudCollapse Expanded="@this.showEnterpriseConfigDetails">
|
||||
<MudText Typo="Typo.body1" Class="mt-2 mb-2">
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<MudIcon Icon="@Icons.Material.Filled.ArrowRightAlt"/>
|
||||
<span>@T("Enterprise configuration ID:") @EnterpriseEnvironmentService.CURRENT_ENVIRONMENT.ConfigurationId</span>
|
||||
<MudCopyClipboardButton TooltipMessage="@T("Copies the config ID to the clipboard")" StringContent=@EnterpriseEnvironmentService.CURRENT_ENVIRONMENT.ConfigurationId.ToString()/>
|
||||
</div>
|
||||
</MudText>
|
||||
|
||||
<MudText Typo="Typo.body1" Class="mt-2 mb-2">
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<MudIcon Icon="@Icons.Material.Filled.ArrowRightAlt"/>
|
||||
<span>@T("Configuration server:") @EnterpriseEnvironmentService.CURRENT_ENVIRONMENT.ConfigurationServerUrl</span>
|
||||
<MudCopyClipboardButton TooltipMessage="@T("Copies the server URL to the clipboard")" StringContent=@EnterpriseEnvironmentService.CURRENT_ENVIRONMENT.ConfigurationServerUrl/>
|
||||
</div>
|
||||
</MudText>
|
||||
|
||||
<MudText Typo="Typo.body1" Class="mt-2 mb-2">
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<MudIcon Icon="@Icons.Material.Filled.ArrowRightAlt"/>
|
||||
<span>@T("Configuration plugin ID:") @this.configPlug!.Id</span>
|
||||
<MudCopyClipboardButton TooltipMessage="@T("Copies the configuration plugin ID to the clipboard")" StringContent=@this.configPlug!.Id.ToString()/>
|
||||
</div>
|
||||
</MudText>
|
||||
</MudCollapse>
|
||||
break;
|
||||
}
|
||||
|
||||
@if (this.HasEnterpriseConfigurationDetails)
|
||||
{
|
||||
<MudButton StartIcon="@(this.showEnterpriseConfigDetails ? Icons.Material.Filled.ExpandLess : Icons.Material.Filled.ExpandMore)"
|
||||
Size="Size.Small"
|
||||
Variant="Variant.Text"
|
||||
OnClick="@this.ToggleEnterpriseConfigDetails">
|
||||
@(this.showEnterpriseConfigDetails ? T("Hide Details") : T("Show Details"))
|
||||
</MudButton>
|
||||
}
|
||||
</MudListItem>
|
||||
</MudList>
|
||||
<MudStack Row="true">
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Update" OnClick="() => this.CheckForUpdate()">
|
||||
@ -48,7 +137,7 @@
|
||||
</MudListItem>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.Timeline" Target="_blank" Href="https://github.com/orgs/MindWorkAI/projects/2/views/3">
|
||||
@T("View our project roadmap and help shape AI Studio's future development.")
|
||||
</MudListItem>
|
||||
</MudListItem>
|
||||
<MudListItem T="string" Icon="@Icons.Material.Outlined.BugReport" Target="_blank" Href="https://github.com/MindWorkAI/AI-Studio/issues">
|
||||
@T("Did you find a bug or are you experiencing issues? Report your concern here.")
|
||||
</MudListItem>
|
||||
@ -114,6 +203,7 @@
|
||||
<ThirdPartyComponent Name="async-stream" Developer="Carl Lerche, Taiki Endo & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/tokio-rs/async-stream/blob/master/LICENSE" RepositoryUrl="https://github.com/tokio-rs/async-stream" UseCase="@T("This library is used to create asynchronous streams in Rust. It allows us to work with streams of data that can be produced asynchronously, making it easier to handle events or data that arrive over time. We use this, e.g., to stream arbitrary data from the file system to the embedding system.")"/>
|
||||
<ThirdPartyComponent Name="flexi_logger" Developer="emabee & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/emabee/flexi_logger/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/emabee/flexi_logger" UseCase="@T("This Rust library is used to output the app's messages to the terminal. This is helpful during development and troubleshooting. This feature is initially invisible; when the app is started via the terminal, the messages become visible.")"/>
|
||||
<ThirdPartyComponent Name="rand" Developer="Rust developers & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rust-random/rand/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/rust-random/rand" UseCase="@T("We must generate random numbers, e.g., for securing the interprocess communication between the user interface and the runtime. The rand library is great for this purpose.")"/>
|
||||
<ThirdPartyComponent Name="pptx-to-md" Developer="Nils Kruthoff & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/nilskruthoff/pptx-parser/blob/master/LICENCE-MIT" RepositoryUrl="https://github.com/nilskruthoff/pptx-parser" UseCase="@T("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.")"/>
|
||||
<ThirdPartyComponent Name="base64" Developer="Marshall Pierce, Alice Maz & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/marshallpierce/rust-base64/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/marshallpierce/rust-base64" UseCase="@T("For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose.")"/>
|
||||
<ThirdPartyComponent Name="Rust Crypto" Developer="Artyom Pavlov, Tony Arcieri, Brian Warner, Arthur Gautier, Vlad Filippov, Friedel Ziegelmayer, Nicolas Stalder & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/RustCrypto/traits/blob/master/cipher/LICENSE-MIT" RepositoryUrl="https://github.com/RustCrypto" UseCase="@T("When transferring sensitive data between Rust runtime and .NET app, we encrypt the data. We use some libraries from the Rust Crypto project for this purpose: cipher, aes, cbc, pbkdf2, hmac, and sha2. We are thankful for the great work of the Rust Crypto project.")"/>
|
||||
<ThirdPartyComponent Name="rcgen" Developer="RustTLS developers, est31 & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rustls/rcgen/blob/main/LICENSE" RepositoryUrl="https://github.com/rustls/rcgen" UseCase="@T("For the secure communication between the user interface and the runtime, we need to create certificates. This Rust library is great for this purpose.")"/>
|
||||
@ -130,7 +220,7 @@
|
||||
</MudGrid>
|
||||
</ExpansionPanel>
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Verified" HeaderText="License: FSL-1.1-MIT">
|
||||
<MudMarkdown Value="@LICENSE" OverrideHeaderTypo="@Markdown.OverrideHeaderTypo"/>
|
||||
<MudMarkdown Value="@LICENSE" Props="Markdown.DefaultConfig"/>
|
||||
</ExpansionPanel>
|
||||
</MudExpansionPanels>
|
||||
</InnerScrolling>
|
||||
|
@ -58,15 +58,64 @@ public partial class About : MSGComponentBase
|
||||
|
||||
private GetLogPathsResponse logPaths;
|
||||
|
||||
private bool showEnterpriseConfigDetails;
|
||||
|
||||
private IPluginMetadata? configPlug = PluginFactory.AvailablePlugins.FirstOrDefault(x => x.Type is PluginType.CONFIGURATION);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the enterprise configuration has details that can be shown/hidden.
|
||||
/// Returns true if there are details available, false otherwise.
|
||||
/// </summary>
|
||||
private bool HasEnterpriseConfigurationDetails
|
||||
{
|
||||
get
|
||||
{
|
||||
return EnterpriseEnvironmentService.CURRENT_ENVIRONMENT.IsActive switch
|
||||
{
|
||||
// Case 1: No enterprise config and no plugin - no details available
|
||||
false when this.configPlug is null => false,
|
||||
|
||||
// Case 2: Enterprise config with plugin but no central management - has details
|
||||
false => true,
|
||||
|
||||
// Case 3: Enterprise config active but no plugin - has details
|
||||
true when this.configPlug is null => true,
|
||||
|
||||
// Case 4: Enterprise config active with plugin - has details
|
||||
true => true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
|
||||
this.osLanguage = await this.RustService.ReadUserLanguage();
|
||||
this.logPaths = await this.RustService.GetLogPaths();
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
await this.DeterminePandocVersion();
|
||||
// Determine the Pandoc version may take some time, so we start it here
|
||||
// without waiting for the result:
|
||||
_ = this.DeterminePandocVersion();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides of MSGComponentBase
|
||||
|
||||
protected override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||
{
|
||||
switch (triggeredEvent)
|
||||
{
|
||||
case Event.PLUGINS_RELOADED:
|
||||
this.configPlug = PluginFactory.AvailablePlugins.FirstOrDefault(x => x.Type is PluginType.CONFIGURATION);
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
break;
|
||||
}
|
||||
|
||||
await base.ProcessIncomingMessage(sendingComponent, triggeredEvent, data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -116,26 +165,10 @@ public partial class About : MSGComponentBase
|
||||
await dialogReference.Result;
|
||||
await this.DeterminePandocVersion();
|
||||
}
|
||||
|
||||
private string GetEnterpriseEnvironment()
|
||||
|
||||
private void ToggleEnterpriseConfigDetails()
|
||||
{
|
||||
var configPlug = PluginFactory.AvailablePlugins.FirstOrDefault(x => x.Type is PluginType.CONFIGURATION);
|
||||
var currentEnvironment = EnterpriseEnvironmentService.CURRENT_ENVIRONMENT;
|
||||
|
||||
switch (currentEnvironment)
|
||||
{
|
||||
case { IsActive: false } when configPlug is null:
|
||||
return T("AI Studio runs without an enterprise configuration.");
|
||||
|
||||
case { IsActive: false }:
|
||||
return string.Format(T("AI Studio runs with an enterprise configuration using the configuration plugin '{0}', without central configuration management."), configPlug.Id);
|
||||
|
||||
case { IsActive: true } when configPlug is null:
|
||||
return string.Format(T("AI Studio runs with an enterprise configuration id '{0}' and configuration server URL '{1}'. The configuration plugin is not yet available."), currentEnvironment.ConfigurationId, currentEnvironment.ConfigurationServerUrl);
|
||||
|
||||
case { IsActive: true }:
|
||||
return string.Format(T("AI Studio runs with an enterprise configuration id '{0}' and configuration server URL '{1}'. The configuration plugin is active."), currentEnvironment.ConfigurationId, currentEnvironment.ConfigurationServerUrl);
|
||||
}
|
||||
this.showEnterpriseConfigDetails = !this.showEnterpriseConfigDetails;
|
||||
}
|
||||
|
||||
private async Task CopyStartupLogPath()
|
||||
|
@ -100,6 +100,23 @@ public partial class Chat : MSGComponentBase
|
||||
|
||||
#region Overrides of MSGComponentBase
|
||||
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.splitterSaveTimer.Stop();
|
||||
this.splitterSaveTimer.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
base.DisposeResources();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected override Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||
{
|
||||
switch (triggeredEvent)
|
||||
@ -111,6 +128,4 @@ public partial class Chat : MSGComponentBase
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
@ -27,7 +27,7 @@
|
||||
</ExpansionPanel>
|
||||
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.EventNote" HeaderText="@T("Last Changelog")">
|
||||
<MudMarkdown Value="@this.LastChangeContent" OverrideHeaderTypo="@Markdown.OverrideHeaderTypo"/>
|
||||
<MudMarkdown Value="@this.LastChangeContent" Props="Markdown.DefaultConfig"/>
|
||||
</ExpansionPanel>
|
||||
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Lightbulb" HeaderText="@T("Vision")">
|
||||
@ -35,7 +35,7 @@
|
||||
</ExpansionPanel>
|
||||
|
||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.RocketLaunch" HeaderText="@T("Quick Start Guide")">
|
||||
<MudMarkdown OverrideHeaderTypo="@Markdown.OverrideHeaderTypo" Value="@QUICK_START_GUIDE"/>
|
||||
<MudMarkdown Props="Markdown.DefaultConfig" Value="@QUICK_START_GUIDE"/>
|
||||
</ExpansionPanel>
|
||||
|
||||
</MudExpansionPanels>
|
||||
|
@ -19,17 +19,19 @@ public partial class Home : MSGComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await this.ReadLastChangeAsync();
|
||||
await base.OnInitializedAsync();
|
||||
|
||||
this.InitializeAdvantagesItems();
|
||||
|
||||
// Read the last change content asynchronously
|
||||
// without blocking the UI thread:
|
||||
_ = this.ReadLastChangeAsync();
|
||||
}
|
||||
|
||||
private void InitializeAdvantagesItems()
|
||||
{
|
||||
this.itemsAdvantages = [
|
||||
new(this.T("Free of charge"), this.T("The app is free to use, both for personal and commercial purposes.")),
|
||||
new(this.T("Independence"), this.T("You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT4o, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.")),
|
||||
new(this.T("Independence"), this.T("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 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.")),
|
||||
new(this.T("Assistants"), this.T("You just want to quickly translate a text? AI Studio has so-called assistants for such and other tasks. No prompting is necessary when working with these assistants.")),
|
||||
new(this.T("Unrestricted usage"), this.T("Unlike services like ChatGPT, which impose limits after intensive use, MindWork AI Studio offers unlimited usage through the providers API.")),
|
||||
new(this.T("Cost-effective"), this.T("You only pay for what you use, which can be cheaper than monthly subscription services like ChatGPT Plus, especially if used infrequently. But beware, here be dragons: For extremely intensive usage, the API costs can be significantly higher. Unfortunately, providers currently do not offer a way to display current costs in the app. Therefore, check your account with the respective provider to see how your costs are developing. When available, use prepaid and set a cost limit.")),
|
||||
@ -61,6 +63,8 @@ public partial class Home : MSGComponentBase
|
||||
var latest = Changelog.LOGS.MaxBy(n => n.Build);
|
||||
using var response = await this.HttpClient.GetAsync($"changelog/{latest.Filename}");
|
||||
this.LastChangeContent = await response.Content.ReadAsStringAsync();
|
||||
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
}
|
||||
|
||||
private const string QUICK_START_GUIDE =
|
||||
|
@ -152,4 +152,23 @@ public partial class Writer : MSGComponentBase
|
||||
this.suggestion = string.Join(' ', words.Skip(1));
|
||||
this.StateHasChanged();
|
||||
}
|
||||
|
||||
#region Overrides of MSGComponentBase
|
||||
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.typeTimer.Stop();
|
||||
this.typeTimer.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
base.DisposeResources();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
@ -64,4 +64,32 @@ CONFIG["SETTINGS"] = {}
|
||||
|
||||
-- Configure the update behavior:
|
||||
-- Allowed values are: NO_CHECK, ONCE_STARTUP, HOURLY, DAILY, WEEKLY
|
||||
-- CONFIG["SETTINGS"]["DataApp.UpdateBehavior"] = "NO_CHECK"
|
||||
-- CONFIG["SETTINGS"]["DataApp.UpdateBehavior"] = "NO_CHECK"
|
||||
|
||||
-- Configure the user permission to add providers:
|
||||
-- Allowed values are: true, false
|
||||
-- CONFIG["SETTINGS"]["DataApp.AllowUserToAddProvider"] = false
|
||||
|
||||
-- Example chat templates for this configuration:
|
||||
CONFIG["CHAT_TEMPLATES"] = {}
|
||||
|
||||
-- A simple example chat template:
|
||||
CONFIG["CHAT_TEMPLATES"][#CONFIG["CHAT_TEMPLATES"]+1] = {
|
||||
["Id"] = "00000000-0000-0000-0000-000000000000",
|
||||
["Name"] = "<user-friendly name of the chat template>",
|
||||
["SystemPrompt"] = "You are <Company Name>'s helpful AI assistant for <Department Name>. Your task is ...",
|
||||
["PredefinedUserPrompt"] = "Please help me with ...",
|
||||
["AllowProfileUsage"] = true,
|
||||
["ExampleConversation"] = {
|
||||
{
|
||||
-- Allowed values are: USER, AI, SYSTEM
|
||||
["Role"] = "USER",
|
||||
["Content"] = "Hello! Can you help me with a quick task?"
|
||||
},
|
||||
{
|
||||
-- Allowed values are: USER, AI, SYSTEM
|
||||
["Role"] = "AI",
|
||||
["Content"] = "Of course. What do you need?"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1129,7 +1129,7 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::REWRITEIMPROVE::ASSISTANTREWRITEIMPROVE::
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::REWRITEIMPROVE::ASSISTANTREWRITEIMPROVE::T3032662264"] = "Benutzerdefinierte Sprache"
|
||||
|
||||
-- Your input to improve
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::REWRITEIMPROVE::ASSISTANTREWRITEIMPROVE::T3037449423"] = "Ihr Vorschlag zur Verbesserung"
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::REWRITEIMPROVE::ASSISTANTREWRITEIMPROVE::T3037449423"] = "Ihr Text, der verbessert werden soll"
|
||||
|
||||
-- Writing style
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::REWRITEIMPROVE::ASSISTANTREWRITEIMPROVE::T3754048862"] = "Schreibstil"
|
||||
@ -1317,9 +1317,6 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CHATROLEEXTENSIONS::T601166687"] = "KI"
|
||||
-- Edit Message
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1183581066"] = "Nachricht bearbeiten"
|
||||
|
||||
-- Copies the content to the clipboard
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T12948066"] = "Kopiert den Inhalt in die Zwischenablage"
|
||||
|
||||
-- Do you really want to remove this message?
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1347427447"] = "Möchten Sie diese Nachricht wirklich löschen?"
|
||||
|
||||
@ -1353,9 +1350,6 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3587744975"] = "Neu gen
|
||||
-- Do you really want to regenerate this message?
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3878878761"] = "Möchten Sie diese Nachricht wirklich neu generieren?"
|
||||
|
||||
-- Cannot copy this content type to clipboard!
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4021525742"] = "Dieser Inhaltstyp kann nicht in die Zwischenablage kopiert werden!"
|
||||
|
||||
-- Remove Message
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4070211974"] = "Nachricht entfernen"
|
||||
|
||||
@ -1416,6 +1410,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T636393754"] = "Verschiebe
|
||||
-- Show your workspaces
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATCOMPONENT::T733672375"] = "Ihre Arbeitsbereiche anzeigen"
|
||||
|
||||
-- Create template from current chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATTEMPLATESELECTION::T1112722156"] = "Vorlage aus aktuellem Chat erstellen"
|
||||
|
||||
-- Start a new chat with a chat template
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHATTEMPLATESELECTION::T1333844707"] = "Neuen Chat mit einer Chat-Vorlage starten"
|
||||
|
||||
@ -1449,6 +1446,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CONFIDENCEINFO::T3243388657"] = "Vertraue
|
||||
-- Shows and hides the confidence card with information about the selected LLM provider.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CONFIDENCEINFO::T847071819"] = "Zeigt oder verbirgt die Vertrauenskarte mit Informationen über den ausgewählten LLM-Anbieter."
|
||||
|
||||
-- This feature is managed by your organization and has therefore been disabled.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CONFIGURATIONBASE::T1416426626"] = "Diese Funktion wird von Ihrer Organisation verwaltet und wurde daher deaktiviert."
|
||||
|
||||
-- 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"] = "Wählen Sie das minimale Vertrauensniveau, das alle LLM-Anbieter erfüllen müssen. So stellen Sie sicher, dass nur vertrauenswürdige Anbieter verwendet werden. Anbieter, die dieses Niveau unterschreiten, können nicht verwendet werden."
|
||||
|
||||
@ -1474,7 +1474,7 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CONFIGURATIONPROVIDERSELECTION::T20906218
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CONFIGURATIONPROVIDERSELECTION::T3672477670"] = "App-Standard verwenden"
|
||||
|
||||
-- Yes, let the AI decide which data sources are needed.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATASOURCESELECTION::T1031370894"] = "Ja, lassen Sie die KI entscheiden, welche Datenquellen benötigt werden."
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATASOURCESELECTION::T1031370894"] = "Ja, die KI soll entscheiden, welche Datenquellen benötigt werden."
|
||||
|
||||
-- Yes, let the AI validate & filter the retrieved data.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATASOURCESELECTION::T1309929755"] = "Ja, die KI soll die abgerufenen Daten überprüfen und filtern."
|
||||
@ -1587,6 +1587,12 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::MOTIVATION::T372007989"] = "Sich auf Webd
|
||||
-- Cross-Platform and Modern Development
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::MOTIVATION::T843057510"] = "Plattformübergreifende und moderne Entwicklung"
|
||||
|
||||
-- Copies the content to the clipboard
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::MUDCOPYCLIPBOARDBUTTON::T12948066"] = "Kopiert den Inhalt in die Zwischenablage"
|
||||
|
||||
-- Cannot copy this content type to clipboard.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::MUDCOPYCLIPBOARDBUTTON::T3937637647"] = "Dieser Inhaltstyp kann nicht in die Zwischenablage kopiert werden."
|
||||
|
||||
-- Alpha phase means that we are working on the last details before the beta phase.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PREVIEWALPHA::T166807685"] = "Alpha-Phase bedeutet, dass wir an den letzten Details arbeiten, bevor die Beta-Phase beginnt."
|
||||
|
||||
@ -1650,11 +1656,17 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROFILESELECTION::T918741365"] = "Hier k
|
||||
-- Provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T900237532"] = "Anbieter"
|
||||
|
||||
-- Use PDF content as input
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READPDFCONTENT::T2849276709"] = "PDF-Inhalt als Eingabe verwenden"
|
||||
-- Images are not supported yet
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T298062956"] = "Bilder werden derzeit nicht unterstützt"
|
||||
|
||||
-- Select PDF file
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READPDFCONTENT::T63272795"] = "PDF-Datei auswählen"
|
||||
-- Use file content as input
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T3499386973"] = "Dokumenteninhalt als Eingabe verwenden"
|
||||
|
||||
-- Select file to read its content
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T354817589"] = "Datei auswählen, um den Inhalt zu lesen"
|
||||
|
||||
-- Executables are not allowed
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T4167762413"] = "Ausführbare Dateien sind nicht erlaubt"
|
||||
|
||||
-- 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"] = "Der Inhalt wird mithilfe eines LLM-Agents bereinigt: Der Hauptinhalt wird extrahiert, Werbung und andere irrelevante Elemente werden nach Möglichkeit entfernt. Relative Links werden nach Möglichkeit in absolute Links umgewandelt, damit sie verwendet werden können."
|
||||
@ -1713,6 +1725,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"] = "Anwendungsfall: Dieser Agent wird verwendet, um Textinhalte zu bereinigen. Er extrahiert den Hauptinhalt, entfernt Werbung und andere irrelevante Elemente und versucht, relative Links in absolute Links umzuwandeln, damit diese verwendet werden können."
|
||||
|
||||
-- Agent: Text Content Cleaner
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTCONTENTCLEANER::T3517268866"] = "Agent: Inhaltsbereinigung"
|
||||
|
||||
-- No options are preselected
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTCONTENTCLEANER::T354528094"] = "Keine Optionen sind vorausgewählt"
|
||||
|
||||
@ -1728,8 +1743,8 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTDATASOURCESEL
|
||||
-- Use Case: this agent is used to select the appropriate data sources for the current prompt.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTDATASOURCESELECTION::T2208143316"] = "Anwendungsfall: Dieser Assistent wird verwendet, um die passenden Datenquellen für die aktuelle Eingabe auszuwählen."
|
||||
|
||||
-- Agent: Data Source Selection Options
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTDATASOURCESELECTION::T2258031949"] = "Agent: Auswahlmöglichkeiten für Datenquellen"
|
||||
-- Agent: Data Source Selection
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTDATASOURCESELECTION::T2258031949"] = "Agent: Wahl der Datenquellen"
|
||||
|
||||
-- No options are preselected
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTDATASOURCESELECTION::T354528094"] = "Keine Optionen sind vorausgewählt"
|
||||
@ -1773,11 +1788,11 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTRETRIEVALCONT
|
||||
-- Options are preselected
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTRETRIEVALCONTEXTVALIDATION::T3875604319"] = "Optionen sind vorausgewählt"
|
||||
|
||||
-- Agent: Retrieval Context Validation Options
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTRETRIEVALCONTEXTVALIDATION::T928989804"] = "Agent: Optionen zur Überprüfung des Abruf-Kontexts"
|
||||
-- Agent: Retrieval Context Validation
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTRETRIEVALCONTEXTVALIDATION::T928989804"] = "Agent: Validierung der Abruf-Kontexte"
|
||||
|
||||
-- No validation is performed
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTRETRIEVALCONTEXTVALIDATION::T95175095"] = "Es findet keine Überprüfung statt"
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTRETRIEVALCONTEXTVALIDATION::T95175095"] = "Es findet keine Validierung statt"
|
||||
|
||||
-- Spellchecking is disabled
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1059411425"] = "Rechtschreibprüfung ist deaktiviert"
|
||||
@ -2190,18 +2205,30 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T1847791252"] = "Aktuali
|
||||
-- The chat template name must not exceed 40 characters.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T204496403"] = "Der Name der Chat-Vorlage darf maximal 40 Zeichen lang sein."
|
||||
|
||||
-- Profile Usage
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2147062613"] = "Profilnutzung"
|
||||
|
||||
-- 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"] = "Fügen Sie Nachrichten einer Beispiel-Konversation hinzu (Nutzereingabe, gefolgt von einer Antwort des Assistenten), um das gewünschte Interaktionsmuster zu demonstrieren. Diese Beispiele helfen der KI, ihre Erwartungen zu verstehen, indem Sie das korrekte Format, den Stil und den Inhalt von Antworten zeigen, bevor tatsächliche Nutzereingaben erfolgen."
|
||||
|
||||
-- Role
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2418769465"] = "Rolle"
|
||||
|
||||
-- What predefined user input do you want to use?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2501284417"] = "Welche vordefinierte Benutzereingabe möchten Sie verwenden?"
|
||||
|
||||
-- Tell the AI your system prompt.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2552381261"] = "Teilen Sie der KI einen System-Prompt mit."
|
||||
|
||||
-- No, prohibit profile use for this template
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2579080722"] = "Nein, die Profilnutzung für diese Vorlage verbieten"
|
||||
|
||||
-- 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"] = "Sie können eine Nachricht vordefinieren, die als Benutzereingabe verwendet wird, wenn Sie diese Chat-Vorlage verwenden. Diese Nachricht könnte beispielsweise eine Vorlage für eine strukturierte Nachricht sein, für die diese Chat-Vorlage entwickelt wurde."
|
||||
|
||||
-- Predefined User Input
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2607897066"] = "Vordefinierte Benutzereingabe"
|
||||
|
||||
-- Entry
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2629823795"] = "Eintrag"
|
||||
|
||||
@ -2256,6 +2283,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3893704289"] = "Nachric
|
||||
-- Use the default system prompt
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T4051106111"] = "Verwenden Sie den Standard-System-Prompt"
|
||||
|
||||
-- Tell the AI your predefined user input.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T4052406705"] = "Teilen Sie der KI ihre vordefinierte Benutzereingabe mit."
|
||||
|
||||
-- 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"] = "Erstellen Sie ihre eigene Chat-Vorlage, um das Verhalten des LLMs für bestimmte Aufgaben oder Bereiche anzupassen. Definieren Sie einen individuellen System-Prompt und geben Sie eine Beispiel-Konversation vor, um eine KI-Erfahrung zu gestalten, die genau auf ihre Anforderungen zugeschnitten ist."
|
||||
|
||||
@ -3387,6 +3417,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T32678
|
||||
-- Close
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3448155331"] = "Schließen"
|
||||
|
||||
-- This template is managed by your organization.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3576775249"] = "Diesee Vorlage wird von Ihrer Organisation verwaltet."
|
||||
|
||||
-- Edit Chat Template
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3596030597"] = "Chat-Vorlage bearbeiten"
|
||||
|
||||
@ -4158,8 +4191,11 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1020427799"] = "Über MindWork AI Stud
|
||||
-- Browse AI Studio's source code on GitHub — we welcome your contributions.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1107156991"] = "Sehen Sie sich den Quellcode von AI Studio auf GitHub an – wir freuen uns über ihre Beiträge."
|
||||
|
||||
-- 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 läuft mit der Konfigurations-ID '{0}' ihrer Organisation und dem Konfigurationsserver '{1}'. Das Konfigurations-Plugin ist noch nicht verfügbar."
|
||||
-- This is a private AI Studio installation. It runs without an enterprise configuration.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1209549230"] = "Dies ist eine private AI Studio-Installation. Sie läuft ohne Unternehmenskonfiguration."
|
||||
|
||||
-- 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 läuft mit einer Unternehmenskonfiguration und einem Konfigurationsserver. Das Konfigurations-Plugin ist noch nicht verfügbar."
|
||||
|
||||
-- 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"] = "Diese Bibliothek wird verwendet, um PDF-Dateien zu lesen. Das ist zum Beispiel notwendig, um PDFs als Datenquelle für einen Chat zu nutzen."
|
||||
@ -4167,12 +4203,6 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1388816916"] = "Diese Bibliothek wird
|
||||
-- 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"] = "Diese Bibliothek wird verwendet, um die MudBlazor-Bibliothek zu erweitern. Sie stellt zusätzliche Komponenten bereit, die nicht Teil der MudBlazor-Bibliothek sind."
|
||||
|
||||
-- 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 läuft mit der Konfigurations-ID '{0}' ihrer Organisation und dem Konfigurationsserver '{1}'. Das Konfigurations-Plugin ist aktiv."
|
||||
|
||||
-- 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 läuft mit einer Unternehmenseinstellung und verwendet das Konfigurations-Plugin '{0}', jedoch ohne zentrale Konfigurationsverwaltung."
|
||||
|
||||
-- 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"] = "Wir verwenden Lua als Sprache für Plugins. Lua-CSharp ermöglicht die Kommunikation zwischen Lua-Skripten und AI Studio in beide Richtungen. Vielen Dank an Yusuke Nakada für diese großartige Bibliothek."
|
||||
|
||||
@ -4191,6 +4221,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1806897624"] = "Wenn Sie auf den jewei
|
||||
-- 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"] = "Kopiert die Konfigurations-Plugin-ID in die Zwischenablage"
|
||||
|
||||
-- Check for updates
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1890416390"] = "Nach Updates suchen"
|
||||
|
||||
@ -4206,21 +4239,30 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1924365263"] = "Diese Bibliothek wird
|
||||
-- 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"] = "Wir verwenden Rocket zur Implementierung der Runtime-API. Dies ist notwendig, da die Runtime mit der Benutzeroberfläche (IPC) kommunizieren muss. Rocket ist ein ausgezeichnetes Framework zur Umsetzung von Web-APIs in Rust."
|
||||
|
||||
-- Copies the server URL to the clipboard
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2037899437"] = "Kopiert die Server-URL in die Zwischenablage"
|
||||
|
||||
-- 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"] = "Diese Bibliothek wird verwendet, um den Dateityp einer Datei zu bestimmen. Das ist zum Beispiel notwendig, wenn wir eine Datei streamen möchten."
|
||||
|
||||
-- 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"] = "Für die sichere Kommunikation zwischen der Benutzeroberfläche und der Laufzeit müssen wir Zertifikate erstellen. Diese Rust-Bibliothek eignet sich hervorragend dafür."
|
||||
|
||||
-- AI Studio runs without an enterprise configuration.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2244723851"] = "AI Studio läuft ohne eine Konfiguration ihrer Organisation."
|
||||
|
||||
-- OK
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2246359087"] = "OK"
|
||||
|
||||
-- Configuration server:
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2272122662"] = "Konfigurationsserver:"
|
||||
|
||||
-- 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"] = "Wir müssen Zufallszahlen erzeugen, z. B. um die Kommunikation zwischen der Benutzeroberfläche und der Laufzeitumgebung abzusichern. Die rand-Bibliothek eignet sich dafür hervorragend."
|
||||
|
||||
-- AI Studio runs with an enterprise configuration using a configuration plugin, without central configuration management.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2280402765"] = "AI Studio läuft mit einer Unternehmenskonfiguration über ein Konfigurations-Plugin, ohne zentrale Konfigurationsverwaltung."
|
||||
|
||||
-- Configuration plugin ID:
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2301484629"] = "Konfigurations-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"] = "Die Programmiersprache C# wird für die Umsetzung der Benutzeroberfläche und des Backends verwendet. Für die Entwicklung der Benutzeroberfläche mit C# kommt die Blazor-Technologie aus ASP.NET Core zum Einsatz. Alle diese Technologien sind im .NET SDK integriert."
|
||||
|
||||
@ -4260,6 +4302,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2765814390"] = "Pandoc-Version wird er
|
||||
-- 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 der Programmiersprache Rust kann als synchron oder asynchron spezifiziert werden. Im Gegensatz zu .NET und der Sprache C# kann Rust asynchronen Code jedoch nicht von selbst ausführen. Dafür benötigt Rust Unterstützung in Form eines Executors. Tokio ist ein solcher Executor."
|
||||
|
||||
-- Show Details
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T27924674"] = "Details anzeigen"
|
||||
|
||||
-- View our project roadmap and help shape AI Studio's future development.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2829971158"] = "Sehen Sie sich unsere Roadmap an und helfen Sie mit, die zukünftige Entwicklung von AI Studio mitzugestalten."
|
||||
|
||||
@ -4275,12 +4320,18 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2868174483"] = "Das .NET-Backend kann
|
||||
-- Changelog
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3017574265"] = "Änderungsprotokoll"
|
||||
|
||||
-- Enterprise configuration ID:
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3092349641"] = "Unternehmenskonfigurations-ID:"
|
||||
|
||||
-- Connect AI Studio to your organization's data with our External Retrieval Interface (ERI).
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T313276297"] = "Verbinden Sie AI Studio mit den Daten ihrer Organisation über unsere Schnittstelle für externe Datenabfrage (ERI)."
|
||||
|
||||
-- Have feature ideas? Submit suggestions for future AI Studio enhancements.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3178730036"] = "Haben Sie Ideen für neue Funktionen? Senden Sie uns Vorschläge für zukünftige Verbesserungen von AI Studio."
|
||||
|
||||
-- Hide Details
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3183837919"] = "Details ausblenden"
|
||||
|
||||
-- Update Pandoc
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3249965383"] = "Pandoc aktualisieren"
|
||||
|
||||
@ -4305,6 +4356,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"] = "Diese Bibliothek wird verwendet, um Excel- und OpenDocument-Tabellendateien zu lesen. Dies ist zum Beispiel notwendig, wenn Tabellen als Datenquelle für einen Chat verwendet werden sollen."
|
||||
|
||||
-- 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 läuft mit einer Unternehmenskonfiguration und einem Konfigurationsserver. Das Konfigurations-Plugin ist aktiv."
|
||||
|
||||
-- this version does not met the requirements
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3813932670"] = "diese Version erfüllt die Anforderungen nicht"
|
||||
|
||||
@ -4347,9 +4401,15 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T639371534"] = "Haben Sie einen Fehler
|
||||
-- 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"] = "Diese Rust-Bibliothek wird verwendet, um die Nachrichten der App im Terminal auszugeben. Das ist während der Entwicklung und Fehlersuche hilfreich. Diese Funktion ist zunächst unsichtbar; werden App über das Terminal gestartet, werden die Nachrichten sichtbar."
|
||||
|
||||
-- Copies the config ID to the clipboard
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T788846912"] = "Kopiert die Konfigurations-ID in die Zwischenablage"
|
||||
|
||||
-- installed by AI Studio
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T833849470"] = "installiert von 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"] = "Wir verwenden diese Bibliothek, um PowerPoint-Dateien lesen zu können. So ist es möglich, Inhalte aus Folien in Prompts einzufügen und PowerPoint-Dateien in RAG-Prozessen zu berücksichtigen. Wir danken Nils Kruthoff für seine Arbeit an diesem 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"] = "Für einige Datenübertragungen müssen wir die Daten in Base64 kodieren. Diese Rust-Bibliothek eignet sich dafür hervorragend."
|
||||
|
||||
@ -4518,9 +4578,6 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T1702902297"] = "Einführung"
|
||||
-- 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"] = "Sie sind nicht an einen einzelnen Anbieter gebunden. Stattdessen können Sie den Anbieter wählen, der am besten zu ihren Bedürfnissen passt. Aktuell unterstützen wir OpenAI (GPT4o, o1 usw.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face sowie selbst gehostete Modelle mit llama.cpp, ollama, LM Studio, Groq oder Fireworks. Für Wissenschaftler und Beschäftigte von Forschungseinrichtungen unterstützen wir außerdem die KI-Dienste von Helmholtz und GWDG. Diese sind über föderierte Logins wie eduGAIN für alle 18 Helmholtz-Zentren, die Max-Planck-Gesellschaft, die meisten deutschen sowie viele internationale Universitäten verfügbar."
|
||||
|
||||
-- Let's get started
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2331588413"] = "Los geht's"
|
||||
|
||||
@ -4530,6 +4587,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2348849647"] = "Letztes Änderungsproto
|
||||
-- Choose the provider and model best suited for your current task.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2588488920"] = "Wählen Sie den Anbieter und das Modell aus, die am besten zu ihrer aktuellen Aufgabe passen."
|
||||
|
||||
-- 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.), 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::T2900280782"] = "Sie sind an keinen einzelnen Anbieter gebunden. Stattdessen können Sie den Anbieter wählen, der am besten zu ihren Bedürfnissen passt. Derzeit unterstützen wir OpenAI (GPT5, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face und selbst gehostete Modelle mit vLLM, llama.cpp, ollama, LM Studio, Groq oder Fireworks. Für Wissenschaftler und Mitarbeiter von Forschungseinrichtungen unterstützen wir auch die KI-Dienste von Helmholtz und GWDG. Diese sind über föderierte Anmeldungen wie eduGAIN für alle 18 Helmholtz-Zentren, die Max-Planck-Gesellschaft, die meisten deutschen und viele internationale Universitäten verfügbar."
|
||||
|
||||
-- Quick Start Guide
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3002014720"] = "Schnellstart-Anleitung"
|
||||
|
||||
@ -4720,7 +4780,7 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::CONFIDENCE::T3010553924"] = "Der Anbieter h
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::CONFIDENCE::T3368531176"] = "Kein Anbieter ausgewählt. Bitte wählen Sie einen Anbieter aus, um dessen Vertrauensniveau zu sehen."
|
||||
|
||||
-- The provider operates its service from the USA and is subject to **US jurisdiction**. In case of suspicion, authorities in the USA can access your data. However, **your data is not used for training** purposes.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::CONFIDENCE::T3528165925"] = "Der Anbieter betreibt seinen Dienst aus den USA und unterliegt der **US-amerikanischen Gerichtsbarkeit**. Bei Verdacht können US-Behörden auf ihre Daten zugreifen. **ihre Daten werden jedoch nicht für Trainingszwecke** verwendet."
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::CONFIDENCE::T3528165925"] = "Der Anbieter betreibt seinen Dienst aus den USA und unterliegt der **US-amerikanischen Gerichtsbarkeit**. Bei Verdacht können US-Behörden auf ihre Daten zugreifen. **Ihre Daten werden jedoch nicht für Trainingszwecke** verwendet."
|
||||
|
||||
-- The provider operates its service from the USA and is subject to **U.S. jurisdiction**. In case of suspicion, authorities in the USA can access your data. Please inform yourself about the use of your data. We do not know if your data is safe.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::CONFIDENCE::T3788466789"] = "Der Anbieter betreibt seinen Service in den USA und unterliegt der **US-amerikanischen Gerichtsbarkeit**. Im Verdachtsfall können US-Behörden auf ihre Daten zugreifen. Bitte informieren Sie sich über die Verwendung ihrer Daten. Wir wissen nicht, ob ihre Daten sicher sind."
|
||||
@ -5014,7 +5074,7 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMMONLANGUAGEEXTENSIONS::T4187368718"] = "Rus
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMMONLANGUAGEEXTENSIONS::T658789330"] = "Sprache nicht ändern"
|
||||
|
||||
-- Coding Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T1082499335"] = "Programmierassistent"
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T1082499335"] = "Programmier-Assistent"
|
||||
|
||||
-- E-Mail Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T1185802704"] = "E-Mail-Assistent"
|
||||
@ -5023,25 +5083,25 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T1185802704"] = "E-Mail-
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T1546040625"] = "Meine Aufgaben-Assistent"
|
||||
|
||||
-- Grammar & Spelling Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T166453786"] = "Grammatik- & Rechtschreibassistent"
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T166453786"] = "Grammatik- & Rechtschreib-Assistent"
|
||||
|
||||
-- Legal Check Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T1886447798"] = "Assistent für rechtliche Prüfungen"
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T1886447798"] = "Rechtlichen Prüfungs-Assistent"
|
||||
|
||||
-- Job Posting Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T2212811874"] = "Assistent für Stellenanzeigen"
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T2212811874"] = "Stellenanzeigen-Assistent"
|
||||
|
||||
-- Icon Finder Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T2457005512"] = "Icon Finder-Assistent"
|
||||
|
||||
-- Text Summarizer Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T2684676843"] = "Assistent zur Textzusammenfassung"
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T2684676843"] = "Texte zusammenfassen-Assistent"
|
||||
|
||||
-- Synonym Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T2921123194"] = "Synonym-Assistent"
|
||||
|
||||
-- Translation Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T3887962308"] = "Übersetzungsassistent"
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T3887962308"] = "Übersetzungs-Assistent"
|
||||
|
||||
-- Agenda Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T4034795997"] = "Agenda-Assistent"
|
||||
@ -5050,7 +5110,7 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T4034795997"] = "Agenda-
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T4204533420"] = "ERI-Server"
|
||||
|
||||
-- Rewrite Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T4262148639"] = "Assistent zum Umformulieren & Verbessern"
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T4262148639"] = "Umformulieren & Verbessern-Assistent"
|
||||
|
||||
-- Localization Assistant
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::COMPONENTSEXTENSIONS::T446674624"] = "Lokalisierungs-Assistent"
|
||||
@ -5478,6 +5538,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::RAG::RAGPROCESSES::AISRCSELWITHRETCTXVAL::T304
|
||||
-- AI-based data source selection with AI retrieval context validation
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RAG::RAGPROCESSES::AISRCSELWITHRETCTXVAL::T3775725978"] = "KI-basierte Datenquellen-Auswahl mit Validierung des Abrufkontexts"
|
||||
|
||||
-- Executable Files
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPEFILTER::T2217313358"] = "Ausführbare Dateien"
|
||||
|
||||
-- PDF Files
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPEFILTER::T3108466742"] = "PDF-Dateien"
|
||||
|
||||
|
@ -1317,9 +1317,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?"
|
||||
|
||||
@ -1353,9 +1350,6 @@ 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"
|
||||
|
||||
@ -1416,6 +1410,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"
|
||||
|
||||
@ -1449,6 +1446,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."
|
||||
|
||||
@ -1587,6 +1587,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."
|
||||
|
||||
@ -1650,11 +1656,17 @@ 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"
|
||||
-- Images are not supported yet
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T298062956"] = "Images are not supported yet"
|
||||
|
||||
-- Select PDF file
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READPDFCONTENT::T63272795"] = "Select PDF file"
|
||||
-- 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."
|
||||
@ -1713,6 +1725,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
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTCONTENTCLEANER::T3517268866"] = "Agent: Text Content Cleaner"
|
||||
|
||||
-- No options are preselected
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTCONTENTCLEANER::T354528094"] = "No options are preselected"
|
||||
|
||||
@ -1728,8 +1743,8 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTDATASOURCESEL
|
||||
-- Use Case: this agent is used to select the appropriate data sources for the current prompt.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTDATASOURCESELECTION::T2208143316"] = "Use Case: this agent is used to select the appropriate data sources for the current prompt."
|
||||
|
||||
-- Agent: Data Source Selection Options
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTDATASOURCESELECTION::T2258031949"] = "Agent: Data Source Selection Options"
|
||||
-- Agent: Data Source Selection
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTDATASOURCESELECTION::T2258031949"] = "Agent: Data Source Selection"
|
||||
|
||||
-- No options are preselected
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTDATASOURCESELECTION::T354528094"] = "No options are preselected"
|
||||
@ -1773,8 +1788,8 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTRETRIEVALCONT
|
||||
-- Options are preselected
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTRETRIEVALCONTEXTVALIDATION::T3875604319"] = "Options are preselected"
|
||||
|
||||
-- Agent: Retrieval Context Validation Options
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTRETRIEVALCONTEXTVALIDATION::T928989804"] = "Agent: Retrieval Context Validation Options"
|
||||
-- Agent: Retrieval Context Validation
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTRETRIEVALCONTEXTVALIDATION::T928989804"] = "Agent: Retrieval Context Validation"
|
||||
|
||||
-- No validation is performed
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTRETRIEVALCONTEXTVALIDATION::T95175095"] = "No validation is performed"
|
||||
@ -2190,18 +2205,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"
|
||||
|
||||
@ -2256,6 +2283,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."
|
||||
|
||||
@ -3387,6 +3417,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"
|
||||
|
||||
@ -4158,8 +4191,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."
|
||||
@ -4167,12 +4203,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."
|
||||
|
||||
@ -4191,6 +4221,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"
|
||||
|
||||
@ -4206,21 +4239,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."
|
||||
|
||||
@ -4260,6 +4302,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."
|
||||
|
||||
@ -4275,12 +4320,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"
|
||||
|
||||
@ -4305,6 +4356,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"
|
||||
|
||||
@ -4347,9 +4401,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."
|
||||
|
||||
@ -4518,9 +4578,6 @@ 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."
|
||||
|
||||
-- Let's get started
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2331588413"] = "Let's get started"
|
||||
|
||||
@ -4530,6 +4587,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2348849647"] = "Last Changelog"
|
||||
-- Choose the provider and model best suited for your current task.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2588488920"] = "Choose the provider and model best suited for your current task."
|
||||
|
||||
-- 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.), 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::T2900280782"] = "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.), 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."
|
||||
|
||||
-- Quick Start Guide
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3002014720"] = "Quick Start Guide"
|
||||
|
||||
@ -5478,6 +5538,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::RAG::RAGPROCESSES::AISRCSELWITHRETCTXVAL::T304
|
||||
-- AI-based data source selection with AI retrieval context validation
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RAG::RAGPROCESSES::AISRCSELWITHRETCTXVAL::T3775725978"] = "AI-based data source selection with AI retrieval context validation"
|
||||
|
||||
-- Executable Files
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPEFILTER::T2217313358"] = "Executable Files"
|
||||
|
||||
-- PDF Files
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPEFILTER::T3108466742"] = "PDF Files"
|
||||
|
||||
|
@ -126,7 +126,6 @@ internal sealed class Program
|
||||
builder.Services.AddSingleton<SettingsManager>();
|
||||
builder.Services.AddSingleton<ThreadSafeRandom>();
|
||||
builder.Services.AddSingleton<DataSourceService>();
|
||||
builder.Services.AddSingleton<SettingsLocker>();
|
||||
builder.Services.AddTransient<HTMLParser>();
|
||||
builder.Services.AddTransient<AgentDataSourceSelection>();
|
||||
builder.Services.AddTransient<AgentRetrievalContextValidation>();
|
||||
@ -160,6 +159,7 @@ internal sealed class Program
|
||||
|
||||
// Get the logging factory for e.g., static classes:
|
||||
LOGGER_FACTORY = app.Services.GetRequiredService<ILoggerFactory>();
|
||||
MessageBus.INSTANCE.Initialize(LOGGER_FACTORY.CreateLogger<MessageBus>());
|
||||
|
||||
// Get a program logger:
|
||||
var programLogger = app.Services.GetRequiredService<ILogger<Program>>();
|
||||
|
@ -285,7 +285,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.GWDG => true,
|
||||
LLMProviders.HUGGINGFACE => true,
|
||||
|
||||
LLMProviders.SELF_HOSTED => host is Host.OLLAMA,
|
||||
LLMProviders.SELF_HOSTED => host is (Host.OLLAMA or Host.VLLM),
|
||||
|
||||
_ => false,
|
||||
};
|
||||
@ -322,6 +322,7 @@ public static class LLMProvidersExtensions
|
||||
|
||||
case Host.OLLAMA:
|
||||
case Host.LM_STUDIO:
|
||||
case Host.VLLM:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -6,11 +6,8 @@ namespace AIStudio.Provider.SelfHosted;
|
||||
/// <param name="Model">Which model to use for chat completion.</param>
|
||||
/// <param name="Messages">The chat messages.</param>
|
||||
/// <param name="Stream">Whether to stream the chat completion.</param>
|
||||
/// <param name="MaxTokens">The maximum number of tokens to generate.</param>
|
||||
public readonly record struct ChatRequest(
|
||||
string Model,
|
||||
IList<Message> Messages,
|
||||
bool Stream,
|
||||
|
||||
int MaxTokens
|
||||
bool Stream
|
||||
);
|
@ -7,4 +7,5 @@ public enum Host
|
||||
LM_STUDIO,
|
||||
LLAMACPP,
|
||||
OLLAMA,
|
||||
VLLM,
|
||||
}
|
@ -9,6 +9,7 @@ public static class HostExtensions
|
||||
Host.LM_STUDIO => "LM Studio",
|
||||
Host.LLAMACPP => "llama.cpp",
|
||||
Host.OLLAMA => "ollama",
|
||||
Host.VLLM => "vLLM",
|
||||
|
||||
_ => "Unknown",
|
||||
};
|
||||
@ -29,6 +30,7 @@ public static class HostExtensions
|
||||
{
|
||||
case Host.LM_STUDIO:
|
||||
case Host.OLLAMA:
|
||||
case Host.VLLM:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
@ -58,8 +58,7 @@ public sealed class ProviderSelfHosted(ILogger logger, Host host, string hostnam
|
||||
}).ToList()],
|
||||
|
||||
// Right now, we only support streaming completions:
|
||||
Stream = true,
|
||||
MaxTokens = -1,
|
||||
Stream = true
|
||||
}, JSON_SERIALIZER_OPTIONS);
|
||||
|
||||
async Task<HttpRequestMessage> RequestBuilder()
|
||||
@ -101,6 +100,7 @@ public sealed class ProviderSelfHosted(ILogger logger, Host host, string hostnam
|
||||
|
||||
case Host.LM_STUDIO:
|
||||
case Host.OLLAMA:
|
||||
case Host.VLLM:
|
||||
return await this.LoadModels(["embed"], [], token, apiKeyProvisional);
|
||||
}
|
||||
|
||||
@ -127,6 +127,7 @@ public sealed class ProviderSelfHosted(ILogger logger, Host host, string hostnam
|
||||
{
|
||||
case Host.LM_STUDIO:
|
||||
case Host.OLLAMA:
|
||||
case Host.VLLM:
|
||||
return await this.LoadModels([], ["embed"], token, apiKeyProvisional);
|
||||
}
|
||||
|
||||
|
@ -3,18 +3,25 @@ using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Settings;
|
||||
|
||||
public readonly record struct ChatTemplate(uint Num, string Id, string Name, string SystemPrompt, List<ContentBlock> ExampleConversation, bool AllowProfileUsage)
|
||||
public record ChatTemplate(uint Num, string Id, string Name, string SystemPrompt, string PredefinedUserPrompt, List<ContentBlock> ExampleConversation, bool AllowProfileUsage, bool IsEnterpriseConfiguration = false, Guid EnterpriseConfigurationPluginId = default)
|
||||
{
|
||||
public ChatTemplate() : this(0, Guid.Empty.ToString(), string.Empty, string.Empty, string.Empty, [], false)
|
||||
{
|
||||
}
|
||||
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ChatTemplate).Namespace, nameof(ChatTemplate));
|
||||
|
||||
public static readonly ChatTemplate NO_CHAT_TEMPLATE = new()
|
||||
{
|
||||
Name = TB("Use no chat template"),
|
||||
SystemPrompt = string.Empty,
|
||||
PredefinedUserPrompt = string.Empty,
|
||||
Id = Guid.Empty.ToString(),
|
||||
Num = uint.MaxValue,
|
||||
ExampleConversation = [],
|
||||
AllowProfileUsage = true,
|
||||
EnterpriseConfigurationPluginId = Guid.Empty,
|
||||
IsEnterpriseConfiguration = false,
|
||||
};
|
||||
|
||||
#region Overrides of ValueType
|
||||
|
88
app/MindWork AI Studio/Settings/ConfigMeta.cs
Normal file
88
app/MindWork AI Studio/Settings/ConfigMeta.cs
Normal file
@ -0,0 +1,88 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
using AIStudio.Settings.DataModel;
|
||||
|
||||
namespace AIStudio.Settings;
|
||||
|
||||
/// <summary>
|
||||
/// Represents configuration metadata for a specific class and property.
|
||||
/// </summary>
|
||||
/// <typeparam name="TClass">The class type that contains the configuration property.</typeparam>
|
||||
/// <typeparam name="TValue">The type of the configuration property value.</typeparam>
|
||||
public record ConfigMeta<TClass, TValue> : ConfigMetaBase
|
||||
{
|
||||
public ConfigMeta(Expression<Func<Data, TClass>> configSelection, Expression<Func<TClass, TValue>> propertyExpression)
|
||||
{
|
||||
this.ConfigSelection = configSelection;
|
||||
this.PropertyExpression = propertyExpression;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The expression to select the configuration class from the settings data.
|
||||
/// </summary>
|
||||
private Expression<Func<Data, TClass>> ConfigSelection { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The expression to select the property within the configuration class.
|
||||
/// </summary>
|
||||
private Expression<Func<TClass, TValue>> PropertyExpression { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the configuration is managed by a plugin and is therefore locked.
|
||||
/// </summary>
|
||||
public bool IsLocked { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the plugin that manages this configuration. This is set when the configuration is locked.
|
||||
/// </summary>
|
||||
public Guid MangedByConfigPluginId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The default value for the configuration property. This is used when resetting the property to its default state.
|
||||
/// </summary>
|
||||
public required TValue Default { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Locks the configuration state, indicating that it is managed by a specific plugin.
|
||||
/// </summary>
|
||||
/// <param name="pluginId">The ID of the plugin that is managing this configuration.</param>
|
||||
public void LockManagedState(Guid pluginId)
|
||||
{
|
||||
this.IsLocked = true;
|
||||
this.MangedByConfigPluginId = pluginId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the managed state of the configuration, allowing it to be modified again.
|
||||
/// This will also reset the property to its default value.
|
||||
/// </summary>
|
||||
public void ResetManagedState()
|
||||
{
|
||||
this.IsLocked = false;
|
||||
this.MangedByConfigPluginId = Guid.Empty;
|
||||
this.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the configuration property to its default value.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
var configInstance = this.ConfigSelection.Compile().Invoke(SETTINGS_MANAGER.ConfigurationData);
|
||||
var memberExpression = this.PropertyExpression.GetMemberExpression();
|
||||
if (memberExpression.Member is System.Reflection.PropertyInfo propertyInfo)
|
||||
propertyInfo.SetValue(configInstance, this.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of the configuration property to the specified value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to set for the configuration property.</param>
|
||||
public void SetValue(TValue value)
|
||||
{
|
||||
var configInstance = this.ConfigSelection.Compile().Invoke(SETTINGS_MANAGER.ConfigurationData);
|
||||
var memberExpression = this.PropertyExpression.GetMemberExpression();
|
||||
if (memberExpression.Member is System.Reflection.PropertyInfo propertyInfo)
|
||||
propertyInfo.SetValue(configInstance, value);
|
||||
}
|
||||
}
|
6
app/MindWork AI Studio/Settings/ConfigMetaBase.cs
Normal file
6
app/MindWork AI Studio/Settings/ConfigMetaBase.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace AIStudio.Settings;
|
||||
|
||||
public abstract record ConfigMetaBase : IConfig
|
||||
{
|
||||
protected static readonly SettingsManager SETTINGS_MANAGER = Program.SERVICE_PROVIDER.GetRequiredService<SettingsManager>();
|
||||
}
|
@ -71,7 +71,7 @@ public sealed class Data
|
||||
/// </summary>
|
||||
public uint NextChatTemplateNum { get; set; } = 1;
|
||||
|
||||
public DataApp App { get; init; } = new();
|
||||
public DataApp App { get; init; } = new(x => x.App);
|
||||
|
||||
public DataChat Chat { get; init; } = new();
|
||||
|
||||
|
@ -1,7 +1,16 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace AIStudio.Settings.DataModel;
|
||||
|
||||
public sealed class DataApp
|
||||
public sealed class DataApp(Expression<Func<Data, DataApp>>? configSelection = null)
|
||||
{
|
||||
/// <summary>
|
||||
/// The default constructor for the JSON deserializer.
|
||||
/// </summary>
|
||||
public DataApp() : this(null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The language behavior.
|
||||
/// </summary>
|
||||
@ -21,7 +30,7 @@ public sealed class DataApp
|
||||
/// Should we save energy? When true, we will update content streamed
|
||||
/// from the server, i.e., AI, less frequently.
|
||||
/// </summary>
|
||||
public bool IsSavingEnergy { get; set; }
|
||||
public bool IsSavingEnergy { get; set; } = ManagedConfiguration.Register(configSelection, n => n.IsSavingEnergy, false);
|
||||
|
||||
/// <summary>
|
||||
/// Should we enable spellchecking for all input fields?
|
||||
@ -31,7 +40,7 @@ public sealed class DataApp
|
||||
/// <summary>
|
||||
/// If and when we should look for updates.
|
||||
/// </summary>
|
||||
public UpdateBehavior UpdateBehavior { get; set; } = UpdateBehavior.HOURLY;
|
||||
public UpdateBehavior UpdateBehavior { get; set; } = ManagedConfiguration.Register(configSelection, n => n.UpdateBehavior, UpdateBehavior.HOURLY);
|
||||
|
||||
/// <summary>
|
||||
/// The navigation behavior.
|
||||
@ -41,7 +50,7 @@ public sealed class DataApp
|
||||
/// <summary>
|
||||
/// The visibility setting for previews features.
|
||||
/// </summary>
|
||||
public PreviewVisibility PreviewVisibility { get; set; } = PreviewVisibility.NONE;
|
||||
public PreviewVisibility PreviewVisibility { get; set; } = ManagedConfiguration.Register(configSelection, n => n.PreviewVisibility, PreviewVisibility.NONE);
|
||||
|
||||
/// <summary>
|
||||
/// The enabled preview features.
|
||||
@ -58,9 +67,13 @@ public sealed class DataApp
|
||||
/// </summary>
|
||||
public string PreselectedProfile { get; set; } = string.Empty;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Should we preselect a chat template for the entire app?
|
||||
/// </summary>
|
||||
public string PreselectedChatTemplate { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Should the user be allowed to add providers?
|
||||
/// </summary>
|
||||
public bool AllowUserToAddProvider { get; set; } = ManagedConfiguration.Register(configSelection, n => n.AllowUserToAddProvider, true);
|
||||
}
|
@ -33,7 +33,7 @@ public sealed class DataV4
|
||||
/// </summary>
|
||||
public uint NextProfileNum { get; set; } = 1;
|
||||
|
||||
public DataApp App { get; init; } = new();
|
||||
public DataApp App { get; init; } = new(x => x.App);
|
||||
|
||||
public DataChat Chat { get; init; } = new();
|
||||
|
||||
|
26
app/MindWork AI Studio/Settings/ExpressionExtensions.cs
Normal file
26
app/MindWork AI Studio/Settings/ExpressionExtensions.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace AIStudio.Settings;
|
||||
|
||||
public static class ExpressionExtensions
|
||||
{
|
||||
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(typeof(ExpressionExtensions));
|
||||
|
||||
public static MemberExpression GetMemberExpression<TIn, TOut>(this Expression<Func<TIn, TOut>> expression)
|
||||
{
|
||||
switch (expression.Body)
|
||||
{
|
||||
// Case for value types, which are wrapped in UnaryExpression:
|
||||
case UnaryExpression { NodeType: ExpressionType.Convert } unaryExpression:
|
||||
return (MemberExpression)unaryExpression.Operand;
|
||||
|
||||
// Case for reference types, which are directly MemberExpressions:
|
||||
case MemberExpression memberExpression:
|
||||
return memberExpression;
|
||||
|
||||
default:
|
||||
LOGGER.LogError($"Expression '{expression}' is not a valid property expression.");
|
||||
throw new ArgumentException($"Expression '{expression}' is not a valid property expression.", nameof(expression));
|
||||
}
|
||||
}
|
||||
}
|
3
app/MindWork AI Studio/Settings/IConfig.cs
Normal file
3
app/MindWork AI Studio/Settings/IConfig.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace AIStudio.Settings;
|
||||
|
||||
public interface IConfig;
|
198
app/MindWork AI Studio/Settings/ManagedConfiguration.cs
Normal file
198
app/MindWork AI Studio/Settings/ManagedConfiguration.cs
Normal file
@ -0,0 +1,198 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
using Lua;
|
||||
|
||||
namespace AIStudio.Settings;
|
||||
|
||||
public static class ManagedConfiguration
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, IConfig> METADATA = new();
|
||||
|
||||
/// <summary>
|
||||
/// Registers a configuration setting with a default value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When called from the JSON deserializer, the configSelection parameter will be null.
|
||||
/// In this case, the method will return the default value without registering the setting.
|
||||
/// </remarks>
|
||||
/// <param name="configSelection">The expression to select the configuration class.</param>
|
||||
/// <param name="propertyExpression">The expression to select the property within the configuration class.</param>
|
||||
/// <param name="defaultValue">The default value to use when the setting is not configured.</param>
|
||||
/// <typeparam name="TClass">The type of the configuration class.</typeparam>
|
||||
/// <typeparam name="TValue">The type of the property within the configuration class.</typeparam>
|
||||
/// <returns>The default value.</returns>
|
||||
public static TValue Register<TClass, TValue>(Expression<Func<Data, TClass>>? configSelection, Expression<Func<TClass, TValue>> propertyExpression, TValue defaultValue)
|
||||
{
|
||||
// When called from the JSON deserializer by using the standard constructor,
|
||||
// we ignore the register call and return the default value:
|
||||
if(configSelection is null)
|
||||
return defaultValue;
|
||||
|
||||
var configPath = Path(configSelection, propertyExpression);
|
||||
|
||||
// If the metadata already exists for this configuration path, we return the default value:
|
||||
if (METADATA.ContainsKey(configPath))
|
||||
return defaultValue;
|
||||
|
||||
METADATA[configPath] = new ConfigMeta<TClass, TValue>(configSelection, propertyExpression)
|
||||
{
|
||||
Default = defaultValue,
|
||||
};
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to retrieve the configuration metadata for a given configuration selection and property expression.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When no configuration metadata is found, it returns a NoConfig instance with the default value set to default(TValue).
|
||||
/// This allows the caller to handle the absence of configuration gracefully. In such cases, the return value of the method will be false.
|
||||
/// </remarks>
|
||||
/// <param name="configSelection">The expression to select the configuration class.</param>
|
||||
/// <param name="propertyExpression">The expression to select the property within the configuration class.</param>
|
||||
/// <param name="configMeta">The output parameter that will hold the configuration metadata if found.</param>
|
||||
/// <typeparam name="TClass">The type of the configuration class.</typeparam>
|
||||
/// <typeparam name="TValue">The type of the property within the configuration class.</typeparam>
|
||||
/// <returns>True if the configuration metadata was found, otherwise false.</returns>
|
||||
public static bool TryGet<TClass, TValue>(Expression<Func<Data, TClass>> configSelection, Expression<Func<TClass, TValue>> propertyExpression, out ConfigMeta<TClass, TValue> configMeta)
|
||||
{
|
||||
var configPath = Path(configSelection, propertyExpression);
|
||||
if (METADATA.TryGetValue(configPath, out var value) && value is ConfigMeta<TClass, TValue> meta)
|
||||
{
|
||||
configMeta = meta;
|
||||
return true;
|
||||
}
|
||||
|
||||
configMeta = new NoConfig<TClass, TValue>(configSelection, propertyExpression)
|
||||
{
|
||||
Default = default!,
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to process the configuration settings from a Lua table.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When the configuration is successfully processed, it updates the configuration metadata with the configured value.
|
||||
/// Furthermore, it locks the managed state of the configuration metadata to the provided configuration plugin ID.
|
||||
/// The setting's value is set to the configured value.
|
||||
/// </remarks>
|
||||
/// <param name="configPluginId">The ID of the related configuration plugin.</param>
|
||||
/// <param name="settings">The Lua table containing the settings to process.</param>
|
||||
/// <param name="configSelection">The expression to select the configuration class.</param>
|
||||
/// <param name="propertyExpression">The expression to select the property within the configuration class.</param>
|
||||
/// <param name="dryRun">When true, the method will not apply any changes, but only check if the configuration can be read.</param>
|
||||
/// <typeparam name="TClass">The type of the configuration class.</typeparam>
|
||||
/// <typeparam name="TValue">The type of the property within the configuration class.</typeparam>
|
||||
/// <returns>True when the configuration was successfully processed, otherwise false.</returns>
|
||||
public static bool TryProcessConfiguration<TClass, TValue>(Expression<Func<Data, TClass>> configSelection, Expression<Func<TClass, TValue>> propertyExpression, Guid configPluginId, LuaTable settings, bool dryRun)
|
||||
{
|
||||
if(!TryGet(configSelection, propertyExpression, out var configMeta))
|
||||
return false;
|
||||
|
||||
var (configuredValue, successful) = configMeta.Default switch
|
||||
{
|
||||
Enum => settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredEnumValue) && configuredEnumValue.TryRead<string>(out var configuredEnumText) && Enum.TryParse(typeof(TValue), configuredEnumText, true, out var configuredEnum) ? ((TValue)configuredEnum, true) : (configMeta.Default, false),
|
||||
Guid => settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredGuidValue) && configuredGuidValue.TryRead<string>(out var configuredGuidText) && Guid.TryParse(configuredGuidText, out var configuredGuid) ? ((TValue)(object)configuredGuid, true) : (configMeta.Default, false),
|
||||
|
||||
string => settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredTextValue) && configuredTextValue.TryRead<string>(out var configuredText) ? ((TValue)(object)configuredText, true) : (configMeta.Default, false),
|
||||
bool => settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredBoolValue) && configuredBoolValue.TryRead<bool>(out var configuredState) ? ((TValue)(object)configuredState, true) : (configMeta.Default, false),
|
||||
|
||||
int => settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredIntValue) && configuredIntValue.TryRead<int>(out var configuredInt) ? ((TValue)(object)configuredInt, true) : (configMeta.Default, false),
|
||||
double => settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredDoubleValue) && configuredDoubleValue.TryRead<double>(out var configuredDouble) ? ((TValue)(object)configuredDouble, true) : (configMeta.Default, false),
|
||||
float => settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredFloatValue) && configuredFloatValue.TryRead<float>(out var configuredFloat) ? ((TValue)(object)configuredFloat, true) : (configMeta.Default, false),
|
||||
|
||||
_ => (configMeta.Default, false),
|
||||
};
|
||||
|
||||
if(dryRun)
|
||||
return successful;
|
||||
|
||||
switch (successful)
|
||||
{
|
||||
case true:
|
||||
//
|
||||
// Case: the setting was configured, and we could read the value successfully.
|
||||
//
|
||||
configMeta.SetValue(configuredValue);
|
||||
configMeta.LockManagedState(configPluginId);
|
||||
break;
|
||||
|
||||
case false when configMeta.IsLocked && configMeta.MangedByConfigPluginId == configPluginId:
|
||||
//
|
||||
// Case: the setting was configured previously, but we could not read the value successfully.
|
||||
// This happens when the setting was removed from the configuration plugin. We handle that
|
||||
// case only when the setting was locked and managed by the same configuration plugin.
|
||||
//
|
||||
// The other case, when the setting was locked and managed by a different configuration plugin,
|
||||
// is handled by the IsConfigurationLeftOver method, which checks if the configuration plugin
|
||||
// is still available. If it is not available, it resets the managed state of the
|
||||
// configuration setting, allowing it to be reconfigured by a different plugin or left unchanged.
|
||||
//
|
||||
configMeta.ResetManagedState();
|
||||
break;
|
||||
|
||||
case false:
|
||||
//
|
||||
// Case: the setting was not configured, or we could not read the value successfully.
|
||||
// We do not change the setting, and it remains at whatever value it had before.
|
||||
//
|
||||
break;
|
||||
}
|
||||
|
||||
return successful;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a configuration setting is left over from a configuration plugin that is no longer available.
|
||||
/// If the configuration setting is locked and managed by a configuration plugin that is not available,
|
||||
/// it resets the managed state of the configuration setting and returns true.
|
||||
/// Otherwise, it returns false.
|
||||
/// </summary>
|
||||
/// <param name="configSelection">The expression to select the configuration class.</param>
|
||||
/// <param name="propertyExpression">The expression to select the property within the configuration class.</param>
|
||||
/// <param name="availablePlugins">The collection of available plugins to check against.</param>
|
||||
/// <typeparam name="TClass">The type of the configuration class.</typeparam>
|
||||
/// <typeparam name="TValue">The type of the property within the configuration class.</typeparam>
|
||||
/// <returns>True if the configuration setting is left over and was reset, otherwise false.</returns>
|
||||
public static bool IsConfigurationLeftOver<TClass, TValue>(Expression<Func<Data, TClass>> configSelection, Expression<Func<TClass, TValue>> propertyExpression, IEnumerable<IAvailablePlugin> availablePlugins)
|
||||
{
|
||||
if (!TryGet(configSelection, propertyExpression, out var configMeta))
|
||||
return false;
|
||||
|
||||
if(configMeta.MangedByConfigPluginId == Guid.Empty || !configMeta.IsLocked)
|
||||
return false;
|
||||
|
||||
// Check if the configuration plugin ID is valid against the available plugin IDs:
|
||||
var plugin = availablePlugins.FirstOrDefault(x => x.Id == configMeta.MangedByConfigPluginId);
|
||||
if (plugin is null)
|
||||
{
|
||||
// Remove the locked state:
|
||||
configMeta.ResetManagedState();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string Path<TClass, TValue>(Expression<Func<Data, TClass>> configSelection, Expression<Func<TClass, TValue>> propertyExpression)
|
||||
{
|
||||
var className = typeof(TClass).Name;
|
||||
|
||||
var memberExpressionConfig = configSelection.GetMemberExpression();
|
||||
var configName = memberExpressionConfig.Member.Name;
|
||||
|
||||
var memberExpressionProperty = propertyExpression.GetMemberExpression();
|
||||
var propertyName = memberExpressionProperty.Member.Name;
|
||||
|
||||
var configPath = $"{configName}.{className}.{propertyName}";
|
||||
return configPath;
|
||||
}
|
||||
}
|
12
app/MindWork AI Studio/Settings/NoConfig.cs
Normal file
12
app/MindWork AI Studio/Settings/NoConfig.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
using AIStudio.Settings.DataModel;
|
||||
|
||||
namespace AIStudio.Settings;
|
||||
|
||||
public sealed record NoConfig<TClass, TValue> : ConfigMeta<TClass, TValue>
|
||||
{
|
||||
public NoConfig(Expression<Func<Data, TClass>> configSelection, Expression<Func<TClass, TValue>> propertyExpression) : base(configSelection, propertyExpression)
|
||||
{
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace AIStudio.Settings;
|
||||
|
||||
public sealed class SettingsLocker
|
||||
{
|
||||
private static readonly ILogger<SettingsLocker> LOGGER = Program.LOGGER_FACTORY.CreateLogger<SettingsLocker>();
|
||||
private readonly Dictionary<string, Dictionary<string, Guid>> lockedProperties = new();
|
||||
|
||||
public void Register<T>(Expression<Func<T, object>> propertyExpression, Guid configurationPluginId)
|
||||
{
|
||||
var memberExpression = GetMemberExpression(propertyExpression);
|
||||
var className = typeof(T).Name;
|
||||
var propertyName = memberExpression.Member.Name;
|
||||
|
||||
if (!this.lockedProperties.ContainsKey(className))
|
||||
this.lockedProperties[className] = [];
|
||||
|
||||
this.lockedProperties[className].TryAdd(propertyName, configurationPluginId);
|
||||
}
|
||||
|
||||
public void Remove<T>(Expression<Func<T, object>> propertyExpression)
|
||||
{
|
||||
var memberExpression = GetMemberExpression(propertyExpression);
|
||||
var className = typeof(T).Name;
|
||||
var propertyName = memberExpression.Member.Name;
|
||||
|
||||
if (this.lockedProperties.TryGetValue(className, out var props))
|
||||
{
|
||||
if (props.Remove(propertyName))
|
||||
{
|
||||
// If the property was removed, check if the class has no more locked properties:
|
||||
if (props.Count == 0)
|
||||
this.lockedProperties.Remove(className);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Guid GetConfigurationPluginId<T>(Expression<Func<T, object>> propertyExpression)
|
||||
{
|
||||
var memberExpression = GetMemberExpression(propertyExpression);
|
||||
var className = typeof(T).Name;
|
||||
var propertyName = memberExpression.Member.Name;
|
||||
|
||||
if (this.lockedProperties.TryGetValue(className, out var props) && props.TryGetValue(propertyName, out var configurationPluginId))
|
||||
return configurationPluginId;
|
||||
|
||||
// No configuration plugin ID found for this property:
|
||||
return Guid.Empty;
|
||||
}
|
||||
|
||||
public bool IsLocked<T>(Expression<Func<T, object>> propertyExpression)
|
||||
{
|
||||
var memberExpression = GetMemberExpression(propertyExpression);
|
||||
var className = typeof(T).Name;
|
||||
var propertyName = memberExpression.Member.Name;
|
||||
|
||||
return this.lockedProperties.TryGetValue(className, out var props) && props.ContainsKey(propertyName);
|
||||
}
|
||||
|
||||
private static MemberExpression GetMemberExpression<T>(Expression<Func<T, object>> expression)
|
||||
{
|
||||
switch (expression.Body)
|
||||
{
|
||||
// Case for value types, which are wrapped in UnaryExpression:
|
||||
case UnaryExpression { NodeType: ExpressionType.Convert } unaryExpression:
|
||||
return (MemberExpression)unaryExpression.Operand;
|
||||
|
||||
// Case for reference types, which are directly MemberExpressions:
|
||||
case MemberExpression memberExpression:
|
||||
return memberExpression;
|
||||
|
||||
default:
|
||||
LOGGER.LogError($"Expression '{expression}' is not a valid property expression.");
|
||||
throw new ArgumentException($"Expression '{expression}' is not a valid property expression.", nameof(expression));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text.Json;
|
||||
|
||||
using AIStudio.Provider;
|
||||
@ -269,11 +270,11 @@ public sealed class SettingsManager
|
||||
public ChatTemplate GetPreselectedChatTemplate(Tools.Components component)
|
||||
{
|
||||
var preselection = component.PreselectedChatTemplate(this);
|
||||
if (preselection != default)
|
||||
if (preselection != ChatTemplate.NO_CHAT_TEMPLATE)
|
||||
return preselection;
|
||||
|
||||
preselection = this.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == this.ConfigurationData.App.PreselectedChatTemplate);
|
||||
return preselection != default ? preselection : ChatTemplate.NO_CHAT_TEMPLATE;
|
||||
return preselection ?? ChatTemplate.NO_CHAT_TEMPLATE;
|
||||
}
|
||||
|
||||
public ConfidenceLevel GetConfiguredConfidenceLevel(LLMProviders llmProvider)
|
||||
@ -347,4 +348,21 @@ public sealed class SettingsManager
|
||||
return ConfidenceLevel.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
public static string ToSettingName<TIn, TOut>(Expression<Func<TIn, TOut>> propertyExpression)
|
||||
{
|
||||
MemberExpression? memberExpr;
|
||||
|
||||
// Handle the case where the expression is a unary expression (e.g., when using Convert):
|
||||
if (propertyExpression.Body is UnaryExpression { NodeType: ExpressionType.Convert } unaryExpr)
|
||||
memberExpr = unaryExpr.Operand as MemberExpression;
|
||||
else
|
||||
memberExpr = propertyExpression.Body as MemberExpression;
|
||||
|
||||
if (memberExpr is null)
|
||||
throw new ArgumentException("Expression must be a property access", nameof(propertyExpression));
|
||||
|
||||
// Return the full name of the property, including the class name:
|
||||
return $"{typeof(TIn).Name}.{memberExpr.Member.Name}";
|
||||
}
|
||||
}
|
@ -137,7 +137,7 @@ public static class SettingsMigrations
|
||||
Providers = previousConfig.Providers,
|
||||
NextProviderNum = previousConfig.NextProviderNum,
|
||||
|
||||
App = new()
|
||||
App = new(x => x.App)
|
||||
{
|
||||
EnableSpellchecking = previousConfig.EnableSpellchecking,
|
||||
IsSavingEnergy = previousConfig.IsSavingEnergy,
|
||||
|
@ -133,8 +133,8 @@ public static class ComponentsExtensions
|
||||
|
||||
public static ChatTemplate PreselectedChatTemplate(this Components component, SettingsManager settingsManager) => component switch
|
||||
{
|
||||
Components.CHAT => settingsManager.ConfigurationData.Chat.PreselectOptions ? settingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Chat.PreselectedChatTemplate) : default,
|
||||
Components.CHAT => settingsManager.ConfigurationData.Chat.PreselectOptions ? settingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Chat.PreselectedChatTemplate) ?? ChatTemplate.NO_CHAT_TEMPLATE : ChatTemplate.NO_CHAT_TEMPLATE,
|
||||
|
||||
_ => default,
|
||||
_ => ChatTemplate.NO_CHAT_TEMPLATE,
|
||||
};
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
public sealed class ContentStreamDocumentMetadata : ContentStreamSseMetadata;
|
@ -0,0 +1,4 @@
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
public sealed class ContentStreamImageMetadata: ContentStreamSseMetadata;
|
@ -0,0 +1,32 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
public sealed class ContentStreamMetadataJsonConverter : JsonConverter<ContentStreamSseMetadata>
|
||||
{
|
||||
public override ContentStreamSseMetadata? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
using var jsonDoc = JsonDocument.ParseValue(ref reader);
|
||||
var root = jsonDoc.RootElement;
|
||||
var rawText = root.GetRawText();
|
||||
|
||||
var propertyName = root.EnumerateObject()
|
||||
.Select(p => p.Name)
|
||||
.FirstOrDefault();
|
||||
|
||||
return propertyName switch
|
||||
{
|
||||
"Text" => JsonSerializer.Deserialize<ContentStreamTextMetadata?>(rawText, options),
|
||||
"Pdf" => JsonSerializer.Deserialize<ContentStreamPdfMetadata?>(rawText, options),
|
||||
"Spreadsheet" => JsonSerializer.Deserialize<ContentStreamSpreadsheetMetadata?>(rawText, options),
|
||||
"Presentation" => JsonSerializer.Deserialize<ContentStreamPresentationMetadata?>(rawText, options),
|
||||
"Image" => JsonSerializer.Deserialize<ContentStreamImageMetadata?>(rawText, options),
|
||||
"Document" => JsonSerializer.Deserialize<ContentStreamDocumentMetadata?>(rawText, options),
|
||||
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, ContentStreamSseMetadata value, JsonSerializerOptions options) => JsonSerializer.Serialize(writer, value, value.GetType(), options);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user