mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-20 17:58:00 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7250b270f5 | ||
|
|
d437d08640 | ||
|
|
61f7a3dd72 | ||
|
|
55d7895f58 | ||
|
|
e2859b3d76 | ||
|
|
0a951ead3e | ||
|
|
432ae30fdf | ||
|
|
799112eb9d | ||
|
|
f806ee28c5 | ||
|
|
32ff143b21 | ||
|
|
90a1f681c5 | ||
|
|
99fe9f3a4f | ||
|
|
cf8a9800f3 | ||
|
|
f57e971f80 | ||
|
|
0d104f5abc | ||
|
|
ba6b8d2b3c | ||
|
|
1c72c4d6ec | ||
|
|
4ca5aba58f | ||
|
|
c4d2574508 | ||
|
|
ceac9cefe5 | ||
|
|
ebe2ad62c6 | ||
|
|
9c0dea9927 | ||
|
|
d2a2d658e6 | ||
|
|
d82427ba56 | ||
|
|
386121d198 | ||
|
|
f9245d52c2 | ||
|
|
9fa2a1a135 | ||
|
|
69288699b3 | ||
|
|
826d15729b | ||
|
|
2ae40c59a3 | ||
|
|
119100bbce | ||
|
|
f92fab24b3 | ||
|
|
671abf3b07 | ||
|
|
d0a366258a | ||
|
|
58d7478f3a | ||
|
|
e8216b70a5 | ||
|
|
152d5f9b4f | ||
|
|
f1104c5e09 | ||
|
|
3ccb097705 | ||
|
|
b9416e66c3 | ||
|
|
9f576e8572 | ||
|
|
629613f75a | ||
|
|
abbb8c1650 | ||
|
|
d2ac4e26b0 | ||
|
|
bd97c0fb4b | ||
|
|
8bd54e6d61 | ||
|
|
e419c5561b | ||
|
|
24a73e92e1 | ||
|
|
832e054dbf | ||
|
|
4703ab04ab | ||
|
|
c1d5be63cc | ||
|
|
5d3ea3d57a | ||
|
|
217b9c4db3 | ||
|
|
e9c805f0df | ||
|
|
11475ea4af | ||
|
|
46a7e98141 | ||
|
|
0d6ccbd237 | ||
|
|
efb30cad87 | ||
|
|
fe33867cb6 | ||
|
|
1b1989e8eb | ||
|
|
afa5df894e | ||
|
|
c8d67b1e4c | ||
|
|
e717b84fb4 | ||
|
|
2463c85b77 | ||
|
|
cbf34580b3 | ||
|
|
e60f815d77 | ||
|
|
a19a6b2188 | ||
|
|
d2dd90d089 | ||
|
|
37e113af0e | ||
|
|
776fa8ac58 | ||
|
|
8715054dda | ||
|
|
95da4c6410 | ||
|
|
92f5b49543 | ||
|
|
e6be9a593e | ||
|
|
375be805a1 | ||
|
|
b99866b8fe | ||
|
|
e9998348c5 | ||
|
|
0ad7b8d4dd | ||
|
|
4175387a93 | ||
|
|
1cac1a7913 | ||
|
|
5fa2f3c822 |
No files matched your search
@@ -11,3 +11,7 @@ media/**/*.pptx filter=lfs diff=lfs merge=lfs -text
|
|||||||
media/**/*.png filter=lfs diff=lfs merge=lfs -text
|
media/**/*.png filter=lfs diff=lfs merge=lfs -text
|
||||||
media/**/*.ico filter=lfs diff=lfs merge=lfs -text
|
media/**/*.ico filter=lfs diff=lfs merge=lfs -text
|
||||||
media/**/*.icns filter=lfs diff=lfs merge=lfs -text
|
media/**/*.icns filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.woff2 filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.afpub filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.afdesign filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.afphoto filter=lfs diff=lfs merge=lfs -text
|
||||||
@@ -278,7 +278,7 @@ jobs:
|
|||||||
if: matrix.platform != 'windows-latest'
|
if: matrix.platform != 'windows-latest'
|
||||||
run: |
|
run: |
|
||||||
if ! cargo tauri --version > /dev/null 2>&1; then
|
if ! cargo tauri --version > /dev/null 2>&1; then
|
||||||
cargo install tauri-cli
|
cargo install --version 1.6.2 tauri-cli
|
||||||
else
|
else
|
||||||
echo "Tauri is already installed"
|
echo "Tauri is already installed"
|
||||||
fi
|
fi
|
||||||
@@ -287,7 +287,7 @@ jobs:
|
|||||||
if: matrix.platform == 'windows-latest'
|
if: matrix.platform == 'windows-latest'
|
||||||
run: |
|
run: |
|
||||||
if (-not (cargo tauri --version 2>$null)) {
|
if (-not (cargo tauri --version 2>$null)) {
|
||||||
cargo install tauri-cli
|
cargo install --version 1.6.2 tauri-cli
|
||||||
} else {
|
} else {
|
||||||
Write-Output "Tauri is already installed"
|
Write-Output "Tauri is already installed"
|
||||||
}
|
}
|
||||||
@@ -500,7 +500,7 @@ jobs:
|
|||||||
apt-get install --yes libwebkit2gtk-4.0-dev build-essential libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev
|
apt-get install --yes libwebkit2gtk-4.0-dev build-essential libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev
|
||||||
|
|
||||||
# Setup Tauri:
|
# Setup Tauri:
|
||||||
cargo install tauri-cli
|
cargo install --version 1.6.2 tauri-cli
|
||||||
|
|
||||||
- name: Add the built runner image to the cache
|
- name: Add the built runner image to the cache
|
||||||
if: ${{ steps.linux_arm_cache.outputs.cache-hit != 'true' && env.SKIP != 'true' }}
|
if: ${{ steps.linux_arm_cache.outputs.cache-hit != 'true' && env.SKIP != 'true' }}
|
||||||
|
|||||||
@@ -1,4 +1,50 @@
|
|||||||
# MindWork AI Studio
|
# MindWork AI Studio
|
||||||
|
|
||||||
|
## News
|
||||||
|
Things we are currently working on:
|
||||||
|
|
||||||
|
- Since November 2024: Work on RAG (integration of your data and files) has begun. We will support the integration of local and external data sources. We need to implement the following runtime (Rust) and app (.NET) steps:
|
||||||
|
|
||||||
|
- [x] ~~Runtime: Restructuring the code into meaningful modules (PR [#192](https://github.com/MindWorkAI/AI-Studio/pull/192))~~
|
||||||
|
- [x] ~~Define the [External Data API (EDI)](https://github.com/MindWorkAI/EDI) as a contract for integrating arbitrary external data (PR [#1](https://github.com/MindWorkAI/EDI/pull/1))~~
|
||||||
|
- [x] ~~App: Metadata for providers (which provider offers embeddings?) (PR [#205](https://github.com/MindWorkAI/AI-Studio/pull/205))~~
|
||||||
|
- [x] ~~App: Add an option to show preview features (PR [#222](https://github.com/MindWorkAI/AI-Studio/pull/222))~~
|
||||||
|
- [x] ~~App: Configure embedding providers (PR [#224](https://github.com/MindWorkAI/AI-Studio/pull/224))~~
|
||||||
|
- [ ] App: Management of data sources (local & external data via [EDI](https://github.com/MindWorkAI/EDI))
|
||||||
|
- [ ] Runtime: Extract data from txt / md / pdf / docx / xlsx files
|
||||||
|
- [ ] (*Optional*) Runtime: Implement internal embedding provider through [fastembed-rs](https://github.com/Anush008/fastembed-rs)
|
||||||
|
- [ ] App: Implement external embedding providers
|
||||||
|
- [ ] App: Implement the process to vectorize one local file using embeddings
|
||||||
|
- [ ] Runtime: Integration of the vector database [LanceDB](https://github.com/lancedb/lancedb)
|
||||||
|
- [ ] App: Implement the continuous process of vectorizing data
|
||||||
|
- [ ] App: Define a common retrieval context interface for the integration of RAG processes in chats
|
||||||
|
- [ ] App: Define a common augmentation interface for the integration of RAG processes in chats
|
||||||
|
- [ ] App: Integrate data sources in chats
|
||||||
|
|
||||||
|
|
||||||
|
- Since September 2024: Experiments have been started on how we can work on long texts with AI Studio. Let's say you want to write a fantasy novel or create a complex project proposal and use LLM for support. The initial experiments were promising, but not yet satisfactory. We are testing further approaches until a satisfactory solution is found. The current state of our experiment is available as an experimental preview feature through your app configuration. Related PR: ~~[#167](https://github.com/MindWorkAI/AI-Studio/pull/167), [#226](https://github.com/MindWorkAI/AI-Studio/pull/226)~~.
|
||||||
|
|
||||||
|
|
||||||
|
Other News:
|
||||||
|
|
||||||
|
- October 2024: We've found the first two financial supporters. Huge thanks to `richard-stanton` and `peerschuett` for backing the project. Thanks for having the courage to be the first to support us.
|
||||||
|
|
||||||
|
- October 2024: The [German Aerospace Center (DLR)](https://en.wikipedia.org/wiki/German_Aerospace_Center) ([Website](https://www.dlr.de/en)) will use AI Studio at least within the scope of one project and will also contribute to its further development. This is great news.
|
||||||
|
|
||||||
|
|
||||||
|
Features we have recently released:
|
||||||
|
|
||||||
|
- v0.9.22: Added options for preview features; added embedding provider configuration for RAG (preview) and writer mode (experimental preview).
|
||||||
|
- v0.9.18: Added the new Anthropic Heiku model; added Groq and Google Gemini as provider options.
|
||||||
|
- v0.9.17: Added the new Anthropic model `claude-3-5-sonnet-20241022`.
|
||||||
|
- v0.9.16: Added workspace display options & improved the layout of the app window.
|
||||||
|
- v0.9.15: Added the bias-of-the-day assistant. Tells you about a cognitive bias every day.
|
||||||
|
- v0.9.13: You can use `ollama` providers secured with API keys.
|
||||||
|
- v0.9.12: Added a job posting assistant to the business category and improved grammar & spelling check and rewrite assistants.
|
||||||
|
- v0.9.11: Added enforcement of minimal confidence levels & dark mode.
|
||||||
|
|
||||||
|
## What is AI Studio?
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
@@ -6,7 +52,7 @@ MindWork AI Studio is a desktop application available for macOS, Windows, and Li
|
|||||||
|
|
||||||
**Key advantages:**
|
**Key advantages:**
|
||||||
- **Free of charge**: The app is free to use, both for personal and commercial purposes.
|
- **Free of charge**: The app is free to use, both for personal and commercial purposes.
|
||||||
- **Independence**: You are not tied to any single provider. Instead, you can choose the provider that best suits their needs. Right now, we support OpenAI (GPT4o etc.), Mistral, Anthropic (Claude), and self-hosted models using [llama.cpp](https://github.com/ggerganov/llama.cpp), [ollama](https://github.com/ollama/ollama), [LM Studio](https://lmstudio.ai/), or [Fireworks](https://fireworks.ai/). Support for Google Gemini, and [Replicate](https://replicate.com/) is planned.
|
- **Independence**: You are not tied to any single provider. Instead, you can choose the provider that best suits their needs. Right now, we support OpenAI (GPT4o etc.), Mistral, Anthropic (Claude), Google Gemini, and self-hosted models using [llama.cpp](https://github.com/ggerganov/llama.cpp), [ollama](https://github.com/ollama/ollama), [LM Studio](https://lmstudio.ai/), [Groq](https://groq.com/), or [Fireworks](https://fireworks.ai/).
|
||||||
- **Unrestricted usage**: Unlike services like ChatGPT, which impose limits after intensive use, MindWork AI Studio offers unlimited usage through the providers API.
|
- **Unrestricted usage**: Unlike services like ChatGPT, which impose limits after intensive use, MindWork AI Studio offers unlimited usage through the providers API.
|
||||||
- **Cost-effective**: You only pay for what you use, which can be cheaper than monthly subscription services like ChatGPT Plus, especially if used infrequently. But beware, here be dragons: For extremely intensive usage, the API costs can be significantly higher. Unfortunately, providers currently do not offer a way to display current costs in the app. Therefore, check your account with the respective provider to see how your costs are developing. When available, use prepaid and set a cost limit.
|
- **Cost-effective**: You only pay for what you use, which can be cheaper than monthly subscription services like ChatGPT Plus, especially if used infrequently. But beware, here be dragons: For extremely intensive usage, the API costs can be significantly higher. Unfortunately, providers currently do not offer a way to display current costs in the app. Therefore, check your account with the respective provider to see how your costs are developing. When available, use prepaid and set a cost limit.
|
||||||
- **Privacy**: The data entered into the app is not used for training by the providers since we are using the provider's API.
|
- **Privacy**: The data entered into the app is not used for training by the providers since we are using the provider's API.
|
||||||
@@ -30,11 +76,13 @@ To view all available tiers, please visit our [GitHub Sponsors page](https://git
|
|||||||
Your support, whether big or small, keeps the wheels turning and is deeply appreciated ❤️.
|
Your support, whether big or small, keeps the wheels turning and is deeply appreciated ❤️.
|
||||||
|
|
||||||
## Planned Features
|
## Planned Features
|
||||||
Here's an exciting look at some of the features we're planning to add to MindWork AI Studio in future releases:
|
Here's an exciting look at some of the features we're planning to add to AI Studio in future releases:
|
||||||
- **More providers**: We plan to add support for additional LLM providers, such as Google Gemini, giving you more options to choose from.
|
- **Integrating your data**: You should be able to integrate your data into AI Studio. For example, your PDF or Office files, or your Markdown notes.
|
||||||
- **System prompts**: Integration of a system prompt library will allow you to control the behavior of the LLM with predefined prompts, ensuring consistency and efficiency.
|
- **Integration of enterprise data:** Soon, it will also be possible to integrate data from the corporate network using an interface that we have specified ([External Data API](https://github.com/MindWorkAI/EDI), EDI for short).
|
||||||
- **Text replacement for better privacy**: Define keywords that will be replaced in your chats before sending content to the provider, enhancing your privacy.
|
- **Writing mode:** We want to integrate a writing mode that should support you in creating extensive works. We are thinking of comprehensive project proposals, tenders, or your next fantasy novel.
|
||||||
- **Advanced interactions**: We're full of ideas for advanced interactions tailored for specific use cases, whether in a business context or for writers and other professionals.
|
- **Browser usage:** We're trying to offer the features from AI Studio to you in the browser via a plugin, so we could use spell-checking or rewriting text directly in the browser.
|
||||||
|
- **Voice control:** You should be able to interact with the AI systems using your voice as well. To achieve this, we want to integrate voice input (speech-to-text) and output (text-to-speech). However, later on, it should also have a natural conversation flow, i.e., seamless conversation.
|
||||||
|
- **Email monitoring:** You should have the option to connect your email inboxes with AI Studio. The AI reads your emails and sends you a notification when something important happens. At the same time, you can access knowledge from your emails in your chats.
|
||||||
|
|
||||||
Stay tuned for more updates and enhancements to make MindWork AI Studio even more powerful and versatile 🤩.
|
Stay tuned for more updates and enhancements to make MindWork AI Studio even more powerful and versatile 🤩.
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="AI Studio" type="DotNetProject" factoryName=".NET Project">
|
|
||||||
<option name="EXE_PATH" value="$PROJECT_DIR$/MindWork AI Studio/bin/Debug/net8.0/osx-arm64/mindworkAIStudio.dll" />
|
|
||||||
<option name="PROGRAM_PARAMETERS" value="" />
|
|
||||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/MindWork AI Studio" />
|
|
||||||
<option name="PASS_PARENT_ENVS" value="1" />
|
|
||||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
|
||||||
<option name="USE_MONO" value="0" />
|
|
||||||
<option name="RUNTIME_ARGUMENTS" value="" />
|
|
||||||
<option name="RUNTIME_TYPE" value="coreclr" />
|
|
||||||
<option name="PROJECT_PATH" value="$PROJECT_DIR$/MindWork AI Studio/MindWork AI Studio.csproj" />
|
|
||||||
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
|
||||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
|
||||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
|
||||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
|
||||||
<option name="PROJECT_TFM" value="net8.0" />
|
|
||||||
<method v="2">
|
|
||||||
<option name="Build" />
|
|
||||||
</method>
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Run Dev" type="CompoundRunConfigurationType">
|
|
||||||
<toRun name="AI Studio" type="DotNetProject" />
|
|
||||||
<toRun name="Tauri Dev" type="ShConfigurationType" />
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -3,5 +3,6 @@
|
|||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LLM/@EntryIndexedValue">LLM</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LLM/@EntryIndexedValue">LLM</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LM/@EntryIndexedValue">LM</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LM/@EntryIndexedValue">LM</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MSG/@EntryIndexedValue">MSG</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MSG/@EntryIndexedValue">MSG</s:String>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=groq/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=ollama/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=ollama/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=tauri_0027s/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=tauri_0027s/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
@@ -1 +0,0 @@
|
|||||||
*.woff2 filter=lfs diff=lfs merge=lfs -text
|
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||||
<link href="system/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
|
<link href="system/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
|
||||||
<link href="system/MudBlazor.Markdown/MudBlazor.Markdown.min.css" rel="stylesheet" />
|
<link href="system/MudBlazor.Markdown/MudBlazor.Markdown.min.css" rel="stylesheet" />
|
||||||
|
<link href="system/CodeBeam.MudBlazor.Extensions/MudExtensions.min.css" rel="stylesheet" />
|
||||||
<link href="app.css" rel="stylesheet" />
|
<link href="app.css" rel="stylesheet" />
|
||||||
<HeadOutlet/>
|
<HeadOutlet/>
|
||||||
<script src="diff.js"></script>
|
<script src="diff.js"></script>
|
||||||
@@ -24,6 +25,7 @@
|
|||||||
<script src="boot.js"></script>
|
<script src="boot.js"></script>
|
||||||
<script src="system/MudBlazor/MudBlazor.min.js"></script>
|
<script src="system/MudBlazor/MudBlazor.min.js"></script>
|
||||||
<script src="system/MudBlazor.Markdown/MudBlazor.Markdown.min.js"></script>
|
<script src="system/MudBlazor.Markdown/MudBlazor.Markdown.min.js"></script>
|
||||||
|
<script src="system/CodeBeam.MudBlazor.Extensions/MudExtensions.min.js"></script>
|
||||||
<script src="app.js"></script>
|
<script src="app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace AIStudio.Assistants.Agenda;
|
|||||||
|
|
||||||
public partial class AssistantAgenda : AssistantBaseCore
|
public partial class AssistantAgenda : AssistantBaseCore
|
||||||
{
|
{
|
||||||
protected override Tools.Components Component => Tools.Components.AGENDA_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.AGENDA_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Agenda Planner";
|
protected override string Title => "Agenda Planner";
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,9 @@
|
|||||||
|
|
||||||
@if (this.Body is not null)
|
@if (this.Body is not null)
|
||||||
{
|
{
|
||||||
@this.Body
|
<CascadingValue Value="@this">
|
||||||
|
@this.Body
|
||||||
|
</CascadingValue>
|
||||||
|
|
||||||
<MudButton Disabled="@this.SubmitDisabled" Variant="Variant.Filled" Class="mb-3" OnClick="() => this.SubmitAction()" Style="@this.SubmitButtonStyle">
|
<MudButton Disabled="@this.SubmitDisabled" Variant="Variant.Filled" Class="mb-3" OnClick="() => this.SubmitAction()" Style="@this.SubmitButtonStyle">
|
||||||
@this.SubmitText
|
@this.SubmitText
|
||||||
@@ -30,6 +32,9 @@
|
|||||||
<div id="@RESULT_DIV_ID" class="mr-2 mt-3">
|
<div id="@RESULT_DIV_ID" class="mr-2 mt-3">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="@BEFORE_RESULT_DIV_ID" class="mt-3">
|
||||||
|
</div>
|
||||||
|
|
||||||
@if (this.ShowResult && this.resultingContentBlock is not null)
|
@if (this.ShowResult && this.resultingContentBlock is not null)
|
||||||
{
|
{
|
||||||
<ContentBlockComponent Role="@(this.resultingContentBlock.Role)" Type="@(this.resultingContentBlock.ContentType)" Time="@(this.resultingContentBlock.Time)" Content="@(this.resultingContentBlock.Content)"/>
|
<ContentBlockComponent Role="@(this.resultingContentBlock.Role)" Type="@(this.resultingContentBlock.ContentType)" Time="@(this.resultingContentBlock.Time)" Content="@(this.resultingContentBlock.Content)"/>
|
||||||
@@ -43,17 +48,17 @@
|
|||||||
|
|
||||||
@if (!this.FooterButtons.Any(x => x.Type is ButtonTypes.SEND_TO))
|
@if (!this.FooterButtons.Any(x => x.Type is ButtonTypes.SEND_TO))
|
||||||
{
|
{
|
||||||
<MudMenu StartIcon="@Icons.Material.Filled.Apps" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="Send to ..." Variant="Variant.Filled" Color="Color.Info">
|
@if (this.ShowSendTo)
|
||||||
@foreach (var assistant in Enum.GetValues<Components>().OrderBy(n => n.Name().Length))
|
{
|
||||||
{
|
<MudMenu StartIcon="@Icons.Material.Filled.Apps" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="Send to ..." Variant="Variant.Filled" Style="@this.GetSendToColor()" Class="rounded">
|
||||||
if (assistant is Components.NONE || this.Component == assistant)
|
@foreach (var assistant in Enum.GetValues<Components>().Where(n => n.AllowSendTo()).OrderBy(n => n.Name().Length))
|
||||||
continue;
|
{
|
||||||
|
<MudMenuItem OnClick="() => this.SendToAssistant(assistant, new())">
|
||||||
<MudMenuItem OnClick="() => this.SendToAssistant(assistant, new())">
|
@assistant.Name()
|
||||||
@assistant.Name()
|
</MudMenuItem>
|
||||||
</MudMenuItem>
|
}
|
||||||
}
|
</MudMenu>
|
||||||
</MudMenu>
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@foreach (var button in this.FooterButtons)
|
@foreach (var button in this.FooterButtons)
|
||||||
@@ -75,12 +80,9 @@
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SendToButton sendToButton:
|
case SendToButton sendToButton:
|
||||||
<MudMenu StartIcon="@Icons.Material.Filled.Apps" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="Send to ..." Variant="Variant.Filled" Color="Color.Info">
|
<MudMenu StartIcon="@Icons.Material.Filled.Apps" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="Send to ..." Variant="Variant.Filled" Style="@this.GetSendToColor()" Class="rounded">
|
||||||
@foreach (var assistant in Enum.GetValues<Components>().OrderBy(n => n.Name().Length))
|
@foreach (var assistant in Enum.GetValues<Components>().Where(n => n.AllowSendTo()).OrderBy(n => n.Name().Length))
|
||||||
{
|
{
|
||||||
if(assistant is Components.NONE || sendToButton.Self == assistant)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
<MudMenuItem OnClick="() => this.SendToAssistant(assistant, sendToButton)">
|
<MudMenuItem OnClick="() => this.SendToAssistant(assistant, sendToButton)">
|
||||||
@assistant.Name()
|
@assistant.Name()
|
||||||
</MudMenuItem>
|
</MudMenuItem>
|
||||||
@@ -90,17 +92,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.ContentCopy" OnClick="() => this.CopyToClipboard()">
|
@if (this.ShowCopyResult)
|
||||||
Copy result
|
{
|
||||||
</MudButton>
|
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.ContentCopy" OnClick="() => this.CopyToClipboard()">
|
||||||
|
Copy result
|
||||||
|
</MudButton>
|
||||||
|
}
|
||||||
|
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Warning" StartIcon="@Icons.Material.Filled.Refresh" OnClick="() => this.InnerResetForm()">
|
@if (this.ShowReset)
|
||||||
Reset
|
{
|
||||||
</MudButton>
|
<MudButton Variant="Variant.Filled" Style="@this.GetResetColor()" StartIcon="@Icons.Material.Filled.Refresh" OnClick="() => this.InnerResetForm()">
|
||||||
|
Reset
|
||||||
|
</MudButton>
|
||||||
|
}
|
||||||
|
|
||||||
@if (this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence)
|
@if (this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence)
|
||||||
{
|
{
|
||||||
<ConfidenceInfo Mode="ConfidenceInfoMode.BUTTON" Provider="@this.providerSettings.UsedProvider"/>
|
<ConfidenceInfo Mode="ConfidenceInfoMode.BUTTON" LLMProvider="@this.providerSettings.UsedLLMProvider"/>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (this.AllowProfiles && this.ShowProfileSelection)
|
@if (this.AllowProfiles && this.ShowProfileSelection)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using RustService = AIStudio.Tools.RustService;
|
|||||||
|
|
||||||
namespace AIStudio.Assistants;
|
namespace AIStudio.Assistants;
|
||||||
|
|
||||||
public abstract partial class AssistantBase : ComponentBase
|
public abstract partial class AssistantBase : ComponentBase, IMessageBusReceiver, IDisposable
|
||||||
{
|
{
|
||||||
[Inject]
|
[Inject]
|
||||||
protected SettingsManager SettingsManager { get; init; } = null!;
|
protected SettingsManager SettingsManager { get; init; } = null!;
|
||||||
@@ -31,8 +31,15 @@ public abstract partial class AssistantBase : ComponentBase
|
|||||||
[Inject]
|
[Inject]
|
||||||
protected ILogger<AssistantBase> Logger { get; init; } = null!;
|
protected ILogger<AssistantBase> Logger { get; init; } = null!;
|
||||||
|
|
||||||
internal const string AFTER_RESULT_DIV_ID = "afterAssistantResult";
|
[Inject]
|
||||||
|
private MudTheme ColorTheme { get; init; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private MessageBus MessageBus { get; init; } = null!;
|
||||||
|
|
||||||
internal const string RESULT_DIV_ID = "assistantResult";
|
internal const string RESULT_DIV_ID = "assistantResult";
|
||||||
|
internal const string BEFORE_RESULT_DIV_ID = "beforeAssistantResult";
|
||||||
|
internal const string AFTER_RESULT_DIV_ID = "afterAssistantResult";
|
||||||
|
|
||||||
protected abstract string Title { get; }
|
protected abstract string Title { get; }
|
||||||
|
|
||||||
@@ -40,7 +47,7 @@ public abstract partial class AssistantBase : ComponentBase
|
|||||||
|
|
||||||
protected abstract string SystemPrompt { get; }
|
protected abstract string SystemPrompt { get; }
|
||||||
|
|
||||||
protected abstract Tools.Components Component { get; }
|
public abstract Tools.Components Component { get; }
|
||||||
|
|
||||||
protected virtual Func<string> Result2Copy => () => this.resultingContentBlock is null ? string.Empty : this.resultingContentBlock.Content switch
|
protected virtual Func<string> Result2Copy => () => this.resultingContentBlock is null ? string.Empty : this.resultingContentBlock.Content switch
|
||||||
{
|
{
|
||||||
@@ -68,6 +75,12 @@ public abstract partial class AssistantBase : ComponentBase
|
|||||||
|
|
||||||
protected virtual bool ShowDedicatedProgress => false;
|
protected virtual bool ShowDedicatedProgress => false;
|
||||||
|
|
||||||
|
protected virtual bool ShowSendTo => true;
|
||||||
|
|
||||||
|
protected virtual bool ShowCopyResult => true;
|
||||||
|
|
||||||
|
protected virtual bool ShowReset => true;
|
||||||
|
|
||||||
protected virtual ChatThread ConvertToChatThread => this.chatThread ?? new();
|
protected virtual ChatThread ConvertToChatThread => this.chatThread ?? new();
|
||||||
|
|
||||||
protected virtual IReadOnlyList<IButtonData> FooterButtons => [];
|
protected virtual IReadOnlyList<IButtonData> FooterButtons => [];
|
||||||
@@ -91,6 +104,10 @@ public abstract partial class AssistantBase : ComponentBase
|
|||||||
this.MightPreselectValues();
|
this.MightPreselectValues();
|
||||||
this.providerSettings = this.SettingsManager.GetPreselectedProvider(this.Component);
|
this.providerSettings = this.SettingsManager.GetPreselectedProvider(this.Component);
|
||||||
this.currentProfile = this.SettingsManager.GetPreselectedProfile(this.Component);
|
this.currentProfile = this.SettingsManager.GetPreselectedProfile(this.Component);
|
||||||
|
|
||||||
|
this.MessageBus.RegisterComponent(this);
|
||||||
|
this.MessageBus.ApplyFilters(this, [], [ Event.COLOR_THEME_CHANGED ]);
|
||||||
|
|
||||||
await base.OnInitializedAsync();
|
await base.OnInitializedAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,11 +131,32 @@ public abstract partial class AssistantBase : ComponentBase
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private string SubmitButtonStyle => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence ? this.providerSettings.UsedProvider.GetConfidence(this.SettingsManager).StyleBorder() : string.Empty;
|
#region Implementation of IMessageBusReceiver
|
||||||
|
|
||||||
|
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
||||||
|
{
|
||||||
|
switch (triggeredEvent)
|
||||||
|
{
|
||||||
|
case Event.COLOR_THEME_CHANGED:
|
||||||
|
this.StateHasChanged();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
|
||||||
|
{
|
||||||
|
return Task.FromResult<TResult?>(default);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private string SubmitButtonStyle => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence ? this.providerSettings.UsedLLMProvider.GetConfidence(this.SettingsManager).StyleBorder(this.SettingsManager) : string.Empty;
|
||||||
|
|
||||||
protected string? ValidatingProvider(AIStudio.Settings.Provider provider)
|
protected string? ValidatingProvider(AIStudio.Settings.Provider provider)
|
||||||
{
|
{
|
||||||
if(provider.UsedProvider == Providers.NONE)
|
if(provider.UsedLLMProvider == LLMProviders.NONE)
|
||||||
return "Please select a provider.";
|
return "Please select a provider.";
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -128,6 +166,7 @@ public abstract partial class AssistantBase : ComponentBase
|
|||||||
{
|
{
|
||||||
this.chatThread = new()
|
this.chatThread = new()
|
||||||
{
|
{
|
||||||
|
SelectedProvider = this.providerSettings.Id,
|
||||||
WorkspaceId = Guid.Empty,
|
WorkspaceId = Guid.Empty,
|
||||||
ChatId = Guid.NewGuid(),
|
ChatId = Guid.NewGuid(),
|
||||||
Name = string.Empty,
|
Name = string.Empty,
|
||||||
@@ -142,13 +181,36 @@ public abstract partial class AssistantBase : ComponentBase
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DateTimeOffset AddUserRequest(string request)
|
protected Guid CreateChatThread(Guid workspaceId, string name)
|
||||||
|
{
|
||||||
|
var chatId = Guid.NewGuid();
|
||||||
|
this.chatThread = new()
|
||||||
|
{
|
||||||
|
SelectedProvider = this.providerSettings.Id,
|
||||||
|
WorkspaceId = workspaceId,
|
||||||
|
ChatId = chatId,
|
||||||
|
Name = name,
|
||||||
|
Seed = this.RNG.Next(),
|
||||||
|
SystemPrompt = !this.AllowProfiles ? this.SystemPrompt :
|
||||||
|
$"""
|
||||||
|
{this.SystemPrompt}
|
||||||
|
|
||||||
|
{this.currentProfile.ToSystemPrompt()}
|
||||||
|
""",
|
||||||
|
Blocks = [],
|
||||||
|
};
|
||||||
|
|
||||||
|
return chatId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DateTimeOffset AddUserRequest(string request, bool hideContentFromUser = false)
|
||||||
{
|
{
|
||||||
var time = DateTimeOffset.Now;
|
var time = DateTimeOffset.Now;
|
||||||
this.chatThread!.Blocks.Add(new ContentBlock
|
this.chatThread!.Blocks.Add(new ContentBlock
|
||||||
{
|
{
|
||||||
Time = time,
|
Time = time,
|
||||||
ContentType = ContentType.TEXT,
|
ContentType = ContentType.TEXT,
|
||||||
|
HideFromUser = hideContentFromUser,
|
||||||
Role = ChatRole.USER,
|
Role = ChatRole.USER,
|
||||||
Content = new ContentText
|
Content = new ContentText
|
||||||
{
|
{
|
||||||
@@ -176,7 +238,12 @@ public abstract partial class AssistantBase : ComponentBase
|
|||||||
Content = aiText,
|
Content = aiText,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.chatThread?.Blocks.Add(this.resultingContentBlock);
|
if (this.chatThread is not null)
|
||||||
|
{
|
||||||
|
this.chatThread.Blocks.Add(this.resultingContentBlock);
|
||||||
|
this.chatThread.SelectedProvider = this.providerSettings.Id;
|
||||||
|
}
|
||||||
|
|
||||||
this.isProcessing = true;
|
this.isProcessing = true;
|
||||||
this.StateHasChanged();
|
this.StateHasChanged();
|
||||||
|
|
||||||
@@ -205,9 +272,12 @@ public abstract partial class AssistantBase : ComponentBase
|
|||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task SendToAssistant(Tools.Components destination, SendToButton sendToButton)
|
protected Task SendToAssistant(Tools.Components destination, SendToButton sendToButton)
|
||||||
{
|
{
|
||||||
var contentToSend = sendToButton.UseResultingContentBlockData switch
|
if (!destination.AllowSendTo())
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
var contentToSend = sendToButton == default ? string.Empty : sendToButton.UseResultingContentBlockData switch
|
||||||
{
|
{
|
||||||
false => sendToButton.GetText(),
|
false => sendToButton.GetText(),
|
||||||
true => this.resultingContentBlock?.Content switch
|
true => this.resultingContentBlock?.Content switch
|
||||||
@@ -221,7 +291,9 @@ public abstract partial class AssistantBase : ComponentBase
|
|||||||
switch (destination)
|
switch (destination)
|
||||||
{
|
{
|
||||||
case Tools.Components.CHAT:
|
case Tools.Components.CHAT:
|
||||||
MessageBus.INSTANCE.DeferMessage(this, sendToData.Event, this.ConvertToChatThread);
|
var convertedChatThread = this.ConvertToChatThread;
|
||||||
|
convertedChatThread = convertedChatThread with { SelectedProvider = this.providerSettings.Id };
|
||||||
|
MessageBus.INSTANCE.DeferMessage(this, sendToData.Event, convertedChatThread);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -251,4 +323,25 @@ public abstract partial class AssistantBase : ComponentBase
|
|||||||
this.StateHasChanged();
|
this.StateHasChanged();
|
||||||
this.form?.ResetValidation();
|
this.form?.ResetValidation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetResetColor() => this.SettingsManager.IsDarkMode switch
|
||||||
|
{
|
||||||
|
true => $"background-color: #804000",
|
||||||
|
false => $"background-color: {this.ColorTheme.GetCurrentPalette(this.SettingsManager).Warning.Value}",
|
||||||
|
};
|
||||||
|
|
||||||
|
private string GetSendToColor() => this.SettingsManager.IsDarkMode switch
|
||||||
|
{
|
||||||
|
true => $"background-color: #004080",
|
||||||
|
false => $"background-color: {this.ColorTheme.GetCurrentPalette(this.SettingsManager).InfoLighten}",
|
||||||
|
};
|
||||||
|
|
||||||
|
#region Implementation of IDisposable
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
this.MessageBus.Unregister(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
@attribute [Route(Routes.ASSISTANT_BIAS)]
|
||||||
|
@inherits AssistantBaseCore
|
||||||
|
|
||||||
|
<MudText Typo="Typo.body1">
|
||||||
|
<b>Links:</b>
|
||||||
|
</MudText>
|
||||||
|
<MudList T="string" Class="mb-6">
|
||||||
|
<MudListItem T="string" Icon="@Icons.Material.Filled.Link" Target="_blank" Href="https://en.wikipedia.org/wiki/List_of_cognitive_biases">Wikipedia list of cognitive biases</MudListItem>
|
||||||
|
<MudListItem T="string" Icon="@Icons.Material.Filled.Link" Target="_blank" Href="https://commons.wikimedia.org/wiki/File:Cognitive_Bias_Codex_With_Definitions_1-2,_an_Extension_of_the_work_of_John_Manoogian_by_Brian_Rene_Morrissette.png">Extended bias poster</MudListItem>
|
||||||
|
<MudListItem T="string" Icon="@Icons.Material.Filled.Link" Target="_blank" Href="https://betterhumans.pub/cognitive-bias-cheat-sheet-55a472476b18">Blog post of Buster Benson: "Cognitive bias cheat sheet"</MudListItem>
|
||||||
|
</MudList>
|
||||||
|
|
||||||
|
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelecting())" @bind-Value="@this.selectedTargetLanguage" ValidateSelection="@this.ValidateTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Target language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom target language" />
|
||||||
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using AIStudio.Components;
|
||||||
|
using AIStudio.Settings.DataModel;
|
||||||
|
|
||||||
|
namespace AIStudio.Assistants.BiasDay;
|
||||||
|
|
||||||
|
public partial class BiasOfTheDayAssistant : AssistantBaseCore
|
||||||
|
{
|
||||||
|
public override Tools.Components Component => Tools.Components.BIAS_DAY_ASSISTANT;
|
||||||
|
|
||||||
|
protected override string Title => "Bias of the Day";
|
||||||
|
|
||||||
|
protected override string Description =>
|
||||||
|
"""
|
||||||
|
Learn about a different cognitive bias every day. You can also ask the LLM your questions. The idea behind
|
||||||
|
"Bias of the Day" is based on work by Buster Benson, John Manoogian III, and Brian Rene Morrissette. Buster
|
||||||
|
Benson grouped the biases, and the original texts come from Wikipedia. Brian Rene Morrissette condensed them
|
||||||
|
into a shorter version. Finally, John Manoogian III created the original poster based on Benson's work and
|
||||||
|
Morrissette's texts. Thorsten Sommer compared all texts for integration into AI Studio with the current Wikipedia
|
||||||
|
versions, updated them, and added source references. The idea of learning about one bias each day based on John's
|
||||||
|
poster comes from Drew Nelson.
|
||||||
|
""";
|
||||||
|
|
||||||
|
protected override string SystemPrompt => $"""
|
||||||
|
You are a friendly, helpful expert on cognitive bias. You studied psychology and
|
||||||
|
have a lot of experience. You explain a bias every day. Today's bias belongs to
|
||||||
|
the category: "{this.biasOfTheDay.Category.ToName()}". We have the following
|
||||||
|
thoughts on this category:
|
||||||
|
|
||||||
|
{this.biasOfTheDay.Category.GetThoughts()}
|
||||||
|
|
||||||
|
Today's bias is:
|
||||||
|
{this.biasOfTheDay.Description}
|
||||||
|
{this.SystemPromptSources()}
|
||||||
|
Important: you use the following language: {this.SystemPromptLanguage()}. Please
|
||||||
|
ask the user a personal question at the end to encourage them to think about
|
||||||
|
this bias.
|
||||||
|
""";
|
||||||
|
|
||||||
|
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||||
|
|
||||||
|
protected override string SubmitText => "Show me the bias of the day";
|
||||||
|
|
||||||
|
protected override Func<Task> SubmitAction => this.TellBias;
|
||||||
|
|
||||||
|
protected override bool ShowSendTo => false;
|
||||||
|
|
||||||
|
protected override bool ShowCopyResult => false;
|
||||||
|
|
||||||
|
protected override bool ShowReset => false;
|
||||||
|
|
||||||
|
protected override void ResetFrom()
|
||||||
|
{
|
||||||
|
if (!this.MightPreselectValues())
|
||||||
|
{
|
||||||
|
this.selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||||
|
this.customTargetLanguage = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool MightPreselectValues()
|
||||||
|
{
|
||||||
|
if (this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)
|
||||||
|
{
|
||||||
|
this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedTargetLanguage;
|
||||||
|
this.customTargetLanguage = this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedOtherLanguage;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bias biasOfTheDay = BiasCatalog.NONE;
|
||||||
|
private CommonLanguages selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||||
|
private string customTargetLanguage = string.Empty;
|
||||||
|
|
||||||
|
private string? ValidateTargetLanguage(CommonLanguages language)
|
||||||
|
{
|
||||||
|
if(language is CommonLanguages.AS_IS)
|
||||||
|
return "Please select a target language for the bias.";
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? ValidateCustomLanguage(string language)
|
||||||
|
{
|
||||||
|
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||||
|
return "Please provide a custom language.";
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string SystemPromptSources()
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
if (this.biasOfTheDay.Links.Count > 0)
|
||||||
|
{
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("Please share the following sources with the user as a Markdown list:");
|
||||||
|
foreach (var link in this.biasOfTheDay.Links)
|
||||||
|
sb.AppendLine($"- {link}");
|
||||||
|
|
||||||
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string SystemPromptLanguage()
|
||||||
|
{
|
||||||
|
if(this.selectedTargetLanguage is CommonLanguages.OTHER)
|
||||||
|
return this.customTargetLanguage;
|
||||||
|
|
||||||
|
return this.selectedTargetLanguage.Name();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task TellBias()
|
||||||
|
{
|
||||||
|
bool useDrawnBias = false;
|
||||||
|
if(this.SettingsManager.ConfigurationData.BiasOfTheDay.RestrictOneBiasPerDay)
|
||||||
|
{
|
||||||
|
if(this.SettingsManager.ConfigurationData.BiasOfTheDay.DateLastBiasDrawn == DateOnly.FromDateTime(DateTime.Now))
|
||||||
|
{
|
||||||
|
var biasChat = new LoadChat
|
||||||
|
{
|
||||||
|
WorkspaceId = Workspaces.WORKSPACE_ID_BIAS,
|
||||||
|
ChatId = this.SettingsManager.ConfigurationData.BiasOfTheDay.BiasOfTheDayChatId,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (WorkspaceBehaviour.IsChatExisting(biasChat))
|
||||||
|
{
|
||||||
|
MessageBus.INSTANCE.DeferMessage(this, Event.LOAD_CHAT, biasChat);
|
||||||
|
this.NavigationManager.NavigateTo(Routes.CHAT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
useDrawnBias = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.form!.Validate();
|
||||||
|
if (!this.inputIsValid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.biasOfTheDay = useDrawnBias ?
|
||||||
|
BiasCatalog.ALL_BIAS[this.SettingsManager.ConfigurationData.BiasOfTheDay.BiasOfTheDayId] :
|
||||||
|
BiasCatalog.GetRandomBias(this.SettingsManager.ConfigurationData.BiasOfTheDay.UsedBias);
|
||||||
|
|
||||||
|
var chatId = this.CreateChatThread(Workspaces.WORKSPACE_ID_BIAS, this.biasOfTheDay.Name);
|
||||||
|
this.SettingsManager.ConfigurationData.BiasOfTheDay.BiasOfTheDayId = this.biasOfTheDay.Id;
|
||||||
|
this.SettingsManager.ConfigurationData.BiasOfTheDay.BiasOfTheDayChatId = chatId;
|
||||||
|
this.SettingsManager.ConfigurationData.BiasOfTheDay.DateLastBiasDrawn = DateOnly.FromDateTime(DateTime.Now);
|
||||||
|
await this.SettingsManager.StoreSettings();
|
||||||
|
var time = this.AddUserRequest(
|
||||||
|
"""
|
||||||
|
Please tell me about the bias of the day.
|
||||||
|
""", true);
|
||||||
|
|
||||||
|
// Start the AI response without waiting for it to finish:
|
||||||
|
_ = this.AddAIResponseAsync(time);
|
||||||
|
await this.SendToAssistant(Tools.Components.CHAT, default);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ namespace AIStudio.Assistants.Coding;
|
|||||||
|
|
||||||
public partial class AssistantCoding : AssistantBaseCore
|
public partial class AssistantCoding : AssistantBaseCore
|
||||||
{
|
{
|
||||||
protected override Tools.Components Component => Tools.Components.CODING_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.CODING_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Coding Assistant";
|
protected override string Title => "Coding Assistant";
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace AIStudio.Assistants.EMail;
|
|||||||
|
|
||||||
public partial class AssistantEMail : AssistantBaseCore
|
public partial class AssistantEMail : AssistantBaseCore
|
||||||
{
|
{
|
||||||
protected override Tools.Components Component => Tools.Components.EMAIL_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.EMAIL_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "E-Mail";
|
protected override string Title => "E-Mail";
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace AIStudio.Assistants.GrammarSpelling;
|
|||||||
|
|
||||||
public partial class AssistantGrammarSpelling : AssistantBaseCore
|
public partial class AssistantGrammarSpelling : AssistantBaseCore
|
||||||
{
|
{
|
||||||
protected override Tools.Components Component => Tools.Components.GRAMMAR_SPELLING_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.GRAMMAR_SPELLING_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Grammar & Spelling Checker";
|
protected override string Title => "Grammar & Spelling Checker";
|
||||||
|
|
||||||
@@ -25,8 +25,6 @@ public partial class AssistantGrammarSpelling : AssistantBaseCore
|
|||||||
|
|
||||||
protected override bool AllowProfiles => false;
|
protected override bool AllowProfiles => false;
|
||||||
|
|
||||||
protected override bool ShowResult => false;
|
|
||||||
|
|
||||||
protected override bool ShowDedicatedProgress => true;
|
protected override bool ShowDedicatedProgress => true;
|
||||||
|
|
||||||
protected override Func<string> Result2Copy => () => this.correctedText;
|
protected override Func<string> Result2Copy => () => this.correctedText;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ namespace AIStudio.Assistants.IconFinder;
|
|||||||
|
|
||||||
public partial class AssistantIconFinder : AssistantBaseCore
|
public partial class AssistantIconFinder : AssistantBaseCore
|
||||||
{
|
{
|
||||||
protected override Tools.Components Component => Tools.Components.ICON_FINDER_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.ICON_FINDER_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Icon Finder";
|
protected override string Title => "Icon Finder";
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
@attribute [Route(Routes.ASSISTANT_JOB_POSTING)]
|
||||||
|
@inherits AssistantBaseCore
|
||||||
|
|
||||||
|
<MudTextField T="string" @bind-Text="@this.inputCompanyName" Label="(Optional) The company name" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Warehouse" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||||
|
<MudTextField T="string" @bind-Text="@this.inputCountryLegalFramework" Label="Provide the country, where the company is located" Validation="@this.ValidateCountryLegalFramework" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Flag" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3" HelperText="This is important to consider the legal framework of the country."/>
|
||||||
|
<MudTextField T="string" @bind-Text="@this.inputMandatoryInformation" Label="(Optional) Provide mandatory information" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.TextSnippet" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Mandatory information that your company requires for all job postings. This can include the company description, etc." />
|
||||||
|
<MudTextField T="string" @bind-Text="@this.inputJobDescription" Label="Job description" Validation="@this.ValidateJobDescription" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Settings" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Describe what the person is supposed to do in the company. This might be just short bullet points." />
|
||||||
|
<MudTextField T="string" @bind-Text="@this.inputQualifications" Label="(Optional) Provide necessary job qualifications" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Settings" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Describe what the person should bring to the table. This might be just short bullet points." />
|
||||||
|
<MudTextField T="string" @bind-Text="@this.inputResponsibilities" Label="(Optional) Provide job responsibilities" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Settings" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Describe the responsibilities the person should take on in the company." />
|
||||||
|
<MudTextField T="string" @bind-Text="@this.inputWorkLocation" Label="(Optional) Provide the work location" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.MyLocation" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||||
|
<MudTextField T="string" @bind-Text="@this.inputEntryDate" Label="(Optional) Provide the entry date" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.DateRange" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||||
|
<MudTextField T="string" @bind-Text="@this.inputValidUntil" Label="(Optional) Provide the date until the job posting is valid" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.DateRange" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="mb-3"/>
|
||||||
|
|
||||||
|
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Target language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom target language" />
|
||||||
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||||
@@ -0,0 +1,290 @@
|
|||||||
|
using AIStudio.Chat;
|
||||||
|
|
||||||
|
namespace AIStudio.Assistants.JobPosting;
|
||||||
|
|
||||||
|
public partial class AssistantJobPostings : AssistantBaseCore
|
||||||
|
{
|
||||||
|
public override Tools.Components Component => Tools.Components.JOB_POSTING_ASSISTANT;
|
||||||
|
|
||||||
|
protected override string Title => "Job Posting";
|
||||||
|
|
||||||
|
protected override string Description =>
|
||||||
|
"""
|
||||||
|
Provide some key points about the job you want to post. The AI will then
|
||||||
|
formulate a suggestion that you can finalize.
|
||||||
|
""";
|
||||||
|
|
||||||
|
protected override string SystemPrompt =>
|
||||||
|
$"""
|
||||||
|
You are an experienced specialist in personnel matters. You write job postings.
|
||||||
|
You follow the usual guidelines in the country of the posting. You ensure that
|
||||||
|
no individuals are discriminated against or excluded by the job posting. You do
|
||||||
|
not ask any questions and you do not repeat the task. You provide your response
|
||||||
|
in a Markdown format. Missing information necessary for the job posting, you try
|
||||||
|
to derive from the other details provided.
|
||||||
|
|
||||||
|
Structure of your response:
|
||||||
|
---
|
||||||
|
# <TITLE>
|
||||||
|
<Description of the job and the company>
|
||||||
|
|
||||||
|
# <TASKS>
|
||||||
|
<Describe what takes the job entails>
|
||||||
|
|
||||||
|
# <QUALIFICATIONS>
|
||||||
|
<What qualifications are required>
|
||||||
|
|
||||||
|
# <RESPONSIBILITIES>
|
||||||
|
<What responsibilities are associated with the job>
|
||||||
|
|
||||||
|
<When available, closing details such as the entry date etc.>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
You write the job posting in the following language: {this.SystemPromptLanguage()}.
|
||||||
|
""";
|
||||||
|
|
||||||
|
protected override IReadOnlyList<IButtonData> FooterButtons => [];
|
||||||
|
|
||||||
|
protected override string SubmitText => "Create the job posting";
|
||||||
|
|
||||||
|
protected override Func<Task> SubmitAction => this.CreateJobPosting;
|
||||||
|
|
||||||
|
protected override bool SubmitDisabled => false;
|
||||||
|
|
||||||
|
protected override bool AllowProfiles => false;
|
||||||
|
|
||||||
|
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||||
|
{
|
||||||
|
SystemPrompt = SystemPrompts.DEFAULT,
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override void ResetFrom()
|
||||||
|
{
|
||||||
|
this.inputEntryDate = string.Empty;
|
||||||
|
this.inputValidUntil = string.Empty;
|
||||||
|
if (!this.MightPreselectValues())
|
||||||
|
{
|
||||||
|
this.selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||||
|
this.customTargetLanguage = string.Empty;
|
||||||
|
this.inputMandatoryInformation = string.Empty;
|
||||||
|
this.inputJobDescription = string.Empty;
|
||||||
|
this.inputQualifications = string.Empty;
|
||||||
|
this.inputResponsibilities = string.Empty;
|
||||||
|
this.inputCompanyName = string.Empty;
|
||||||
|
this.inputWorkLocation = string.Empty;
|
||||||
|
this.inputCountryLegalFramework = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool MightPreselectValues()
|
||||||
|
{
|
||||||
|
if (this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)
|
||||||
|
{
|
||||||
|
this.inputMandatoryInformation = this.SettingsManager.ConfigurationData.JobPostings.PreselectedMandatoryInformation;
|
||||||
|
if(string.IsNullOrWhiteSpace(this.inputJobDescription))
|
||||||
|
this.inputJobDescription = this.SettingsManager.ConfigurationData.JobPostings.PreselectedJobDescription;
|
||||||
|
this.inputQualifications = this.SettingsManager.ConfigurationData.JobPostings.PreselectedQualifications;
|
||||||
|
this.inputResponsibilities = this.SettingsManager.ConfigurationData.JobPostings.PreselectedResponsibilities;
|
||||||
|
this.inputCompanyName = this.SettingsManager.ConfigurationData.JobPostings.PreselectedCompanyName;
|
||||||
|
this.inputWorkLocation = this.SettingsManager.ConfigurationData.JobPostings.PreselectedWorkLocation;
|
||||||
|
this.inputCountryLegalFramework = this.SettingsManager.ConfigurationData.JobPostings.PreselectedCountryLegalFramework;
|
||||||
|
this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.JobPostings.PreselectedTargetLanguage;
|
||||||
|
this.customTargetLanguage = this.SettingsManager.ConfigurationData.JobPostings.PreselectOtherLanguage;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string inputMandatoryInformation = string.Empty;
|
||||||
|
private string inputJobDescription = string.Empty;
|
||||||
|
private string inputQualifications = string.Empty;
|
||||||
|
private string inputResponsibilities = string.Empty;
|
||||||
|
private string inputCompanyName = string.Empty;
|
||||||
|
private string inputEntryDate = string.Empty;
|
||||||
|
private string inputValidUntil = string.Empty;
|
||||||
|
private string inputWorkLocation = string.Empty;
|
||||||
|
private string inputCountryLegalFramework = string.Empty;
|
||||||
|
private CommonLanguages selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||||
|
private string customTargetLanguage = string.Empty;
|
||||||
|
|
||||||
|
#region Overrides of ComponentBase
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages<string>(Event.SEND_TO_JOB_POSTING_ASSISTANT).FirstOrDefault();
|
||||||
|
if (deferredContent is not null)
|
||||||
|
this.inputJobDescription = deferredContent;
|
||||||
|
|
||||||
|
await base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private string? ValidateCustomLanguage(string language)
|
||||||
|
{
|
||||||
|
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
|
||||||
|
return "Please provide a custom target language.";
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? ValidateJobDescription(string jobDescription)
|
||||||
|
{
|
||||||
|
if(string.IsNullOrWhiteSpace(jobDescription))
|
||||||
|
return "Please provide a job description.";
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? ValidateCountryLegalFramework(string countryLegalFramework)
|
||||||
|
{
|
||||||
|
if(string.IsNullOrWhiteSpace(countryLegalFramework))
|
||||||
|
return "Please provide the country where the job is posted (legal framework).";
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string SystemPromptLanguage()
|
||||||
|
{
|
||||||
|
if(this.selectedTargetLanguage is CommonLanguages.AS_IS)
|
||||||
|
return "Use the same language as the input";
|
||||||
|
|
||||||
|
if(this.selectedTargetLanguage is CommonLanguages.OTHER)
|
||||||
|
return this.customTargetLanguage;
|
||||||
|
|
||||||
|
return this.selectedTargetLanguage.Name();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string UserPromptMandatoryInformation()
|
||||||
|
{
|
||||||
|
if(string.IsNullOrWhiteSpace(this.inputMandatoryInformation))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
return $"""
|
||||||
|
# Mandatory Information
|
||||||
|
{this.inputMandatoryInformation}
|
||||||
|
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string UserPromptJobDescription()
|
||||||
|
{
|
||||||
|
if(string.IsNullOrWhiteSpace(this.inputJobDescription))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
return $"""
|
||||||
|
# Job Description
|
||||||
|
{this.inputJobDescription}
|
||||||
|
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string UserPromptQualifications()
|
||||||
|
{
|
||||||
|
if(string.IsNullOrWhiteSpace(this.inputQualifications))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
return $"""
|
||||||
|
# Qualifications
|
||||||
|
{this.inputQualifications}
|
||||||
|
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string UserPromptResponsibilities()
|
||||||
|
{
|
||||||
|
if(string.IsNullOrWhiteSpace(this.inputResponsibilities))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
return $"""
|
||||||
|
# Responsibilities
|
||||||
|
{this.inputResponsibilities}
|
||||||
|
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string UserPromptCompanyName()
|
||||||
|
{
|
||||||
|
if(string.IsNullOrWhiteSpace(this.inputCompanyName))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
return $"""
|
||||||
|
# Company Name
|
||||||
|
{this.inputCompanyName}
|
||||||
|
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string UserPromptEntryDate()
|
||||||
|
{
|
||||||
|
if(string.IsNullOrWhiteSpace(this.inputEntryDate))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
return $"""
|
||||||
|
# Entry Date
|
||||||
|
{this.inputEntryDate}
|
||||||
|
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string UserPromptValidUntil()
|
||||||
|
{
|
||||||
|
if(string.IsNullOrWhiteSpace(this.inputValidUntil))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
return $"""
|
||||||
|
# Job Posting Valid Until
|
||||||
|
{this.inputValidUntil}
|
||||||
|
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string UserPromptWorkLocation()
|
||||||
|
{
|
||||||
|
if(string.IsNullOrWhiteSpace(this.inputWorkLocation))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
return $"""
|
||||||
|
# Work Location
|
||||||
|
{this.inputWorkLocation}
|
||||||
|
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string UserPromptCountryLegalFramework()
|
||||||
|
{
|
||||||
|
if(string.IsNullOrWhiteSpace(this.inputCountryLegalFramework))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
return $"""
|
||||||
|
# Country where the job is posted (legal framework)
|
||||||
|
{this.inputCountryLegalFramework}
|
||||||
|
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CreateJobPosting()
|
||||||
|
{
|
||||||
|
await this.form!.Validate();
|
||||||
|
if (!this.inputIsValid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.CreateChatThread();
|
||||||
|
var time = this.AddUserRequest(
|
||||||
|
$"""
|
||||||
|
{this.UserPromptCompanyName()}
|
||||||
|
{this.UserPromptCountryLegalFramework()}
|
||||||
|
{this.UserPromptMandatoryInformation()}
|
||||||
|
{this.UserPromptJobDescription()}
|
||||||
|
{this.UserPromptQualifications()}
|
||||||
|
{this.UserPromptResponsibilities()}
|
||||||
|
{this.UserPromptWorkLocation()}
|
||||||
|
{this.UserPromptEntryDate()}
|
||||||
|
{this.UserPromptValidUntil()}
|
||||||
|
""");
|
||||||
|
|
||||||
|
await this.AddAIResponseAsync(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
|
using AIStudio.Chat;
|
||||||
|
|
||||||
namespace AIStudio.Assistants.LegalCheck;
|
namespace AIStudio.Assistants.LegalCheck;
|
||||||
|
|
||||||
public partial class AssistantLegalCheck : AssistantBaseCore
|
public partial class AssistantLegalCheck : AssistantBaseCore
|
||||||
{
|
{
|
||||||
protected override Tools.Components Component => Tools.Components.LEGAL_CHECK_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.LEGAL_CHECK_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Legal Check";
|
protected override string Title => "Legal Check";
|
||||||
|
|
||||||
@@ -30,6 +32,11 @@ public partial class AssistantLegalCheck : AssistantBaseCore
|
|||||||
|
|
||||||
protected override bool SubmitDisabled => this.isAgentRunning;
|
protected override bool SubmitDisabled => this.isAgentRunning;
|
||||||
|
|
||||||
|
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||||
|
{
|
||||||
|
SystemPrompt = SystemPrompts.DEFAULT,
|
||||||
|
};
|
||||||
|
|
||||||
protected override void ResetFrom()
|
protected override void ResetFrom()
|
||||||
{
|
{
|
||||||
this.inputLegalDocument = string.Empty;
|
this.inputLegalDocument = string.Empty;
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
|
using AIStudio.Chat;
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
|
|
||||||
namespace AIStudio.Assistants.MyTasks;
|
namespace AIStudio.Assistants.MyTasks;
|
||||||
|
|
||||||
public partial class AssistantMyTasks : AssistantBaseCore
|
public partial class AssistantMyTasks : AssistantBaseCore
|
||||||
{
|
{
|
||||||
protected override Tools.Components Component => Tools.Components.MY_TASKS_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.MY_TASKS_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "My Tasks";
|
protected override string Title => "My Tasks";
|
||||||
|
|
||||||
@@ -35,6 +36,11 @@ public partial class AssistantMyTasks : AssistantBaseCore
|
|||||||
|
|
||||||
protected override bool ShowProfileSelection => false;
|
protected override bool ShowProfileSelection => false;
|
||||||
|
|
||||||
|
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
|
||||||
|
{
|
||||||
|
SystemPrompt = SystemPrompts.DEFAULT,
|
||||||
|
};
|
||||||
|
|
||||||
protected override void ResetFrom()
|
protected override void ResetFrom()
|
||||||
{
|
{
|
||||||
this.inputText = string.Empty;
|
this.inputText = string.Empty;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace AIStudio.Assistants.RewriteImprove;
|
|||||||
|
|
||||||
public partial class AssistantRewriteImprove : AssistantBaseCore
|
public partial class AssistantRewriteImprove : AssistantBaseCore
|
||||||
{
|
{
|
||||||
protected override Tools.Components Component => Tools.Components.REWRITE_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.REWRITE_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Rewrite & Improve Text";
|
protected override string Title => "Rewrite & Improve Text";
|
||||||
|
|
||||||
@@ -26,8 +26,6 @@ public partial class AssistantRewriteImprove : AssistantBaseCore
|
|||||||
|
|
||||||
protected override bool AllowProfiles => false;
|
protected override bool AllowProfiles => false;
|
||||||
|
|
||||||
protected override bool ShowResult => false;
|
|
||||||
|
|
||||||
protected override bool ShowDedicatedProgress => true;
|
protected override bool ShowDedicatedProgress => true;
|
||||||
|
|
||||||
protected override Func<string> Result2Copy => () => this.rewrittenText;
|
protected override Func<string> Result2Copy => () => this.rewrittenText;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace AIStudio.Assistants.Synonym;
|
|||||||
|
|
||||||
public partial class AssistantSynonyms : AssistantBaseCore
|
public partial class AssistantSynonyms : AssistantBaseCore
|
||||||
{
|
{
|
||||||
protected override Tools.Components Component => Tools.Components.SYNONYMS_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.SYNONYMS_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Synonyms";
|
protected override string Title => "Synonyms";
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace AIStudio.Assistants.TextSummarizer;
|
|||||||
|
|
||||||
public partial class AssistantTextSummarizer : AssistantBaseCore
|
public partial class AssistantTextSummarizer : AssistantBaseCore
|
||||||
{
|
{
|
||||||
protected override Tools.Components Component => Tools.Components.TEXT_SUMMARIZER_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.TEXT_SUMMARIZER_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Text Summarizer";
|
protected override string Title => "Text Summarizer";
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace AIStudio.Assistants.Translation;
|
|||||||
|
|
||||||
public partial class AssistantTranslation : AssistantBaseCore
|
public partial class AssistantTranslation : AssistantBaseCore
|
||||||
{
|
{
|
||||||
protected override Tools.Components Component => Tools.Components.TRANSLATION_ASSISTANT;
|
public override Tools.Components Component => Tools.Components.TRANSLATION_ASSISTANT;
|
||||||
|
|
||||||
protected override string Title => "Translation";
|
protected override string Title => "Translation";
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ public sealed record ChatThread
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Guid WorkspaceId { get; set; }
|
public Guid WorkspaceId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the provider selected for the chat thread.
|
||||||
|
/// </summary>
|
||||||
|
public string SelectedProvider { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the chat thread. Usually generated by an AI model or manually edited by the user.
|
/// The name of the chat thread. Usually generated by an AI model or manually edited by the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -18,10 +18,15 @@ public class ContentBlock
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The content of the block.
|
/// The content of the block.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IContent? Content { get; init; } = null;
|
public IContent? Content { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The role of the content block in the chat thread, e.g., user, AI, etc.
|
/// The role of the content block in the chat thread, e.g., user, AI, etc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ChatRole Role { get; init; } = ChatRole.NONE;
|
public ChatRole Role { get; init; } = ChatRole.NONE;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should the content block be hidden from the user?
|
||||||
|
/// </summary>
|
||||||
|
public bool HideFromUser { get; set; }
|
||||||
}
|
}
|
||||||
@@ -64,7 +64,7 @@ public partial class ContentBlockComponent : ComponentBase
|
|||||||
private async Task AfterStreaming()
|
private async Task AfterStreaming()
|
||||||
{
|
{
|
||||||
// Might be called from a different thread, so we need to invoke the UI thread:
|
// Might be called from a different thread, so we need to invoke the UI thread:
|
||||||
await this.InvokeAsync(() =>
|
await this.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// Issue we try to solve: When the content changes during streaming,
|
// Issue we try to solve: When the content changes during streaming,
|
||||||
@@ -83,6 +83,9 @@ public partial class ContentBlockComponent : ComponentBase
|
|||||||
|
|
||||||
// Let Blazor update the UI, i.e., to see the render tree diff:
|
// Let Blazor update the UI, i.e., to see the render tree diff:
|
||||||
this.StateHasChanged();
|
this.StateHasChanged();
|
||||||
|
|
||||||
|
// Inform the chat that the streaming is done:
|
||||||
|
await MessageBus.INSTANCE.SendMessage<bool>(this, Event.CHAT_STREAMING_DONE);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public sealed class ContentText : IContent
|
|||||||
if(chatThread is null)
|
if(chatThread is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Store the last time we got a response. We use this ater
|
// Store the last time we got a response. We use this later
|
||||||
// to determine whether we should notify the UI about the
|
// to determine whether we should notify the UI about the
|
||||||
// new content or not. Depends on the energy saving mode
|
// new content or not. Depends on the energy saving mode
|
||||||
// the user chose.
|
// the user chose.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<MudCard Outlined="@true" Style="border-width: 2px; border-color: #0d47a1; border-radius: 12px; border-style: solid; max-width: 20em;">
|
<MudCard Outlined="@true" Style="@this.BlockStyle">
|
||||||
<MudCardHeader>
|
<MudCardHeader>
|
||||||
<CardHeaderContent>
|
<CardHeaderContent>
|
||||||
<MudStack AlignItems="AlignItems.Center" Row="@true">
|
<MudStack AlignItems="AlignItems.Center" Row="@true">
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
using AIStudio.Settings;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
namespace AIStudio.Components;
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
public partial class AssistantBlock : ComponentBase
|
public partial class AssistantBlock : ComponentBase, IMessageBusReceiver, IDisposable
|
||||||
{
|
{
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
@@ -18,4 +20,63 @@ public partial class AssistantBlock : ComponentBase
|
|||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Link { get; set; } = string.Empty;
|
public string Link { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private MudTheme ColorTheme { get; init; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private SettingsManager SettingsManager { get; init; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private MessageBus MessageBus { get; init; } = null!;
|
||||||
|
|
||||||
|
#region Overrides of ComponentBase
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
this.MessageBus.RegisterComponent(this);
|
||||||
|
this.MessageBus.ApplyFilters(this, [], [ Event.COLOR_THEME_CHANGED ]);
|
||||||
|
|
||||||
|
await base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation of IMessageBusReceiver
|
||||||
|
|
||||||
|
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
||||||
|
{
|
||||||
|
switch (triggeredEvent)
|
||||||
|
{
|
||||||
|
case Event.COLOR_THEME_CHANGED:
|
||||||
|
this.StateHasChanged();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
|
||||||
|
{
|
||||||
|
return Task.FromResult<TResult?>(default);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private string BorderColor => this.SettingsManager.IsDarkMode switch
|
||||||
|
{
|
||||||
|
true => this.ColorTheme.GetCurrentPalette(this.SettingsManager).GrayLight,
|
||||||
|
false => this.ColorTheme.GetCurrentPalette(this.SettingsManager).Primary.Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
private string BlockStyle => $"border-width: 2px; border-color: {this.BorderColor}; border-radius: 12px; border-style: solid; max-width: 20em;";
|
||||||
|
|
||||||
|
#region Implementation of IDisposable
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
this.MessageBus.Unregister(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,18 @@ public partial class Changelog
|
|||||||
|
|
||||||
public static readonly Log[] LOGS =
|
public static readonly Log[] LOGS =
|
||||||
[
|
[
|
||||||
|
new (197, "v0.9.22, build 197 (2024-12-04 10:58 UTC)", "v0.9.22.md"),
|
||||||
|
new (196, "v0.9.21, build 196 (2024-11-23 12:22 UTC)", "v0.9.21.md"),
|
||||||
|
new (195, "v0.9.20, build 195 (2024-11-16 20:44 UTC)", "v0.9.20.md"),
|
||||||
|
new (194, "v0.9.19, build 194 (2024-11-14 05:58 UTC)", "v0.9.19.md"),
|
||||||
|
new (193, "v0.9.18, build 193 (2024-11-09 21:10 UTC)", "v0.9.18.md"),
|
||||||
|
new (192, "v0.9.17, build 192 (2024-11-03 11:11 UTC)", "v0.9.17.md"),
|
||||||
|
new (191, "v0.9.16, build 191 (2024-11-02 22:04 UTC)", "v0.9.16.md"),
|
||||||
|
new (190, "v0.9.15, build 190 (2024-10-28 15:04 UTC)", "v0.9.15.md"),
|
||||||
|
new (189, "v0.9.14, build 189 (2024-10-18 08:48 UTC)", "v0.9.14.md"),
|
||||||
|
new (188, "v0.9.13, build 188 (2024-10-07 11:18 UTC)", "v0.9.13.md"),
|
||||||
|
new (187, "v0.9.12, build 187 (2024-09-15 20:49 UTC)", "v0.9.12.md"),
|
||||||
|
new (186, "v0.9.11, build 186 (2024-09-15 10:33 UTC)", "v0.9.11.md"),
|
||||||
new (185, "v0.9.10, build 185 (2024-09-12 20:52 UTC)", "v0.9.10.md"),
|
new (185, "v0.9.10, build 185 (2024-09-12 20:52 UTC)", "v0.9.10.md"),
|
||||||
new (184, "v0.9.9, build 184 (2024-09-11 21:10 UTC)", "v0.9.9.md"),
|
new (184, "v0.9.9, build 184 (2024-09-11 21:10 UTC)", "v0.9.9.md"),
|
||||||
new (183, "v0.9.8, build 183 (2024-09-09 13:10 UTC)", "v0.9.8.md"),
|
new (183, "v0.9.8, build 183 (2024-09-09 13:10 UTC)", "v0.9.8.md"),
|
||||||
|
|||||||
@@ -1,56 +1,58 @@
|
|||||||
@using AIStudio.Provider
|
@using AIStudio.Provider
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
@if (this.Mode is ConfidenceInfoMode.ICON)
|
<MudTooltip Text="Shows and hides the confidence card with information about the selected LLM provider.">
|
||||||
{
|
@if (this.Mode is ConfidenceInfoMode.ICON)
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Security" Class="confidence-icon" Style="@this.Provider.GetConfidence(this.SettingsManager).SetColorStyle()" OnClick="@(() => this.ToggleConfidence())"/>
|
{
|
||||||
}
|
<MudIconButton Icon="@Icons.Material.Filled.Security" Class="confidence-icon" Style="@this.LLMProvider.GetConfidence(this.SettingsManager).SetColorStyle(this.SettingsManager)" OnClick="@(() => this.ToggleConfidence())"/>
|
||||||
else
|
}
|
||||||
{
|
else
|
||||||
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.Security" IconClass="confidence-icon" Style="@this.Provider.GetConfidence(this.SettingsManager).SetColorStyle()" OnClick="@(() => this.ToggleConfidence())">
|
{
|
||||||
Confidence
|
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.Security" IconClass="confidence-icon" Style="@this.LLMProvider.GetConfidence(this.SettingsManager).SetColorStyle(this.SettingsManager)" OnClick="@(() => this.ToggleConfidence())">
|
||||||
</MudButton>
|
Confidence
|
||||||
}
|
|
||||||
|
|
||||||
<MudPopover Open="@this.showConfidence" AnchorOrigin="Origin.TopLeft" TransformOrigin="Origin.BottomLeft" Style="@this.GetPopoverStyle()" DropShadow="@true" Class="border-solid border-4 rounded-lg">
|
|
||||||
<MudCard>
|
|
||||||
<MudCardHeader>
|
|
||||||
<CardHeaderContent>
|
|
||||||
<MudText Typo="Typo.h5">Confidence Card</MudText>
|
|
||||||
</CardHeaderContent>
|
|
||||||
</MudCardHeader>
|
|
||||||
<MudCardContent>
|
|
||||||
<MudText Typo="Typo.h6">Description</MudText>
|
|
||||||
<MudMarkdown Value="@this.currentConfidence.Description"/>
|
|
||||||
|
|
||||||
@if (this.currentConfidence.Sources.Count > 0)
|
|
||||||
{
|
|
||||||
<MudText Typo="Typo.h6">Sources</MudText>
|
|
||||||
<MudList T="@string">
|
|
||||||
@foreach (var sourceTuple in this.GetConfidenceSources())
|
|
||||||
{
|
|
||||||
<MudListItem Icon="@Icons.Material.Filled.Link" Href="@sourceTuple.Source" Target="_blank" Text="@sourceTuple.Index"/>
|
|
||||||
}
|
|
||||||
</MudList>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(this.currentConfidence.Region))
|
|
||||||
{
|
|
||||||
<MudText Typo="Typo.h6">Region</MudText>
|
|
||||||
<MudText Typo="Typo.body1" Class="mb-3">
|
|
||||||
<b>@this.currentConfidence.Region</b>
|
|
||||||
</MudText>
|
|
||||||
}
|
|
||||||
|
|
||||||
<MudText Typo="Typo.h6">Confidence Level</MudText>
|
|
||||||
<MudText Typo="Typo.body1" Style="@this.GetCurrentConfidenceColor()">
|
|
||||||
<b>@this.currentConfidence.Level.GetName()</b>
|
|
||||||
</MudText>
|
|
||||||
</MudCardContent>
|
|
||||||
<MudCardActions>
|
|
||||||
<MudButton Variant="Variant.Filled" OnClick="@(() => this.HideConfidence())">
|
|
||||||
Close
|
|
||||||
</MudButton>
|
</MudButton>
|
||||||
</MudCardActions>
|
}
|
||||||
</MudCard>
|
</MudTooltip>
|
||||||
</MudPopover>
|
|
||||||
|
<MudPopover Open="@this.showConfidence" AnchorOrigin="Origin.TopLeft" TransformOrigin="Origin.BottomLeft" Style="@this.GetPopoverStyle()" DropShadow="@true" Class="border-solid border-4 rounded-lg">
|
||||||
|
<MudCard>
|
||||||
|
<MudCardHeader>
|
||||||
|
<CardHeaderContent>
|
||||||
|
<MudText Typo="Typo.h5">Confidence Card</MudText>
|
||||||
|
</CardHeaderContent>
|
||||||
|
</MudCardHeader>
|
||||||
|
<MudCardContent>
|
||||||
|
<MudText Typo="Typo.h6">Description</MudText>
|
||||||
|
<MudMarkdown Value="@this.currentConfidence.Description"/>
|
||||||
|
|
||||||
|
@if (this.currentConfidence.Sources.Count > 0)
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.h6">Sources</MudText>
|
||||||
|
<MudList T="@string">
|
||||||
|
@foreach (var sourceTuple in this.GetConfidenceSources())
|
||||||
|
{
|
||||||
|
<MudListItem Icon="@Icons.Material.Filled.Link" Href="@sourceTuple.Source" Target="_blank" Text="@sourceTuple.Index"/>
|
||||||
|
}
|
||||||
|
</MudList>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (!string.IsNullOrWhiteSpace(this.currentConfidence.Region))
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.h6">Region</MudText>
|
||||||
|
<MudText Typo="Typo.body1" Class="mb-3">
|
||||||
|
<b>@this.currentConfidence.Region</b>
|
||||||
|
</MudText>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudText Typo="Typo.h6">Confidence Level</MudText>
|
||||||
|
<MudText Typo="Typo.body1" Style="@this.GetCurrentConfidenceColor()">
|
||||||
|
<b>@this.currentConfidence.Level.GetName()</b>
|
||||||
|
</MudText>
|
||||||
|
</MudCardContent>
|
||||||
|
<MudCardActions>
|
||||||
|
<MudButton Variant="Variant.Filled" OnClick="@(() => this.HideConfidence())">
|
||||||
|
Close
|
||||||
|
</MudButton>
|
||||||
|
</MudCardActions>
|
||||||
|
</MudCard>
|
||||||
|
</MudPopover>
|
||||||
</div>
|
</div>
|
||||||
@@ -5,30 +5,36 @@ using Microsoft.AspNetCore.Components;
|
|||||||
|
|
||||||
namespace AIStudio.Components;
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
public partial class ConfidenceInfo : ComponentBase
|
public partial class ConfidenceInfo : ComponentBase, IMessageBusReceiver, IDisposable
|
||||||
{
|
{
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public ConfidenceInfoMode Mode { get; set; } = ConfidenceInfoMode.BUTTON;
|
public ConfidenceInfoMode Mode { get; set; } = ConfidenceInfoMode.BUTTON;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Providers Provider { get; set; }
|
public LLMProviders LLMProvider { get; set; }
|
||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
private SettingsManager SettingsManager { get; init; } = null!;
|
private SettingsManager SettingsManager { get; init; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private MessageBus MessageBus { get; init; } = null!;
|
||||||
|
|
||||||
private Confidence currentConfidence;
|
private Confidence currentConfidence;
|
||||||
private bool showConfidence;
|
private bool showConfidence;
|
||||||
|
|
||||||
public ConfidenceInfo()
|
public ConfidenceInfo()
|
||||||
{
|
{
|
||||||
this.currentConfidence = Providers.NONE.GetConfidence(this.SettingsManager);
|
this.currentConfidence = LLMProviders.NONE.GetConfidence(this.SettingsManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Overrides of ComponentBase
|
#region Overrides of ComponentBase
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
this.currentConfidence = this.Provider.GetConfidence(this.SettingsManager);
|
this.MessageBus.RegisterComponent(this);
|
||||||
|
this.MessageBus.ApplyFilters(this, [], [ Event.COLOR_THEME_CHANGED ]);
|
||||||
|
|
||||||
|
this.currentConfidence = this.LLMProvider.GetConfidence(this.SettingsManager);
|
||||||
await base.OnParametersSetAsync();
|
await base.OnParametersSetAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +57,38 @@ public partial class ConfidenceInfo : ComponentBase
|
|||||||
yield return ($"Source {++index}", source);
|
yield return ($"Source {++index}", source);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetCurrentConfidenceColor() => $"color: {this.currentConfidence.Level.GetColor()};";
|
private string GetCurrentConfidenceColor() => $"color: {this.currentConfidence.Level.GetColor(this.SettingsManager)};";
|
||||||
|
|
||||||
private string GetPopoverStyle() => $"border-color: {this.currentConfidence.Level.GetColor()}; max-width: calc(35vw);";
|
private string GetPopoverStyle() => $"border-color: {this.currentConfidence.Level.GetColor(this.SettingsManager)}; max-width: calc(35vw);";
|
||||||
|
|
||||||
|
#region Implementation of IMessageBusReceiver
|
||||||
|
|
||||||
|
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
||||||
|
{
|
||||||
|
switch (triggeredEvent)
|
||||||
|
{
|
||||||
|
case Event.COLOR_THEME_CHANGED:
|
||||||
|
this.showConfidence = false;
|
||||||
|
this.StateHasChanged();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
|
||||||
|
{
|
||||||
|
return Task.FromResult<TResult?>(default);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation of IDisposable
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
this.MessageBus.Unregister(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
@using AIStudio.Settings
|
||||||
|
<ConfigurationSelect Disabled="@this.Disabled" OptionDescription="Select a minimum confidence level" SelectedValue="@this.FilteredSelectedValue" Data="@ConfigurationSelectDataFactory.GetConfidenceLevelsData(this.SettingsManager, this.RestrictToGlobalMinimumConfidence)" SelectionUpdate="@this.SelectionUpdate" OptionHelp="Choose the minimum confidence level that all LLM providers must meet. This way, you can ensure that only trustworthy providers are used. You cannot use any provider that falls below this level."/>
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using AIStudio.Provider;
|
||||||
|
using AIStudio.Settings;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
|
public partial class ConfigurationMinConfidenceSelection : ComponentBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The selected value.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Func<ConfidenceLevel> SelectedValue { get; set; } = () => default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An action that is called when the selection changes.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Action<ConfidenceLevel> SelectionUpdate { get; set; } = _ => { };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is the selection component disabled?
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Func<bool> Disabled { get; set; } = () => false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Boolean value indicating whether the selection is restricted to a global minimum confidence level.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool RestrictToGlobalMinimumConfidence { get; set; }
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private SettingsManager SettingsManager { get; init; } = null!;
|
||||||
|
|
||||||
|
private ConfidenceLevel FilteredSelectedValue()
|
||||||
|
{
|
||||||
|
if (this.SelectedValue() is ConfidenceLevel.NONE)
|
||||||
|
return ConfidenceLevel.NONE;
|
||||||
|
|
||||||
|
if(this.RestrictToGlobalMinimumConfidence && this.SettingsManager.ConfigurationData.LLMProviders.EnforceGlobalMinimumConfidence)
|
||||||
|
{
|
||||||
|
var minimumLevel = this.SettingsManager.ConfigurationData.LLMProviders.GlobalMinimumConfidence;
|
||||||
|
if(this.SelectedValue() < minimumLevel)
|
||||||
|
return minimumLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.SelectedValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
@inherits ConfigurationBase
|
||||||
|
@typeparam T
|
||||||
|
|
||||||
|
<MudSelectExtended
|
||||||
|
T="T"
|
||||||
|
MultiSelection="@true"
|
||||||
|
MultiSelectionTextFunc="@this.GetMultiSelectionText"
|
||||||
|
SelectedValues="@this.SelectedValues()"
|
||||||
|
Strict="@true"
|
||||||
|
Disabled="@this.Disabled()"
|
||||||
|
Margin="Margin.Dense"
|
||||||
|
Label="@this.OptionDescription"
|
||||||
|
Class="@GetClass"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
HelperText="@this.OptionHelp"
|
||||||
|
SelectedValuesChanged="@this.OptionChanged">
|
||||||
|
@foreach (var data in this.Data)
|
||||||
|
{
|
||||||
|
<MudSelectItemExtended Value="@data.Value">
|
||||||
|
@data.Name
|
||||||
|
</MudSelectItemExtended>
|
||||||
|
}
|
||||||
|
</MudSelectExtended>
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
using AIStudio.Settings;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configuration component for selecting many values from a list.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the value to select.</typeparam>
|
||||||
|
public partial class ConfigurationMultiSelect<T> : ConfigurationBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The data to select from.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public IEnumerable<ConfigurationSelectData<T>> Data { get; set; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The selected values.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Func<HashSet<T>> SelectedValues { get; set; } = () => [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An action that is called when the selection changes.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Action<HashSet<T>> SelectionUpdate { get; set; } = _ => { };
|
||||||
|
|
||||||
|
private async Task OptionChanged(IEnumerable<T?>? updatedValues)
|
||||||
|
{
|
||||||
|
if(updatedValues is null)
|
||||||
|
this.SelectionUpdate([]);
|
||||||
|
else
|
||||||
|
this.SelectionUpdate(updatedValues.Where(n => n is not null).ToHashSet()!);
|
||||||
|
|
||||||
|
await this.SettingsManager.StoreSettings();
|
||||||
|
await this.InformAboutChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetClass => $"{MARGIN_CLASS} rounded-lg";
|
||||||
|
|
||||||
|
private string GetMultiSelectionText(List<T?>? selectedValues)
|
||||||
|
{
|
||||||
|
if(selectedValues is null || selectedValues.Count == 0)
|
||||||
|
return "No preview features selected.";
|
||||||
|
|
||||||
|
if(selectedValues.Count == 1)
|
||||||
|
return $"You have selected 1 preview feature.";
|
||||||
|
|
||||||
|
return $"You have selected {selectedValues.Count} preview features.";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
<ConfigurationSelect OptionDescription="Preselected provider" Disabled="@this.Disabled" OptionHelp="@this.HelpText()" Data="@this.Data" SelectedValue="@this.SelectedValue" SelectionUpdate="@this.SelectionUpdate"/>
|
<ConfigurationSelect OptionDescription="Preselected provider" Disabled="@this.Disabled" OptionHelp="@this.HelpText()" Data="@this.FilteredData()" SelectedValue="@this.SelectedValue" SelectionUpdate="@this.SelectionUpdate"/>
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using AIStudio.Provider;
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
@@ -24,6 +25,9 @@ public partial class ConfigurationProviderSelection : ComponentBase, IMessageBus
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public Func<string> HelpText { get; set; } = () => "Select a provider that is preselected.";
|
public Func<string> HelpText { get; set; } = () => "Select a provider that is preselected.";
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public Tools.Components Component { get; set; } = Tools.Components.NONE;
|
||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
private SettingsManager SettingsManager { get; init; } = null!;
|
private SettingsManager SettingsManager { get; init; } = null!;
|
||||||
|
|
||||||
@@ -43,6 +47,17 @@ public partial class ConfigurationProviderSelection : ComponentBase, IMessageBus
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
private IEnumerable<ConfigurationSelectData<string>> FilteredData()
|
||||||
|
{
|
||||||
|
var minimumLevel = this.SettingsManager.GetMinimumConfidenceLevel(this.Component);
|
||||||
|
foreach (var providerId in this.Data)
|
||||||
|
{
|
||||||
|
var provider = this.SettingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == providerId.Value);
|
||||||
|
if (provider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level >= minimumLevel)
|
||||||
|
yield return providerId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#region Implementation of IMessageBusReceiver
|
#region Implementation of IMessageBusReceiver
|
||||||
|
|
||||||
public async Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
public async Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
@inherits ConfigurationBase
|
@inherits ConfigurationBase
|
||||||
@typeparam T
|
@typeparam T
|
||||||
|
|
||||||
<MudSelect T="T" Value="@this.SelectedValue()" Strict="@true" Disabled="@this.Disabled()" Margin="Margin.Dense" Label="@this.OptionDescription" Class="@GetClass" Variant="Variant.Outlined" HelperText="@this.OptionHelp" ValueChanged="@this.OptionChanged">
|
<MudSelect T="T" Value="@this.SelectedValue()" Strict="@true" ShrinkLabel="@true" Disabled="@this.Disabled()" Margin="Margin.Dense" Label="@this.OptionDescription" Class="@GetClass" Variant="Variant.Outlined" HelperText="@this.OptionHelp" ValueChanged="@this.OptionChanged">
|
||||||
@foreach (var data in this.Data)
|
@foreach (var data in this.Data)
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="@data.Value">
|
<MudSelectItem Value="@data.Value">
|
||||||
|
|||||||
@@ -3,16 +3,17 @@
|
|||||||
<MudTextField
|
<MudTextField
|
||||||
T="string"
|
T="string"
|
||||||
Text="@this.Text()"
|
Text="@this.Text()"
|
||||||
TextChanged="@this.OptionChanged"
|
TextChanged="@this.InternalUpdate"
|
||||||
Label="@this.OptionDescription"
|
Label="@this.OptionDescription"
|
||||||
Disabled="@this.Disabled()"
|
Disabled="@this.Disabled()"
|
||||||
Class="mb-3"
|
Class="@MARGIN_CLASS"
|
||||||
Adornment="Adornment.Start"
|
Adornment="Adornment.Start"
|
||||||
AdornmentIcon="@this.Icon"
|
AdornmentIcon="@this.Icon"
|
||||||
AdornmentColor="@this.IconColor"
|
AdornmentColor="@this.IconColor"
|
||||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||||
Lines="1"
|
Lines="@this.NumLines"
|
||||||
Immediate="@false"
|
AutoGrow="@this.AutoGrow"
|
||||||
DebounceInterval="500"
|
MaxLines="@this.GetMaxLines"
|
||||||
|
Immediate="@true"
|
||||||
Variant="Variant.Outlined"
|
Variant="Variant.Outlined"
|
||||||
/>
|
/>
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
using Timer = System.Timers.Timer;
|
||||||
|
|
||||||
namespace AIStudio.Components;
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
public partial class ConfigurationText : ConfigurationBase
|
public partial class ConfigurationText : ConfigurationBase
|
||||||
@@ -11,7 +13,7 @@ public partial class ConfigurationText : ConfigurationBase
|
|||||||
public Func<string> Text { get; set; } = () => string.Empty;
|
public Func<string> Text { get; set; } = () => string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An action which is called when the option is changed.
|
/// An action which is called when the text was changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Action<string> TextUpdate { get; set; } = _ => { };
|
public Action<string> TextUpdate { get; set; } = _ => { };
|
||||||
@@ -28,6 +30,55 @@ public partial class ConfigurationText : ConfigurationBase
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public Color IconColor { get; set; } = Color.Default;
|
public Color IconColor { get; set; } = Color.Default;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How many lines should the textfield have?
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public int NumLines { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// What is the maximum number of lines?
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public int MaxLines { get; set; } = 12;
|
||||||
|
|
||||||
|
private string internalText = string.Empty;
|
||||||
|
private Timer timer = new(TimeSpan.FromMilliseconds(500))
|
||||||
|
{
|
||||||
|
AutoReset = false
|
||||||
|
};
|
||||||
|
|
||||||
|
#region Overrides of ConfigurationBase
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
this.timer.Elapsed += async (_, _) => await this.InvokeAsync(async () => await this.OptionChanged(this.internalText));
|
||||||
|
await base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Overrides of ComponentBase
|
||||||
|
|
||||||
|
protected override async Task OnParametersSetAsync()
|
||||||
|
{
|
||||||
|
this.internalText = this.Text();
|
||||||
|
await base.OnParametersSetAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private bool AutoGrow => this.NumLines > 1;
|
||||||
|
|
||||||
|
private int GetMaxLines => this.AutoGrow ? this.MaxLines : 1;
|
||||||
|
|
||||||
|
private void InternalUpdate(string text)
|
||||||
|
{
|
||||||
|
this.timer.Stop();
|
||||||
|
this.internalText = text;
|
||||||
|
this.timer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task OptionChanged(string updatedText)
|
private async Task OptionChanged(string updatedText)
|
||||||
{
|
{
|
||||||
this.TextUpdate(updatedText);
|
this.TextUpdate(updatedText);
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
@inherits MSGComponentBase
|
@inherits MSGComponentBase
|
||||||
|
|
||||||
<div class="d-flex flex-column" style="@this.Height">
|
<div class="@this.Classes" style="@this.Styles">
|
||||||
|
@if (this.HeaderContent is not null)
|
||||||
|
{
|
||||||
|
<div>
|
||||||
|
@this.HeaderContent
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div class="flex-auto overflow-auto">
|
<div class="flex-auto overflow-auto">
|
||||||
@this.ChildContent
|
@this.ChildContent
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ namespace AIStudio.Components;
|
|||||||
|
|
||||||
public partial class InnerScrolling : MSGComponentBase
|
public partial class InnerScrolling : MSGComponentBase
|
||||||
{
|
{
|
||||||
|
[Parameter]
|
||||||
|
public bool FillEntireHorizontalSpace { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set the height of anything above the scrolling content; usually a header.
|
/// Set the height of anything above the scrolling content; usually a header.
|
||||||
/// What we do is calc(100vh - HeaderHeight). Means, you can use multiple measures like
|
/// What we do is calc(100vh - HeaderHeight). Means, you can use multiple measures like
|
||||||
@@ -14,6 +17,9 @@ public partial class InnerScrolling : MSGComponentBase
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public string HeaderHeight { get; set; } = "3em";
|
public string HeaderHeight { get; set; } = "3em";
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment? HeaderContent { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment? ChildContent { get; set; }
|
public RenderFragment? ChildContent { get; set; }
|
||||||
|
|
||||||
@@ -23,6 +29,9 @@ public partial class InnerScrolling : MSGComponentBase
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment? FooterContent { get; set; }
|
public RenderFragment? FooterContent { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string Class { get; set; } = string.Empty;
|
||||||
|
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
private MainLayout MainLayout { get; set; } = null!;
|
private MainLayout MainLayout { get; set; } = null!;
|
||||||
|
|
||||||
@@ -43,7 +52,7 @@ public partial class InnerScrolling : MSGComponentBase
|
|||||||
|
|
||||||
#region Overrides of MSGComponentBase
|
#region Overrides of MSGComponentBase
|
||||||
|
|
||||||
public override Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
public override Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||||
{
|
{
|
||||||
switch (triggeredEvent)
|
switch (triggeredEvent)
|
||||||
{
|
{
|
||||||
@@ -62,7 +71,9 @@ public partial class InnerScrolling : MSGComponentBase
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private string Height => $"height: calc(100vh - {this.HeaderHeight} - {this.MainLayout.AdditionalHeight});";
|
private string Styles => this.FillEntireHorizontalSpace ? $"height: calc(100vh - {this.HeaderHeight} - {this.MainLayout.AdditionalHeight}); overflow-x: auto; min-width: 0;" : $"height: calc(100vh - {this.HeaderHeight} - {this.MainLayout.AdditionalHeight}); flex-shrink: 0;";
|
||||||
|
|
||||||
|
private string Classes => this.FillEntireHorizontalSpace ? $"{this.Class} d-flex flex-column flex-grow-1" : $"{this.Class} d-flex flex-column";
|
||||||
|
|
||||||
public async Task ScrollToBottom()
|
public async Task ScrollToBottom()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
|
using AIStudio.Settings;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
namespace AIStudio.Components;
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBusReceiver
|
public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBusReceiver
|
||||||
{
|
{
|
||||||
|
[Inject]
|
||||||
|
protected SettingsManager SettingsManager { get; init; } = null!;
|
||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
protected MessageBus MessageBus { get; init; } = null!;
|
protected MessageBus MessageBus { get; init; } = null!;
|
||||||
|
|
||||||
@@ -19,7 +24,22 @@ public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBus
|
|||||||
|
|
||||||
#region Implementation of IMessageBusReceiver
|
#region Implementation of IMessageBusReceiver
|
||||||
|
|
||||||
public abstract Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data);
|
public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
|
||||||
|
{
|
||||||
|
switch (triggeredEvent)
|
||||||
|
{
|
||||||
|
case Event.COLOR_THEME_CHANGED:
|
||||||
|
this.StateHasChanged();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return this.ProcessIncomingMessage(sendingComponent, triggeredEvent, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data);
|
||||||
|
|
||||||
public abstract Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data);
|
public abstract Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data);
|
||||||
|
|
||||||
@@ -46,6 +66,12 @@ public abstract class MSGComponentBase : ComponentBase, IDisposable, IMessageBus
|
|||||||
|
|
||||||
protected void ApplyFilters(ComponentBase[] components, Event[] events)
|
protected void ApplyFilters(ComponentBase[] components, Event[] events)
|
||||||
{
|
{
|
||||||
this.MessageBus.ApplyFilters(this, components, events);
|
// Append the color theme changed event to the list of events:
|
||||||
|
var eventsList = new List<Event>(events)
|
||||||
|
{
|
||||||
|
Event.COLOR_THEME_CHANGED
|
||||||
|
};
|
||||||
|
|
||||||
|
this.MessageBus.ApplyFilters(this, components, eventsList.ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<MudTooltip Placement="Placement.Bottom" Arrow="@true">
|
||||||
|
<ChildContent>
|
||||||
|
<MudChip T="string" Icon="@Icons.Material.Filled.FirstPage" Color="Color.Error" Class="mb-3">
|
||||||
|
Alpha
|
||||||
|
</MudChip>
|
||||||
|
</ChildContent>
|
||||||
|
<TooltipContent>
|
||||||
|
<div style="max-width: 22em;">
|
||||||
|
<MudText Typo="Typo.body2" Class="mb-3">
|
||||||
|
This feature is currently in the alpha phase.
|
||||||
|
Expect bugs and unfinished work.
|
||||||
|
</MudText>
|
||||||
|
|
||||||
|
<MudText Typo="Typo.body2">
|
||||||
|
Alpha phase means that we are working on the
|
||||||
|
last details before the beta phase.
|
||||||
|
</MudText>
|
||||||
|
</div>
|
||||||
|
</TooltipContent>
|
||||||
|
</MudTooltip>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
|
public partial class PreviewAlpha : ComponentBase;
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<MudTooltip Placement="Placement.Bottom" Arrow="@true">
|
||||||
|
<ChildContent>
|
||||||
|
<MudChip T="string" Icon="@Icons.Material.Filled.HourglassTop" Color="Color.Info" Class="mb-3">
|
||||||
|
Beta
|
||||||
|
</MudChip>
|
||||||
|
</ChildContent>
|
||||||
|
<TooltipContent>
|
||||||
|
<div style="max-width: 20em;">
|
||||||
|
<MudText Typo="Typo.body2" Class="mb-3">
|
||||||
|
This feature is currently in the beta phase.
|
||||||
|
It is still be possible that there are some bugs.
|
||||||
|
</MudText>
|
||||||
|
|
||||||
|
<MudText Typo="Typo.body2">
|
||||||
|
Beta phase means that we are testing the feature.
|
||||||
|
</MudText>
|
||||||
|
</div>
|
||||||
|
</TooltipContent>
|
||||||
|
</MudTooltip>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
|
public partial class PreviewBeta : ComponentBase;
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<MudTooltip Placement="Placement.Bottom" Arrow="@true">
|
||||||
|
<ChildContent>
|
||||||
|
<MudChip T="string" Icon="@Icons.Material.Filled.Science" Color="Color.Error" Class="mb-3">
|
||||||
|
Experimental
|
||||||
|
</MudChip>
|
||||||
|
</ChildContent>
|
||||||
|
<TooltipContent>
|
||||||
|
<div style="max-width: 26em;">
|
||||||
|
<MudText Typo="Typo.body2" Class="mb-3">
|
||||||
|
This feature is currently in the experimental phase.
|
||||||
|
Expect bugs, unfinished work, changes in future
|
||||||
|
versions, and more.
|
||||||
|
</MudText>
|
||||||
|
|
||||||
|
<MudText Typo="Typo.body2">
|
||||||
|
Experimental phase means that we have a vision for a feature
|
||||||
|
but not a clear plan yet. We are still exploring the
|
||||||
|
possibilities.
|
||||||
|
</MudText>
|
||||||
|
</div>
|
||||||
|
</TooltipContent>
|
||||||
|
</MudTooltip>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
|
public partial class PreviewExperimental : ComponentBase;
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<MudTooltip Placement="Placement.Bottom" Arrow="@true">
|
||||||
|
<ChildContent>
|
||||||
|
<MudChip T="string" Icon="@Icons.Material.Filled.HourglassBottom" Color="Color.Error" Class="mb-3">
|
||||||
|
Prototype
|
||||||
|
</MudChip>
|
||||||
|
</ChildContent>
|
||||||
|
<TooltipContent>
|
||||||
|
<div style="max-width: 22em;">
|
||||||
|
<MudText Typo="Typo.body2" Class="mb-3">
|
||||||
|
This feature is currently in the prototype phase.
|
||||||
|
Expect bugs, unfinished work, changes in future
|
||||||
|
versions, and more.
|
||||||
|
</MudText>
|
||||||
|
|
||||||
|
<MudText Typo="Typo.body2">
|
||||||
|
Prototype phase means that we have a plan but we
|
||||||
|
are still working on it.
|
||||||
|
</MudText>
|
||||||
|
</div>
|
||||||
|
</TooltipContent>
|
||||||
|
</MudTooltip>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
|
public partial class PreviewPrototype : ComponentBase;
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<MudTooltip Placement="Placement.Bottom" Arrow="@true">
|
||||||
|
<ChildContent>
|
||||||
|
<MudChip T="string" Icon="@Icons.Material.Filled.VerifiedUser" Color="Color.Success" Class="mb-3">
|
||||||
|
Release Candidate
|
||||||
|
</MudChip>
|
||||||
|
</ChildContent>
|
||||||
|
<TooltipContent>
|
||||||
|
<div style="max-width: 20em;">
|
||||||
|
<MudText Typo="Typo.body2" Class="mb-3">
|
||||||
|
This feature is about to be released. We think it's ready for production.
|
||||||
|
There should be no more bugs.
|
||||||
|
</MudText>
|
||||||
|
|
||||||
|
<MudText Typo="Typo.body2">
|
||||||
|
Release candidates are the final step before a feature is proven to be stable.
|
||||||
|
</MudText>
|
||||||
|
</div>
|
||||||
|
</TooltipContent>
|
||||||
|
</MudTooltip>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
|
public partial class PreviewReleaseCandidate : ComponentBase;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
@using AIStudio.Settings
|
@using AIStudio.Settings
|
||||||
|
|
||||||
<MudSelect T="Provider" Value="@this.ProviderSettings" ValueChanged="@this.SelectionChanged" Validation="@this.ValidateProvider" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Apps" Margin="Margin.Dense" Label="Provider" Class="mb-3 rounded-lg" Variant="Variant.Outlined">
|
<MudSelect T="Provider" Value="@this.ProviderSettings" ValueChanged="@this.SelectionChanged" Validation="@this.ValidateProvider" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Apps" Margin="Margin.Dense" Label="Provider" Class="mb-3 rounded-lg" Variant="Variant.Outlined">
|
||||||
@foreach (var provider in this.SettingsManager.ConfigurationData.Providers)
|
@foreach (var provider in this.GetAvailableProviders())
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="@provider"/>
|
<MudSelectItem Value="@provider"/>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using AIStudio.Assistants;
|
||||||
|
using AIStudio.Provider;
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
@@ -6,6 +8,9 @@ namespace AIStudio.Components;
|
|||||||
|
|
||||||
public partial class ProviderSelection : ComponentBase
|
public partial class ProviderSelection : ComponentBase
|
||||||
{
|
{
|
||||||
|
[CascadingParameter]
|
||||||
|
public AssistantBase? AssistantBase { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Settings.Provider ProviderSettings { get; set; }
|
public Settings.Provider ProviderSettings { get; set; }
|
||||||
|
|
||||||
@@ -16,11 +21,19 @@ public partial class ProviderSelection : ComponentBase
|
|||||||
public Func<Settings.Provider, string?> ValidateProvider { get; set; } = _ => null;
|
public Func<Settings.Provider, string?> ValidateProvider { get; set; } = _ => null;
|
||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
protected SettingsManager SettingsManager { get; set; } = null!;
|
private SettingsManager SettingsManager { get; init; } = null!;
|
||||||
|
|
||||||
private async Task SelectionChanged(Settings.Provider provider)
|
private async Task SelectionChanged(Settings.Provider provider)
|
||||||
{
|
{
|
||||||
this.ProviderSettings = provider;
|
this.ProviderSettings = provider;
|
||||||
await this.ProviderSettingsChanged.InvokeAsync(provider);
|
await this.ProviderSettingsChanged.InvokeAsync(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IEnumerable<Settings.Provider> GetAvailableProviders()
|
||||||
|
{
|
||||||
|
var minimumLevel = this.SettingsManager.GetMinimumConfidenceLevel(this.AssistantBase?.Component ?? Tools.Components.NONE);
|
||||||
|
foreach (var provider in this.SettingsManager.ConfigurationData.Providers)
|
||||||
|
if (provider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level >= minimumLevel)
|
||||||
|
yield return provider;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<MudListItem T="string" Icon="@this.Icon">
|
||||||
|
@if (string.IsNullOrWhiteSpace(this.URL))
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.body1" Inline="@true">
|
||||||
|
@this.Name
|
||||||
|
</MudText>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudLink Href="@this.URL" Target="_blank">
|
||||||
|
@this.Name
|
||||||
|
</MudLink>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudText Typo="Typo.body1" Class="ml-3" Inline="@true">
|
||||||
|
@this.Acknowledgment
|
||||||
|
</MudText>
|
||||||
|
</MudListItem>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
|
public partial class Supporter : ComponentBase
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string Acknowledgment { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string? URL { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public SupporterType Type { get; set; }
|
||||||
|
|
||||||
|
private string Icon => this.Type switch
|
||||||
|
{
|
||||||
|
SupporterType.INDIVIDUAL => Icons.Material.Filled.Person,
|
||||||
|
SupporterType.ORGANIZATION => Icons.Material.Filled.Business,
|
||||||
|
|
||||||
|
_ => Icons.Material.Filled.Person4,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
|
public enum SupporterType
|
||||||
|
{
|
||||||
|
UNKNOWN,
|
||||||
|
INDIVIDUAL,
|
||||||
|
ORGANIZATION
|
||||||
|
}
|
||||||
@@ -6,11 +6,11 @@ public partial class Vision : ComponentBase
|
|||||||
{
|
{
|
||||||
private static readonly TextItem[] ITEMS_VISION =
|
private static readonly TextItem[] ITEMS_VISION =
|
||||||
[
|
[
|
||||||
new TextItem("Meet your needs", "Whatever your job or task is, MindWork AI Studio aims to meet your needs: whether you're a project manager, scientist, artist, author, software developer, or game developer."),
|
new("Meet your needs", "Whatever your job or task is, MindWork AI Studio aims to meet your needs: whether you're a project manager, scientist, artist, author, software developer, or game developer."),
|
||||||
new TextItem("One stop shop", "The app will strive to fulfill all your AI needs: text-generation AI (LLM), image-generation AI, audio-generation AI (text-to-speech, potentially even text-to-music), and audio input (transcription, dictation). When there's a provider and an API available, we'll try to integrate it."),
|
new("One stop shop", "The app will strive to fulfill all your AI needs: text-generation AI (LLM), image-generation AI, audio-generation AI (text-to-speech, potentially even text-to-music), and audio input (transcription, dictation). When there's a provider and an API available, we'll try to integrate it."),
|
||||||
new TextItem("Local file system", "When you wish, we aim to integrate your local system. Local documents could be incorporated using Retrieval-Augmented Generation (RAG), and we could directly save AI-generated content to your file system."),
|
new("Local file system", "When you wish, we aim to integrate your local system. Local documents could be incorporated using Retrieval-Augmented Generation (RAG), and we could directly save AI-generated content to your file system."),
|
||||||
new TextItem("Local AI systems", "Want to use AI systems locally and offline? We aim to make that possible too."),
|
new("Local AI systems", "Want to use AI systems locally and offline? We aim to make that possible too."),
|
||||||
new TextItem("Your AI systems", "Prefer to run your AI systems with providers like replicate.com? We plan to support that!"),
|
new("Your AI systems", "Prefer to run your AI systems with providers like replicate.com? We plan to support that!"),
|
||||||
new TextItem("Assistants", "We aim to integrate specialized user interfaces as assistants. For example, a UI specifically for writing emails, or one designed for translating and correcting text, and more."),
|
new("Assistants", "We aim to integrate specialized user interfaces as assistants. For example, a UI specifically for writing emails, or one designed for translating and correcting text, and more."),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<MudTreeView T="ITreeItem" Items="@this.treeItems" SelectionMode="SelectionMode.SingleSelection" Hover="@true" ExpandOnClick="@true">
|
<MudTreeView T="ITreeItem" Items="@this.treeItems" SelectionMode="SelectionMode.SingleSelection" Hover="@true" ExpandOnClick="@true" Class="ma-3">
|
||||||
<ItemTemplate Context="item">
|
<ItemTemplate Context="item">
|
||||||
@switch (item.Value)
|
@switch (item.Value)
|
||||||
{
|
{
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
case TreeItemData treeItem:
|
case TreeItemData treeItem:
|
||||||
@if (treeItem.Type is TreeItemType.CHAT)
|
@if (treeItem.Type is TreeItemType.CHAT)
|
||||||
{
|
{
|
||||||
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" CanExpand="@treeItem.Expandable" Items="@treeItem.Children" OnClick="() => this.LoadChat(treeItem.Path, true)">
|
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" Expanded="@item.Expanded" CanExpand="@treeItem.Expandable" Items="@treeItem.Children" OnClick="() => this.LoadChat(treeItem.Path, true)">
|
||||||
<BodyContent>
|
<BodyContent>
|
||||||
<div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
|
<div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
|
||||||
<MudText Style="justify-self: start;">
|
<MudText Style="justify-self: start;">
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
}
|
}
|
||||||
else if (treeItem.Type is TreeItemType.WORKSPACE)
|
else if (treeItem.Type is TreeItemType.WORKSPACE)
|
||||||
{
|
{
|
||||||
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" CanExpand="@treeItem.Expandable" Items="@treeItem.Children">
|
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" Expanded="@item.Expanded" CanExpand="@treeItem.Expandable" Items="@treeItem.Children">
|
||||||
<BodyContent>
|
<BodyContent>
|
||||||
<div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
|
<div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
|
||||||
<MudText Style="justify-self: start;">@treeItem.Text</MudText>
|
<MudText Style="justify-self: start;">@treeItem.Text</MudText>
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" CanExpand="@treeItem.Expandable" Items="@treeItem.Children">
|
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" Expanded="@item.Expanded" CanExpand="@treeItem.Expandable" Items="@treeItem.Children">
|
||||||
<BodyContent>
|
<BodyContent>
|
||||||
<div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
|
<div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
|
||||||
<MudText Style="justify-self: start;">@treeItem.Text</MudText>
|
<MudText Style="justify-self: start;">@treeItem.Text</MudText>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
using AIStudio.Chat;
|
using AIStudio.Chat;
|
||||||
using AIStudio.Dialogs;
|
using AIStudio.Dialogs;
|
||||||
@@ -35,20 +34,12 @@ public partial class Workspaces : ComponentBase
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public Func<Task> LoadedChatWasChanged { get; set; } = () => Task.CompletedTask;
|
public Func<Task> LoadedChatWasChanged { get; set; } = () => Task.CompletedTask;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool ExpandRootNodes { get; set; } = true;
|
||||||
|
|
||||||
private const Placement WORKSPACE_ITEM_TOOLTIP_PLACEMENT = Placement.Bottom;
|
private const Placement WORKSPACE_ITEM_TOOLTIP_PLACEMENT = Placement.Bottom;
|
||||||
|
|
||||||
private static readonly JsonSerializerOptions JSON_OPTIONS = new()
|
public static readonly Guid WORKSPACE_ID_BIAS = Guid.Parse("82050a4e-ee92-43d7-8ee5-ab512f847e02");
|
||||||
{
|
|
||||||
WriteIndented = true,
|
|
||||||
AllowTrailingCommas = true,
|
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
|
|
||||||
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
|
|
||||||
PropertyNameCaseInsensitive = true,
|
|
||||||
Converters =
|
|
||||||
{
|
|
||||||
new JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseUpper),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly List<TreeItemData<ITreeItem>> treeItems = new();
|
private readonly List<TreeItemData<ITreeItem>> treeItems = new();
|
||||||
|
|
||||||
@@ -63,6 +54,7 @@ public partial class Workspaces : ComponentBase
|
|||||||
// - When assigning the tree items to the MudTreeViewItem component, we must set the Value property to the value of the item
|
// - When assigning the tree items to the MudTreeViewItem component, we must set the Value property to the value of the item
|
||||||
//
|
//
|
||||||
|
|
||||||
|
await this.EnsureBiasWorkspace();
|
||||||
await this.LoadTreeItems();
|
await this.LoadTreeItems();
|
||||||
await base.OnInitializedAsync();
|
await base.OnInitializedAsync();
|
||||||
}
|
}
|
||||||
@@ -71,9 +63,9 @@ public partial class Workspaces : ComponentBase
|
|||||||
|
|
||||||
private async Task LoadTreeItems()
|
private async Task LoadTreeItems()
|
||||||
{
|
{
|
||||||
this.treeItems.Clear();
|
var workspacesNode = new TreeItemData<ITreeItem>
|
||||||
this.treeItems.Add(new TreeItemData<ITreeItem>
|
|
||||||
{
|
{
|
||||||
|
Expanded = this.ExpandRootNodes,
|
||||||
Expandable = true,
|
Expandable = true,
|
||||||
Value = new TreeItemData
|
Value = new TreeItemData
|
||||||
{
|
{
|
||||||
@@ -85,16 +77,11 @@ public partial class Workspaces : ComponentBase
|
|||||||
Path = "root",
|
Path = "root",
|
||||||
Children = await this.LoadWorkspaces(),
|
Children = await this.LoadWorkspaces(),
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
|
|
||||||
this.treeItems.Add(new TreeItemData<ITreeItem>
|
var tempChatNode = new TreeItemData<ITreeItem>
|
||||||
{
|
|
||||||
Expandable = false,
|
|
||||||
Value = new TreeDivider(),
|
|
||||||
});
|
|
||||||
|
|
||||||
this.treeItems.Add(new TreeItemData<ITreeItem>
|
|
||||||
{
|
{
|
||||||
|
Expanded = this.ExpandRootNodes,
|
||||||
Expandable = true,
|
Expandable = true,
|
||||||
Value = new TreeItemData
|
Value = new TreeItemData
|
||||||
{
|
{
|
||||||
@@ -106,7 +93,16 @@ public partial class Workspaces : ComponentBase
|
|||||||
Path = "temp",
|
Path = "temp",
|
||||||
Children = await this.LoadTemporaryChats(),
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IReadOnlyCollection<TreeItemData<ITreeItem>>> LoadTemporaryChats()
|
private async Task<IReadOnlyCollection<TreeItemData<ITreeItem>>> LoadTemporaryChats()
|
||||||
@@ -151,16 +147,6 @@ public partial class Workspaces : ComponentBase
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> LoadWorkspaceName(Guid workspaceId)
|
|
||||||
{
|
|
||||||
if(workspaceId == Guid.Empty)
|
|
||||||
return string.Empty;
|
|
||||||
|
|
||||||
var workspacePath = Path.Join(SettingsManager.DataDirectory, "workspaces", workspaceId.ToString());
|
|
||||||
var workspaceNamePath = Path.Join(workspacePath, "name");
|
|
||||||
return await File.ReadAllTextAsync(workspaceNamePath, Encoding.UTF8);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<IReadOnlyCollection<TreeItemData<ITreeItem>>> LoadWorkspaces()
|
private async Task<IReadOnlyCollection<TreeItemData<ITreeItem>>> LoadWorkspaces()
|
||||||
{
|
{
|
||||||
var workspaces = new List<TreeItemData<ITreeItem>>();
|
var workspaces = new List<TreeItemData<ITreeItem>>();
|
||||||
@@ -250,27 +236,14 @@ public partial class Workspaces : ComponentBase
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StoreChat(ChatThread chat)
|
public async Task StoreChat(ChatThread chat, bool reloadTreeItems = true)
|
||||||
{
|
{
|
||||||
string chatDirectory;
|
await WorkspaceBehaviour.StoreChat(chat);
|
||||||
if (chat.WorkspaceId == Guid.Empty)
|
|
||||||
chatDirectory = Path.Join(SettingsManager.DataDirectory, "tempChats", chat.ChatId.ToString());
|
|
||||||
else
|
|
||||||
chatDirectory = Path.Join(SettingsManager.DataDirectory, "workspaces", chat.WorkspaceId.ToString(), chat.ChatId.ToString());
|
|
||||||
|
|
||||||
// Ensure the directory exists:
|
|
||||||
Directory.CreateDirectory(chatDirectory);
|
|
||||||
|
|
||||||
// Save the chat name:
|
|
||||||
var chatNamePath = Path.Join(chatDirectory, "name");
|
|
||||||
await File.WriteAllTextAsync(chatNamePath, chat.Name);
|
|
||||||
|
|
||||||
// Save the thread as thread.json:
|
|
||||||
var chatPath = Path.Join(chatDirectory, "thread.json");
|
|
||||||
await File.WriteAllTextAsync(chatPath, JsonSerializer.Serialize(chat, JSON_OPTIONS), Encoding.UTF8);
|
|
||||||
|
|
||||||
// Reload the tree items:
|
// Reload the tree items:
|
||||||
await this.LoadTreeItems();
|
if(reloadTreeItems)
|
||||||
|
await this.LoadTreeItems();
|
||||||
|
|
||||||
this.StateHasChanged();
|
this.StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,7 +272,7 @@ public partial class Workspaces : ComponentBase
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var chatData = await File.ReadAllTextAsync(Path.Join(chatPath, "thread.json"), Encoding.UTF8);
|
var chatData = await File.ReadAllTextAsync(Path.Join(chatPath, "thread.json"), Encoding.UTF8);
|
||||||
var chat = JsonSerializer.Deserialize<ChatThread>(chatData, JSON_OPTIONS);
|
var chat = JsonSerializer.Deserialize<ChatThread>(chatData, WorkspaceBehaviour.JSON_OPTIONS);
|
||||||
if (switchToChat)
|
if (switchToChat)
|
||||||
{
|
{
|
||||||
this.CurrentChatThread = chat;
|
this.CurrentChatThread = chat;
|
||||||
@@ -325,7 +298,7 @@ public partial class Workspaces : ComponentBase
|
|||||||
|
|
||||||
if (askForConfirmation)
|
if (askForConfirmation)
|
||||||
{
|
{
|
||||||
var workspaceName = await this.LoadWorkspaceName(chat.WorkspaceId);
|
var workspaceName = await WorkspaceBehaviour.LoadWorkspaceName(chat.WorkspaceId);
|
||||||
var dialogParameters = new DialogParameters
|
var dialogParameters = new DialogParameters
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@@ -390,7 +363,7 @@ public partial class Workspaces : ComponentBase
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var workspaceId = Guid.Parse(Path.GetFileName(workspacePath));
|
var workspaceId = Guid.Parse(Path.GetFileName(workspacePath));
|
||||||
var workspaceName = await this.LoadWorkspaceName(workspaceId);
|
var workspaceName = await WorkspaceBehaviour.LoadWorkspaceName(workspaceId);
|
||||||
var dialogParameters = new DialogParameters
|
var dialogParameters = new DialogParameters
|
||||||
{
|
{
|
||||||
{ "Message", $"Please enter a new or edit the name for your workspace '{workspaceName}':" },
|
{ "Message", $"Please enter a new or edit the name for your workspace '{workspaceName}':" },
|
||||||
@@ -435,13 +408,25 @@ public partial class Workspaces : ComponentBase
|
|||||||
await this.LoadTreeItems();
|
await this.LoadTreeItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task EnsureBiasWorkspace()
|
||||||
|
{
|
||||||
|
var workspacePath = Path.Join(SettingsManager.DataDirectory, "workspaces", WORKSPACE_ID_BIAS.ToString());
|
||||||
|
|
||||||
|
if(Path.Exists(workspacePath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Directory.CreateDirectory(workspacePath);
|
||||||
|
var workspaceNamePath = Path.Join(workspacePath, "name");
|
||||||
|
await File.WriteAllTextAsync(workspaceNamePath, "Bias of the Day", Encoding.UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task DeleteWorkspace(string? workspacePath)
|
private async Task DeleteWorkspace(string? workspacePath)
|
||||||
{
|
{
|
||||||
if(workspacePath is null)
|
if(workspacePath is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var workspaceId = Guid.Parse(Path.GetFileName(workspacePath));
|
var workspaceId = Guid.Parse(Path.GetFileName(workspacePath));
|
||||||
var workspaceName = await this.LoadWorkspaceName(workspaceId);
|
var workspaceName = await WorkspaceBehaviour.LoadWorkspaceName(workspaceId);
|
||||||
|
|
||||||
// Determine how many chats are in the workspace:
|
// Determine how many chats are in the workspace:
|
||||||
var chatCount = Directory.EnumerateDirectories(workspacePath).Count();
|
var chatCount = Directory.EnumerateDirectories(workspacePath).Count();
|
||||||
@@ -531,7 +516,7 @@ public partial class Workspaces : ComponentBase
|
|||||||
ChatId = Guid.NewGuid(),
|
ChatId = Guid.NewGuid(),
|
||||||
Name = string.Empty,
|
Name = string.Empty,
|
||||||
Seed = this.RNG.Next(),
|
Seed = this.RNG.Next(),
|
||||||
SystemPrompt = "You are a helpful assistant!",
|
SystemPrompt = SystemPrompts.DEFAULT,
|
||||||
Blocks = [],
|
Blocks = [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
@using AIStudio
|
@using AIStudio
|
||||||
@using AIStudio.Tools
|
@using AIStudio.Tools
|
||||||
@using MudBlazor
|
@using MudBlazor
|
||||||
|
@using MudExtensions
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
@using AIStudio.Provider
|
||||||
|
@using AIStudio.Provider.SelfHosted
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<DialogContent>
|
||||||
|
<MudForm @ref="@this.form" @bind-IsValid="@this.dataIsValid" @bind-Errors="@this.dataIssues">
|
||||||
|
<MudStack Row="@true" AlignItems="AlignItems.Center">
|
||||||
|
@* ReSharper disable once CSharpWarnings::CS8974 *@
|
||||||
|
<MudSelect @bind-Value="@this.DataLLMProvider" Label="Provider" Class="mb-3" OpenIcon="@Icons.Material.Filled.AccountBalance" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.providerValidation.ValidatingProvider">
|
||||||
|
@foreach (LLMProviders provider in Enum.GetValues(typeof(LLMProviders)))
|
||||||
|
{
|
||||||
|
if (provider.ProvideEmbeddings())
|
||||||
|
{
|
||||||
|
<MudSelectItem Value="@provider">@provider</MudSelectItem>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
<MudButton Disabled="@(!this.DataLLMProvider.ShowRegisterButton())" Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.OpenInBrowser" Href="@this.DataLLMProvider.GetCreationURL()" Target="_blank">Create account</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
@* ReSharper disable once CSharpWarnings::CS8974 *@
|
||||||
|
<MudTextField
|
||||||
|
T="string"
|
||||||
|
@bind-Text="@this.dataAPIKey"
|
||||||
|
Label="@this.APIKeyText"
|
||||||
|
Disabled="@(!this.DataLLMProvider.IsAPIKeyNeeded(this.DataHost))"
|
||||||
|
Class="mb-3"
|
||||||
|
Adornment="Adornment.Start"
|
||||||
|
AdornmentIcon="@Icons.Material.Filled.VpnKey"
|
||||||
|
AdornmentColor="Color.Info"
|
||||||
|
InputType="InputType.Password"
|
||||||
|
Validation="@this.providerValidation.ValidatingAPIKey"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MudTextField
|
||||||
|
T="string"
|
||||||
|
@bind-Text="@this.DataHostname"
|
||||||
|
Label="Hostname"
|
||||||
|
Disabled="@(!this.DataLLMProvider.IsHostnameNeeded())"
|
||||||
|
Class="mb-3"
|
||||||
|
Adornment="Adornment.Start"
|
||||||
|
AdornmentIcon="@Icons.Material.Filled.Dns"
|
||||||
|
AdornmentColor="Color.Info"
|
||||||
|
Validation="@this.providerValidation.ValidatingHostname"
|
||||||
|
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MudSelect Disabled="@(!this.DataLLMProvider.IsHostNeeded())" @bind-Value="@this.DataHost" Label="Host" Class="mb-3" OpenIcon="@Icons.Material.Filled.ExpandMore" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.providerValidation.ValidatingHost">
|
||||||
|
@foreach (Host host in Enum.GetValues(typeof(Host)))
|
||||||
|
{
|
||||||
|
if (host.AreEmbeddingsSupported())
|
||||||
|
{
|
||||||
|
<MudSelectItem Value="@host">@host.Name()</MudSelectItem>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
|
||||||
|
<MudStack Row="@true" AlignItems="AlignItems.Center">
|
||||||
|
@if (this.DataLLMProvider.IsEmbeddingModelProvidedManually(this.DataHost))
|
||||||
|
{
|
||||||
|
<MudTextField
|
||||||
|
T="string"
|
||||||
|
@bind-Text="@this.dataManuallyModel"
|
||||||
|
Label="Model"
|
||||||
|
Class="mb-3"
|
||||||
|
Adornment="Adornment.Start"
|
||||||
|
AdornmentIcon="@Icons.Material.Filled.Dns"
|
||||||
|
AdornmentColor="Color.Info"
|
||||||
|
Validation="@this.ValidateManuallyModel"
|
||||||
|
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||||
|
HelperText="Currently, we cannot query the embedding models of self-hosted systems. Therefore, enter the model name manually."
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudButton Disabled="@(!this.DataLLMProvider.CanLoadModels(this.DataHost, this.dataAPIKey))" Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.Refresh" OnClick="this.ReloadModels">Load</MudButton>
|
||||||
|
<MudSelect Disabled="@this.IsNoneProvider" @bind-Value="@this.DataModel" Label="Model" Class="mb-3" OpenIcon="@Icons.Material.Filled.FaceRetouchingNatural" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.providerValidation.ValidatingModel">
|
||||||
|
@foreach (var model in this.availableModels)
|
||||||
|
{
|
||||||
|
<MudSelectItem Value="@model">@model</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
@* ReSharper disable once CSharpWarnings::CS8974 *@
|
||||||
|
<MudTextField
|
||||||
|
T="string"
|
||||||
|
@bind-Text="@this.DataName"
|
||||||
|
Label="Instance Name"
|
||||||
|
Class="mb-3"
|
||||||
|
MaxLength="40"
|
||||||
|
Counter="40"
|
||||||
|
Immediate="@true"
|
||||||
|
Adornment="Adornment.Start"
|
||||||
|
AdornmentIcon="@Icons.Material.Filled.Lightbulb"
|
||||||
|
AdornmentColor="Color.Info"
|
||||||
|
Validation="@this.providerValidation.ValidatingInstanceName"
|
||||||
|
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</MudForm>
|
||||||
|
<Issues IssuesData="@this.dataIssues"/>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">Cancel</MudButton>
|
||||||
|
<MudButton OnClick="@this.Store" Variant="Variant.Filled" Color="Color.Primary">
|
||||||
|
@if(this.IsEditing)
|
||||||
|
{
|
||||||
|
@:Update
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@:Add
|
||||||
|
}
|
||||||
|
</MudButton>
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
using AIStudio.Provider;
|
||||||
|
using AIStudio.Settings;
|
||||||
|
using AIStudio.Tools.Validation;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
using Host = AIStudio.Provider.SelfHosted.Host;
|
||||||
|
|
||||||
|
namespace AIStudio.Dialogs;
|
||||||
|
|
||||||
|
public partial class EmbeddingDialog : ComponentBase, ISecretId
|
||||||
|
{
|
||||||
|
[CascadingParameter]
|
||||||
|
private MudDialogInstance MudDialog { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The embedding's number in the list.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public uint DataNum { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The embedding's ID.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string DataId { get; set; } = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The user chosen name.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string DataName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The chosen hostname for self-hosted providers.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string DataHostname { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The host to use, e.g., llama.cpp.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Host DataHost { get; set; } = Host.NONE;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is this provider self-hosted?
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool IsSelfHosted { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The provider to use.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public LLMProviders DataLLMProvider { get; set; } = LLMProviders.NONE;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The embedding model to use.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Model DataModel { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should the dialog be in editing mode?
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool IsEditing { get; init; }
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private SettingsManager SettingsManager { get; init; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private ILogger<ProviderDialog> Logger { get; init; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private RustService RustService { get; init; } = null!;
|
||||||
|
|
||||||
|
private static readonly Dictionary<string, object?> SPELLCHECK_ATTRIBUTES = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The list of used instance names. We need this to check for uniqueness.
|
||||||
|
/// </summary>
|
||||||
|
private List<string> UsedInstanceNames { get; set; } = [];
|
||||||
|
|
||||||
|
private bool dataIsValid;
|
||||||
|
private string[] dataIssues = [];
|
||||||
|
private string dataAPIKey = string.Empty;
|
||||||
|
private string dataManuallyModel = string.Empty;
|
||||||
|
private string dataAPIKeyStorageIssue = string.Empty;
|
||||||
|
private string dataEditingPreviousInstanceName = string.Empty;
|
||||||
|
|
||||||
|
// We get the form reference from Blazor code to validate it manually:
|
||||||
|
private MudForm form = null!;
|
||||||
|
|
||||||
|
private readonly List<Model> availableModels = new();
|
||||||
|
private readonly Encryption encryption = Program.ENCRYPTION;
|
||||||
|
private readonly ProviderValidation providerValidation;
|
||||||
|
|
||||||
|
public EmbeddingDialog()
|
||||||
|
{
|
||||||
|
this.providerValidation = new()
|
||||||
|
{
|
||||||
|
GetProvider = () => this.DataLLMProvider,
|
||||||
|
GetAPIKeyStorageIssue = () => this.dataAPIKeyStorageIssue,
|
||||||
|
GetPreviousInstanceName = () => this.dataEditingPreviousInstanceName,
|
||||||
|
GetUsedInstanceNames = () => this.UsedInstanceNames,
|
||||||
|
GetHost = () => this.DataHost,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private EmbeddingProvider CreateEmbeddingProviderSettings()
|
||||||
|
{
|
||||||
|
var cleanedHostname = this.DataHostname.Trim();
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
Num = this.DataNum,
|
||||||
|
Id = this.DataId,
|
||||||
|
Name = this.DataName,
|
||||||
|
UsedLLMProvider = this.DataLLMProvider,
|
||||||
|
Model = this.DataLLMProvider is LLMProviders.SELF_HOSTED ? new Model(this.dataManuallyModel, null) : this.DataModel,
|
||||||
|
IsSelfHosted = this.DataLLMProvider is LLMProviders.SELF_HOSTED,
|
||||||
|
Hostname = cleanedHostname.EndsWith('/') ? cleanedHostname[..^1] : cleanedHostname,
|
||||||
|
Host = this.DataHost,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Overrides of ComponentBase
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
// Configure the spellchecking for the instance name input:
|
||||||
|
this.SettingsManager.InjectSpellchecking(SPELLCHECK_ATTRIBUTES);
|
||||||
|
|
||||||
|
// Load the used instance names:
|
||||||
|
this.UsedInstanceNames = this.SettingsManager.ConfigurationData.EmbeddingProviders.Select(x => x.Name.ToLowerInvariant()).ToList();
|
||||||
|
|
||||||
|
// When editing, we need to load the data:
|
||||||
|
if(this.IsEditing)
|
||||||
|
{
|
||||||
|
this.dataEditingPreviousInstanceName = this.DataName.ToLowerInvariant();
|
||||||
|
|
||||||
|
// When using self-hosted embedding, we must copy the model name:
|
||||||
|
if (this.DataLLMProvider is LLMProviders.SELF_HOSTED)
|
||||||
|
this.dataManuallyModel = this.DataModel.Id;
|
||||||
|
|
||||||
|
//
|
||||||
|
// We cannot load the API key for self-hosted providers:
|
||||||
|
//
|
||||||
|
if (this.DataLLMProvider is LLMProviders.SELF_HOSTED && this.DataHost is not Host.OLLAMA)
|
||||||
|
{
|
||||||
|
await this.ReloadModels();
|
||||||
|
await base.OnInitializedAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the API key:
|
||||||
|
var requestedSecret = await this.RustService.GetAPIKey(this, isTrying: this.DataLLMProvider is LLMProviders.SELF_HOSTED);
|
||||||
|
if (requestedSecret.Success)
|
||||||
|
this.dataAPIKey = await requestedSecret.Secret.Decrypt(this.encryption);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.dataAPIKey = string.Empty;
|
||||||
|
if (this.DataLLMProvider is not LLMProviders.SELF_HOSTED)
|
||||||
|
{
|
||||||
|
this.dataAPIKeyStorageIssue = $"Failed to load the API key from the operating system. The message was: {requestedSecret.Issue}. You might ignore this message and provide the API key again.";
|
||||||
|
await this.form.Validate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.ReloadModels();
|
||||||
|
}
|
||||||
|
|
||||||
|
await base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
// Reset the validation when not editing and on the first render.
|
||||||
|
// We don't want to show validation errors when the user opens the dialog.
|
||||||
|
if(!this.IsEditing && firstRender)
|
||||||
|
this.form.ResetValidation();
|
||||||
|
|
||||||
|
await base.OnAfterRenderAsync(firstRender);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation of ISecretId
|
||||||
|
|
||||||
|
public string SecretId => this.DataId;
|
||||||
|
|
||||||
|
public string SecretName => this.DataName;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private async Task Store()
|
||||||
|
{
|
||||||
|
await this.form.Validate();
|
||||||
|
if (!string.IsNullOrWhiteSpace(this.dataAPIKeyStorageIssue))
|
||||||
|
this.dataAPIKeyStorageIssue = string.Empty;
|
||||||
|
|
||||||
|
// When the data is not valid, we don't store it:
|
||||||
|
if (!this.dataIsValid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Use the data model to store the provider.
|
||||||
|
// We just return this data to the parent component:
|
||||||
|
var addedProviderSettings = this.CreateEmbeddingProviderSettings();
|
||||||
|
if (!string.IsNullOrWhiteSpace(this.dataAPIKey))
|
||||||
|
{
|
||||||
|
// Store the API key in the OS secure storage:
|
||||||
|
var storeResponse = await this.RustService.SetAPIKey(this, this.dataAPIKey);
|
||||||
|
if (!storeResponse.Success)
|
||||||
|
{
|
||||||
|
this.dataAPIKeyStorageIssue = $"Failed to store the API key in the operating system. The message was: {storeResponse.Issue}. Please try again.";
|
||||||
|
await this.form.Validate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.MudDialog.Close(DialogResult.Ok(addedProviderSettings));
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? ValidateManuallyModel(string manuallyModel)
|
||||||
|
{
|
||||||
|
if (this.DataLLMProvider is LLMProviders.SELF_HOSTED && string.IsNullOrWhiteSpace(manuallyModel))
|
||||||
|
return "Please enter an embedding model name.";
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel() => this.MudDialog.Cancel();
|
||||||
|
|
||||||
|
private async Task ReloadModels()
|
||||||
|
{
|
||||||
|
var currentEmbeddingProviderSettings = this.CreateEmbeddingProviderSettings();
|
||||||
|
var provider = currentEmbeddingProviderSettings.CreateProvider(this.Logger);
|
||||||
|
if(provider is NoProvider)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var models = await provider.GetEmbeddingModels(this.dataAPIKey);
|
||||||
|
|
||||||
|
// Order descending by ID means that the newest models probably come first:
|
||||||
|
var orderedModels = models.OrderByDescending(n => n.Id);
|
||||||
|
|
||||||
|
this.availableModels.Clear();
|
||||||
|
this.availableModels.AddRange(orderedModels);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string APIKeyText => this.DataLLMProvider switch
|
||||||
|
{
|
||||||
|
LLMProviders.SELF_HOSTED => "(Optional) API Key",
|
||||||
|
_ => "API Key",
|
||||||
|
};
|
||||||
|
|
||||||
|
private bool IsNoneProvider => this.DataLLMProvider is LLMProviders.NONE;
|
||||||
|
}
|
||||||
@@ -6,43 +6,43 @@
|
|||||||
<MudForm @ref="@this.form" @bind-IsValid="@this.dataIsValid" @bind-Errors="@this.dataIssues">
|
<MudForm @ref="@this.form" @bind-IsValid="@this.dataIsValid" @bind-Errors="@this.dataIssues">
|
||||||
<MudStack Row="@true" AlignItems="AlignItems.Center">
|
<MudStack Row="@true" AlignItems="AlignItems.Center">
|
||||||
@* ReSharper disable once CSharpWarnings::CS8974 *@
|
@* ReSharper disable once CSharpWarnings::CS8974 *@
|
||||||
<MudSelect @bind-Value="@this.DataProvider" Label="Provider" Class="mb-3" OpenIcon="@Icons.Material.Filled.AccountBalance" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.ValidatingProvider">
|
<MudSelect @bind-Value="@this.DataLLMProvider" Label="Provider" Class="mb-3" OpenIcon="@Icons.Material.Filled.AccountBalance" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.providerValidation.ValidatingProvider">
|
||||||
@foreach (Providers provider in Enum.GetValues(typeof(Providers)))
|
@foreach (LLMProviders provider in Enum.GetValues(typeof(LLMProviders)))
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="@provider">@provider</MudSelectItem>
|
<MudSelectItem Value="@provider">@provider</MudSelectItem>
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
<MudButton Disabled="@(!this.ShowRegisterButton)" Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.OpenInBrowser" Href="@this.GetProviderCreationURL()" Target="_blank">Create account</MudButton>
|
<MudButton Disabled="@(!this.DataLLMProvider.ShowRegisterButton())" Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.OpenInBrowser" Href="@this.DataLLMProvider.GetCreationURL()" Target="_blank">Create account</MudButton>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
|
||||||
@* ReSharper disable once CSharpWarnings::CS8974 *@
|
@* ReSharper disable once CSharpWarnings::CS8974 *@
|
||||||
<MudTextField
|
<MudTextField
|
||||||
T="string"
|
T="string"
|
||||||
@bind-Text="@this.dataAPIKey"
|
@bind-Text="@this.dataAPIKey"
|
||||||
Label="API Key"
|
Label="@this.APIKeyText"
|
||||||
Disabled="@(!this.NeedAPIKey)"
|
Disabled="@(!this.DataLLMProvider.IsAPIKeyNeeded(this.DataHost))"
|
||||||
Class="mb-3"
|
Class="mb-3"
|
||||||
Adornment="Adornment.Start"
|
Adornment="Adornment.Start"
|
||||||
AdornmentIcon="@Icons.Material.Filled.VpnKey"
|
AdornmentIcon="@Icons.Material.Filled.VpnKey"
|
||||||
AdornmentColor="Color.Info"
|
AdornmentColor="Color.Info"
|
||||||
InputType="InputType.Password"
|
InputType="InputType.Password"
|
||||||
Validation="@this.ValidatingAPIKey"
|
Validation="@this.providerValidation.ValidatingAPIKey"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MudTextField
|
<MudTextField
|
||||||
T="string"
|
T="string"
|
||||||
@bind-Text="@this.DataHostname"
|
@bind-Text="@this.DataHostname"
|
||||||
Label="Hostname"
|
Label="Hostname"
|
||||||
Disabled="@(!this.NeedHostname)"
|
Disabled="@(!this.DataLLMProvider.IsHostnameNeeded())"
|
||||||
Class="mb-3"
|
Class="mb-3"
|
||||||
Adornment="Adornment.Start"
|
Adornment="Adornment.Start"
|
||||||
AdornmentIcon="@Icons.Material.Filled.Dns"
|
AdornmentIcon="@Icons.Material.Filled.Dns"
|
||||||
AdornmentColor="Color.Info"
|
AdornmentColor="Color.Info"
|
||||||
Validation="@this.ValidatingHostname"
|
Validation="@this.providerValidation.ValidatingHostname"
|
||||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MudSelect Disabled="@(!this.NeedHost)" @bind-Value="@this.DataHost" Label="Host" Class="mb-3" OpenIcon="@Icons.Material.Filled.ExpandMore" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.ValidatingHost">
|
<MudSelect Disabled="@(!this.DataLLMProvider.IsHostNeeded())" @bind-Value="@this.DataHost" Label="Host" Class="mb-3" OpenIcon="@Icons.Material.Filled.ExpandMore" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.providerValidation.ValidatingHost">
|
||||||
@foreach (Host host in Enum.GetValues(typeof(Host)))
|
@foreach (Host host in Enum.GetValues(typeof(Host)))
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="@host">@host.Name()</MudSelectItem>
|
<MudSelectItem Value="@host">@host.Name()</MudSelectItem>
|
||||||
@@ -50,9 +50,9 @@
|
|||||||
</MudSelect>
|
</MudSelect>
|
||||||
|
|
||||||
<MudStack Row="@true" AlignItems="AlignItems.Center">
|
<MudStack Row="@true" AlignItems="AlignItems.Center">
|
||||||
@if (this.ProvideModelManually)
|
@if (this.DataLLMProvider.IsLLMModelProvidedManually())
|
||||||
{
|
{
|
||||||
<MudButton Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.OpenInBrowser" Href="@this.GetModelOverviewURL()" Target="_blank">Show available models</MudButton>
|
<MudButton Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.OpenInBrowser" Href="@this.DataLLMProvider.GetModelsOverviewURL()" Target="_blank">Show available models</MudButton>
|
||||||
<MudTextField
|
<MudTextField
|
||||||
T="string"
|
T="string"
|
||||||
@bind-Text="@this.dataManuallyModel"
|
@bind-Text="@this.dataManuallyModel"
|
||||||
@@ -67,8 +67,8 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<MudButton Disabled="@(!this.CanLoadModels())" Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.Refresh" OnClick="this.ReloadModels">Load</MudButton>
|
<MudButton Disabled="@(!this.DataLLMProvider.CanLoadModels(this.DataHost, this.dataAPIKey))" Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.Refresh" OnClick="this.ReloadModels">Load</MudButton>
|
||||||
<MudSelect Disabled="@this.IsNoneProvider" @bind-Value="@this.DataModel" Label="Model" Class="mb-3" OpenIcon="@Icons.Material.Filled.FaceRetouchingNatural" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.ValidatingModel">
|
<MudSelect Disabled="@this.IsNoneProvider" @bind-Value="@this.DataModel" Label="Model" Class="mb-3" OpenIcon="@Icons.Material.Filled.FaceRetouchingNatural" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.providerValidation.ValidatingModel">
|
||||||
@foreach (var model in this.availableModels)
|
@foreach (var model in this.availableModels)
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="@model">@model</MudSelectItem>
|
<MudSelectItem Value="@model">@model</MudSelectItem>
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
Adornment="Adornment.Start"
|
Adornment="Adornment.Start"
|
||||||
AdornmentIcon="@Icons.Material.Filled.Lightbulb"
|
AdornmentIcon="@Icons.Material.Filled.Lightbulb"
|
||||||
AdornmentColor="Color.Info"
|
AdornmentColor="Color.Info"
|
||||||
Validation="@this.ValidatingInstanceName"
|
Validation="@this.providerValidation.ValidatingInstanceName"
|
||||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using AIStudio.Provider;
|
using AIStudio.Provider;
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
|
using AIStudio.Tools.Validation;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@ namespace AIStudio.Dialogs;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The provider settings dialog.
|
/// The provider settings dialog.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ProviderDialog : ComponentBase
|
public partial class ProviderDialog : ComponentBase, ISecretId
|
||||||
{
|
{
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
private MudDialogInstance MudDialog { get; set; } = null!;
|
private MudDialogInstance MudDialog { get; set; } = null!;
|
||||||
@@ -41,7 +42,7 @@ public partial class ProviderDialog : ComponentBase
|
|||||||
public string DataHostname { get; set; } = string.Empty;
|
public string DataHostname { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The local host to use, e.g., llama.cpp.
|
/// The host to use, e.g., llama.cpp.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Host DataHost { get; set; } = Host.NONE;
|
public Host DataHost { get; set; } = Host.NONE;
|
||||||
@@ -56,7 +57,7 @@ public partial class ProviderDialog : ComponentBase
|
|||||||
/// The provider to use.
|
/// The provider to use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Providers DataProvider { get; set; } = Providers.NONE;
|
public LLMProviders DataLLMProvider { get; set; } = LLMProviders.NONE;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The LLM model to use, e.g., GPT-4o.
|
/// The LLM model to use, e.g., GPT-4o.
|
||||||
@@ -98,18 +99,35 @@ public partial class ProviderDialog : ComponentBase
|
|||||||
|
|
||||||
private readonly List<Model> availableModels = new();
|
private readonly List<Model> availableModels = new();
|
||||||
private readonly Encryption encryption = Program.ENCRYPTION;
|
private readonly Encryption encryption = Program.ENCRYPTION;
|
||||||
|
private readonly ProviderValidation providerValidation;
|
||||||
|
|
||||||
private Settings.Provider CreateProviderSettings() => new()
|
public ProviderDialog()
|
||||||
{
|
{
|
||||||
Num = this.DataNum,
|
this.providerValidation = new()
|
||||||
Id = this.DataId,
|
{
|
||||||
InstanceName = this.DataInstanceName,
|
GetProvider = () => this.DataLLMProvider,
|
||||||
UsedProvider = this.DataProvider,
|
GetAPIKeyStorageIssue = () => this.dataAPIKeyStorageIssue,
|
||||||
Model = this.DataProvider is Providers.FIREWORKS ? new Model(this.dataManuallyModel) : this.DataModel,
|
GetPreviousInstanceName = () => this.dataEditingPreviousInstanceName,
|
||||||
IsSelfHosted = this.DataProvider is Providers.SELF_HOSTED,
|
GetUsedInstanceNames = () => this.UsedInstanceNames,
|
||||||
Hostname = this.DataHostname.EndsWith('/') ? this.DataHostname[..^1] : this.DataHostname,
|
GetHost = () => this.DataHost,
|
||||||
Host = this.DataHost,
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
|
private Settings.Provider CreateProviderSettings()
|
||||||
|
{
|
||||||
|
var cleanedHostname = this.DataHostname.Trim();
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
Num = this.DataNum,
|
||||||
|
Id = this.DataId,
|
||||||
|
InstanceName = this.DataInstanceName,
|
||||||
|
UsedLLMProvider = this.DataLLMProvider,
|
||||||
|
Model = this.DataLLMProvider is LLMProviders.FIREWORKS ? new Model(this.dataManuallyModel, null) : this.DataModel,
|
||||||
|
IsSelfHosted = this.DataLLMProvider is LLMProviders.SELF_HOSTED,
|
||||||
|
Hostname = cleanedHostname.EndsWith('/') ? cleanedHostname[..^1] : cleanedHostname,
|
||||||
|
Host = this.DataHost,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#region Overrides of ComponentBase
|
#region Overrides of ComponentBase
|
||||||
|
|
||||||
@@ -127,41 +145,34 @@ public partial class ProviderDialog : ComponentBase
|
|||||||
this.dataEditingPreviousInstanceName = this.DataInstanceName.ToLowerInvariant();
|
this.dataEditingPreviousInstanceName = this.DataInstanceName.ToLowerInvariant();
|
||||||
|
|
||||||
// When using Fireworks, we must copy the model name:
|
// When using Fireworks, we must copy the model name:
|
||||||
if (this.DataProvider is Providers.FIREWORKS)
|
if (this.DataLLMProvider is LLMProviders.FIREWORKS)
|
||||||
this.dataManuallyModel = this.DataModel.Id;
|
this.dataManuallyModel = this.DataModel.Id;
|
||||||
|
|
||||||
//
|
//
|
||||||
// We cannot load the API key for self-hosted providers:
|
// We cannot load the API key for self-hosted providers:
|
||||||
//
|
//
|
||||||
if (this.DataProvider is Providers.SELF_HOSTED)
|
if (this.DataLLMProvider is LLMProviders.SELF_HOSTED && this.DataHost is not Host.OLLAMA)
|
||||||
{
|
{
|
||||||
await this.ReloadModels();
|
await this.ReloadModels();
|
||||||
await base.OnInitializedAsync();
|
await base.OnInitializedAsync();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var loadedProviderSettings = this.CreateProviderSettings();
|
|
||||||
var provider = loadedProviderSettings.CreateProvider(this.Logger);
|
|
||||||
if(provider is NoProvider)
|
|
||||||
{
|
|
||||||
await base.OnInitializedAsync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the API key:
|
// Load the API key:
|
||||||
var requestedSecret = await this.RustService.GetAPIKey(provider);
|
var requestedSecret = await this.RustService.GetAPIKey(this, isTrying: this.DataLLMProvider is LLMProviders.SELF_HOSTED);
|
||||||
if(requestedSecret.Success)
|
if (requestedSecret.Success)
|
||||||
{
|
|
||||||
this.dataAPIKey = await requestedSecret.Secret.Decrypt(this.encryption);
|
this.dataAPIKey = await requestedSecret.Secret.Decrypt(this.encryption);
|
||||||
|
|
||||||
// Now, we try to load the list of available models:
|
|
||||||
await this.ReloadModels();
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.dataAPIKeyStorageIssue = $"Failed to load the API key from the operating system. The message was: {requestedSecret.Issue}. You might ignore this message and provide the API key again.";
|
this.dataAPIKey = string.Empty;
|
||||||
await this.form.Validate();
|
if (this.DataLLMProvider is not LLMProviders.SELF_HOSTED)
|
||||||
|
{
|
||||||
|
this.dataAPIKeyStorageIssue = $"Failed to load the API key from the operating system. The message was: {requestedSecret.Issue}. You might ignore this message and provide the API key again.";
|
||||||
|
await this.form.Validate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.ReloadModels();
|
||||||
}
|
}
|
||||||
|
|
||||||
await base.OnInitializedAsync();
|
await base.OnInitializedAsync();
|
||||||
@@ -179,6 +190,14 @@ public partial class ProviderDialog : ComponentBase
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation of ISecretId
|
||||||
|
|
||||||
|
public string SecretId => this.DataLLMProvider.ToName();
|
||||||
|
|
||||||
|
public string SecretName => this.DataInstanceName;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
private async Task Store()
|
private async Task Store()
|
||||||
{
|
{
|
||||||
await this.form.Validate();
|
await this.form.Validate();
|
||||||
@@ -192,13 +211,10 @@ public partial class ProviderDialog : ComponentBase
|
|||||||
// Use the data model to store the provider.
|
// Use the data model to store the provider.
|
||||||
// We just return this data to the parent component:
|
// We just return this data to the parent component:
|
||||||
var addedProviderSettings = this.CreateProviderSettings();
|
var addedProviderSettings = this.CreateProviderSettings();
|
||||||
if (addedProviderSettings.UsedProvider != Providers.SELF_HOSTED)
|
if (!string.IsNullOrWhiteSpace(this.dataAPIKey))
|
||||||
{
|
{
|
||||||
// We need to instantiate the provider to store the API key:
|
|
||||||
var provider = addedProviderSettings.CreateProvider(this.Logger);
|
|
||||||
|
|
||||||
// Store the API key in the OS secure storage:
|
// Store the API key in the OS secure storage:
|
||||||
var storeResponse = await this.RustService.SetAPIKey(provider, this.dataAPIKey);
|
var storeResponse = await this.RustService.SetAPIKey(this, this.dataAPIKey);
|
||||||
if (!storeResponse.Success)
|
if (!storeResponse.Success)
|
||||||
{
|
{
|
||||||
this.dataAPIKeyStorageIssue = $"Failed to store the API key in the operating system. The message was: {storeResponse.Issue}. Please try again.";
|
this.dataAPIKeyStorageIssue = $"Failed to store the API key in the operating system. The message was: {storeResponse.Issue}. Please try again.";
|
||||||
@@ -210,91 +226,14 @@ public partial class ProviderDialog : ComponentBase
|
|||||||
this.MudDialog.Close(DialogResult.Ok(addedProviderSettings));
|
this.MudDialog.Close(DialogResult.Ok(addedProviderSettings));
|
||||||
}
|
}
|
||||||
|
|
||||||
private string? ValidatingProvider(Providers provider)
|
|
||||||
{
|
|
||||||
if (provider == Providers.NONE)
|
|
||||||
return "Please select a provider.";
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string? ValidatingHost(Host host)
|
|
||||||
{
|
|
||||||
if(this.DataProvider is not Providers.SELF_HOSTED)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (host == Host.NONE)
|
|
||||||
return "Please select a host.";
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string? ValidateManuallyModel(string manuallyModel)
|
private string? ValidateManuallyModel(string manuallyModel)
|
||||||
{
|
{
|
||||||
if (this.DataProvider is Providers.FIREWORKS && string.IsNullOrWhiteSpace(manuallyModel))
|
if (this.DataLLMProvider is LLMProviders.FIREWORKS && string.IsNullOrWhiteSpace(manuallyModel))
|
||||||
return "Please enter a model name.";
|
return "Please enter a model name.";
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string? ValidatingModel(Model model)
|
|
||||||
{
|
|
||||||
if(this.DataProvider is Providers.SELF_HOSTED && this.DataHost == Host.LLAMACPP)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (model == default)
|
|
||||||
return "Please select a model.";
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string? ValidatingInstanceName(string instanceName)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(instanceName))
|
|
||||||
return "Please enter an instance name.";
|
|
||||||
|
|
||||||
if (instanceName.Length > 40)
|
|
||||||
return "The instance name must not exceed 40 characters.";
|
|
||||||
|
|
||||||
// The instance name must be unique:
|
|
||||||
var lowerInstanceName = instanceName.ToLowerInvariant();
|
|
||||||
if (lowerInstanceName != this.dataEditingPreviousInstanceName && this.UsedInstanceNames.Contains(lowerInstanceName))
|
|
||||||
return "The instance name must be unique; the chosen name is already in use.";
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string? ValidatingAPIKey(string apiKey)
|
|
||||||
{
|
|
||||||
if(this.DataProvider is Providers.SELF_HOSTED)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if(!string.IsNullOrWhiteSpace(this.dataAPIKeyStorageIssue))
|
|
||||||
return this.dataAPIKeyStorageIssue;
|
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(apiKey))
|
|
||||||
return "Please enter an API key.";
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string? ValidatingHostname(string hostname)
|
|
||||||
{
|
|
||||||
if(this.DataProvider != Providers.SELF_HOSTED)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(hostname))
|
|
||||||
return "Please enter a hostname, e.g., http://localhost:1234";
|
|
||||||
|
|
||||||
if(!hostname.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase) && !hostname.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase))
|
|
||||||
return "The hostname must start with either http:// or https://";
|
|
||||||
|
|
||||||
if(!Uri.TryCreate(hostname, UriKind.Absolute, out _))
|
|
||||||
return "The hostname is not a valid HTTP(S) URL.";
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Cancel() => this.MudDialog.Cancel();
|
private void Cancel() => this.MudDialog.Cancel();
|
||||||
|
|
||||||
private async Task ReloadModels()
|
private async Task ReloadModels()
|
||||||
@@ -313,95 +252,11 @@ public partial class ProviderDialog : ComponentBase
|
|||||||
this.availableModels.AddRange(orderedModels);
|
this.availableModels.AddRange(orderedModels);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CanLoadModels()
|
private string APIKeyText => this.DataLLMProvider switch
|
||||||
{
|
{
|
||||||
if (this.DataProvider is Providers.SELF_HOSTED)
|
LLMProviders.SELF_HOSTED => "(Optional) API Key",
|
||||||
{
|
_ => "API Key",
|
||||||
switch (this.DataHost)
|
|
||||||
{
|
|
||||||
case Host.NONE:
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case Host.LLAMACPP:
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case Host.LM_STUDIO:
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case Host.OLLAMA:
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.DataProvider is Providers.NONE)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(this.dataAPIKey))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ShowRegisterButton => this.DataProvider switch
|
|
||||||
{
|
|
||||||
Providers.OPEN_AI => true,
|
|
||||||
Providers.MISTRAL => true,
|
|
||||||
Providers.ANTHROPIC => true,
|
|
||||||
|
|
||||||
Providers.FIREWORKS => true,
|
|
||||||
|
|
||||||
_ => false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private bool NeedAPIKey => this.DataProvider switch
|
private bool IsNoneProvider => this.DataLLMProvider is LLMProviders.NONE;
|
||||||
{
|
|
||||||
Providers.OPEN_AI => true,
|
|
||||||
Providers.MISTRAL => true,
|
|
||||||
Providers.ANTHROPIC => true,
|
|
||||||
|
|
||||||
Providers.FIREWORKS => true,
|
|
||||||
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
private bool NeedHostname => this.DataProvider switch
|
|
||||||
{
|
|
||||||
Providers.SELF_HOSTED => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
private bool NeedHost => this.DataProvider switch
|
|
||||||
{
|
|
||||||
Providers.SELF_HOSTED => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
private bool ProvideModelManually => this.DataProvider switch
|
|
||||||
{
|
|
||||||
Providers.FIREWORKS => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
private string GetModelOverviewURL() => this.DataProvider switch
|
|
||||||
{
|
|
||||||
Providers.FIREWORKS => "https://fireworks.ai/models?show=Serverless",
|
|
||||||
|
|
||||||
_ => string.Empty,
|
|
||||||
};
|
|
||||||
|
|
||||||
private string GetProviderCreationURL() => this.DataProvider switch
|
|
||||||
{
|
|
||||||
Providers.OPEN_AI => "https://platform.openai.com/signup",
|
|
||||||
Providers.MISTRAL => "https://console.mistral.ai/",
|
|
||||||
Providers.ANTHROPIC => "https://console.anthropic.com/dashboard",
|
|
||||||
|
|
||||||
Providers.FIREWORKS => "https://fireworks.ai/login",
|
|
||||||
|
|
||||||
_ => string.Empty,
|
|
||||||
};
|
|
||||||
|
|
||||||
private bool IsNoneProvider => this.DataProvider is Providers.NONE;
|
|
||||||
}
|
}
|
||||||
@@ -5,3 +5,4 @@ global using AIStudio.Tools;
|
|||||||
global using Microsoft.JSInterop;
|
global using Microsoft.JSInterop;
|
||||||
|
|
||||||
global using MudBlazor;
|
global using MudBlazor;
|
||||||
|
global using MudExtensions;
|
||||||
@@ -11,17 +11,17 @@
|
|||||||
<MudDrawerContainer Class="mud-height-full absolute">
|
<MudDrawerContainer Class="mud-height-full absolute">
|
||||||
<MudDrawer @bind-Open="@this.navBarOpen" MiniWidth="@NAVBAR_COLLAPSED_WIDTH" Width="@NAVBAR_EXPANDED_WIDTH" Elevation="1" Fixed="@true" Variant="@DrawerVariant.Mini" OpenMiniOnHover="@(this.SettingsManager.ConfigurationData.App.NavigationBehavior is NavBehavior.EXPAND_ON_HOVER)" Color="Color.Default">
|
<MudDrawer @bind-Open="@this.navBarOpen" MiniWidth="@NAVBAR_COLLAPSED_WIDTH" Width="@NAVBAR_EXPANDED_WIDTH" Elevation="1" Fixed="@true" Variant="@DrawerVariant.Mini" OpenMiniOnHover="@(this.SettingsManager.ConfigurationData.App.NavigationBehavior is NavBehavior.EXPAND_ON_HOVER)" Color="Color.Default">
|
||||||
<MudNavMenu>
|
<MudNavMenu>
|
||||||
@foreach (var navBarItem in NAV_ITEMS)
|
@foreach (var navBarItem in this.navItems)
|
||||||
{
|
{
|
||||||
if (this.SettingsManager.ConfigurationData.App.NavigationBehavior is NavBehavior.NEVER_EXPAND_USE_TOOLTIPS)
|
if (this.SettingsManager.ConfigurationData.App.NavigationBehavior is NavBehavior.NEVER_EXPAND_USE_TOOLTIPS)
|
||||||
{
|
{
|
||||||
<MudTooltip Text="@navBarItem.Name" Placement="Placement.Right">
|
<MudTooltip Text="@navBarItem.Name" Placement="Placement.Right">
|
||||||
<MudNavLink Href="@navBarItem.Path" Match="@(navBarItem.MatchAll ? NavLinkMatch.All : NavLinkMatch.Prefix)" Icon="@navBarItem.Icon" IconColor="@navBarItem.IconColor">@navBarItem.Name</MudNavLink>
|
<MudNavLink Href="@navBarItem.Path" Match="@(navBarItem.MatchAll ? NavLinkMatch.All : NavLinkMatch.Prefix)" Icon="@navBarItem.Icon" Style="@navBarItem.SetColorStyle(this.SettingsManager)" Class="custom-icon-color">@navBarItem.Name</MudNavLink>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<MudNavLink Href="@navBarItem.Path" Match="@(navBarItem.MatchAll ? NavLinkMatch.All : NavLinkMatch.Prefix)" Icon="@navBarItem.Icon" IconColor="@navBarItem.IconColor">@navBarItem.Name</MudNavLink>
|
<MudNavLink Href="@navBarItem.Path" Match="@(navBarItem.MatchAll ? NavLinkMatch.All : NavLinkMatch.Prefix)" Icon="@navBarItem.Icon" Style="@navBarItem.SetColorStyle(this.SettingsManager)" Class="custom-icon-color">@navBarItem.Name</MudNavLink>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</MudNavMenu>
|
</MudNavMenu>
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
<MudMainContent Class="mud-height-full pt-1" Style="@this.PaddingLeft">
|
<MudMainContent Class="mud-height-full pt-1" Style="@this.PaddingLeft">
|
||||||
<MudContainer Fixed="@true" Class="mud-height-full" Style="margin-left: 5em; width: calc(100% - 5em);">
|
<MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="mud-height-full" Style="margin-left: 5em; width: calc(100% - 5em);">
|
||||||
@if (!this.performingUpdate && this.IsUpdateAlertVisible)
|
@if (!this.performingUpdate && this.IsUpdateAlertVisible)
|
||||||
{
|
{
|
||||||
<MudAlert NoIcon="@true" Severity="Severity.Info" Variant="Variant.Filled" ShowCloseIcon="@true" Dense="@true" CloseIconClicked="() => this.DismissUpdate()" Class="mt-2 mb-2">
|
<MudAlert NoIcon="@true" Severity="Severity.Info" Variant="Variant.Filled" ShowCloseIcon="@true" Dense="@true" CloseIconClicked="() => this.DismissUpdate()" Class="mt-2 mb-2">
|
||||||
@@ -59,3 +59,5 @@
|
|||||||
</MudMainContent>
|
</MudMainContent>
|
||||||
</MudLayout>
|
</MudLayout>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
|
||||||
|
<MudThemeProvider @ref="@this.themeProvider" Theme="@this.ColorTheme" IsDarkMode="@this.useDarkMode" />
|
||||||
@@ -35,6 +35,9 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
|
|||||||
[Inject]
|
[Inject]
|
||||||
private ILogger<MainLayout> Logger { get; init; } = null!;
|
private ILogger<MainLayout> Logger { get; init; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private MudTheme ColorTheme { get; init; } = null!;
|
||||||
|
|
||||||
public string AdditionalHeight { get; private set; } = "0em";
|
public string AdditionalHeight { get; private set; } = "0em";
|
||||||
|
|
||||||
private string PaddingLeft => this.navBarOpen ? $"padding-left: {NAVBAR_EXPANDED_WIDTH_INT - NAVBAR_COLLAPSED_WIDTH_INT}em;" : "padding-left: 0em;";
|
private string PaddingLeft => this.navBarOpen ? $"padding-left: {NAVBAR_EXPANDED_WIDTH_INT - NAVBAR_COLLAPSED_WIDTH_INT}em;" : "padding-left: 0em;";
|
||||||
@@ -50,16 +53,10 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
|
|||||||
private bool userDismissedUpdate;
|
private bool userDismissedUpdate;
|
||||||
private string updateToVersion = string.Empty;
|
private string updateToVersion = string.Empty;
|
||||||
private UpdateResponse? currentUpdateResponse;
|
private UpdateResponse? currentUpdateResponse;
|
||||||
|
private MudThemeProvider themeProvider = null!;
|
||||||
|
private bool useDarkMode;
|
||||||
|
|
||||||
private static readonly IReadOnlyCollection<NavBarItem> NAV_ITEMS = new List<NavBarItem>
|
private IReadOnlyCollection<NavBarItem> navItems = [];
|
||||||
{
|
|
||||||
new("Home", Icons.Material.Filled.Home, Color.Default, Routes.HOME, true),
|
|
||||||
new("Chat", Icons.Material.Filled.Chat, Color.Default, Routes.CHAT, false),
|
|
||||||
new("Assistants", Icons.Material.Filled.Apps, Color.Default, Routes.ASSISTANTS, false),
|
|
||||||
new("Supporters", Icons.Material.Filled.Favorite, Color.Error, Routes.SUPPORTERS, false),
|
|
||||||
new("About", Icons.Material.Filled.Info, Color.Default, Routes.ABOUT, false),
|
|
||||||
new("Settings", Icons.Material.Filled.Settings, Color.Default, Routes.SETTINGS, false),
|
|
||||||
};
|
|
||||||
|
|
||||||
#region Overrides of ComponentBase
|
#region Overrides of ComponentBase
|
||||||
|
|
||||||
@@ -87,7 +84,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
|
|||||||
|
|
||||||
// Register this component with the message bus:
|
// Register this component with the message bus:
|
||||||
this.MessageBus.RegisterComponent(this);
|
this.MessageBus.RegisterComponent(this);
|
||||||
this.MessageBus.ApplyFilters(this, [], [ Event.UPDATE_AVAILABLE, Event.USER_SEARCH_FOR_UPDATE, Event.CONFIGURATION_CHANGED ]);
|
this.MessageBus.ApplyFilters(this, [], [ Event.UPDATE_AVAILABLE, Event.USER_SEARCH_FOR_UPDATE, Event.CONFIGURATION_CHANGED, Event.COLOR_THEME_CHANGED ]);
|
||||||
|
|
||||||
// Set the snackbar for the update service:
|
// Set the snackbar for the update service:
|
||||||
UpdateService.SetBlazorDependencies(this.Snackbar);
|
UpdateService.SetBlazorDependencies(this.Snackbar);
|
||||||
@@ -97,9 +94,44 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
|
|||||||
if(this.SettingsManager.ConfigurationData.App.NavigationBehavior is NavBehavior.ALWAYS_EXPAND)
|
if(this.SettingsManager.ConfigurationData.App.NavigationBehavior is NavBehavior.ALWAYS_EXPAND)
|
||||||
this.navBarOpen = true;
|
this.navBarOpen = true;
|
||||||
|
|
||||||
|
await this.themeProvider.WatchSystemPreference(this.SystemeThemeChanged);
|
||||||
|
await this.UpdateThemeConfiguration();
|
||||||
|
this.LoadNavItems();
|
||||||
|
|
||||||
await base.OnInitializedAsync();
|
await base.OnInitializedAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void LoadNavItems()
|
||||||
|
{
|
||||||
|
var palette = this.ColorTheme.GetCurrentPalette(this.SettingsManager);
|
||||||
|
var isWriterModePreviewEnabled = PreviewFeatures.PRE_WRITER_MODE_2024.IsEnabled(this.SettingsManager);
|
||||||
|
if (!isWriterModePreviewEnabled)
|
||||||
|
{
|
||||||
|
this.navItems = new List<NavBarItem>
|
||||||
|
{
|
||||||
|
new("Home", Icons.Material.Filled.Home, palette.DarkLighten, palette.GrayLight, Routes.HOME, true),
|
||||||
|
new("Chat", Icons.Material.Filled.Chat, palette.DarkLighten, palette.GrayLight, Routes.CHAT, false),
|
||||||
|
new("Assistants", Icons.Material.Filled.Apps, palette.DarkLighten, palette.GrayLight, Routes.ASSISTANTS, false),
|
||||||
|
new("Supporters", Icons.Material.Filled.Favorite, palette.Error.Value, "#801a00", Routes.SUPPORTERS, false),
|
||||||
|
new("About", Icons.Material.Filled.Info, palette.DarkLighten, palette.GrayLight, Routes.ABOUT, false),
|
||||||
|
new("Settings", Icons.Material.Filled.Settings, palette.DarkLighten, palette.GrayLight, Routes.SETTINGS, false),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.navItems = new List<NavBarItem>
|
||||||
|
{
|
||||||
|
new("Home", Icons.Material.Filled.Home, palette.DarkLighten, palette.GrayLight, Routes.HOME, true),
|
||||||
|
new("Chat", Icons.Material.Filled.Chat, palette.DarkLighten, palette.GrayLight, Routes.CHAT, false),
|
||||||
|
new("Assistants", Icons.Material.Filled.Apps, palette.DarkLighten, palette.GrayLight, Routes.ASSISTANTS, false),
|
||||||
|
new("Writer", Icons.Material.Filled.Create, palette.DarkLighten, palette.GrayLight, Routes.WRITER, false),
|
||||||
|
new("Supporters", Icons.Material.Filled.Favorite, palette.Error.Value, "#801a00", Routes.SUPPORTERS, false),
|
||||||
|
new("About", Icons.Material.Filled.Info, palette.DarkLighten, palette.GrayLight, Routes.ABOUT, false),
|
||||||
|
new("Settings", Icons.Material.Filled.Settings, palette.DarkLighten, palette.GrayLight, Routes.SETTINGS, false),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Implementation of IMessageBusReceiver
|
#region Implementation of IMessageBusReceiver
|
||||||
@@ -131,6 +163,12 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
|
|||||||
else
|
else
|
||||||
this.navBarOpen = false;
|
this.navBarOpen = false;
|
||||||
|
|
||||||
|
await this.UpdateThemeConfiguration();
|
||||||
|
this.LoadNavItems();
|
||||||
|
this.StateHasChanged();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Event.COLOR_THEME_CHANGED:
|
||||||
this.StateHasChanged();
|
this.StateHasChanged();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -218,6 +256,24 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task SystemeThemeChanged(bool isDark)
|
||||||
|
{
|
||||||
|
this.Logger.LogInformation($"The system theme changed to {(isDark ? "dark" : "light")}.");
|
||||||
|
await this.UpdateThemeConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateThemeConfiguration()
|
||||||
|
{
|
||||||
|
if (this.SettingsManager.ConfigurationData.App.PreferredTheme is Themes.SYSTEM)
|
||||||
|
this.useDarkMode = await this.themeProvider.GetSystemPreference();
|
||||||
|
else
|
||||||
|
this.useDarkMode = this.SettingsManager.ConfigurationData.App.PreferredTheme == Themes.DARK;
|
||||||
|
|
||||||
|
this.SettingsManager.IsDarkMode = this.useDarkMode;
|
||||||
|
await this.MessageBus.SendMessage<bool>(this, Event.COLOR_THEME_CHANGED);
|
||||||
|
this.StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
#region Implementation of IDisposable
|
#region Implementation of IDisposable
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
using AIStudio.Settings;
|
||||||
|
|
||||||
namespace AIStudio.Layout;
|
namespace AIStudio.Layout;
|
||||||
|
|
||||||
public record NavBarItem(string Name, string Icon, Color IconColor, string Path, bool MatchAll);
|
public record NavBarItem(string Name, string Icon, string IconLightColor, string IconDarkColor, string Path, bool MatchAll)
|
||||||
|
{
|
||||||
|
public string SetColorStyle(SettingsManager settingsManager) => $"--custom-icon-color: {(settingsManager.IsDarkMode ? this.IconDarkColor : this.IconLightColor)};";
|
||||||
|
}
|
||||||
@@ -45,10 +45,11 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.65" />
|
<PackageReference Include="CodeBeam.MudBlazor.Extensions" Version="7.1.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="8.0.8" />
|
<PackageReference Include="HtmlAgilityPack" Version="1.11.71" />
|
||||||
<PackageReference Include="MudBlazor" Version="7.8.0" />
|
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.0" />
|
||||||
<PackageReference Include="MudBlazor.Markdown" Version="7.8.0" />
|
<PackageReference Include="MudBlazor" Version="7.15.0" />
|
||||||
|
<PackageReference Include="MudBlazor.Markdown" Version="7.14.0" />
|
||||||
<PackageReference Include="ReverseMarkdown" Version="4.6.0" />
|
<PackageReference Include="ReverseMarkdown" Version="4.6.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
<ThirdPartyComponent Name=".NET" Developer="Microsoft & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/dotnet/runtime/blob/main/LICENSE.TXT" RepositoryUrl="https://github.com/dotnet" UseCase="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."/>
|
<ThirdPartyComponent Name=".NET" Developer="Microsoft & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/dotnet/runtime/blob/main/LICENSE.TXT" RepositoryUrl="https://github.com/dotnet" UseCase="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."/>
|
||||||
<ThirdPartyComponent Name="MudBlazor" Developer="Jonny Larsson, Meinrad Recheis & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/MudBlazor/MudBlazor/blob/dev/LICENSE" RepositoryUrl="https://github.com/MudBlazor/MudBlazor/" UseCase="Building on .NET, ASP.NET Core, and Blazor, MudBlazor is used as a library for designing and developing the user interface. It is a great project that significantly accelerates the development of advanced user interfaces with Blazor."/>
|
<ThirdPartyComponent Name="MudBlazor" Developer="Jonny Larsson, Meinrad Recheis & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/MudBlazor/MudBlazor/blob/dev/LICENSE" RepositoryUrl="https://github.com/MudBlazor/MudBlazor/" UseCase="Building on .NET, ASP.NET Core, and Blazor, MudBlazor is used as a library for designing and developing the user interface. It is a great project that significantly accelerates the development of advanced user interfaces with Blazor."/>
|
||||||
<ThirdPartyComponent Name="MudBlazor.Markdown" Developer="My Nihongo & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/MyNihongo/MudBlazor.Markdown/blob/main/LICENSE" RepositoryUrl="https://github.com/MyNihongo/MudBlazor.Markdown" UseCase="This component is used to render Markdown text. This is important because the LLM often responds with Markdown-formatted text, allowing us to present it in a way that is easier to read."/>
|
<ThirdPartyComponent Name="MudBlazor.Markdown" Developer="My Nihongo & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/MyNihongo/MudBlazor.Markdown/blob/main/LICENSE" RepositoryUrl="https://github.com/MyNihongo/MudBlazor.Markdown" UseCase="This component is used to render Markdown text. This is important because the LLM often responds with Markdown-formatted text, allowing us to present it in a way that is easier to read."/>
|
||||||
|
<ThirdPartyComponent Name="CodeBeam.MudBlazor.Extensions" Developer="Mehmet Can Karagöz & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/CodeBeamOrg/CodeBeam.MudBlazor.Extensions/blob/dev/LICENSE" RepositoryUrl="https://github.com/CodeBeamOrg/CodeBeam.MudBlazor.Extensions" UseCase="This library is used to extend the MudBlazor library. It provides additional components that are not part of the MudBlazor library."/>
|
||||||
<ThirdPartyComponent Name="Rust" Developer="Graydon Hoare, Rust Foundation, Rust developers & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rust-lang/rust/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/rust-lang/rust" UseCase="The .NET backend cannot be started as a desktop app. Therefore, I use a second backend in Rust, which I call runtime. With Rust as the runtime, Tauri can be used to realize a typical desktop app. Thanks to Rust, this app can be offered for Windows, macOS, and Linux desktops. Rust is a great language for developing safe and high-performance software."/>
|
<ThirdPartyComponent Name="Rust" Developer="Graydon Hoare, Rust Foundation, Rust developers & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rust-lang/rust/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/rust-lang/rust" UseCase="The .NET backend cannot be started as a desktop app. Therefore, I use a second backend in Rust, which I call runtime. With Rust as the runtime, Tauri can be used to realize a typical desktop app. Thanks to Rust, this app can be offered for Windows, macOS, and Linux desktops. Rust is a great language for developing safe and high-performance software."/>
|
||||||
<ThirdPartyComponent Name="Tauri" Developer="Daniel Thompson-Yvetot, Lucas Nogueira, Tensor, Boscop, Serge Zaitsev, George Burton & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/tauri-apps/tauri/blob/dev/LICENSE_MIT" RepositoryUrl="https://github.com/tauri-apps/tauri" UseCase="Tauri is used to host the Blazor user interface. It is a great project that allows the creation of desktop applications using web technologies. I love Tauri!"/>
|
<ThirdPartyComponent Name="Tauri" Developer="Daniel Thompson-Yvetot, Lucas Nogueira, Tensor, Boscop, Serge Zaitsev, George Burton & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/tauri-apps/tauri/blob/dev/LICENSE_MIT" RepositoryUrl="https://github.com/tauri-apps/tauri" UseCase="Tauri is used to host the Blazor user interface. It is a great project that allows the creation of desktop applications using web technologies. I love Tauri!"/>
|
||||||
<ThirdPartyComponent Name="Rocket" Developer="Sergio Benitez & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rwf2/Rocket/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/rwf2/Rocket" UseCase="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."/>
|
<ThirdPartyComponent Name="Rocket" Developer="Sergio Benitez & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/rwf2/Rocket/blob/master/LICENSE-MIT" RepositoryUrl="https://github.com/rwf2/Rocket" UseCase="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."/>
|
||||||
|
|||||||
@@ -24,10 +24,18 @@
|
|||||||
<AssistantBlock Name="E-Mail" Description="Generate an e-mail for a given context." Icon="@Icons.Material.Filled.Email" Link="@Routes.ASSISTANT_EMAIL"/>
|
<AssistantBlock Name="E-Mail" Description="Generate an e-mail for a given context." Icon="@Icons.Material.Filled.Email" Link="@Routes.ASSISTANT_EMAIL"/>
|
||||||
<AssistantBlock Name="My Tasks" Description="Analyze a text or an email for tasks you need to complete." Icon="@Icons.Material.Filled.Task" Link="@Routes.ASSISTANT_MY_TASKS"/>
|
<AssistantBlock Name="My Tasks" Description="Analyze a text or an email for tasks you need to complete." Icon="@Icons.Material.Filled.Task" Link="@Routes.ASSISTANT_MY_TASKS"/>
|
||||||
<AssistantBlock Name="Agenda Planner" Description="Generate an agenda for a given meeting, seminar, etc." Icon="@Icons.Material.Filled.CalendarToday" Link="@Routes.ASSISTANT_AGENDA"/>
|
<AssistantBlock Name="Agenda Planner" Description="Generate an agenda for a given meeting, seminar, etc." Icon="@Icons.Material.Filled.CalendarToday" Link="@Routes.ASSISTANT_AGENDA"/>
|
||||||
|
<AssistantBlock Name="Job Posting" Description="Generate a job posting for a given job description." Icon="@Icons.Material.Filled.Work" Link="@Routes.ASSISTANT_JOB_POSTING"/>
|
||||||
<AssistantBlock Name="Legal Check" Description="Ask a question about a legal document." Icon="@Icons.Material.Filled.Gavel" Link="@Routes.ASSISTANT_LEGAL_CHECK"/>
|
<AssistantBlock Name="Legal Check" Description="Ask a question about a legal document." Icon="@Icons.Material.Filled.Gavel" Link="@Routes.ASSISTANT_LEGAL_CHECK"/>
|
||||||
<AssistantBlock Name="Icon Finder" Description="Using a LLM to find an icon for a given context." Icon="@Icons.Material.Filled.FindInPage" Link="@Routes.ASSISTANT_ICON_FINDER"/>
|
<AssistantBlock Name="Icon Finder" Description="Using a LLM to find an icon for a given context." Icon="@Icons.Material.Filled.FindInPage" Link="@Routes.ASSISTANT_ICON_FINDER"/>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
|
||||||
|
<MudText Typo="Typo.h4" Class="mb-2 mr-3 mt-6">
|
||||||
|
Learning
|
||||||
|
</MudText>
|
||||||
|
<MudStack Row="@true" Wrap="@Wrap.Wrap" Class="mb-3">
|
||||||
|
<AssistantBlock Name="Bias of the Day" Description="Learn about one cognitive bias every day." Icon="@Icons.Material.Filled.Psychology" Link="@Routes.ASSISTANT_BIAS"/>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
<MudText Typo="Typo.h4" Class="mb-2 mr-3 mt-6">
|
<MudText Typo="Typo.h4" Class="mb-2 mr-3 mt-6">
|
||||||
Software Engineering
|
Software Engineering
|
||||||
</MudText>
|
</MudText>
|
||||||
|
|||||||
@@ -16,79 +16,125 @@
|
|||||||
</MudText>
|
</MudText>
|
||||||
|
|
||||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings"/>
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings"/>
|
||||||
<InnerScrolling @ref="@this.scrollingArea" HeaderHeight="12.3em">
|
<MudStack Row="@true" Style="width: 100%; overflow: hidden;">
|
||||||
<ChildContent>
|
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is not WorkspaceStorageBehavior.DISABLE_WORKSPACES
|
||||||
@if (this.chatThread is not null)
|
&& this.SettingsManager.ConfigurationData.Workspace.DisplayBehavior is WorkspaceDisplayBehavior.TOGGLE_SIDEBAR
|
||||||
|
&& !this.SettingsManager.ConfigurationData.Workspace.IsSidebarVisible)
|
||||||
|
{
|
||||||
|
<MudPaper Class="border border-solid rounded-lg">
|
||||||
|
<MudTooltip Text="Show your workspaces" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||||
|
<MudIconButton Size="Size.Large" Icon="@this.WorkspaceSidebarToggleIcon" OnClick="() => this.ToggleWorkspaceSidebar()"/>
|
||||||
|
</MudTooltip>
|
||||||
|
</MudPaper>
|
||||||
|
}
|
||||||
|
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is not WorkspaceStorageBehavior.DISABLE_WORKSPACES)
|
||||||
|
{
|
||||||
|
@if ((this.SettingsManager.ConfigurationData.Workspace.DisplayBehavior is WorkspaceDisplayBehavior.TOGGLE_SIDEBAR && this.SettingsManager.ConfigurationData.Workspace.IsSidebarVisible) || this.SettingsManager.ConfigurationData.Workspace.DisplayBehavior is WorkspaceDisplayBehavior.SIDEBAR_ALWAYS_VISIBLE)
|
||||||
{
|
{
|
||||||
foreach (var block in this.chatThread.Blocks.OrderBy(n => n.Time))
|
@if (this.SettingsManager.ConfigurationData.Workspace.DisplayBehavior is WorkspaceDisplayBehavior.TOGGLE_SIDEBAR && this.SettingsManager.ConfigurationData.Workspace.IsSidebarVisible)
|
||||||
{
|
{
|
||||||
<ContentBlockComponent Role="@block.Role" Type="@block.ContentType" Time="@block.Time" Content="@block.Content"/>
|
<InnerScrolling HeaderHeight="12.3em" Class="border border-solid rounded-lg">
|
||||||
|
<HeaderContent>
|
||||||
|
<MudTooltip Text="Hide your workspaces" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||||
|
<MudIconButton Size="Size.Large" Icon="@this.WorkspaceSidebarToggleIcon" OnClick="() => this.ToggleWorkspaceSidebar()"/>
|
||||||
|
</MudTooltip>
|
||||||
|
</HeaderContent>
|
||||||
|
<ChildContent>
|
||||||
|
<Workspaces @ref="this.workspaces" @bind-CurrentChatThread="@this.chatThread" LoadedChatWasChanged="this.LoadedChatChanged"/>
|
||||||
|
</ChildContent>
|
||||||
|
</InnerScrolling>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<InnerScrolling HeaderHeight="12.3em" Class="border border-solid rounded-lg">
|
||||||
|
<ChildContent>
|
||||||
|
<Workspaces @ref="this.workspaces" @bind-CurrentChatThread="@this.chatThread" LoadedChatWasChanged="this.LoadedChatChanged"/>
|
||||||
|
</ChildContent>
|
||||||
|
</InnerScrolling>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</ChildContent>
|
}
|
||||||
<FooterContent>
|
<InnerScrolling FillEntireHorizontalSpace="@true" @ref="@this.scrollingArea" HeaderHeight="12.3em">
|
||||||
<MudElement Style="flex: 0 0 auto;">
|
<ChildContent>
|
||||||
<MudTextField T="string" @ref="@this.inputField" @bind-Text="@this.userInput" Variant="Variant.Outlined" AutoGrow="@true" Lines="3" MaxLines="12" Label="@this.InputLabel" Placeholder="@this.ProviderPlaceholder" Adornment="Adornment.End" AdornmentIcon="@Icons.Material.Filled.Send" OnAdornmentClick="() => this.SendMessage()" ReadOnly="!this.IsProviderSelected || this.isStreaming" Immediate="@true" OnKeyUp="this.InputKeyEvent" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="@this.UserInputClass" Style="@this.UserInputStyle"/>
|
@if (this.chatThread is not null)
|
||||||
</MudElement>
|
|
||||||
<MudToolBar WrapContent="true" Gutters="@false" Class="border border-solid rounded" Style="border-color: lightgrey;">
|
|
||||||
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is not WorkspaceStorageBehavior.DISABLE_WORKSPACES)
|
|
||||||
{
|
{
|
||||||
<MudTooltip Text="Your workspaces" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
foreach (var block in this.chatThread.Blocks.OrderBy(n => n.Time))
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.SnippetFolder" OnClick="() => this.ToggleWorkspaces()"/>
|
{
|
||||||
|
@if (!block.HideFromUser)
|
||||||
|
{
|
||||||
|
<ContentBlockComponent Role="@block.Role" Type="@block.ContentType" Time="@block.Time" Content="@block.Content"/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</ChildContent>
|
||||||
|
<FooterContent>
|
||||||
|
<MudElement Style="flex: 0 0 auto;">
|
||||||
|
<MudTextField T="string" @ref="@this.inputField" @bind-Text="@this.userInput" Variant="Variant.Outlined" AutoGrow="@true" Lines="3" MaxLines="12" Label="@this.InputLabel" Placeholder="@this.ProviderPlaceholder" Adornment="Adornment.End" AdornmentIcon="@Icons.Material.Filled.Send" OnAdornmentClick="() => this.SendMessage()" ReadOnly="!this.IsProviderSelected || this.isStreaming" Immediate="@true" OnKeyUp="this.InputKeyEvent" UserAttributes="@USER_INPUT_ATTRIBUTES" Class="@this.UserInputClass" Style="@this.UserInputStyle"/>
|
||||||
|
</MudElement>
|
||||||
|
<MudToolBar WrapContent="true" Gutters="@false" Class="border border-solid rounded" Style="border-color: lightgrey;">
|
||||||
|
@if (
|
||||||
|
this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is not WorkspaceStorageBehavior.DISABLE_WORKSPACES
|
||||||
|
&& this.SettingsManager.ConfigurationData.Workspace.DisplayBehavior is WorkspaceDisplayBehavior.TOGGLE_OVERLAY)
|
||||||
|
{
|
||||||
|
<MudTooltip Text="Show your workspaces" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.SnippetFolder" OnClick="() => this.ToggleWorkspaceOverlay()"/>
|
||||||
|
</MudTooltip>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_MANUALLY)
|
||||||
|
{
|
||||||
|
<MudTooltip Text="Save chat" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Save" OnClick="() => this.SaveThread()" Disabled="@(!this.CanThreadBeSaved)"/>
|
||||||
|
</MudTooltip>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudTooltip Text="Start temporary chat" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.AddComment" OnClick="() => this.StartNewChat(useSameWorkspace: false)"/>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
|
||||||
|
|
||||||
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_MANUALLY)
|
@if (!string.IsNullOrWhiteSpace(this.currentWorkspaceName))
|
||||||
{
|
{
|
||||||
<MudTooltip Text="Save chat" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
<MudTooltip Text="@this.TooltipAddChatToWorkspace" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Save" OnClick="() => this.SaveThread()" Disabled="@(!this.CanThreadBeSaved)"/>
|
<MudIconButton Icon="@Icons.Material.Filled.CommentBank" OnClick="() => this.StartNewChat(useSameWorkspace: true)"/>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
|
|
||||||
<MudTooltip Text="Start temporary chat" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY)
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.AddComment" OnClick="() => this.StartNewChat(useSameWorkspace: false)"/>
|
{
|
||||||
</MudTooltip>
|
<MudTooltip Text="Delete this chat & start a new one" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Refresh" OnClick="() => this.StartNewChat(useSameWorkspace: true, deletePreviousChat: true)" Disabled="@(!this.CanThreadBeSaved)"/>
|
||||||
|
</MudTooltip>
|
||||||
|
}
|
||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(this.currentWorkspaceName))
|
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is not WorkspaceStorageBehavior.DISABLE_WORKSPACES)
|
||||||
{
|
{
|
||||||
<MudTooltip Text="@this.TooltipAddChatToWorkspace" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
<MudTooltip Text="Move the chat to a workspace, or to another if it is already in one." Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.CommentBank" OnClick="() => this.StartNewChat(useSameWorkspace: true)"/>
|
<MudIconButton Icon="@Icons.Material.Filled.MoveToInbox" Disabled="@(!this.CanThreadBeSaved)" OnClick="() => this.MoveChatToWorkspace()"/>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY)
|
@if (this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence)
|
||||||
{
|
{
|
||||||
<MudTooltip Text="Delete this chat & start a new one" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
<ConfidenceInfo Mode="ConfidenceInfoMode.ICON" LLMProvider="@this.providerSettings.UsedLLMProvider"/>
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Refresh" OnClick="() => this.StartNewChat(useSameWorkspace: true, deletePreviousChat: true)" Disabled="@(!this.CanThreadBeSaved)"/>
|
}
|
||||||
</MudTooltip>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is not WorkspaceStorageBehavior.DISABLE_WORKSPACES)
|
<ProfileSelection CurrentProfile="@this.currentProfile" CurrentProfileChanged="@this.ProfileWasChanged" />
|
||||||
{
|
</MudToolBar>
|
||||||
<MudTooltip Text="Move the chat to a workspace, or to another if it is already in one." Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
</FooterContent>
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.MoveToInbox" Disabled="@(!this.CanThreadBeSaved)" OnClick="() => this.MoveChatToWorkspace()"/>
|
</InnerScrolling>
|
||||||
</MudTooltip>
|
</MudStack>
|
||||||
}
|
|
||||||
|
|
||||||
@if (this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence)
|
@if (
|
||||||
{
|
this.SettingsManager.ConfigurationData.Workspace.StorageBehavior != WorkspaceStorageBehavior.DISABLE_WORKSPACES
|
||||||
<ConfidenceInfo Mode="ConfidenceInfoMode.ICON" Provider="@this.providerSettings.UsedProvider"/>
|
&& this.SettingsManager.ConfigurationData.Workspace.DisplayBehavior is WorkspaceDisplayBehavior.TOGGLE_OVERLAY)
|
||||||
}
|
|
||||||
|
|
||||||
<ProfileSelection CurrentProfile="@this.currentProfile" CurrentProfileChanged="@this.ProfileWasChanged" />
|
|
||||||
</MudToolBar>
|
|
||||||
</FooterContent>
|
|
||||||
</InnerScrolling>
|
|
||||||
|
|
||||||
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior != WorkspaceStorageBehavior.DISABLE_WORKSPACES)
|
|
||||||
{
|
{
|
||||||
<MudDrawer @bind-Open="@this.workspacesVisible" Width="40em" Height="100%" Anchor="Anchor.Start" Variant="DrawerVariant.Temporary" Elevation="1">
|
<MudDrawer @bind-Open="@this.workspaceOverlayVisible" Width="40em" Height="100%" Anchor="Anchor.Start" Variant="DrawerVariant.Temporary" Elevation="1">
|
||||||
<MudDrawerHeader>
|
<MudDrawerHeader>
|
||||||
<MudStack Row="@true" AlignItems="AlignItems.Center">
|
<MudStack Row="@true" AlignItems="AlignItems.Center">
|
||||||
<MudText Typo="Typo.h6" Class="mr-3">
|
<MudText Typo="Typo.h6" Class="mr-3">
|
||||||
Your workspaces
|
Your workspaces
|
||||||
</MudText>
|
</MudText>
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Close" Variant="Variant.Filled" Color="Color.Default" Size="Size.Small" OnClick="() => this.ToggleWorkspaces()"/>
|
<MudIconButton Icon="@Icons.Material.Filled.Close" Variant="Variant.Filled" Color="Color.Default" Size="Size.Small" OnClick="() => this.ToggleWorkspaceOverlay()"/>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
</MudDrawerHeader>
|
</MudDrawerHeader>
|
||||||
<MudDrawerContainer Class="ml-6">
|
<MudDrawerContainer Class="ml-6">
|
||||||
|
|||||||
@@ -17,9 +17,6 @@ namespace AIStudio.Pages;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class Chat : MSGComponentBase, IAsyncDisposable
|
public partial class Chat : MSGComponentBase, IAsyncDisposable
|
||||||
{
|
{
|
||||||
[Inject]
|
|
||||||
private SettingsManager SettingsManager { get; init; } = null!;
|
|
||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
private ThreadSafeRandom RNG { get; init; } = null!;
|
private ThreadSafeRandom RNG { get; init; } = null!;
|
||||||
|
|
||||||
@@ -42,10 +39,14 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
|||||||
private string userInput = string.Empty;
|
private string userInput = string.Empty;
|
||||||
private string currentWorkspaceName = string.Empty;
|
private string currentWorkspaceName = string.Empty;
|
||||||
private Guid currentWorkspaceId = Guid.Empty;
|
private Guid currentWorkspaceId = Guid.Empty;
|
||||||
private bool workspacesVisible;
|
private bool workspaceOverlayVisible;
|
||||||
private Workspaces? workspaces;
|
private Workspaces? workspaces;
|
||||||
private bool mustScrollToBottomAfterRender;
|
private bool mustScrollToBottomAfterRender;
|
||||||
|
private bool mustStoreChat;
|
||||||
|
private bool mustLoadChat;
|
||||||
|
private LoadChat loadChat;
|
||||||
private byte scrollRenderCountdown;
|
private byte scrollRenderCountdown;
|
||||||
|
private bool autoSaveEnabled;
|
||||||
|
|
||||||
// Unfortunately, we need the input field reference to blur the focus away. Without
|
// Unfortunately, we need the input field reference to blur the focus away. Without
|
||||||
// this, we cannot clear the input field.
|
// this, we cannot clear the input field.
|
||||||
@@ -55,12 +56,11 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
|||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
this.ApplyFilters([], [ Event.HAS_CHAT_UNSAVED_CHANGES, Event.RESET_CHAT_STATE ]);
|
this.ApplyFilters([], [ Event.HAS_CHAT_UNSAVED_CHANGES, Event.RESET_CHAT_STATE, Event.CHAT_STREAMING_DONE ]);
|
||||||
|
|
||||||
// Configure the spellchecking for the user input:
|
// Configure the spellchecking for the user input:
|
||||||
this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES);
|
this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES);
|
||||||
|
|
||||||
this.providerSettings = this.SettingsManager.GetPreselectedProvider(Tools.Components.CHAT);
|
|
||||||
this.currentProfile = this.SettingsManager.GetPreselectedProfile(Tools.Components.CHAT);
|
this.currentProfile = this.SettingsManager.GetPreselectedProfile(Tools.Components.CHAT);
|
||||||
var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages<ChatThread>(Event.SEND_TO_CHAT).FirstOrDefault();
|
var deferredContent = MessageBus.INSTANCE.CheckDeferredMessages<ChatThread>(Event.SEND_TO_CHAT).FirstOrDefault();
|
||||||
if (deferredContent is not null)
|
if (deferredContent is not null)
|
||||||
@@ -68,14 +68,23 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
|||||||
this.chatThread = deferredContent;
|
this.chatThread = deferredContent;
|
||||||
if (this.chatThread is not null)
|
if (this.chatThread is not null)
|
||||||
{
|
{
|
||||||
var firstUserBlock = this.chatThread.Blocks.FirstOrDefault(x => x.Role == ChatRole.USER);
|
if (string.IsNullOrWhiteSpace(this.chatThread.Name))
|
||||||
if (firstUserBlock is not null)
|
|
||||||
{
|
{
|
||||||
this.chatThread.Name = firstUserBlock.Content switch
|
var firstUserBlock = this.chatThread.Blocks.FirstOrDefault(x => x.Role == ChatRole.USER);
|
||||||
|
if (firstUserBlock is not null)
|
||||||
{
|
{
|
||||||
ContentText textBlock => this.ExtractThreadName(textBlock.Text),
|
this.chatThread.Name = firstUserBlock.Content switch
|
||||||
_ => "Thread"
|
{
|
||||||
};
|
ContentText textBlock => this.ExtractThreadName(textBlock.Text),
|
||||||
|
_ => "Thread"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY)
|
||||||
|
{
|
||||||
|
this.autoSaveEnabled = true;
|
||||||
|
this.mustStoreChat = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,11 +96,46 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var deferredLoading = MessageBus.INSTANCE.CheckDeferredMessages<LoadChat>(Event.LOAD_CHAT).FirstOrDefault();
|
||||||
|
if (deferredLoading != default)
|
||||||
|
{
|
||||||
|
this.loadChat = deferredLoading;
|
||||||
|
this.mustLoadChat = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.SelectProviderWhenLoadingChat();
|
||||||
await base.OnInitializedAsync();
|
await base.OnInitializedAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
|
if (firstRender && this.chatThread is not null && this.mustStoreChat)
|
||||||
|
{
|
||||||
|
this.mustStoreChat = false;
|
||||||
|
|
||||||
|
if(this.workspaces is not null)
|
||||||
|
await this.workspaces.StoreChat(this.chatThread);
|
||||||
|
else
|
||||||
|
await WorkspaceBehaviour.StoreChat(this.chatThread);
|
||||||
|
|
||||||
|
this.currentWorkspaceId = this.chatThread.WorkspaceId;
|
||||||
|
this.currentWorkspaceName = await WorkspaceBehaviour.LoadWorkspaceName(this.chatThread.WorkspaceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstRender && this.mustLoadChat)
|
||||||
|
{
|
||||||
|
this.mustLoadChat = false;
|
||||||
|
this.chatThread = await WorkspaceBehaviour.LoadChat(this.loadChat);
|
||||||
|
|
||||||
|
if(this.chatThread is not null)
|
||||||
|
{
|
||||||
|
this.currentWorkspaceName = await WorkspaceBehaviour.LoadWorkspaceName(this.chatThread.WorkspaceId);
|
||||||
|
this.SelectProviderWhenLoadingChat();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
if(this.mustScrollToBottomAfterRender)
|
if(this.mustScrollToBottomAfterRender)
|
||||||
{
|
{
|
||||||
if (--this.scrollRenderCountdown == 0)
|
if (--this.scrollRenderCountdown == 0)
|
||||||
@@ -110,20 +154,22 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private bool IsProviderSelected => this.providerSettings.UsedProvider != Providers.NONE;
|
private bool IsProviderSelected => this.providerSettings.UsedLLMProvider != LLMProviders.NONE;
|
||||||
|
|
||||||
private string ProviderPlaceholder => this.IsProviderSelected ? "Type your input here..." : "Select a provider first";
|
private string ProviderPlaceholder => this.IsProviderSelected ? "Type your input here..." : "Select a provider first";
|
||||||
|
|
||||||
private string InputLabel => this.IsProviderSelected ? $"Your Prompt (use selected instance '{this.providerSettings.InstanceName}', provider '{this.providerSettings.UsedProvider.ToName()}')" : "Select a provider first";
|
private string InputLabel => this.IsProviderSelected ? $"Your Prompt (use selected instance '{this.providerSettings.InstanceName}', provider '{this.providerSettings.UsedLLMProvider.ToName()}')" : "Select a provider first";
|
||||||
|
|
||||||
private bool CanThreadBeSaved => this.chatThread is not null && this.chatThread.Blocks.Count > 0;
|
private bool CanThreadBeSaved => this.chatThread is not null && this.chatThread.Blocks.Count > 0;
|
||||||
|
|
||||||
private string TooltipAddChatToWorkspace => $"Start new chat in workspace \"{this.currentWorkspaceName}\"";
|
private string TooltipAddChatToWorkspace => $"Start new chat in workspace \"{this.currentWorkspaceName}\"";
|
||||||
|
|
||||||
private string UserInputStyle => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence ? this.providerSettings.UsedProvider.GetConfidence(this.SettingsManager).SetColorStyle() : string.Empty;
|
private string UserInputStyle => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence ? this.providerSettings.UsedLLMProvider.GetConfidence(this.SettingsManager).SetColorStyle(this.SettingsManager) : string.Empty;
|
||||||
|
|
||||||
private string UserInputClass => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence ? "confidence-border" : string.Empty;
|
private string UserInputClass => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence ? "confidence-border" : string.Empty;
|
||||||
|
|
||||||
|
private string WorkspaceSidebarToggleIcon => this.SettingsManager.ConfigurationData.Workspace.IsSidebarVisible ? Icons.Material.Filled.ArrowCircleLeft : Icons.Material.Filled.ArrowCircleRight;
|
||||||
|
|
||||||
private void ProfileWasChanged(Profile profile)
|
private void ProfileWasChanged(Profile profile)
|
||||||
{
|
{
|
||||||
this.currentProfile = profile;
|
this.currentProfile = profile;
|
||||||
@@ -156,6 +202,7 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
|||||||
{
|
{
|
||||||
this.chatThread = new()
|
this.chatThread = new()
|
||||||
{
|
{
|
||||||
|
SelectedProvider = this.providerSettings.Id,
|
||||||
WorkspaceId = this.currentWorkspaceId,
|
WorkspaceId = this.currentWorkspaceId,
|
||||||
ChatId = Guid.NewGuid(),
|
ChatId = Guid.NewGuid(),
|
||||||
Name = threadName,
|
Name = threadName,
|
||||||
@@ -274,23 +321,35 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToggleWorkspaces()
|
private void ToggleWorkspaceOverlay()
|
||||||
{
|
{
|
||||||
this.workspacesVisible = !this.workspacesVisible;
|
this.workspaceOverlayVisible = !this.workspaceOverlayVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ToggleWorkspaceSidebar()
|
||||||
|
{
|
||||||
|
this.SettingsManager.ConfigurationData.Workspace.IsSidebarVisible = !this.SettingsManager.ConfigurationData.Workspace.IsSidebarVisible;
|
||||||
|
await this.SettingsManager.StoreSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveThread()
|
private async Task SaveThread()
|
||||||
{
|
{
|
||||||
if(this.workspaces is null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(this.chatThread is null)
|
if(this.chatThread is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!this.CanThreadBeSaved)
|
if (!this.CanThreadBeSaved)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await this.workspaces.StoreChat(this.chatThread);
|
//
|
||||||
|
// When the workspace component is visible, we store the chat
|
||||||
|
// through the workspace component. The advantage of this is that
|
||||||
|
// the workspace gets updated automatically when the chat is saved.
|
||||||
|
//
|
||||||
|
if (this.workspaces is not null)
|
||||||
|
await this.workspaces.StoreChat(this.chatThread);
|
||||||
|
else
|
||||||
|
await WorkspaceBehaviour.StoreChat(this.chatThread);
|
||||||
|
|
||||||
this.hasUnsavedChanges = false;
|
this.hasUnsavedChanges = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,25 +381,37 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.chatThread is not null && this.workspaces is not null && deletePreviousChat)
|
if (this.chatThread is not null && deletePreviousChat)
|
||||||
{
|
{
|
||||||
string chatPath;
|
string chatPath;
|
||||||
if (this.chatThread.WorkspaceId == Guid.Empty)
|
if (this.chatThread.WorkspaceId == Guid.Empty)
|
||||||
{
|
|
||||||
chatPath = Path.Join(SettingsManager.DataDirectory, "tempChats", this.chatThread.ChatId.ToString());
|
chatPath = Path.Join(SettingsManager.DataDirectory, "tempChats", this.chatThread.ChatId.ToString());
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
chatPath = Path.Join(SettingsManager.DataDirectory, "workspaces", this.chatThread.WorkspaceId.ToString(), this.chatThread.ChatId.ToString());
|
chatPath = Path.Join(SettingsManager.DataDirectory, "workspaces", this.chatThread.WorkspaceId.ToString(), this.chatThread.ChatId.ToString());
|
||||||
}
|
|
||||||
|
|
||||||
await this.workspaces.DeleteChat(chatPath, askForConfirmation: false, unloadChat: true);
|
if(this.workspaces is null)
|
||||||
|
await WorkspaceBehaviour.DeleteChat(this.DialogService, this.chatThread.WorkspaceId, this.chatThread.ChatId, askForConfirmation: false);
|
||||||
|
else
|
||||||
|
await this.workspaces.DeleteChat(chatPath, askForConfirmation: false, unloadChat: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isStreaming = false;
|
this.isStreaming = false;
|
||||||
this.hasUnsavedChanges = false;
|
this.hasUnsavedChanges = false;
|
||||||
this.userInput = string.Empty;
|
this.userInput = string.Empty;
|
||||||
|
|
||||||
|
switch (this.SettingsManager.ConfigurationData.Chat.AddChatProviderBehavior)
|
||||||
|
{
|
||||||
|
case AddChatProviderBehavior.ADDED_CHATS_USE_DEFAULT_PROVIDER:
|
||||||
|
this.providerSettings = this.SettingsManager.GetPreselectedProvider(Tools.Components.CHAT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
case AddChatProviderBehavior.ADDED_CHATS_USE_LATEST_PROVIDER:
|
||||||
|
if(this.providerSettings == default)
|
||||||
|
this.providerSettings = this.SettingsManager.GetPreselectedProvider(Tools.Components.CHAT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (!useSameWorkspace)
|
if (!useSameWorkspace)
|
||||||
{
|
{
|
||||||
this.chatThread = null;
|
this.chatThread = null;
|
||||||
@@ -351,6 +422,7 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
|||||||
{
|
{
|
||||||
this.chatThread = new()
|
this.chatThread = new()
|
||||||
{
|
{
|
||||||
|
SelectedProvider = this.providerSettings.Id,
|
||||||
WorkspaceId = this.currentWorkspaceId,
|
WorkspaceId = this.currentWorkspaceId,
|
||||||
ChatId = Guid.NewGuid(),
|
ChatId = Guid.NewGuid(),
|
||||||
Name = string.Empty,
|
Name = string.Empty,
|
||||||
@@ -372,9 +444,6 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
|||||||
if(this.chatThread is null)
|
if(this.chatThread is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(this.workspaces is null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_MANUALLY && this.hasUnsavedChanges)
|
if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_MANUALLY && this.hasUnsavedChanges)
|
||||||
{
|
{
|
||||||
var confirmationDialogParameters = new DialogParameters
|
var confirmationDialogParameters = new DialogParameters
|
||||||
@@ -405,26 +474,21 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Delete the chat from the current workspace or the temporary storage:
|
// Delete the chat from the current workspace or the temporary storage:
|
||||||
if (this.chatThread!.WorkspaceId == Guid.Empty)
|
await WorkspaceBehaviour.DeleteChat(this.DialogService, this.chatThread!.WorkspaceId, this.chatThread.ChatId, askForConfirmation: false);
|
||||||
{
|
|
||||||
// Case: The chat is stored in the temporary storage:
|
|
||||||
await this.workspaces.DeleteChat(Path.Join(SettingsManager.DataDirectory, "tempChats", this.chatThread.ChatId.ToString()), askForConfirmation: false, unloadChat: false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Case: The chat is stored in a workspace.
|
|
||||||
await this.workspaces.DeleteChat(Path.Join(SettingsManager.DataDirectory, "workspaces", this.chatThread.WorkspaceId.ToString(), this.chatThread.ChatId.ToString()), askForConfirmation: false, unloadChat: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chatThread!.WorkspaceId = workspaceId;
|
this.chatThread!.WorkspaceId = workspaceId;
|
||||||
await this.SaveThread();
|
await this.SaveThread();
|
||||||
|
|
||||||
this.currentWorkspaceId = this.chatThread.WorkspaceId;
|
this.currentWorkspaceId = this.chatThread.WorkspaceId;
|
||||||
this.currentWorkspaceName = await this.workspaces.LoadWorkspaceName(this.chatThread.WorkspaceId);
|
this.currentWorkspaceName = await WorkspaceBehaviour.LoadWorkspaceName(this.chatThread.WorkspaceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadedChatChanged()
|
private async Task LoadedChatChanged()
|
||||||
{
|
{
|
||||||
|
//
|
||||||
|
// It should not happen that the workspace component is not loaded
|
||||||
|
// because the workspace component is calling this method.
|
||||||
|
//
|
||||||
if(this.workspaces is null)
|
if(this.workspaces is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -432,7 +496,9 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
|||||||
this.hasUnsavedChanges = false;
|
this.hasUnsavedChanges = false;
|
||||||
this.userInput = string.Empty;
|
this.userInput = string.Empty;
|
||||||
this.currentWorkspaceId = this.chatThread?.WorkspaceId ?? Guid.Empty;
|
this.currentWorkspaceId = this.chatThread?.WorkspaceId ?? Guid.Empty;
|
||||||
this.currentWorkspaceName = this.chatThread is null ? string.Empty : await this.workspaces.LoadWorkspaceName(this.chatThread.WorkspaceId);
|
this.currentWorkspaceName = this.chatThread is null ? string.Empty : await WorkspaceBehaviour.LoadWorkspaceName(this.chatThread.WorkspaceId);
|
||||||
|
|
||||||
|
this.SelectProviderWhenLoadingChat();
|
||||||
|
|
||||||
this.userInput = string.Empty;
|
this.userInput = string.Empty;
|
||||||
if (this.SettingsManager.ConfigurationData.Chat.ShowLatestMessageAfterLoading)
|
if (this.SettingsManager.ConfigurationData.Chat.ShowLatestMessageAfterLoading)
|
||||||
@@ -453,18 +519,42 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
|||||||
this.chatThread = null;
|
this.chatThread = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SelectProviderWhenLoadingChat()
|
||||||
|
{
|
||||||
|
var chatProvider = this.chatThread?.SelectedProvider;
|
||||||
|
switch (this.SettingsManager.ConfigurationData.Chat.LoadingProviderBehavior)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case LoadingChatProviderBehavior.USE_CHAT_PROVIDER_IF_AVAILABLE:
|
||||||
|
this.providerSettings = this.SettingsManager.GetPreselectedProvider(Tools.Components.CHAT, chatProvider);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LoadingChatProviderBehavior.ALWAYS_USE_DEFAULT_CHAT_PROVIDER:
|
||||||
|
this.providerSettings = this.SettingsManager.GetPreselectedProvider(Tools.Components.CHAT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LoadingChatProviderBehavior.ALWAYS_USE_LATEST_CHAT_PROVIDER:
|
||||||
|
if(this.providerSettings == default)
|
||||||
|
this.providerSettings = this.SettingsManager.GetPreselectedProvider(Tools.Components.CHAT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#region Overrides of MSGComponentBase
|
#region Overrides of MSGComponentBase
|
||||||
|
|
||||||
public override Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
public override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||||
{
|
{
|
||||||
switch (triggeredEvent)
|
switch (triggeredEvent)
|
||||||
{
|
{
|
||||||
case Event.RESET_CHAT_STATE:
|
case Event.RESET_CHAT_STATE:
|
||||||
this.ResetState();
|
this.ResetState();
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
case Event.CHAT_STREAMING_DONE:
|
||||||
|
if(this.autoSaveEnabled)
|
||||||
|
await this.SaveThread();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data) where TResult : default where TPayload : default
|
public override Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data) where TResult : default where TPayload : default
|
||||||
|
|||||||
@@ -32,13 +32,13 @@ public partial class Home : ComponentBase
|
|||||||
|
|
||||||
private static readonly TextItem[] ITEMS_ADVANTAGES =
|
private static readonly TextItem[] ITEMS_ADVANTAGES =
|
||||||
[
|
[
|
||||||
new TextItem("Free of charge", "The app is free to use, both for personal and commercial purposes."),
|
new("Free of charge", "The app is free to use, both for personal and commercial purposes."),
|
||||||
new TextItem("Independence", "You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT4o etc.), Mistral, Anthropic (Claude), and self-hosted models using llama.cpp, ollama, LM Studio, or Fireworks. Support for Google Gemini and Replicate is planned."),
|
new("Independence", "You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT4o etc.), Mistral, Anthropic (Claude), Google Gemini, and self-hosted models using llama.cpp, ollama, LM Studio, Groq, or Fireworks."),
|
||||||
new TextItem("Unrestricted usage", "Unlike services like ChatGPT, which impose limits after intensive use, MindWork AI Studio offers unlimited usage through the providers API."),
|
new("Unrestricted usage", "Unlike services like ChatGPT, which impose limits after intensive use, MindWork AI Studio offers unlimited usage through the providers API."),
|
||||||
new TextItem("Cost-effective", "You only pay for what you use, which can be cheaper than monthly subscription services like ChatGPT Plus, especially if used infrequently. But beware, here be dragons: For extremely intensive usage, the API costs can be significantly higher. Unfortunately, providers currently do not offer a way to display current costs in the app. Therefore, check your account with the respective provider to see how your costs are developing. When available, use prepaid and set a cost limit."),
|
new("Cost-effective", "You only pay for what you use, which can be cheaper than monthly subscription services like ChatGPT Plus, especially if used infrequently. But beware, here be dragons: For extremely intensive usage, the API costs can be significantly higher. Unfortunately, providers currently do not offer a way to display current costs in the app. Therefore, check your account with the respective provider to see how your costs are developing. When available, use prepaid and set a cost limit."),
|
||||||
new TextItem("Privacy", "The data entered into the app is not used for training by the providers since we are using the provider's API."),
|
new("Privacy", "The data entered into the app is not used for training by the providers since we are using the provider's API."),
|
||||||
new TextItem("Flexibility", "Choose the provider and model best suited for your current task."),
|
new("Flexibility", "Choose the provider and model best suited for your current task."),
|
||||||
new TextItem("No bloatware", "The app requires minimal storage for installation and operates with low memory usage. Additionally, it has a minimal impact on system resources, which is beneficial for battery life."),
|
new("No bloatware", "The app requires minimal storage for installation and operates with low memory usage. Additionally, it has a minimal impact on system resources, which is beneficial for battery life."),
|
||||||
];
|
];
|
||||||
|
|
||||||
private const string QUICK_START_GUIDE =
|
private const string QUICK_START_GUIDE =
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
@using AIStudio.Assistants.TextSummarizer
|
@using AIStudio.Assistants.TextSummarizer
|
||||||
@using AIStudio.Provider
|
@using AIStudio.Provider
|
||||||
@using AIStudio.Settings
|
@using AIStudio.Settings
|
||||||
|
@using AIStudio.Settings.DataModel
|
||||||
@using Host = AIStudio.Provider.SelfHosted.Host
|
@using Host = AIStudio.Provider.SelfHosted.Host
|
||||||
|
|
||||||
<MudText Typo="Typo.h3" Class="mb-12">Settings</MudText>
|
<MudText Typo="Typo.h3" Class="mb-12">Settings</MudText>
|
||||||
@@ -35,15 +36,15 @@
|
|||||||
<RowTemplate>
|
<RowTemplate>
|
||||||
<MudTd>@context.Num</MudTd>
|
<MudTd>@context.Num</MudTd>
|
||||||
<MudTd>@context.InstanceName</MudTd>
|
<MudTd>@context.InstanceName</MudTd>
|
||||||
<MudTd>@context.UsedProvider</MudTd>
|
<MudTd>@context.UsedLLMProvider</MudTd>
|
||||||
<MudTd>
|
<MudTd>
|
||||||
@if (context.UsedProvider is not Providers.SELF_HOSTED)
|
@if (context.UsedLLMProvider is not LLMProviders.SELF_HOSTED)
|
||||||
{
|
{
|
||||||
@this.GetProviderModelName(context)
|
@this.GetLLMProviderModelName(context)
|
||||||
}
|
}
|
||||||
else if (context.UsedProvider is Providers.SELF_HOSTED && context.Host is not Host.LLAMACPP)
|
else if (context.UsedLLMProvider is LLMProviders.SELF_HOSTED && context.Host is not Host.LLAMACPP)
|
||||||
{
|
{
|
||||||
@this.GetProviderModelName(context)
|
@this.GetLLMProviderModelName(context)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -51,13 +52,13 @@
|
|||||||
}
|
}
|
||||||
</MudTd>
|
</MudTd>
|
||||||
<MudTd Style="text-align: left;">
|
<MudTd Style="text-align: left;">
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.OpenInBrowser" Class="ma-2" Href="@this.GetProviderDashboardURL(context.UsedProvider)" Target="_blank" Disabled="@(!this.HasDashboard(context.UsedProvider))">
|
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.OpenInBrowser" Class="ma-2" Href="@context.UsedLLMProvider.GetDashboardURL()" Target="_blank" Disabled="@(!context.UsedLLMProvider.HasDashboard())">
|
||||||
Open Dashboard
|
Open Dashboard
|
||||||
</MudButton>
|
</MudButton>
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" Class="ma-2" OnClick="() => this.EditProvider(context)">
|
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" Class="ma-2" OnClick="() => this.EditLLMProvider(context)">
|
||||||
Edit
|
Edit
|
||||||
</MudButton>
|
</MudButton>
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" Class="ma-2" OnClick="() => this.DeleteProvider(context)">
|
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" Class="ma-2" OnClick="() => this.DeleteLLMProvider(context)">
|
||||||
Delete
|
Delete
|
||||||
</MudButton>
|
</MudButton>
|
||||||
</MudTd>
|
</MudTd>
|
||||||
@@ -69,7 +70,7 @@
|
|||||||
<MudText Typo="Typo.h6" Class="mt-3">No providers configured yet.</MudText>
|
<MudText Typo="Typo.h6" Class="mt-3">No providers configured yet.</MudText>
|
||||||
}
|
}
|
||||||
|
|
||||||
<MudButton Variant="Variant.Filled" Color="@Color.Primary" StartIcon="@Icons.Material.Filled.AddRoad" Class="mt-3 mb-6" OnClick="@this.AddProvider">
|
<MudButton Variant="Variant.Filled" Color="@Color.Primary" StartIcon="@Icons.Material.Filled.AddRoad" Class="mt-3 mb-6" OnClick="@this.AddLLMProvider">
|
||||||
Add Provider
|
Add Provider
|
||||||
</MudButton>
|
</MudButton>
|
||||||
|
|
||||||
@@ -80,13 +81,19 @@
|
|||||||
Either you choose a common schema, or you configure the trust levels for each LLM provider yourself.
|
Either you choose a common schema, or you configure the trust levels for each LLM provider yourself.
|
||||||
</MudJustifiedText>
|
</MudJustifiedText>
|
||||||
|
|
||||||
|
<ConfigurationOption OptionDescription="Do you want to enforce an app-wide minimum confidence level?" LabelOn="Yes, enforce a minimum confidence level" LabelOff="No, do not enforce a minimum confidence level" State="@(() => this.SettingsManager.ConfigurationData.LLMProviders.EnforceGlobalMinimumConfidence)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.LLMProviders.EnforceGlobalMinimumConfidence = updatedState)" OptionHelp="When enabled, you can enforce a minimum confidence level for all LLM providers. This way, you can ensure that only trustworthy providers are used."/>
|
||||||
|
@if(this.SettingsManager.ConfigurationData.LLMProviders.EnforceGlobalMinimumConfidence)
|
||||||
|
{
|
||||||
|
<ConfigurationMinConfidenceSelection RestrictToGlobalMinimumConfidence="@false" SelectedValue="@(() => this.SettingsManager.ConfigurationData.LLMProviders.GlobalMinimumConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LLMProviders.GlobalMinimumConfidence = selectedValue)"/>
|
||||||
|
}
|
||||||
|
|
||||||
<ConfigurationOption OptionDescription="Show provider's confidence level?" LabelOn="Yes, show me the confidence level" LabelOff="No, please hide the confidence level" State="@(() => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence = updatedState)" OptionHelp="When enabled, we show you the confidence level for the selected provider in the app. This helps you assess where you are sending your data at any time. Example: are you currently working with sensitive data? Then choose a particularly trustworthy provider, etc."/>
|
<ConfigurationOption OptionDescription="Show provider's confidence level?" LabelOn="Yes, show me the confidence level" LabelOff="No, please hide the confidence level" State="@(() => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence = updatedState)" OptionHelp="When enabled, we show you the confidence level for the selected provider in the app. This helps you assess where you are sending your data at any time. Example: are you currently working with sensitive data? Then choose a particularly trustworthy provider, etc."/>
|
||||||
@if (this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence)
|
@if (this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence)
|
||||||
{
|
{
|
||||||
<ConfigurationSelect OptionDescription="Select a confidence scheme" SelectedValue="@(() => this.SettingsManager.ConfigurationData.LLMProviders.ConfidenceScheme)" Data="@ConfigurationSelectDataFactory.GetConfidenceSchemesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LLMProviders.ConfidenceScheme = selectedValue)" OptionHelp="Choose the scheme that best suits you and your life. Do you trust any western provider? Or only providers from the USA or exclusively European providers? Then choose the appropriate scheme. Alternatively, you can assign the confidence levels to each provider yourself."/>
|
<ConfigurationSelect OptionDescription="Select a confidence scheme" SelectedValue="@(() => this.SettingsManager.ConfigurationData.LLMProviders.ConfidenceScheme)" Data="@ConfigurationSelectDataFactory.GetConfidenceSchemesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LLMProviders.ConfidenceScheme = selectedValue)" OptionHelp="Choose the scheme that best suits you and your life. Do you trust any western provider? Or only providers from the USA or exclusively European providers? Then choose the appropriate scheme. Alternatively, you can assign the confidence levels to each provider yourself."/>
|
||||||
@if (this.SettingsManager.ConfigurationData.LLMProviders.ConfidenceScheme is ConfidenceSchemes.CUSTOM)
|
@if (this.SettingsManager.ConfigurationData.LLMProviders.ConfidenceScheme is ConfidenceSchemes.CUSTOM)
|
||||||
{
|
{
|
||||||
<MudTable Items="@(Enum.GetValues<Providers>().Where(x => x is not Providers.NONE))" Hover="@true" Class="border-dashed border rounded-lg">
|
<MudTable Items="@(Enum.GetValues<LLMProviders>().Where(x => x is not LLMProviders.NONE))" Hover="@true" Class="border-dashed border rounded-lg">
|
||||||
<ColGroup>
|
<ColGroup>
|
||||||
<col style="width: 12em;"/>
|
<col style="width: 12em;"/>
|
||||||
<col/>
|
<col/>
|
||||||
@@ -124,6 +131,73 @@
|
|||||||
|
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
|
|
||||||
|
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
|
||||||
|
{
|
||||||
|
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.IntegrationInstructions" HeaderText="Configure Embeddings">
|
||||||
|
<PreviewPrototype/>
|
||||||
|
<MudText Typo="Typo.h4" Class="mb-3">
|
||||||
|
Configured Embeddings
|
||||||
|
</MudText>
|
||||||
|
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||||
|
Embeddings are a way to represent words, sentences, entire documents, or even images and videos as digital
|
||||||
|
fingerprints. Just like each person has a unique fingerprint, embedding models create unique digital patterns
|
||||||
|
that capture the meaning and characteristics of the content they analyze. When two things are similar in meaning
|
||||||
|
or content, their digital fingerprints will look very similar. For example, the fingerprints for 'happy' and
|
||||||
|
'joyful' would be more alike than those for 'happy' and 'sad'.
|
||||||
|
</MudJustifiedText>
|
||||||
|
|
||||||
|
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||||
|
This helps AI Studio understand and compare things in a way that's similar to how humans do. When you're working on
|
||||||
|
something, AI Studio can automatically identify related documents and data by comparing their digital fingerprints.
|
||||||
|
For instance, if you're writing about customer service, AI Studio can instantly find other documents in your data that
|
||||||
|
discuss similar topics or experiences, even if they use different words.
|
||||||
|
</MudJustifiedText>
|
||||||
|
<MudTable Items="@this.SettingsManager.ConfigurationData.EmbeddingProviders" Hover="@true" Class="border-dashed border rounded-lg">
|
||||||
|
<ColGroup>
|
||||||
|
<col style="width: 3em;"/>
|
||||||
|
<col style="width: 12em;"/>
|
||||||
|
<col style="width: 12em;"/>
|
||||||
|
<col/>
|
||||||
|
<col style="width: 40em;"/>
|
||||||
|
</ColGroup>
|
||||||
|
<HeaderContent>
|
||||||
|
<MudTh>#</MudTh>
|
||||||
|
<MudTh>Name</MudTh>
|
||||||
|
<MudTh>Provider</MudTh>
|
||||||
|
<MudTh>Model</MudTh>
|
||||||
|
<MudTh Style="text-align: left;">Actions</MudTh>
|
||||||
|
</HeaderContent>
|
||||||
|
<RowTemplate>
|
||||||
|
<MudTd>@context.Num</MudTd>
|
||||||
|
<MudTd>@context.Name</MudTd>
|
||||||
|
<MudTd>@context.UsedLLMProvider</MudTd>
|
||||||
|
<MudTd>@this.GetEmbeddingProviderModelName(context)</MudTd>
|
||||||
|
|
||||||
|
<MudTd Style="text-align: left;">
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.OpenInBrowser" Class="ma-2" Href="@context.UsedLLMProvider.GetDashboardURL()" Target="_blank" Disabled="@(!context.UsedLLMProvider.HasDashboard())">
|
||||||
|
Open Dashboard
|
||||||
|
</MudButton>
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" Class="ma-2" OnClick="() => this.EditEmbeddingProvider(context)">
|
||||||
|
Edit
|
||||||
|
</MudButton>
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" Class="ma-2" OnClick="() => this.DeleteEmbeddingProvider(context)">
|
||||||
|
Delete
|
||||||
|
</MudButton>
|
||||||
|
</MudTd>
|
||||||
|
</RowTemplate>
|
||||||
|
</MudTable>
|
||||||
|
|
||||||
|
@if (this.SettingsManager.ConfigurationData.EmbeddingProviders.Count == 0)
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.h6" Class="mt-3">No embeddings configured yet.</MudText>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudButton Variant="Variant.Filled" Color="@Color.Primary" StartIcon="@Icons.Material.Filled.AddRoad" Class="mt-3 mb-6" OnClick="@this.AddEmbeddingProvider">
|
||||||
|
Add Embedding
|
||||||
|
</MudButton>
|
||||||
|
</ExpansionPanel>
|
||||||
|
}
|
||||||
|
|
||||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Person4" HeaderText="Configure Profiles">
|
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Person4" HeaderText="Configure Profiles">
|
||||||
<MudText Typo="Typo.h4" Class="mb-3">Your Profiles</MudText>
|
<MudText Typo="Typo.h4" Class="mb-3">Your Profiles</MudText>
|
||||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||||
@@ -174,34 +248,55 @@
|
|||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
|
|
||||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Apps" HeaderText="App Options">
|
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Apps" HeaderText="App Options">
|
||||||
|
<ConfigurationSelect OptionDescription="Color theme" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreferredTheme)" Data="@ConfigurationSelectDataFactory.GetThemesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreferredTheme = selectedValue)" OptionHelp="Choose the color theme that best suits for you."/>
|
||||||
<ConfigurationOption OptionDescription="Save energy?" LabelOn="Energy saving is enabled" LabelOff="Energy saving is disabled" State="@(() => this.SettingsManager.ConfigurationData.App.IsSavingEnergy)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.IsSavingEnergy = updatedState)" OptionHelp="When enabled, streamed content from the AI is updated once every third second. When disabled, streamed content will be updated as soon as it is available."/>
|
<ConfigurationOption OptionDescription="Save energy?" LabelOn="Energy saving is enabled" LabelOff="Energy saving is disabled" State="@(() => this.SettingsManager.ConfigurationData.App.IsSavingEnergy)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.IsSavingEnergy = updatedState)" OptionHelp="When enabled, streamed content from the AI is updated once every third second. When disabled, streamed content will be updated as soon as it is available."/>
|
||||||
<ConfigurationOption OptionDescription="Enable spellchecking?" LabelOn="Spellchecking is enabled" LabelOff="Spellchecking is disabled" State="@(() => this.SettingsManager.ConfigurationData.App.EnableSpellchecking)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.EnableSpellchecking = updatedState)" OptionHelp="When enabled, spellchecking will be active in all input fields. Depending on your operating system, errors may not be visually highlighted, but right-clicking may still offer possible corrections." />
|
<ConfigurationOption OptionDescription="Enable spellchecking?" LabelOn="Spellchecking is enabled" LabelOff="Spellchecking is disabled" State="@(() => this.SettingsManager.ConfigurationData.App.EnableSpellchecking)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.EnableSpellchecking = updatedState)" OptionHelp="When enabled, spellchecking will be active in all input fields. Depending on your operating system, errors may not be visually highlighted, but right-clicking may still offer possible corrections." />
|
||||||
<ConfigurationSelect OptionDescription="Check for updates" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.UpdateBehavior)" Data="@ConfigurationSelectDataFactory.GetUpdateBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UpdateBehavior = selectedValue)" OptionHelp="How often should we check for app updates?"/>
|
<ConfigurationSelect OptionDescription="Check for updates" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.UpdateBehavior)" Data="@ConfigurationSelectDataFactory.GetUpdateBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UpdateBehavior = selectedValue)" OptionHelp="How often should we check for app updates?"/>
|
||||||
<ConfigurationSelect OptionDescription="Navigation bar behavior" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.NavigationBehavior)" Data="@ConfigurationSelectDataFactory.GetNavBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.NavigationBehavior = selectedValue)" OptionHelp="Select the desired behavior for the navigation bar."/>
|
<ConfigurationSelect OptionDescription="Navigation bar behavior" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.NavigationBehavior)" Data="@ConfigurationSelectDataFactory.GetNavBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.NavigationBehavior = selectedValue)" OptionHelp="Select the desired behavior for the navigation bar."/>
|
||||||
<ConfigurationProviderSelection Data="@this.availableProviders" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProvider = selectedValue)" HelpText="@(() => "Would you like to set one provider as the default for the entire app? When you configure a different provider for an assistant, it will always take precedence.")"/>
|
<ConfigurationSelect OptionDescription="Preview feature visibility" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreviewVisibility)" Data="@ConfigurationSelectDataFactory.GetPreviewVisibility()" SelectionUpdate="@this.UpdatePreviewFeatures" OptionHelp="Do you want to show preview features in the app?"/>
|
||||||
|
|
||||||
|
@if(this.SettingsManager.ConfigurationData.App.PreviewVisibility > PreviewVisibility.NONE)
|
||||||
|
{
|
||||||
|
var availablePreviewFeatures = ConfigurationSelectDataFactory.GetPreviewFeaturesData(this.SettingsManager).ToList();
|
||||||
|
if (availablePreviewFeatures.Count > 0)
|
||||||
|
{
|
||||||
|
<ConfigurationMultiSelect OptionDescription="Select preview features" SelectedValues="@(() => this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures)" Data="@availablePreviewFeatures" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures = selectedValue)" OptionHelp="Which preview features would you like to enable?"/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<ConfigurationProviderSelection Data="@this.availableLLMProviders" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProvider = selectedValue)" HelpText="@(() => "Would you like to set one provider as the default for the entire app? When you configure a different provider for an assistant, it will always take precedence.")"/>
|
||||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProfile = selectedValue)" OptionHelp="Would you like to set one of your profiles as the default for the entire app? When you configure a different profile for an assistant, it will always take precedence."/>
|
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProfile = selectedValue)" OptionHelp="Would you like to set one of your profiles as the default for the entire app? When you configure a different profile for an assistant, it will always take precedence."/>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
|
|
||||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Chat" HeaderText="Chat Options">
|
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Chat" HeaderText="Chat Options">
|
||||||
<ConfigurationSelect OptionDescription="Shortcut to send input" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.ShortcutSendBehavior)" Data="@ConfigurationSelectDataFactory.GetSendBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.ShortcutSendBehavior = selectedValue)" OptionHelp="Do you want to use any shortcut to send your input?"/>
|
<ConfigurationSelect OptionDescription="Shortcut to send input" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.ShortcutSendBehavior)" Data="@ConfigurationSelectDataFactory.GetSendBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.ShortcutSendBehavior = selectedValue)" OptionHelp="Do you want to use any shortcut to send your input?"/>
|
||||||
<ConfigurationOption OptionDescription="Show the latest message after loading?" LabelOn="Latest message is shown, after loading a chat" LabelOff="First (oldest) message is shown, after loading a chat" State="@(() => this.SettingsManager.ConfigurationData.Chat.ShowLatestMessageAfterLoading)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Chat.ShowLatestMessageAfterLoading = updatedState)" OptionHelp="When enabled, the latest message is shown after loading a chat. When disabled, the first (oldest) message is shown."/>
|
<ConfigurationOption OptionDescription="Show the latest message after loading?" LabelOn="Latest message is shown, after loading a chat" LabelOff="First (oldest) message is shown, after loading a chat" State="@(() => this.SettingsManager.ConfigurationData.Chat.ShowLatestMessageAfterLoading)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Chat.ShowLatestMessageAfterLoading = updatedState)" OptionHelp="When enabled, the latest message is shown after loading a chat. When disabled, the first (oldest) message is shown."/>
|
||||||
|
<ConfigurationSelect OptionDescription="Provider selection when creating new chats" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.AddChatProviderBehavior)" Data="@ConfigurationSelectDataFactory.GetAddChatProviderBehavior()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.AddChatProviderBehavior = selectedValue)" OptionHelp="Control how the LLM provider for added chats is selected."/>
|
||||||
|
<ConfigurationSelect OptionDescription="Provider selection when loading a chat and sending assistant results to chat" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.LoadingProviderBehavior)" Data="@ConfigurationSelectDataFactory.GetLoadingChatProviderBehavior()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.LoadingProviderBehavior = selectedValue)" OptionHelp="Control how the LLM provider for loaded chats is selected and when assistant results are sent to chat."/>
|
||||||
|
|
||||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||||
<ConfigurationOption OptionDescription="Preselect chat options?" LabelOn="Chat options are preselected" LabelOff="No chat options are preselected" State="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Chat.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect chat options. This is might be useful when you prefer a specific provider."/>
|
<ConfigurationOption OptionDescription="Preselect chat options?" LabelOn="Chat options are preselected" LabelOff="No chat options are preselected" State="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Chat.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect chat options. This is might be useful when you prefer a specific provider."/>
|
||||||
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.PreselectedProvider = selectedValue)"/>
|
||||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.PreselectedProfile = selectedValue)" OptionHelp="Would you like to set one of your profiles as the default for chats?"/>
|
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.PreselectedProfile = selectedValue)" OptionHelp="Would you like to set one of your profiles as the default for chats?"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
|
|
||||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Work" HeaderText="Workspace Options">
|
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Work" HeaderText="Workspace Options">
|
||||||
<ConfigurationSelect OptionDescription="Workspace behavior" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Workspace.StorageBehavior)" Data="@ConfigurationSelectDataFactory.GetWorkspaceStorageBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Workspace.StorageBehavior = selectedValue)" OptionHelp="Should we store your chats?"/>
|
<ConfigurationSelect OptionDescription="Workspace behavior" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Workspace.StorageBehavior)" Data="@ConfigurationSelectDataFactory.GetWorkspaceStorageBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Workspace.StorageBehavior = selectedValue)" OptionHelp="Should we store your chats?"/>
|
||||||
<ConfigurationSelect OptionDescription="Workspace maintenance" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Workspace.StorageTemporaryMaintenancePolicy)" Data="@ConfigurationSelectDataFactory.GetWorkspaceStorageTemporaryMaintenancePolicyData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Workspace.StorageTemporaryMaintenancePolicy = selectedValue)" OptionHelp="If and when should we delete your temporary chats?"/>
|
|
||||||
|
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is not WorkspaceStorageBehavior.DISABLE_WORKSPACES)
|
||||||
|
{
|
||||||
|
<ConfigurationSelect OptionDescription="Workspace maintenance" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Workspace.StorageTemporaryMaintenancePolicy)" Data="@ConfigurationSelectDataFactory.GetWorkspaceStorageTemporaryMaintenancePolicyData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Workspace.StorageTemporaryMaintenancePolicy = selectedValue)" OptionHelp="If and when should we delete your temporary chats?"/>
|
||||||
|
<ConfigurationSelect OptionDescription="Workspace display behavior" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Workspace.DisplayBehavior)" Data="@ConfigurationSelectDataFactory.GetWorkspaceDisplayBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Workspace.DisplayBehavior = selectedValue)" OptionHelp="How should we display your workspaces?"/>
|
||||||
|
}
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
|
|
||||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.FindInPage" HeaderText="Assistant: Icon Finder Options">
|
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.FindInPage" HeaderText="Assistant: Icon Finder Options">
|
||||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||||
<ConfigurationOption OptionDescription="Preselect icon options?" LabelOn="Icon options are preselected" LabelOff="No icon options are preselected" State="@(() => this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect the icon options. This is might be useful when you prefer a specific icon source or LLM model."/>
|
<ConfigurationOption OptionDescription="Preselect icon options?" LabelOn="Icon options are preselected" LabelOff="No icon options are preselected" State="@(() => this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect the icon options. This is might be useful when you prefer a specific icon source or LLM model."/>
|
||||||
<ConfigurationSelect OptionDescription="Preselect the icon source" Disabled="@(() => !this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.IconFinder.PreselectedSource)" Data="@ConfigurationSelectDataFactory.GetIconSourcesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.IconFinder.PreselectedSource = selectedValue)" OptionHelp="Which icon source should be preselected?"/>
|
<ConfigurationSelect OptionDescription="Preselect the icon source" Disabled="@(() => !this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.IconFinder.PreselectedSource)" Data="@ConfigurationSelectDataFactory.GetIconSourcesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.IconFinder.PreselectedSource = selectedValue)" OptionHelp="Which icon source should be preselected?"/>
|
||||||
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.IconFinder.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.IconFinder.PreselectedProvider = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.IconFinder.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.IconFinder.MinimumProviderConfidence = selectedValue)"/>
|
||||||
|
<ConfigurationProviderSelection Component="Components.ICON_FINDER_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.IconFinder.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.IconFinder.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
|
|
||||||
@@ -218,7 +313,8 @@
|
|||||||
{
|
{
|
||||||
<ConfigurationText OptionDescription="Preselect another target language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Translation.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.Translation.PreselectOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Translation.PreselectOtherLanguage = updatedText)"/>
|
<ConfigurationText OptionDescription="Preselect another target language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Translation.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.Translation.PreselectOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Translation.PreselectOtherLanguage = updatedText)"/>
|
||||||
}
|
}
|
||||||
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Translation.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Translation.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Translation.PreselectedProvider = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.Translation.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Translation.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Translation.MinimumProviderConfidence = selectedValue)"/>
|
||||||
|
<ConfigurationProviderSelection Component="Components.TRANSLATION_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Translation.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Translation.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Translation.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
|
|
||||||
@@ -231,7 +327,8 @@
|
|||||||
{
|
{
|
||||||
<ConfigurationText OptionDescription="Preselect another programming language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" Icon="@Icons.Material.Filled.Code" Text="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectedOtherProgrammingLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Coding.PreselectedOtherProgrammingLanguage = updatedText)"/>
|
<ConfigurationText OptionDescription="Preselect another programming language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" Icon="@Icons.Material.Filled.Code" Text="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectedOtherProgrammingLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Coding.PreselectedOtherProgrammingLanguage = updatedText)"/>
|
||||||
}
|
}
|
||||||
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Coding.PreselectedProvider = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Coding.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Coding.MinimumProviderConfidence = selectedValue)"/>
|
||||||
|
<ConfigurationProviderSelection Component="Components.CODING_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Coding.PreselectedProvider = selectedValue)"/>
|
||||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Coding.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Coding.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
@@ -252,7 +349,8 @@
|
|||||||
{
|
{
|
||||||
<ConfigurationText OptionDescription="Preselect your expertise" Disabled="@(() => !this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions)" Icon="@Icons.Material.Filled.Person" Text="@(() => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedExpertInField)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedExpertInField = updatedText)"/>
|
<ConfigurationText OptionDescription="Preselect your expertise" Disabled="@(() => !this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions)" Icon="@Icons.Material.Filled.Person" Text="@(() => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedExpertInField)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedExpertInField = updatedText)"/>
|
||||||
}
|
}
|
||||||
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedProvider = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.TextSummarizer.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.TextSummarizer.MinimumProviderConfidence = selectedValue)"/>
|
||||||
|
<ConfigurationProviderSelection Component="Components.TEXT_SUMMARIZER_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
|
|
||||||
@@ -280,7 +378,8 @@
|
|||||||
{
|
{
|
||||||
<ConfigurationText OptionDescription="Preselect another agenda language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectedOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Agenda.PreselectedOtherLanguage = updatedText)"/>
|
<ConfigurationText OptionDescription="Preselect another agenda language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectedOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Agenda.PreselectedOtherLanguage = updatedText)"/>
|
||||||
}
|
}
|
||||||
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectedProvider = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Agenda.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.MinimumProviderConfidence = selectedValue)"/>
|
||||||
|
<ConfigurationProviderSelection Component="Components.AGENDA_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectedProvider = selectedValue)"/>
|
||||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
@@ -293,7 +392,8 @@
|
|||||||
{
|
{
|
||||||
<ConfigurationText OptionDescription="Preselect another target language" Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedOtherLanguage = updatedText)"/>
|
<ConfigurationText OptionDescription="Preselect another target language" Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedOtherLanguage = updatedText)"/>
|
||||||
}
|
}
|
||||||
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.GrammarSpelling.MinimumProviderConfidence = selectedValue)"/>
|
||||||
|
<ConfigurationProviderSelection Component="Components.GRAMMAR_SPELLING_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
|
|
||||||
@@ -307,7 +407,8 @@
|
|||||||
}
|
}
|
||||||
<ConfigurationSelect OptionDescription="Preselect a writing style" Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedWritingStyle)" Data="@ConfigurationSelectDataFactory.GetWritingStyles4RewriteData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedWritingStyle = selectedValue)" OptionHelp="Which writing style should be preselected?"/>
|
<ConfigurationSelect OptionDescription="Preselect a writing style" Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedWritingStyle)" Data="@ConfigurationSelectDataFactory.GetWritingStyles4RewriteData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedWritingStyle = selectedValue)" OptionHelp="Which writing style should be preselected?"/>
|
||||||
<ConfigurationSelect OptionDescription="Preselect a sentence structure" Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedSentenceStructure)" Data="@ConfigurationSelectDataFactory.GetSentenceStructureData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedSentenceStructure = selectedValue)" OptionHelp="Which voice should be preselected for the sentence structure?"/>
|
<ConfigurationSelect OptionDescription="Preselect a sentence structure" Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedSentenceStructure)" Data="@ConfigurationSelectDataFactory.GetSentenceStructureData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedSentenceStructure = selectedValue)" OptionHelp="Which voice should be preselected for the sentence structure?"/>
|
||||||
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedProvider = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.MinimumProviderConfidence = selectedValue)"/>
|
||||||
|
<ConfigurationProviderSelection Component="Components.REWRITE_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
|
|
||||||
@@ -322,18 +423,40 @@
|
|||||||
<ConfigurationText OptionDescription="Preselect another target language" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.EMail.PreselectOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.EMail.PreselectOtherLanguage = updatedText)"/>
|
<ConfigurationText OptionDescription="Preselect another target language" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.EMail.PreselectOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.EMail.PreselectOtherLanguage = updatedText)"/>
|
||||||
}
|
}
|
||||||
<ConfigurationSelect OptionDescription="Preselect a writing style" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.PreselectedWritingStyle)" Data="@ConfigurationSelectDataFactory.GetWritingStyles4EMailData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.PreselectedWritingStyle = selectedValue)" OptionHelp="Which writing style should be preselected?"/>
|
<ConfigurationSelect OptionDescription="Preselect a writing style" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.PreselectedWritingStyle)" Data="@ConfigurationSelectDataFactory.GetWritingStyles4EMailData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.PreselectedWritingStyle = selectedValue)" OptionHelp="Which writing style should be preselected?"/>
|
||||||
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.PreselectedProvider = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.MinimumProviderConfidence = selectedValue)"/>
|
||||||
|
<ConfigurationProviderSelection Component="Components.EMAIL_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.PreselectedProvider = selectedValue)"/>
|
||||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
|
|
||||||
|
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Work" HeaderText="Assistant: Job Postings">
|
||||||
|
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||||
|
<ConfigurationOption OptionDescription="Preselect job posting options?" LabelOn="Job posting options are preselected" LabelOff="No job posting options are preselected" State="@(() => this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect some job posting options. This is might be useful when you prefer a specific LLM model."/>
|
||||||
|
<ConfigurationText OptionDescription="Preselect the company name?" Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" Icon="@Icons.Material.Filled.Warehouse" Text="@(() => this.SettingsManager.ConfigurationData.JobPostings.PreselectedCompanyName)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.JobPostings.PreselectedCompanyName = updatedText)" />
|
||||||
|
<ConfigurationText OptionDescription="Preselect some mandatory information about the job posting?" NumLines="6" Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" Icon="@Icons.Material.Filled.TextSnippet" Text="@(() => this.SettingsManager.ConfigurationData.JobPostings.PreselectedMandatoryInformation)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.JobPostings.PreselectedMandatoryInformation = updatedText)" />
|
||||||
|
<ConfigurationText OptionDescription="Preselect the job description?" NumLines="3" MaxLines="6" Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" Icon="@Icons.Material.Filled.Settings" Text="@(() => this.SettingsManager.ConfigurationData.JobPostings.PreselectedJobDescription)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.JobPostings.PreselectedJobDescription = updatedText)" />
|
||||||
|
<ConfigurationText OptionDescription="Preselect the job qualifications?" NumLines="3" MaxLines="6" Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" Icon="@Icons.Material.Filled.Settings" Text="@(() => this.SettingsManager.ConfigurationData.JobPostings.PreselectedQualifications)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.JobPostings.PreselectedQualifications = updatedText)" />
|
||||||
|
<ConfigurationText OptionDescription="Preselect the job responsibilities?" NumLines="3" MaxLines="6" Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" Icon="@Icons.Material.Filled.Settings" Text="@(() => this.SettingsManager.ConfigurationData.JobPostings.PreselectedResponsibilities)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.JobPostings.PreselectedResponsibilities = updatedText)" />
|
||||||
|
<ConfigurationText OptionDescription="Preselect the work location?" Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" Icon="@Icons.Material.Filled.MyLocation" Text="@(() => this.SettingsManager.ConfigurationData.JobPostings.PreselectedWorkLocation)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.JobPostings.PreselectedWorkLocation = updatedText)" />
|
||||||
|
<ConfigurationText OptionDescription="Preselect the country (legal framework)?" Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" Icon="@Icons.Material.Filled.Flag" Text="@(() => this.SettingsManager.ConfigurationData.JobPostings.PreselectedCountryLegalFramework)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.JobPostings.PreselectedCountryLegalFramework = updatedText)" />
|
||||||
|
<ConfigurationSelect OptionDescription="Preselect the language" Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.JobPostings.PreselectedTargetLanguage)" Data="@ConfigurationSelectDataFactory.GetCommonLanguagesOptionalData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.JobPostings.PreselectedTargetLanguage = selectedValue)" OptionHelp="Which target language should be preselected?"/>
|
||||||
|
@if (this.SettingsManager.ConfigurationData.JobPostings.PreselectedTargetLanguage is CommonLanguages.OTHER)
|
||||||
|
{
|
||||||
|
<ConfigurationText OptionDescription="Preselect another target language" Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.JobPostings.PreselectOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.JobPostings.PreselectOtherLanguage = updatedText)"/>
|
||||||
|
}
|
||||||
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.JobPostings.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.JobPostings.MinimumProviderConfidence = selectedValue)"/>
|
||||||
|
<ConfigurationProviderSelection Component="Components.JOB_POSTING_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.JobPostings.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.JobPostings.PreselectedProvider = selectedValue)"/>
|
||||||
|
</MudPaper>
|
||||||
|
</ExpansionPanel>
|
||||||
|
|
||||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Gavel" HeaderText="Assistant: Legal Check">
|
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Gavel" HeaderText="Assistant: Legal Check">
|
||||||
<ConfigurationOption OptionDescription="Hide the web content reader?" LabelOn="Web content reader is hidden" LabelOff="Web content reader is shown" State="@(() => this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader = updatedState)" OptionHelp="When activated, the web content reader is hidden and cannot be used. As a result, the user interface becomes a bit easier to use."/>
|
<ConfigurationOption OptionDescription="Hide the web content reader?" LabelOn="Web content reader is hidden" LabelOff="Web content reader is shown" State="@(() => this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader = updatedState)" OptionHelp="When activated, the web content reader is hidden and cannot be used. As a result, the user interface becomes a bit easier to use."/>
|
||||||
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||||
<ConfigurationOption OptionDescription="Preselect legal check options?" LabelOn="Legal check options are preselected" LabelOff="No legal check options are preselected" State="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect some legal check options. This is might be useful when you prefer a specific LLM model."/>
|
<ConfigurationOption OptionDescription="Preselect legal check options?" LabelOn="Legal check options are preselected" LabelOff="No legal check options are preselected" State="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect some legal check options. This is might be useful when you prefer a specific LLM model."/>
|
||||||
<ConfigurationOption OptionDescription="Preselect the web content reader?" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions || this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader)" LabelOn="Web content reader is preselected" LabelOff="Web content reader is not preselected" State="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectWebContentReader)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.LegalCheck.PreselectWebContentReader = updatedState)" OptionHelp="When enabled, the web content reader is preselected. This is might be useful when you prefer to load legal content from the web very often."/>
|
<ConfigurationOption OptionDescription="Preselect the web content reader?" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions || this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader)" LabelOn="Web content reader is preselected" LabelOff="Web content reader is not preselected" State="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectWebContentReader)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.LegalCheck.PreselectWebContentReader = updatedState)" OptionHelp="When enabled, the web content reader is preselected. This is might be useful when you prefer to load legal content from the web very often."/>
|
||||||
<ConfigurationOption OptionDescription="Preselect the content cleaner agent?" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions || this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader)" LabelOn="Content cleaner agent is preselected" LabelOff="Content cleaner agent is not preselected" State="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectContentCleanerAgent)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.LegalCheck.PreselectContentCleanerAgent = updatedState)" OptionHelp="When enabled, the content cleaner agent is preselected. This is might be useful when you prefer to clean up the legal content before translating it."/>
|
<ConfigurationOption OptionDescription="Preselect the content cleaner agent?" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions || this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader)" LabelOn="Content cleaner agent is preselected" LabelOff="Content cleaner agent is not preselected" State="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectContentCleanerAgent)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.LegalCheck.PreselectContentCleanerAgent = updatedState)" OptionHelp="When enabled, the content cleaner agent is preselected. This is might be useful when you prefer to clean up the legal content before translating it."/>
|
||||||
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProvider = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.LegalCheck.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LegalCheck.MinimumProviderConfidence = selectedValue)"/>
|
||||||
|
<ConfigurationProviderSelection Component="Components.LEGAL_CHECK_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProvider = selectedValue)"/>
|
||||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
@@ -346,7 +469,8 @@
|
|||||||
{
|
{
|
||||||
<ConfigurationText OptionDescription="Preselect another language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Synonyms.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.Synonyms.PreselectedOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Synonyms.PreselectedOtherLanguage = updatedText)"/>
|
<ConfigurationText OptionDescription="Preselect another language" Disabled="@(() => !this.SettingsManager.ConfigurationData.Synonyms.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.Synonyms.PreselectedOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.Synonyms.PreselectedOtherLanguage = updatedText)"/>
|
||||||
}
|
}
|
||||||
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Synonyms.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Synonyms.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Synonyms.PreselectedProvider = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.Synonyms.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Synonyms.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Synonyms.MinimumProviderConfidence = selectedValue)"/>
|
||||||
|
<ConfigurationProviderSelection Component="Components.SYNONYMS_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Synonyms.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Synonyms.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Synonyms.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
|
|
||||||
@@ -359,7 +483,34 @@
|
|||||||
<ConfigurationText OptionDescription="Preselect another language" Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.MyTasks.PreselectOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.MyTasks.PreselectOtherLanguage = updatedText)"/>
|
<ConfigurationText OptionDescription="Preselect another language" Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.MyTasks.PreselectOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.MyTasks.PreselectOtherLanguage = updatedText)"/>
|
||||||
}
|
}
|
||||||
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
||||||
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProvider = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.MyTasks.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.MyTasks.MinimumProviderConfidence = selectedValue)"/>
|
||||||
|
<ConfigurationProviderSelection Component="Components.MY_TASKS_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProvider = selectedValue)"/>
|
||||||
|
</MudPaper>
|
||||||
|
</ExpansionPanel>
|
||||||
|
|
||||||
|
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Psychology" HeaderText="Assistant: Bias of the Day">
|
||||||
|
|
||||||
|
<ConfigurationOption OptionDescription="Restrict to one bias a day?" LabelOn="Yes, you can only retrieve one bias per day" LabelOff="No restriction. You can retrieve as many biases as you want per day." State="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.RestrictOneBiasPerDay)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.BiasOfTheDay.RestrictOneBiasPerDay = updatedState)"/>
|
||||||
|
|
||||||
|
<MudField Label="Statistics" Variant="Variant.Outlined" Class="mb-2">
|
||||||
|
<MudText Typo="Typo.body1">
|
||||||
|
You have learned about @this.SettingsManager.ConfigurationData.BiasOfTheDay.UsedBias.Count out of @BiasCatalog.ALL_BIAS.Count biases.
|
||||||
|
</MudText>
|
||||||
|
<MudButton Size="Size.Small" Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.Restore" Color="Color.Error" OnClick="@(() => this.ResetBiasOfTheDayHistory())">
|
||||||
|
Reset
|
||||||
|
</MudButton>
|
||||||
|
</MudField>
|
||||||
|
|
||||||
|
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
|
||||||
|
<ConfigurationOption OptionDescription="Preselect options?" LabelOn="Options are preselected" LabelOff="No options are preselected" State="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions = updatedState)" OptionHelp="When enabled, you can preselect options. This is might be useful when you prefer a specific language or LLM model."/>
|
||||||
|
<ConfigurationSelect OptionDescription="Preselect the language" Disabled="@(() => !this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedTargetLanguage)" Data="@ConfigurationSelectDataFactory.GetCommonLanguagesOptionalData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedTargetLanguage = selectedValue)" OptionHelp="Which language should be preselected?"/>
|
||||||
|
@if (this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedTargetLanguage is CommonLanguages.OTHER)
|
||||||
|
{
|
||||||
|
<ConfigurationText OptionDescription="Preselect another language" Disabled="@(() => !this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)" Icon="@Icons.Material.Filled.Translate" Text="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedOtherLanguage)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedOtherLanguage = updatedText)"/>
|
||||||
|
}
|
||||||
|
<ConfigurationSelect OptionDescription="Preselect one of your profiles?" Disabled="@(() => !this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedProfile = selectedValue)" OptionHelp="Would you like to preselect one of your profiles?"/>
|
||||||
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.BiasOfTheDay.MinimumProviderConfidence = selectedValue)"/>
|
||||||
|
<ConfigurationProviderSelection Component="Components.BIAS_DAY_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
|
|
||||||
@@ -370,7 +521,7 @@
|
|||||||
and attempts to convert relative links into absolute links so that they can be used.
|
and attempts to convert relative links into absolute links so that they can be used.
|
||||||
</MudText>
|
</MudText>
|
||||||
<ConfigurationOption OptionDescription="Preselect text content cleaner options?" LabelOn="Options are preselected" LabelOff="No options are preselected" State="@(() => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions = updatedState)" OptionHelp="When enabled, you can preselect some agent options. This is might be useful when you prefer a LLM."/>
|
<ConfigurationOption OptionDescription="Preselect text content cleaner options?" LabelOn="Options are preselected" LabelOff="No options are preselected" State="@(() => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions = updatedState)" OptionHelp="When enabled, you can preselect some agent options. This is might be useful when you prefer a LLM."/>
|
||||||
<ConfigurationProviderSelection Data="@this.availableProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectedAgentProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectedAgentProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectedAgentProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectedAgentProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
</MudExpansionPanels>
|
</MudExpansionPanels>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using AIStudio.Dialogs;
|
using AIStudio.Dialogs;
|
||||||
using AIStudio.Provider;
|
using AIStudio.Provider;
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
|
using AIStudio.Settings.DataModel;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
@@ -22,13 +23,11 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable
|
|||||||
[Inject]
|
[Inject]
|
||||||
private MessageBus MessageBus { get; init; } = null!;
|
private MessageBus MessageBus { get; init; } = null!;
|
||||||
|
|
||||||
[Inject]
|
|
||||||
private ILogger<Settings> Logger { get; init; } = null!;
|
|
||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
private RustService RustService { get; init; } = null!;
|
private RustService RustService { get; init; } = null!;
|
||||||
|
|
||||||
private readonly List<ConfigurationSelectData<string>> availableProviders = new();
|
private readonly List<ConfigurationSelectData<string>> availableLLMProviders = new();
|
||||||
|
private readonly List<ConfigurationSelectData<string>> availableEmbeddingProviders = new();
|
||||||
|
|
||||||
#region Overrides of ComponentBase
|
#region Overrides of ComponentBase
|
||||||
|
|
||||||
@@ -44,16 +43,26 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Preview-feature related
|
||||||
|
|
||||||
|
private void UpdatePreviewFeatures(PreviewVisibility previewVisibility)
|
||||||
|
{
|
||||||
|
this.SettingsManager.ConfigurationData.App.PreviewVisibility = previewVisibility;
|
||||||
|
this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures = previewVisibility.FilterPreviewFeatures(this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Provider related
|
#region Provider related
|
||||||
|
|
||||||
private async Task AddProvider()
|
private async Task AddLLMProvider()
|
||||||
{
|
{
|
||||||
var dialogParameters = new DialogParameters<ProviderDialog>
|
var dialogParameters = new DialogParameters<ProviderDialog>
|
||||||
{
|
{
|
||||||
{ x => x.IsEditing, false },
|
{ x => x.IsEditing, false },
|
||||||
};
|
};
|
||||||
|
|
||||||
var dialogReference = await this.DialogService.ShowAsync<ProviderDialog>("Add Provider", dialogParameters, DialogOptions.FULLSCREEN);
|
var dialogReference = await this.DialogService.ShowAsync<ProviderDialog>("Add LLM Provider", dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
var dialogResult = await dialogReference.Result;
|
var dialogResult = await dialogReference.Result;
|
||||||
if (dialogResult is null || dialogResult.Canceled)
|
if (dialogResult is null || dialogResult.Canceled)
|
||||||
return;
|
return;
|
||||||
@@ -68,14 +77,14 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable
|
|||||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task EditProvider(AIStudio.Settings.Provider provider)
|
private async Task EditLLMProvider(AIStudio.Settings.Provider provider)
|
||||||
{
|
{
|
||||||
var dialogParameters = new DialogParameters<ProviderDialog>
|
var dialogParameters = new DialogParameters<ProviderDialog>
|
||||||
{
|
{
|
||||||
{ x => x.DataNum, provider.Num },
|
{ x => x.DataNum, provider.Num },
|
||||||
{ x => x.DataId, provider.Id },
|
{ x => x.DataId, provider.Id },
|
||||||
{ x => x.DataInstanceName, provider.InstanceName },
|
{ x => x.DataInstanceName, provider.InstanceName },
|
||||||
{ x => x.DataProvider, provider.UsedProvider },
|
{ x => x.DataLLMProvider, provider.UsedLLMProvider },
|
||||||
{ x => x.DataModel, provider.Model },
|
{ x => x.DataModel, provider.Model },
|
||||||
{ x => x.DataHostname, provider.Hostname },
|
{ x => x.DataHostname, provider.Hostname },
|
||||||
{ x => x.IsSelfHosted, provider.IsSelfHosted },
|
{ x => x.IsSelfHosted, provider.IsSelfHosted },
|
||||||
@@ -83,7 +92,7 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable
|
|||||||
{ x => x.DataHost, provider.Host },
|
{ x => x.DataHost, provider.Host },
|
||||||
};
|
};
|
||||||
|
|
||||||
var dialogReference = await this.DialogService.ShowAsync<ProviderDialog>("Edit Provider", dialogParameters, DialogOptions.FULLSCREEN);
|
var dialogReference = await this.DialogService.ShowAsync<ProviderDialog>("Edit LLM Provider", dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
var dialogResult = await dialogReference.Result;
|
var dialogResult = await dialogReference.Result;
|
||||||
if (dialogResult is null || dialogResult.Canceled)
|
if (dialogResult is null || dialogResult.Canceled)
|
||||||
return;
|
return;
|
||||||
@@ -102,20 +111,19 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable
|
|||||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DeleteProvider(AIStudio.Settings.Provider provider)
|
private async Task DeleteLLMProvider(AIStudio.Settings.Provider provider)
|
||||||
{
|
{
|
||||||
var dialogParameters = new DialogParameters
|
var dialogParameters = new DialogParameters
|
||||||
{
|
{
|
||||||
{ "Message", $"Are you sure you want to delete the provider '{provider.InstanceName}'?" },
|
{ "Message", $"Are you sure you want to delete the provider '{provider.InstanceName}'?" },
|
||||||
};
|
};
|
||||||
|
|
||||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Delete Provider", dialogParameters, DialogOptions.FULLSCREEN);
|
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Delete LLM Provider", dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
var dialogResult = await dialogReference.Result;
|
var dialogResult = await dialogReference.Result;
|
||||||
if (dialogResult is null || dialogResult.Canceled)
|
if (dialogResult is null || dialogResult.Canceled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var providerInstance = provider.CreateProvider(this.Logger);
|
var deleteSecretResponse = await this.RustService.DeleteAPIKey(provider);
|
||||||
var deleteSecretResponse = await this.RustService.DeleteAPIKey(providerInstance);
|
|
||||||
if(deleteSecretResponse.Success)
|
if(deleteSecretResponse.Success)
|
||||||
{
|
{
|
||||||
this.SettingsManager.ConfigurationData.Providers.Remove(provider);
|
this.SettingsManager.ConfigurationData.Providers.Remove(provider);
|
||||||
@@ -126,27 +134,7 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable
|
|||||||
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool HasDashboard(Providers provider) => provider switch
|
private string GetLLMProviderModelName(AIStudio.Settings.Provider provider)
|
||||||
{
|
|
||||||
Providers.OPEN_AI => true,
|
|
||||||
Providers.MISTRAL => true,
|
|
||||||
Providers.ANTHROPIC => true,
|
|
||||||
Providers.FIREWORKS => true,
|
|
||||||
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
private string GetProviderDashboardURL(Providers provider) => provider switch
|
|
||||||
{
|
|
||||||
Providers.OPEN_AI => "https://platform.openai.com/usage",
|
|
||||||
Providers.MISTRAL => "https://console.mistral.ai/usage/",
|
|
||||||
Providers.ANTHROPIC => "https://console.anthropic.com/settings/plans",
|
|
||||||
Providers.FIREWORKS => "https://fireworks.ai/account/billing",
|
|
||||||
|
|
||||||
_ => string.Empty,
|
|
||||||
};
|
|
||||||
|
|
||||||
private string GetProviderModelName(AIStudio.Settings.Provider provider)
|
|
||||||
{
|
{
|
||||||
const int MAX_LENGTH = 36;
|
const int MAX_LENGTH = 36;
|
||||||
var modelName = provider.Model.ToString();
|
var modelName = provider.Model.ToString();
|
||||||
@@ -155,35 +143,132 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable
|
|||||||
|
|
||||||
private void UpdateProviders()
|
private void UpdateProviders()
|
||||||
{
|
{
|
||||||
this.availableProviders.Clear();
|
this.availableLLMProviders.Clear();
|
||||||
foreach (var provider in this.SettingsManager.ConfigurationData.Providers)
|
foreach (var provider in this.SettingsManager.ConfigurationData.Providers)
|
||||||
this.availableProviders.Add(new (provider.InstanceName, provider.Id));
|
this.availableLLMProviders.Add(new (provider.InstanceName, provider.Id));
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetCurrentConfidenceLevelName(Providers provider)
|
private string GetCurrentConfidenceLevelName(LLMProviders llmProvider)
|
||||||
{
|
{
|
||||||
if (this.SettingsManager.ConfigurationData.LLMProviders.CustomConfidenceScheme.TryGetValue(provider, out var level))
|
if (this.SettingsManager.ConfigurationData.LLMProviders.CustomConfidenceScheme.TryGetValue(llmProvider, out var level))
|
||||||
return level.GetName();
|
return level.GetName();
|
||||||
|
|
||||||
return "Not yet configured";
|
return "Not yet configured";
|
||||||
}
|
}
|
||||||
|
|
||||||
private string SetCurrentConfidenceLevelColorStyle(Providers provider)
|
private string SetCurrentConfidenceLevelColorStyle(LLMProviders llmProvider)
|
||||||
{
|
{
|
||||||
if (this.SettingsManager.ConfigurationData.LLMProviders.CustomConfidenceScheme.TryGetValue(provider, out var level))
|
if (this.SettingsManager.ConfigurationData.LLMProviders.CustomConfidenceScheme.TryGetValue(llmProvider, out var level))
|
||||||
return $"background-color: {level.GetColor()};";
|
return $"background-color: {level.GetColor(this.SettingsManager)};";
|
||||||
|
|
||||||
return $"background-color: {ConfidenceLevel.UNKNOWN.GetColor()};";
|
return $"background-color: {ConfidenceLevel.UNKNOWN.GetColor(this.SettingsManager)};";
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ChangeCustomConfidenceLevel(Providers provider, ConfidenceLevel level)
|
private async Task ChangeCustomConfidenceLevel(LLMProviders llmProvider, ConfidenceLevel level)
|
||||||
{
|
{
|
||||||
this.SettingsManager.ConfigurationData.LLMProviders.CustomConfidenceScheme[provider] = level;
|
this.SettingsManager.ConfigurationData.LLMProviders.CustomConfidenceScheme[llmProvider] = level;
|
||||||
await this.SettingsManager.StoreSettings();
|
await this.SettingsManager.StoreSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Embedding provider related
|
||||||
|
|
||||||
|
private string GetEmbeddingProviderModelName(EmbeddingProvider provider)
|
||||||
|
{
|
||||||
|
const int MAX_LENGTH = 36;
|
||||||
|
var modelName = provider.Model.ToString();
|
||||||
|
return modelName.Length > MAX_LENGTH ? "[...] " + modelName[^Math.Min(MAX_LENGTH, modelName.Length)..] : modelName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AddEmbeddingProvider()
|
||||||
|
{
|
||||||
|
var dialogParameters = new DialogParameters<EmbeddingDialog>
|
||||||
|
{
|
||||||
|
{ x => x.IsEditing, false },
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialogReference = await this.DialogService.ShowAsync<EmbeddingDialog>("Add Embedding Provider", dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
|
var dialogResult = await dialogReference.Result;
|
||||||
|
if (dialogResult is null || dialogResult.Canceled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var addedEmbedding = (EmbeddingProvider)dialogResult.Data!;
|
||||||
|
addedEmbedding = addedEmbedding with { Num = this.SettingsManager.ConfigurationData.NextEmbeddingNum++ };
|
||||||
|
|
||||||
|
this.SettingsManager.ConfigurationData.EmbeddingProviders.Add(addedEmbedding);
|
||||||
|
this.UpdateEmbeddingProviders();
|
||||||
|
|
||||||
|
await this.SettingsManager.StoreSettings();
|
||||||
|
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task EditEmbeddingProvider(EmbeddingProvider embeddingProvider)
|
||||||
|
{
|
||||||
|
var dialogParameters = new DialogParameters<EmbeddingDialog>
|
||||||
|
{
|
||||||
|
{ x => x.DataNum, embeddingProvider.Num },
|
||||||
|
{ x => x.DataId, embeddingProvider.Id },
|
||||||
|
{ x => x.DataName, embeddingProvider.Name },
|
||||||
|
{ x => x.DataLLMProvider, embeddingProvider.UsedLLMProvider },
|
||||||
|
{ x => x.DataModel, embeddingProvider.Model },
|
||||||
|
{ x => x.DataHostname, embeddingProvider.Hostname },
|
||||||
|
{ x => x.IsSelfHosted, embeddingProvider.IsSelfHosted },
|
||||||
|
{ x => x.IsEditing, true },
|
||||||
|
{ x => x.DataHost, embeddingProvider.Host },
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialogReference = await this.DialogService.ShowAsync<EmbeddingDialog>("Edit Embedding Provider", dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
|
var dialogResult = await dialogReference.Result;
|
||||||
|
if (dialogResult is null || dialogResult.Canceled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var editedEmbeddingProvider = (EmbeddingProvider)dialogResult.Data!;
|
||||||
|
|
||||||
|
// Set the provider number if it's not set. This is important for providers
|
||||||
|
// added before we started saving the provider number.
|
||||||
|
if(editedEmbeddingProvider.Num == 0)
|
||||||
|
editedEmbeddingProvider = editedEmbeddingProvider with { Num = this.SettingsManager.ConfigurationData.NextEmbeddingNum++ };
|
||||||
|
|
||||||
|
this.SettingsManager.ConfigurationData.EmbeddingProviders[this.SettingsManager.ConfigurationData.EmbeddingProviders.IndexOf(embeddingProvider)] = editedEmbeddingProvider;
|
||||||
|
this.UpdateEmbeddingProviders();
|
||||||
|
|
||||||
|
await this.SettingsManager.StoreSettings();
|
||||||
|
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeleteEmbeddingProvider(EmbeddingProvider provider)
|
||||||
|
{
|
||||||
|
var dialogParameters = new DialogParameters
|
||||||
|
{
|
||||||
|
{ "Message", $"Are you sure you want to delete the embedding provider '{provider.Name}'?" },
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Delete Embedding Provider", dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
|
var dialogResult = await dialogReference.Result;
|
||||||
|
if (dialogResult is null || dialogResult.Canceled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var deleteSecretResponse = await this.RustService.DeleteAPIKey(provider);
|
||||||
|
if(deleteSecretResponse.Success)
|
||||||
|
{
|
||||||
|
this.SettingsManager.ConfigurationData.EmbeddingProviders.Remove(provider);
|
||||||
|
await this.SettingsManager.StoreSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.UpdateEmbeddingProviders();
|
||||||
|
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateEmbeddingProviders()
|
||||||
|
{
|
||||||
|
this.availableEmbeddingProviders.Clear();
|
||||||
|
foreach (var provider in this.SettingsManager.ConfigurationData.EmbeddingProviders)
|
||||||
|
this.availableEmbeddingProviders.Add(new (provider.Name, provider.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Profile related
|
#region Profile related
|
||||||
|
|
||||||
private async Task AddProfile()
|
private async Task AddProfile()
|
||||||
@@ -251,6 +336,29 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Bias-of-the-day related
|
||||||
|
|
||||||
|
private async Task ResetBiasOfTheDayHistory()
|
||||||
|
{
|
||||||
|
var dialogParameters = new DialogParameters
|
||||||
|
{
|
||||||
|
{ "Message", "Are you sure you want to reset your bias-of-the-day statistics? The system will no longer remember which biases you already know. As a result, biases you are already familiar with may be addressed again." },
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Reset your bias-of-the-day statistics", dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
|
var dialogResult = await dialogReference.Result;
|
||||||
|
if (dialogResult is null || dialogResult.Canceled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.SettingsManager.ConfigurationData.BiasOfTheDay.UsedBias.Clear();
|
||||||
|
this.SettingsManager.ConfigurationData.BiasOfTheDay.DateLastBiasDrawn = DateOnly.MinValue;
|
||||||
|
await this.SettingsManager.StoreSettings();
|
||||||
|
|
||||||
|
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Implementation of IMessageBusReceiver
|
#region Implementation of IMessageBusReceiver
|
||||||
|
|
||||||
public Task ProcessMessage<TMsg>(ComponentBase? sendingComponent, Event triggeredEvent, TMsg? data)
|
public Task ProcessMessage<TMsg>(ComponentBase? sendingComponent, Event triggeredEvent, TMsg? data)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
</MudButton>
|
</MudButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MudGrid Justify="Justify.Center" Spacing="2" Style="width: 100%;" Class="align-center ma-0">
|
<MudGrid Justify="Justify.Center" Spacing="2" Style="width: 100%;" Class="ma-0">
|
||||||
<MudItem Class="pa-0 pr-2" xs="6">
|
<MudItem Class="pa-0 pr-2" xs="6">
|
||||||
<MudPaper Elevation="3" Class="border-solid border rounded-lg pa-3">
|
<MudPaper Elevation="3" Class="border-solid border rounded-lg pa-3">
|
||||||
<MudText Typo="Typo.h4" Class="mb-3 d-flex align-center">
|
<MudText Typo="Typo.h4" Class="mb-3 d-flex align-center">
|
||||||
@@ -28,6 +28,21 @@
|
|||||||
<MudButton Href="https://github.com/sponsors/MindWorkAI" StartIcon="@Icons.Material.Filled.People" Variant="Variant.Filled" Target="_blank">
|
<MudButton Href="https://github.com/sponsors/MindWorkAI" StartIcon="@Icons.Material.Filled.People" Variant="Variant.Filled" Target="_blank">
|
||||||
Become a contributor
|
Become a contributor
|
||||||
</MudButton>
|
</MudButton>
|
||||||
|
|
||||||
|
<MudText Typo="Typo.h6" Class="mt-8">
|
||||||
|
The first 10 supporters who make a monthly contribution:
|
||||||
|
</MudText>
|
||||||
|
<MudList T="string">
|
||||||
|
<Supporter Name="richard-stanton" Type="SupporterType.INDIVIDUAL" URL="https://github.com/richard-stanton" Acknowledgment="Thank you, Richard, for being the first."/>
|
||||||
|
<Supporter Name="peerschuett" Type="SupporterType.INDIVIDUAL" URL="https://github.com/peerschuett" Acknowledgment="Thank you, Peer, for your courage in being the second person to support the project financially."/>
|
||||||
|
</MudList>
|
||||||
|
|
||||||
|
<MudText Typo="Typo.h6" Class="mt-8">
|
||||||
|
The first 10 supporters who make a one-time contribution:
|
||||||
|
</MudText>
|
||||||
|
<MudList T="string">
|
||||||
|
<Supporter Name="Bessieres" Type="SupporterType.INDIVIDUAL" URL="https://github.com/Bessieres" Acknowledgment="Thank you for being the first to contribute a one-time donation."/>
|
||||||
|
</MudList>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</MudItem>
|
</MudItem>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
@attribute [Route(Routes.WRITER)]
|
||||||
|
@inherits MSGComponentBase
|
||||||
|
|
||||||
|
<MudText Typo="Typo.h3" Class="mb-2 mr-3">
|
||||||
|
Writer
|
||||||
|
</MudText>
|
||||||
|
|
||||||
|
<PreviewExperimental/>
|
||||||
|
|
||||||
|
<ProviderSelection @bind-ProviderSettings="@this.providerSettings"/>
|
||||||
|
<InnerScrolling HeaderHeight="12.3em">
|
||||||
|
<ChildContent>
|
||||||
|
<MudTextField
|
||||||
|
@ref="@this.textField"
|
||||||
|
T="string"
|
||||||
|
Label="Write your text"
|
||||||
|
@bind-Text="@this.userInput"
|
||||||
|
Immediate="@true"
|
||||||
|
Lines="16"
|
||||||
|
MaxLines="16"
|
||||||
|
Typo="Typo.body1"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
InputMode="InputMode.text"
|
||||||
|
FullWidth="@true"
|
||||||
|
OnKeyDown="@this.InputKeyEvent"
|
||||||
|
UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||||
|
|
||||||
|
<MudTextField
|
||||||
|
T="string"
|
||||||
|
Label="Your stage directions"
|
||||||
|
@bind-Text="@this.userDirection"
|
||||||
|
Immediate="@true"
|
||||||
|
Lines="4"
|
||||||
|
MaxLines="4"
|
||||||
|
Typo="Typo.body1"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
InputMode="InputMode.text"
|
||||||
|
FullWidth="@true"
|
||||||
|
UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||||
|
</ChildContent>
|
||||||
|
<FooterContent>
|
||||||
|
@if (this.isStreaming)
|
||||||
|
{
|
||||||
|
<MudProgressLinear Color="Color.Primary" Indeterminate="true" Class="mb-6" />
|
||||||
|
}
|
||||||
|
<MudTextField
|
||||||
|
T="string"
|
||||||
|
Label="Suggestion"
|
||||||
|
@bind-Text="@this.suggestion"
|
||||||
|
ReadOnly="@true"
|
||||||
|
Lines="3"
|
||||||
|
Typo="Typo.body1"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
FullWidth="@true"
|
||||||
|
UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||||
|
</FooterContent>
|
||||||
|
</InnerScrolling>
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
using AIStudio.Chat;
|
||||||
|
using AIStudio.Components;
|
||||||
|
using AIStudio.Provider;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.AspNetCore.Components.Web;
|
||||||
|
|
||||||
|
using Timer = System.Timers.Timer;
|
||||||
|
|
||||||
|
namespace AIStudio.Pages;
|
||||||
|
|
||||||
|
public partial class Writer : MSGComponentBase, IAsyncDisposable
|
||||||
|
{
|
||||||
|
[Inject]
|
||||||
|
private ILogger<Chat> Logger { get; init; } = null!;
|
||||||
|
|
||||||
|
private static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();
|
||||||
|
private readonly Timer typeTimer = new(TimeSpan.FromMilliseconds(1_500));
|
||||||
|
|
||||||
|
private MudTextField<string> textField = null!;
|
||||||
|
private AIStudio.Settings.Provider providerSettings;
|
||||||
|
private ChatThread? chatThread;
|
||||||
|
private bool isStreaming;
|
||||||
|
private string userInput = string.Empty;
|
||||||
|
private string userDirection = string.Empty;
|
||||||
|
private string suggestion = string.Empty;
|
||||||
|
|
||||||
|
#region Overrides of ComponentBase
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
this.ApplyFilters([], []);
|
||||||
|
this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES);
|
||||||
|
this.typeTimer.Elapsed += async (_, _) => await this.InvokeAsync(this.GetSuggestions);
|
||||||
|
this.typeTimer.AutoReset = false;
|
||||||
|
|
||||||
|
await base.OnInitializedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Overrides of MSGComponentBase
|
||||||
|
|
||||||
|
public override Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data) where TResult : default where TPayload : default
|
||||||
|
{
|
||||||
|
return Task.FromResult(default(TResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private bool IsProviderSelected => this.providerSettings.UsedLLMProvider != LLMProviders.NONE;
|
||||||
|
|
||||||
|
private async Task InputKeyEvent(KeyboardEventArgs keyEvent)
|
||||||
|
{
|
||||||
|
var key = keyEvent.Code.ToLowerInvariant();
|
||||||
|
var isTab = key is "tab";
|
||||||
|
var isModifier = keyEvent.AltKey || keyEvent.CtrlKey || keyEvent.MetaKey || keyEvent.ShiftKey;
|
||||||
|
|
||||||
|
if (isTab && !isModifier)
|
||||||
|
{
|
||||||
|
await this.textField.FocusAsync();
|
||||||
|
this.AcceptNextWord();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTab && isModifier)
|
||||||
|
{
|
||||||
|
await this.textField.FocusAsync();
|
||||||
|
this.AcceptEntireSuggestion();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isModifier)
|
||||||
|
{
|
||||||
|
this.typeTimer.Stop();
|
||||||
|
this.typeTimer.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task GetSuggestions()
|
||||||
|
{
|
||||||
|
if (!this.IsProviderSelected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.chatThread ??= new()
|
||||||
|
{
|
||||||
|
WorkspaceId = Guid.Empty,
|
||||||
|
ChatId = Guid.NewGuid(),
|
||||||
|
Name = string.Empty,
|
||||||
|
Seed = 798798,
|
||||||
|
SystemPrompt = """
|
||||||
|
You are an assistant who helps with writing documents. You receive a sample
|
||||||
|
from a document as input. As output, you provide how the begun sentence could
|
||||||
|
continue. You give exactly one variant, not multiple. If the current sentence
|
||||||
|
is complete, you provide an empty response. You do not ask questions, and you
|
||||||
|
do not repeat the task.
|
||||||
|
""",
|
||||||
|
Blocks = [],
|
||||||
|
};
|
||||||
|
|
||||||
|
var time = DateTimeOffset.Now;
|
||||||
|
this.chatThread.Blocks.Clear();
|
||||||
|
this.chatThread.Blocks.Add(new ContentBlock
|
||||||
|
{
|
||||||
|
Time = time,
|
||||||
|
ContentType = ContentType.TEXT,
|
||||||
|
Role = ChatRole.USER,
|
||||||
|
Content = new ContentText
|
||||||
|
{
|
||||||
|
// We use the maximum 160 characters from the end of the text:
|
||||||
|
Text = this.userInput.Length > 160 ? this.userInput[^160..] : this.userInput,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var aiText = new ContentText
|
||||||
|
{
|
||||||
|
// We have to wait for the remote
|
||||||
|
// for the content stream:
|
||||||
|
InitialRemoteWait = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.chatThread?.Blocks.Add(new ContentBlock
|
||||||
|
{
|
||||||
|
Time = time,
|
||||||
|
ContentType = ContentType.TEXT,
|
||||||
|
Role = ChatRole.AI,
|
||||||
|
Content = aiText,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.isStreaming = true;
|
||||||
|
this.StateHasChanged();
|
||||||
|
|
||||||
|
await aiText.CreateFromProviderAsync(this.providerSettings.CreateProvider(this.Logger), this.SettingsManager, this.providerSettings.Model, this.chatThread);
|
||||||
|
this.suggestion = aiText.Text;
|
||||||
|
|
||||||
|
this.isStreaming = false;
|
||||||
|
this.StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AcceptEntireSuggestion()
|
||||||
|
{
|
||||||
|
if(this.userInput.Last() != ' ')
|
||||||
|
this.userInput += ' ';
|
||||||
|
|
||||||
|
this.userInput += this.suggestion;
|
||||||
|
this.suggestion = string.Empty;
|
||||||
|
this.StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AcceptNextWord()
|
||||||
|
{
|
||||||
|
var words = this.suggestion.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if(words.Length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(this.userInput.Last() != ' ')
|
||||||
|
this.userInput += ' ';
|
||||||
|
|
||||||
|
this.userInput += words[0] + ' ';
|
||||||
|
this.suggestion = string.Join(' ', words.Skip(1));
|
||||||
|
this.StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Implementation of IAsyncDisposable
|
||||||
|
|
||||||
|
public ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
return ValueTask.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -3,3 +3,4 @@
|
|||||||
@using AIStudio.Tools
|
@using AIStudio.Tools
|
||||||
@using Microsoft.AspNetCore.Components.Routing
|
@using Microsoft.AspNetCore.Components.Routing
|
||||||
@using MudBlazor
|
@using MudBlazor
|
||||||
|
@using MudExtensions
|
||||||
@@ -7,6 +7,8 @@ using Microsoft.Extensions.Logging.Console;
|
|||||||
|
|
||||||
using MudBlazor.Services;
|
using MudBlazor.Services;
|
||||||
|
|
||||||
|
using MudExtensions.Services;
|
||||||
|
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Microsoft.Extensions.FileProviders;
|
||||||
@@ -93,6 +95,7 @@ internal sealed class Program
|
|||||||
options.FormatterName = TerminalLogger.FORMATTER_NAME;
|
options.FormatterName = TerminalLogger.FORMATTER_NAME;
|
||||||
}).AddConsoleFormatter<TerminalLogger, ConsoleFormatterOptions>();
|
}).AddConsoleFormatter<TerminalLogger, ConsoleFormatterOptions>();
|
||||||
|
|
||||||
|
builder.Services.AddMudExtensions();
|
||||||
builder.Services.AddMudServices(config =>
|
builder.Services.AddMudServices(config =>
|
||||||
{
|
{
|
||||||
config.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomLeft;
|
config.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomLeft;
|
||||||
@@ -106,6 +109,7 @@ internal sealed class Program
|
|||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddMudMarkdownServices();
|
builder.Services.AddMudMarkdownServices();
|
||||||
|
builder.Services.AddSingleton(new MudTheme());
|
||||||
builder.Services.AddSingleton(MessageBus.INSTANCE);
|
builder.Services.AddSingleton(MessageBus.INSTANCE);
|
||||||
builder.Services.AddSingleton(rust);
|
builder.Services.AddSingleton(rust);
|
||||||
builder.Services.AddMudMarkdownClipboardService<MarkdownClipboardService>();
|
builder.Services.AddMudMarkdownClipboardService<MarkdownClipboardService>();
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using AIStudio.Provider.OpenAI;
|
|||||||
|
|
||||||
namespace AIStudio.Provider.Anthropic;
|
namespace AIStudio.Provider.Anthropic;
|
||||||
|
|
||||||
public sealed class ProviderAnthropic(ILogger logger) : BaseProvider("https://api.anthropic.com/v1/", logger), IProvider
|
public sealed class ProviderAnthropic(ILogger logger) : BaseProvider("https://api.anthropic.com/v1/", logger)
|
||||||
{
|
{
|
||||||
private static readonly JsonSerializerOptions JSON_SERIALIZER_OPTIONS = new()
|
private static readonly JsonSerializerOptions JSON_SERIALIZER_OPTIONS = new()
|
||||||
{
|
{
|
||||||
@@ -16,12 +16,12 @@ public sealed class ProviderAnthropic(ILogger logger) : BaseProvider("https://ap
|
|||||||
|
|
||||||
#region Implementation of IProvider
|
#region Implementation of IProvider
|
||||||
|
|
||||||
public string Id => "Anthropic";
|
public override string Id => LLMProviders.ANTHROPIC.ToName();
|
||||||
|
|
||||||
public string InstanceName { get; set; } = "Anthropic";
|
public override string InstanceName { get; set; } = "Anthropic";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, [EnumeratorCancellation] CancellationToken token = default)
|
public override async IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
{
|
{
|
||||||
// Get the API key:
|
// Get the API key:
|
||||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||||
@@ -136,31 +136,43 @@ public sealed class ProviderAnthropic(ILogger logger) : BaseProvider("https://ap
|
|||||||
|
|
||||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async IAsyncEnumerable<ImageURL> StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
public override async IAsyncEnumerable<ImageURL> StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
{
|
{
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
public override Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
return Task.FromResult(new[]
|
return Task.FromResult(new[]
|
||||||
{
|
{
|
||||||
new Model("claude-3-5-sonnet-20240620"),
|
new Model("claude-3-5-sonnet-latest", "Claude 3.5 Sonnet (latest)"),
|
||||||
new Model("claude-3-opus-20240229"),
|
new Model("claude-3-5-sonnet-20240620", "Claude 3.5 Sonnet (20. June 2024)"),
|
||||||
new Model("claude-3-sonnet-20240229"),
|
new Model("claude-3-5-sonnet-20241022", "Claude 3.5 Sonnet (22. October 2024)"),
|
||||||
new Model("claude-3-haiku-20240307"),
|
|
||||||
|
new Model("claude-3-5-haiku-latest", "Claude 3.5 Haiku (latest)"),
|
||||||
|
new Model("claude-3-5-heiku-20241022", "Claude 3.5 Haiku (22. October 2024)"),
|
||||||
|
|
||||||
|
new Model("claude-3-opus-20240229", "Claude 3.0 Opus (29. February 2024)"),
|
||||||
|
new Model("claude-3-opus-latest", "Claude 3.0 Opus (latest)"),
|
||||||
|
|
||||||
|
new Model("claude-3-sonnet-20240229", "Claude 3.0 Sonnet (29. February 2024)"),
|
||||||
|
new Model("claude-3-haiku-20240307", "Claude 3.0 Haiku (7. March 2024)"),
|
||||||
}.AsEnumerable());
|
}.AsEnumerable());
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<IEnumerable<Model>> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
public override Task<IEnumerable<Model>> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult(Enumerable.Empty<Model>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task<IEnumerable<Model>> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
return Task.FromResult(Enumerable.Empty<Model>());
|
return Task.FromResult(Enumerable.Empty<Model>());
|
||||||
}
|
}
|
||||||
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using AIStudio.Chat;
|
||||||
|
|
||||||
using RustService = AIStudio.Tools.RustService;
|
using RustService = AIStudio.Tools.RustService;
|
||||||
|
|
||||||
namespace AIStudio.Provider;
|
namespace AIStudio.Provider;
|
||||||
@@ -5,10 +7,10 @@ namespace AIStudio.Provider;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The base class for all providers.
|
/// The base class for all providers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class BaseProvider
|
public abstract class BaseProvider : IProvider, ISecretId
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The HTTP client to use for all requests.
|
/// The HTTP client to use it for all requests.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly HttpClient httpClient = new();
|
protected readonly HttpClient httpClient = new();
|
||||||
|
|
||||||
@@ -39,4 +41,37 @@ public abstract class BaseProvider
|
|||||||
// Set the base URL:
|
// Set the base URL:
|
||||||
this.httpClient.BaseAddress = new(url);
|
this.httpClient.BaseAddress = new(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Handling of IProvider, which all providers must implement
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public abstract string Id { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public abstract string InstanceName { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public abstract IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, CancellationToken token = default);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public abstract IAsyncEnumerable<ImageURL> StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, CancellationToken token = default);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public abstract Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public abstract Task<IEnumerable<Model>> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public abstract Task<IEnumerable<Model>> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation of ISecretId
|
||||||
|
|
||||||
|
public string SecretId => this.Id;
|
||||||
|
|
||||||
|
public string SecretName => this.InstanceName;
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using AIStudio.Settings;
|
||||||
|
|
||||||
namespace AIStudio.Provider;
|
namespace AIStudio.Provider;
|
||||||
|
|
||||||
public sealed record Confidence
|
public sealed record Confidence
|
||||||
@@ -20,9 +22,9 @@ public sealed record Confidence
|
|||||||
|
|
||||||
public Confidence WithLevel(ConfidenceLevel level) => this with { Level = level };
|
public Confidence WithLevel(ConfidenceLevel level) => this with { Level = level };
|
||||||
|
|
||||||
public string StyleBorder() => $"border: 2px solid {this.Level.GetColor()}; border-radius: 6px;";
|
public string StyleBorder(SettingsManager settingsManager) => $"border: 2px solid {this.Level.GetColor(settingsManager)}; border-radius: 6px;";
|
||||||
|
|
||||||
public string SetColorStyle() => $"--confidence-color: {this.Level.GetColor()};";
|
public string SetColorStyle(SettingsManager settingsManager) => $"--confidence-color: {this.Level.GetColor(settingsManager)};";
|
||||||
|
|
||||||
public static readonly Confidence NONE = new()
|
public static readonly Confidence NONE = new()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using AIStudio.Settings;
|
||||||
|
|
||||||
namespace AIStudio.Provider;
|
namespace AIStudio.Provider;
|
||||||
|
|
||||||
public static class ConfidenceLevelExtensions
|
public static class ConfidenceLevelExtensions
|
||||||
@@ -16,19 +18,31 @@ public static class ConfidenceLevelExtensions
|
|||||||
_ => "Unknown confidence level",
|
_ => "Unknown confidence level",
|
||||||
};
|
};
|
||||||
|
|
||||||
public static string GetColor(this ConfidenceLevel level) => level switch
|
public static string GetColor(this ConfidenceLevel level, SettingsManager settingsManager) => (level, settingsManager.IsDarkMode) switch
|
||||||
{
|
{
|
||||||
ConfidenceLevel.NONE => "#cccccc",
|
(ConfidenceLevel.NONE, _) => "#cccccc",
|
||||||
|
|
||||||
ConfidenceLevel.UNTRUSTED => "#ff0000",
|
(ConfidenceLevel.UNTRUSTED, false) => "#ff0000",
|
||||||
ConfidenceLevel.VERY_LOW => "#ff6600",
|
(ConfidenceLevel.UNTRUSTED, true) => "#800000",
|
||||||
ConfidenceLevel.LOW => "#ffcc00",
|
|
||||||
ConfidenceLevel.MODERATE => "#99cc00",
|
|
||||||
ConfidenceLevel.MEDIUM => "#86b300",
|
|
||||||
ConfidenceLevel.HIGH => "#009933",
|
|
||||||
|
|
||||||
_ => "#cc6600",
|
(ConfidenceLevel.VERY_LOW, false) => "#ff6600",
|
||||||
|
(ConfidenceLevel.VERY_LOW, true) => "#803300",
|
||||||
|
|
||||||
|
(ConfidenceLevel.LOW, false) => "#ffcc00",
|
||||||
|
(ConfidenceLevel.LOW, true) => "#806600",
|
||||||
|
|
||||||
|
(ConfidenceLevel.MODERATE, false) => "#99cc00",
|
||||||
|
(ConfidenceLevel.MODERATE, true) => "#4d6600",
|
||||||
|
|
||||||
|
(ConfidenceLevel.MEDIUM, false) => "#86b300",
|
||||||
|
(ConfidenceLevel.MEDIUM, true) => "#394d00",
|
||||||
|
|
||||||
|
(ConfidenceLevel.HIGH, false) => "#009933",
|
||||||
|
(ConfidenceLevel.HIGH, true) => "#004d1a",
|
||||||
|
|
||||||
|
(_, false) => "#cc6600",
|
||||||
|
(_, true) => "#663300",
|
||||||
};
|
};
|
||||||
|
|
||||||
public static string SetColorStyle(this ConfidenceLevel level) => $"--confidence-color: {level.GetColor()};";
|
public static string SetColorStyle(this ConfidenceLevel level, SettingsManager settingsManager) => $"--confidence-color: {level.GetColor(settingsManager)};";
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ using AIStudio.Chat;
|
|||||||
|
|
||||||
namespace AIStudio.Provider.Fireworks;
|
namespace AIStudio.Provider.Fireworks;
|
||||||
|
|
||||||
public class ProviderFireworks(ILogger logger) : BaseProvider("https://api.fireworks.ai/inference/v1/", logger), IProvider
|
public class ProviderFireworks(ILogger logger) : BaseProvider("https://api.fireworks.ai/inference/v1/", logger)
|
||||||
{
|
{
|
||||||
private static readonly JsonSerializerOptions JSON_SERIALIZER_OPTIONS = new()
|
private static readonly JsonSerializerOptions JSON_SERIALIZER_OPTIONS = new()
|
||||||
{
|
{
|
||||||
@@ -17,13 +17,13 @@ public class ProviderFireworks(ILogger logger) : BaseProvider("https://api.firew
|
|||||||
#region Implementation of IProvider
|
#region Implementation of IProvider
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Id => "Fireworks.ai";
|
public override string Id => LLMProviders.FIREWORKS.ToName();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string InstanceName { get; set; } = "Fireworks.ai";
|
public override string InstanceName { get; set; } = "Fireworks.ai";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, [EnumeratorCancellation] CancellationToken token = default)
|
public override async IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
{
|
{
|
||||||
// Get the API key:
|
// Get the API key:
|
||||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||||
@@ -138,20 +138,26 @@ public class ProviderFireworks(ILogger logger) : BaseProvider("https://api.firew
|
|||||||
|
|
||||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async IAsyncEnumerable<ImageURL> StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
public override async IAsyncEnumerable<ImageURL> StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
{
|
{
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
public override Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
return Task.FromResult(Enumerable.Empty<Model>());
|
return Task.FromResult(Enumerable.Empty<Model>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<IEnumerable<Model>> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
public override Task<IEnumerable<Model>> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult(Enumerable.Empty<Model>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task<IEnumerable<Model>> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
return Task.FromResult(Enumerable.Empty<Model>());
|
return Task.FromResult(Enumerable.Empty<Model>());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using AIStudio.Provider.OpenAI;
|
||||||
|
|
||||||
|
namespace AIStudio.Provider.Google;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Google chat request model.
|
||||||
|
/// </summary>
|
||||||
|
/// <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>
|
||||||
|
public readonly record struct ChatRequest(
|
||||||
|
string Model,
|
||||||
|
IList<Message> Messages,
|
||||||
|
bool Stream
|
||||||
|
);
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
namespace AIStudio.Provider.Google;
|
||||||
|
|
||||||
|
public readonly record struct Model(string Name, string DisplayName);
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace AIStudio.Provider.Google;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A data model for the response from the model endpoint.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Models"></param>
|
||||||
|
public readonly record struct ModelsResponse(IList<Model> Models);
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
using AIStudio.Chat;
|
||||||
|
using AIStudio.Provider.OpenAI;
|
||||||
|
|
||||||
|
namespace AIStudio.Provider.Google;
|
||||||
|
|
||||||
|
public class ProviderGoogle(ILogger logger) : BaseProvider("https://generativelanguage.googleapis.com/v1beta/", logger)
|
||||||
|
{
|
||||||
|
private static readonly JsonSerializerOptions JSON_SERIALIZER_OPTIONS = new()
|
||||||
|
{
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
|
||||||
|
};
|
||||||
|
|
||||||
|
#region Implementation of IProvider
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string Id => LLMProviders.GOOGLE.ToName();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string InstanceName { get; set; } = "Google Gemini";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async IAsyncEnumerable<string> StreamChatCompletion(Provider.Model chatModel, ChatThread chatThread, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
|
{
|
||||||
|
// Get the API key:
|
||||||
|
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||||
|
if(!requestedSecret.Success)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
// Prepare the system prompt:
|
||||||
|
var systemPrompt = new Message
|
||||||
|
{
|
||||||
|
Role = "system",
|
||||||
|
Content = chatThread.SystemPrompt,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prepare the Google HTTP chat request:
|
||||||
|
var geminiChatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||||
|
{
|
||||||
|
Model = chatModel.Id,
|
||||||
|
|
||||||
|
// Build the messages:
|
||||||
|
// - First of all the system prompt
|
||||||
|
// - Then none-empty user and AI messages
|
||||||
|
Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message
|
||||||
|
{
|
||||||
|
Role = n.Role switch
|
||||||
|
{
|
||||||
|
ChatRole.USER => "user",
|
||||||
|
ChatRole.AI => "assistant",
|
||||||
|
ChatRole.AGENT => "assistant",
|
||||||
|
ChatRole.SYSTEM => "system",
|
||||||
|
|
||||||
|
_ => "user",
|
||||||
|
},
|
||||||
|
|
||||||
|
Content = n.Content switch
|
||||||
|
{
|
||||||
|
ContentText text => text.Text,
|
||||||
|
_ => string.Empty,
|
||||||
|
}
|
||||||
|
}).ToList()],
|
||||||
|
|
||||||
|
// Right now, we only support streaming completions:
|
||||||
|
Stream = true,
|
||||||
|
}, JSON_SERIALIZER_OPTIONS);
|
||||||
|
|
||||||
|
// Build the HTTP post request:
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
|
||||||
|
|
||||||
|
// Set the authorization header:
|
||||||
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
||||||
|
|
||||||
|
// Set the content:
|
||||||
|
request.Content = new StringContent(geminiChatRequest, Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
// Send the request with the ResponseHeadersRead option.
|
||||||
|
// This allows us to read the stream as soon as the headers are received.
|
||||||
|
// This is important because we want to stream the responses.
|
||||||
|
var response = await this.httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token);
|
||||||
|
|
||||||
|
// Open the response stream:
|
||||||
|
var geminiStream = await response.Content.ReadAsStreamAsync(token);
|
||||||
|
|
||||||
|
// Add a stream reader to read the stream, line by line:
|
||||||
|
var streamReader = new StreamReader(geminiStream);
|
||||||
|
|
||||||
|
// Read the stream, line by line:
|
||||||
|
while(!streamReader.EndOfStream)
|
||||||
|
{
|
||||||
|
// Check if the token is canceled:
|
||||||
|
if(token.IsCancellationRequested)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
// Read the next line:
|
||||||
|
var line = await streamReader.ReadLineAsync(token);
|
||||||
|
|
||||||
|
// Skip empty lines:
|
||||||
|
if(string.IsNullOrWhiteSpace(line))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Skip lines that do not start with "data: ". Regard
|
||||||
|
// to the specification, we only want to read the data lines:
|
||||||
|
if(!line.StartsWith("data: ", StringComparison.InvariantCulture))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check if the line is the end of the stream:
|
||||||
|
if (line.StartsWith("data: [DONE]", StringComparison.InvariantCulture))
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
ResponseStreamLine geminiResponse;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// We know that the line starts with "data: ". Hence, we can
|
||||||
|
// skip the first 6 characters to get the JSON data after that.
|
||||||
|
var jsonData = line[6..];
|
||||||
|
|
||||||
|
// Deserialize the JSON data:
|
||||||
|
geminiResponse = JsonSerializer.Deserialize<ResponseStreamLine>(jsonData, JSON_SERIALIZER_OPTIONS);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Skip invalid JSON data:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip empty responses:
|
||||||
|
if(geminiResponse == default || geminiResponse.Choices.Count == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Yield the response:
|
||||||
|
yield return geminiResponse.Choices[0].Delta.Content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async IAsyncEnumerable<ImageURL> StreamImageCompletion(Provider.Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async Task<IEnumerable<Provider.Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var modelResponse = await this.LoadModels(token, apiKeyProvisional);
|
||||||
|
if(modelResponse == default)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
return modelResponse.Models.Where(model =>
|
||||||
|
model.Name.StartsWith("models/gemini-", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
.Select(n => new Provider.Model(n.Name.Replace("models/", string.Empty), n.DisplayName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task<IEnumerable<Provider.Model>> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult(Enumerable.Empty<Provider.Model>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<IEnumerable<Provider.Model>> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var modelResponse = await this.LoadModels(token, apiKeyProvisional);
|
||||||
|
if(modelResponse == default)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
return modelResponse.Models.Where(model =>
|
||||||
|
model.Name.StartsWith("models/text-embedding-", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
.Select(n => new Provider.Model(n.Name.Replace("models/", string.Empty), n.DisplayName));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private async Task<ModelsResponse> LoadModels(CancellationToken token, string? apiKeyProvisional = null)
|
||||||
|
{
|
||||||
|
var secretKey = apiKeyProvisional switch
|
||||||
|
{
|
||||||
|
not null => apiKeyProvisional,
|
||||||
|
_ => await RUST_SERVICE.GetAPIKey(this) switch
|
||||||
|
{
|
||||||
|
{ Success: true } result => await result.Secret.Decrypt(ENCRYPTION),
|
||||||
|
_ => null,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (secretKey is null)
|
||||||
|
return default;
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, $"models?key={secretKey}");
|
||||||
|
var response = await this.httpClient.SendAsync(request, token);
|
||||||
|
|
||||||
|
if(!response.IsSuccessStatusCode)
|
||||||
|
return default;
|
||||||
|
|
||||||
|
var modelResponse = await response.Content.ReadFromJsonAsync<ModelsResponse>(token);
|
||||||
|
return modelResponse;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using AIStudio.Provider.OpenAI;
|
||||||
|
|
||||||
|
namespace AIStudio.Provider.Groq;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Groq chat request model.
|
||||||
|
/// </summary>
|
||||||
|
/// <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="Seed">The seed for the chat completion.</param>
|
||||||
|
public readonly record struct ChatRequest(
|
||||||
|
string Model,
|
||||||
|
IList<Message> Messages,
|
||||||
|
bool Stream,
|
||||||
|
int Seed
|
||||||
|
);
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
using AIStudio.Chat;
|
||||||
|
using AIStudio.Provider.OpenAI;
|
||||||
|
|
||||||
|
namespace AIStudio.Provider.Groq;
|
||||||
|
|
||||||
|
public class ProviderGroq(ILogger logger) : BaseProvider("https://api.groq.com/openai/v1/", logger)
|
||||||
|
{
|
||||||
|
private static readonly JsonSerializerOptions JSON_SERIALIZER_OPTIONS = new()
|
||||||
|
{
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
|
||||||
|
};
|
||||||
|
|
||||||
|
#region Implementation of IProvider
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string Id => LLMProviders.GROQ.ToName();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string InstanceName { get; set; } = "Groq";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
|
{
|
||||||
|
// Get the API key:
|
||||||
|
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||||
|
if(!requestedSecret.Success)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
// Prepare the system prompt:
|
||||||
|
var systemPrompt = new Message
|
||||||
|
{
|
||||||
|
Role = "system",
|
||||||
|
Content = chatThread.SystemPrompt,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prepare the OpenAI HTTP chat request:
|
||||||
|
var groqChatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||||
|
{
|
||||||
|
Model = chatModel.Id,
|
||||||
|
|
||||||
|
// Build the messages:
|
||||||
|
// - First of all the system prompt
|
||||||
|
// - Then none-empty user and AI messages
|
||||||
|
Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message
|
||||||
|
{
|
||||||
|
Role = n.Role switch
|
||||||
|
{
|
||||||
|
ChatRole.USER => "user",
|
||||||
|
ChatRole.AI => "assistant",
|
||||||
|
ChatRole.AGENT => "assistant",
|
||||||
|
ChatRole.SYSTEM => "system",
|
||||||
|
|
||||||
|
_ => "user",
|
||||||
|
},
|
||||||
|
|
||||||
|
Content = n.Content switch
|
||||||
|
{
|
||||||
|
ContentText text => text.Text,
|
||||||
|
_ => string.Empty,
|
||||||
|
}
|
||||||
|
}).ToList()],
|
||||||
|
|
||||||
|
Seed = chatThread.Seed,
|
||||||
|
|
||||||
|
// Right now, we only support streaming completions:
|
||||||
|
Stream = true,
|
||||||
|
}, JSON_SERIALIZER_OPTIONS);
|
||||||
|
|
||||||
|
// Build the HTTP post request:
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
|
||||||
|
|
||||||
|
// Set the authorization header:
|
||||||
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
||||||
|
|
||||||
|
// Set the content:
|
||||||
|
request.Content = new StringContent(groqChatRequest, Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
// Send the request with the ResponseHeadersRead option.
|
||||||
|
// This allows us to read the stream as soon as the headers are received.
|
||||||
|
// This is important because we want to stream the responses.
|
||||||
|
var response = await this.httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token);
|
||||||
|
|
||||||
|
// Open the response stream:
|
||||||
|
var groqStream = await response.Content.ReadAsStreamAsync(token);
|
||||||
|
|
||||||
|
// Add a stream reader to read the stream, line by line:
|
||||||
|
var streamReader = new StreamReader(groqStream);
|
||||||
|
|
||||||
|
// Read the stream, line by line:
|
||||||
|
while(!streamReader.EndOfStream)
|
||||||
|
{
|
||||||
|
// Check if the token is canceled:
|
||||||
|
if(token.IsCancellationRequested)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
// Read the next line:
|
||||||
|
var line = await streamReader.ReadLineAsync(token);
|
||||||
|
|
||||||
|
// Skip empty lines:
|
||||||
|
if(string.IsNullOrWhiteSpace(line))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Skip lines that do not start with "data: ". Regard
|
||||||
|
// to the specification, we only want to read the data lines:
|
||||||
|
if(!line.StartsWith("data: ", StringComparison.InvariantCulture))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check if the line is the end of the stream:
|
||||||
|
if (line.StartsWith("data: [DONE]", StringComparison.InvariantCulture))
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
ResponseStreamLine groqResponse;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// We know that the line starts with "data: ". Hence, we can
|
||||||
|
// skip the first 6 characters to get the JSON data after that.
|
||||||
|
var jsonData = line[6..];
|
||||||
|
|
||||||
|
// Deserialize the JSON data:
|
||||||
|
groqResponse = JsonSerializer.Deserialize<ResponseStreamLine>(jsonData, JSON_SERIALIZER_OPTIONS);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Skip invalid JSON data:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip empty responses:
|
||||||
|
if(groqResponse == default || groqResponse.Choices.Count == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Yield the response:
|
||||||
|
yield return groqResponse.Choices[0].Delta.Content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async IAsyncEnumerable<ImageURL> StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return this.LoadModels(token, apiKeyProvisional);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task<IEnumerable<Model>> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult<IEnumerable<Model>>(Array.Empty<Model>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task<IEnumerable<Model>> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult(Enumerable.Empty<Model>());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private async Task<IEnumerable<Model>> LoadModels(CancellationToken token, string? apiKeyProvisional = null)
|
||||||
|
{
|
||||||
|
var secretKey = apiKeyProvisional switch
|
||||||
|
{
|
||||||
|
not null => apiKeyProvisional,
|
||||||
|
_ => await RUST_SERVICE.GetAPIKey(this) switch
|
||||||
|
{
|
||||||
|
{ Success: true } result => await result.Secret.Decrypt(ENCRYPTION),
|
||||||
|
_ => null,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (secretKey is null)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, "models");
|
||||||
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secretKey);
|
||||||
|
|
||||||
|
var response = await this.httpClient.SendAsync(request, token);
|
||||||
|
if(!response.IsSuccessStatusCode)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
var modelResponse = await response.Content.ReadFromJsonAsync<ModelsResponse>(token);
|
||||||
|
return modelResponse.Data.Where(n =>
|
||||||
|
!n.Id.StartsWith("whisper-", StringComparison.InvariantCultureIgnoreCase) &&
|
||||||
|
!n.Id.StartsWith("distil-", StringComparison.InvariantCultureIgnoreCase));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ public interface IProvider
|
|||||||
/// The provider's instance name. Useful for multiple instances of the same provider,
|
/// The provider's instance name. Useful for multiple instances of the same provider,
|
||||||
/// e.g., to distinguish between different OpenAI API keys.
|
/// e.g., to distinguish between different OpenAI API keys.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string InstanceName { get; set; }
|
public string InstanceName { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Starts a chat completion stream.
|
/// Starts a chat completion stream.
|
||||||
@@ -53,4 +53,12 @@ public interface IProvider
|
|||||||
/// <param name="token">The cancellation token.</param>
|
/// <param name="token">The cancellation token.</param>
|
||||||
/// <returns>The list of image models.</returns>
|
/// <returns>The list of image models.</returns>
|
||||||
public Task<IEnumerable<Model>> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default);
|
public Task<IEnumerable<Model>> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load all possible embedding models that can be used with this provider.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="apiKeyProvisional">The provisional API key to use. Useful when the user is adding a new provider. When null, the stored API key is used.</param>
|
||||||
|
/// <param name="token">The cancellation token.</param>
|
||||||
|
/// <returns>The list of embedding models.</returns>
|
||||||
|
public Task<IEnumerable<Model>> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default);
|
||||||
}
|
}
|
||||||
+3
-1
@@ -3,15 +3,17 @@ namespace AIStudio.Provider;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enum for all available providers.
|
/// Enum for all available providers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum Providers
|
public enum LLMProviders
|
||||||
{
|
{
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
|
|
||||||
OPEN_AI = 1,
|
OPEN_AI = 1,
|
||||||
ANTHROPIC = 2,
|
ANTHROPIC = 2,
|
||||||
MISTRAL = 3,
|
MISTRAL = 3,
|
||||||
|
GOOGLE = 7,
|
||||||
|
|
||||||
FIREWORKS = 5,
|
FIREWORKS = 5,
|
||||||
|
GROQ = 6,
|
||||||
|
|
||||||
SELF_HOSTED = 4,
|
SELF_HOSTED = 4,
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,267 @@
|
|||||||
|
using AIStudio.Provider.Anthropic;
|
||||||
|
using AIStudio.Provider.Fireworks;
|
||||||
|
using AIStudio.Provider.Google;
|
||||||
|
using AIStudio.Provider.Groq;
|
||||||
|
using AIStudio.Provider.Mistral;
|
||||||
|
using AIStudio.Provider.OpenAI;
|
||||||
|
using AIStudio.Provider.SelfHosted;
|
||||||
|
using AIStudio.Settings;
|
||||||
|
|
||||||
|
using Host = AIStudio.Provider.SelfHosted.Host;
|
||||||
|
|
||||||
|
namespace AIStudio.Provider;
|
||||||
|
|
||||||
|
public static class LLMProvidersExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the human-readable name of the provider.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="llmProvider">The provider.</param>
|
||||||
|
/// <returns>The human-readable name of the provider.</returns>
|
||||||
|
public static string ToName(this LLMProviders llmProvider) => llmProvider switch
|
||||||
|
{
|
||||||
|
LLMProviders.NONE => "No provider selected",
|
||||||
|
|
||||||
|
LLMProviders.OPEN_AI => "OpenAI",
|
||||||
|
LLMProviders.ANTHROPIC => "Anthropic",
|
||||||
|
LLMProviders.MISTRAL => "Mistral",
|
||||||
|
LLMProviders.GOOGLE => "Google",
|
||||||
|
|
||||||
|
LLMProviders.GROQ => "Groq",
|
||||||
|
LLMProviders.FIREWORKS => "Fireworks.ai",
|
||||||
|
|
||||||
|
LLMProviders.SELF_HOSTED => "Self-hosted",
|
||||||
|
|
||||||
|
_ => "Unknown",
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a provider's confidence.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="llmProvider">The provider.</param>
|
||||||
|
/// <param name="settingsManager">The settings manager.</param>
|
||||||
|
/// <returns>The confidence of the provider.</returns>
|
||||||
|
public static Confidence GetConfidence(this LLMProviders llmProvider, SettingsManager settingsManager) => llmProvider switch
|
||||||
|
{
|
||||||
|
LLMProviders.NONE => Confidence.NONE,
|
||||||
|
|
||||||
|
LLMProviders.FIREWORKS => Confidence.USA_NOT_TRUSTED.WithRegion("America, U.S.").WithSources("https://fireworks.ai/terms-of-service").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||||
|
|
||||||
|
LLMProviders.OPEN_AI => Confidence.USA_NO_TRAINING.WithRegion("America, U.S.").WithSources(
|
||||||
|
"https://platform.openai.com/docs/models/default-usage-policies-by-endpoint",
|
||||||
|
"https://openai.com/policies/terms-of-use/",
|
||||||
|
"https://help.openai.com/en/articles/5722486-how-your-data-is-used-to-improve-model-performance",
|
||||||
|
"https://openai.com/enterprise-privacy/"
|
||||||
|
).WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||||
|
|
||||||
|
LLMProviders.GOOGLE => Confidence.USA_NO_TRAINING.WithRegion("America, U.S.").WithSources("https://ai.google.dev/gemini-api/terms").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||||
|
|
||||||
|
LLMProviders.GROQ => Confidence.USA_NO_TRAINING.WithRegion("America, U.S.").WithSources("https://wow.groq.com/terms-of-use/").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||||
|
|
||||||
|
LLMProviders.ANTHROPIC => Confidence.USA_NO_TRAINING.WithRegion("America, U.S.").WithSources("https://www.anthropic.com/legal/commercial-terms").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||||
|
|
||||||
|
LLMProviders.MISTRAL => Confidence.GDPR_NO_TRAINING.WithRegion("Europe, France").WithSources("https://mistral.ai/terms/#terms-of-service-la-plateforme").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||||
|
|
||||||
|
LLMProviders.SELF_HOSTED => Confidence.SELF_HOSTED.WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||||
|
|
||||||
|
_ => Confidence.UNKNOWN.WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if the specified provider supports embeddings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="llmProvider">The provider to check.</param>
|
||||||
|
/// <returns>True if the provider supports embeddings; otherwise, false.</returns>
|
||||||
|
public static bool ProvideEmbeddings(this LLMProviders llmProvider) => llmProvider switch
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Providers that support embeddings:
|
||||||
|
//
|
||||||
|
LLMProviders.OPEN_AI => true,
|
||||||
|
LLMProviders.MISTRAL => true,
|
||||||
|
LLMProviders.GOOGLE => true,
|
||||||
|
|
||||||
|
//
|
||||||
|
// Providers that do not support embeddings:
|
||||||
|
//
|
||||||
|
LLMProviders.GROQ => false,
|
||||||
|
LLMProviders.ANTHROPIC => false,
|
||||||
|
LLMProviders.FIREWORKS => false,
|
||||||
|
|
||||||
|
//
|
||||||
|
// Self-hosted providers are treated as a special case anyway.
|
||||||
|
//
|
||||||
|
LLMProviders.SELF_HOSTED => true,
|
||||||
|
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new provider instance based on the provider value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="providerSettings">The provider settings.</param>
|
||||||
|
/// <param name="logger">The logger to use.</param>
|
||||||
|
/// <returns>The provider instance.</returns>
|
||||||
|
public static IProvider CreateProvider(this Settings.Provider providerSettings, ILogger logger)
|
||||||
|
{
|
||||||
|
return providerSettings.UsedLLMProvider.CreateProvider(providerSettings.InstanceName, providerSettings.Host, providerSettings.Hostname, logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new provider instance based on the embedding provider value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="embeddingProviderSettings">The embedding provider settings.</param>
|
||||||
|
/// <param name="logger">The logger to use.</param>
|
||||||
|
/// <returns>The provider instance.</returns>
|
||||||
|
public static IProvider CreateProvider(this EmbeddingProvider embeddingProviderSettings, ILogger logger)
|
||||||
|
{
|
||||||
|
return embeddingProviderSettings.UsedLLMProvider.CreateProvider(embeddingProviderSettings.Name, embeddingProviderSettings.Host, embeddingProviderSettings.Hostname, logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IProvider CreateProvider(this LLMProviders provider, string instanceName, Host host, string hostname, ILogger logger)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return provider switch
|
||||||
|
{
|
||||||
|
LLMProviders.OPEN_AI => new ProviderOpenAI(logger) { InstanceName = instanceName },
|
||||||
|
LLMProviders.ANTHROPIC => new ProviderAnthropic(logger) { InstanceName = instanceName },
|
||||||
|
LLMProviders.MISTRAL => new ProviderMistral(logger) { InstanceName = instanceName },
|
||||||
|
LLMProviders.GOOGLE => new ProviderGoogle(logger) { InstanceName = instanceName },
|
||||||
|
|
||||||
|
LLMProviders.GROQ => new ProviderGroq(logger) { InstanceName = instanceName },
|
||||||
|
LLMProviders.FIREWORKS => new ProviderFireworks(logger) { InstanceName = instanceName },
|
||||||
|
|
||||||
|
LLMProviders.SELF_HOSTED => new ProviderSelfHosted(logger, host, hostname) { InstanceName = instanceName },
|
||||||
|
|
||||||
|
_ => new NoProvider(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.LogError($"Failed to create provider: {e.Message}");
|
||||||
|
return new NoProvider();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetCreationURL(this LLMProviders provider) => provider switch
|
||||||
|
{
|
||||||
|
LLMProviders.OPEN_AI => "https://platform.openai.com/signup",
|
||||||
|
LLMProviders.MISTRAL => "https://console.mistral.ai/",
|
||||||
|
LLMProviders.ANTHROPIC => "https://console.anthropic.com/dashboard",
|
||||||
|
LLMProviders.GOOGLE => "https://console.cloud.google.com/",
|
||||||
|
|
||||||
|
LLMProviders.GROQ => "https://console.groq.com/",
|
||||||
|
LLMProviders.FIREWORKS => "https://fireworks.ai/login",
|
||||||
|
|
||||||
|
_ => string.Empty,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string GetDashboardURL(this LLMProviders provider) => provider switch
|
||||||
|
{
|
||||||
|
LLMProviders.OPEN_AI => "https://platform.openai.com/usage",
|
||||||
|
LLMProviders.MISTRAL => "https://console.mistral.ai/usage/",
|
||||||
|
LLMProviders.ANTHROPIC => "https://console.anthropic.com/settings/plans",
|
||||||
|
LLMProviders.GROQ => "https://console.groq.com/settings/usage",
|
||||||
|
LLMProviders.GOOGLE => "https://console.cloud.google.com/billing",
|
||||||
|
LLMProviders.FIREWORKS => "https://fireworks.ai/account/billing",
|
||||||
|
|
||||||
|
_ => string.Empty,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static bool HasDashboard(this LLMProviders provider) => provider switch
|
||||||
|
{
|
||||||
|
LLMProviders.OPEN_AI => true,
|
||||||
|
LLMProviders.MISTRAL => true,
|
||||||
|
LLMProviders.ANTHROPIC => true,
|
||||||
|
LLMProviders.GROQ => true,
|
||||||
|
LLMProviders.FIREWORKS => true,
|
||||||
|
LLMProviders.GOOGLE => true,
|
||||||
|
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string GetModelsOverviewURL(this LLMProviders provider) => provider switch
|
||||||
|
{
|
||||||
|
LLMProviders.FIREWORKS => "https://fireworks.ai/models?show=Serverless",
|
||||||
|
_ => string.Empty,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static bool IsLLMModelProvidedManually(this LLMProviders provider) => provider switch
|
||||||
|
{
|
||||||
|
LLMProviders.FIREWORKS => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static bool IsEmbeddingModelProvidedManually(this LLMProviders provider, Host host) => provider switch
|
||||||
|
{
|
||||||
|
LLMProviders.SELF_HOSTED => host is not Host.LM_STUDIO,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static bool IsHostNeeded(this LLMProviders provider) => provider switch
|
||||||
|
{
|
||||||
|
LLMProviders.SELF_HOSTED => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static bool IsHostnameNeeded(this LLMProviders provider) => provider switch
|
||||||
|
{
|
||||||
|
LLMProviders.SELF_HOSTED => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static bool IsAPIKeyNeeded(this LLMProviders provider, Host host) => provider switch
|
||||||
|
{
|
||||||
|
LLMProviders.OPEN_AI => true,
|
||||||
|
LLMProviders.MISTRAL => true,
|
||||||
|
LLMProviders.ANTHROPIC => true,
|
||||||
|
LLMProviders.GOOGLE => true,
|
||||||
|
|
||||||
|
LLMProviders.GROQ => true,
|
||||||
|
LLMProviders.FIREWORKS => true,
|
||||||
|
|
||||||
|
LLMProviders.SELF_HOSTED => host is Host.OLLAMA,
|
||||||
|
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static bool ShowRegisterButton(this LLMProviders provider) => provider switch
|
||||||
|
{
|
||||||
|
LLMProviders.OPEN_AI => true,
|
||||||
|
LLMProviders.MISTRAL => true,
|
||||||
|
LLMProviders.ANTHROPIC => true,
|
||||||
|
LLMProviders.GOOGLE => true,
|
||||||
|
|
||||||
|
LLMProviders.GROQ => true,
|
||||||
|
LLMProviders.FIREWORKS => true,
|
||||||
|
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static bool CanLoadModels(this LLMProviders provider, Host host, string? apiKey)
|
||||||
|
{
|
||||||
|
if (provider is LLMProviders.SELF_HOSTED)
|
||||||
|
{
|
||||||
|
switch (host)
|
||||||
|
{
|
||||||
|
case Host.NONE:
|
||||||
|
case Host.LLAMACPP:
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case Host.OLLAMA:
|
||||||
|
case Host.LM_STUDIO:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(provider is LLMProviders.NONE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(string.IsNullOrWhiteSpace(apiKey))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loaded 100 of 240 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user