mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-20 00:38:00 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b0ee49ba9 | ||
|
|
45ac3eee2b | ||
|
|
0b6c2f1041 | ||
|
|
70fbc74a1d | ||
|
|
bac0b49dce | ||
|
|
cfb7fe403d | ||
|
|
164b07c629 | ||
|
|
473bfd068d | ||
|
|
120d5c808a | ||
|
|
9ef1a047f0 | ||
|
|
d6f5dc143d | ||
|
|
d90a82de64 | ||
|
|
9418b99275 | ||
|
|
37bd42e41f | ||
|
|
38103121c9 | ||
|
|
9587a07556 | ||
|
|
c3bbcacaed | ||
|
|
f00722e0a6 | ||
|
|
ce243913ed | ||
|
|
fc37882773 | ||
|
|
864bda1213 | ||
|
|
73b2f1fa6a | ||
|
|
d966929dca | ||
|
|
6a37ce6d25 | ||
|
|
70e64f2d14 | ||
|
|
38ec098430 | ||
|
|
4e167d58ea | ||
|
|
f61fe87181 | ||
|
|
2a01d3bb6e | ||
|
|
adf61a1384 | ||
|
|
f1c2a65450 | ||
|
|
e03289b680 | ||
|
|
99dc2606aa | ||
|
|
e931e28ab1 | ||
|
|
5d17dcc29b | ||
|
|
c1826426d9 | ||
|
|
be11efed67 | ||
|
|
7d10bb00f7 | ||
|
|
9ca2860079 |
No files matched your search
@@ -0,0 +1,17 @@
|
||||
# Project Guidelines
|
||||
|
||||
## Repository Structure
|
||||
- The repository and the app consist of a Rust project in the `runtime` folder and a .NET solution in the `app` folder.
|
||||
- The .NET solution then contains 4 .NET projects:
|
||||
- `Build Script` is not required for running the app; instead, it contains the build script for creating new releases, for example.
|
||||
- `MindWork AI Studio` contains the actual app code.
|
||||
- `SharedTools` contains types that are needed in the build script and in the app, for example.
|
||||
- `SourceCodeRules` is a Roslyn analyzer project. It contains analyzers and code fixes that we use to enforce code style rules within the team.
|
||||
|
||||
## Changelogs
|
||||
- There is a changelog in Markdown format for each version.
|
||||
- All changelogs are located in the folder `app/MindWork AI Studio/wwwroot/changelog`.
|
||||
- These changelogs are intended for end users, not for developers.
|
||||
- Therefore, we don't mention all changes in the changelog: changes that end users wouldn't understand remain unmentioned. For complex refactorings, for example, we mention a generic point that the code quality has been improved to enhance future maintenance.
|
||||
- The changelog is always written in US English.
|
||||
- The changelog doesn't mention bug fixes if the bug was never shipped and users don't know about it.
|
||||
@@ -1,52 +1,85 @@
|
||||
# MindWork AI Studio
|
||||
<img src="app/MindWork%20AI%20Studio/wwwroot/svg/banner.svg" alt="MindWork AI Studio Banner"/>
|
||||
|
||||
Are you new here? [Read here](#what-is-ai-studio) what AI Studio is.
|
||||
|
||||
## News
|
||||
Things we are currently working on:
|
||||
<details>
|
||||
<summary>
|
||||
<h3 style="display:inline-block">
|
||||
Things we are currently working on
|
||||
</h3>
|
||||
</summary>
|
||||
|
||||
- Since November 2024: Work on RAG (integration of your data and files) has begun. We will support the integration of local and external data sources. We need to implement the following runtime (Rust) and app (.NET) steps:
|
||||
<details>
|
||||
<summary>
|
||||
<h4 style="display:inline-block">
|
||||
RAG (Retrieval-Augmented Generation)
|
||||
</h4>
|
||||
</summary>
|
||||
|
||||
- [x] ~~Runtime: Restructuring the code into meaningful modules (PR [#192](https://github.com/MindWorkAI/AI-Studio/pull/192))~~
|
||||
- [x] ~~Define the [External Retrieval Interface (ERI)](https://github.com/MindWorkAI/ERI) as a contract for integrating arbitrary external data (PR [#1](https://github.com/MindWorkAI/ERI/pull/1))~~
|
||||
- [x] ~~App: Metadata for providers (which provider offers embeddings?) (PR [#205](https://github.com/MindWorkAI/AI-Studio/pull/205))~~
|
||||
- [x] ~~App: Add an option to show preview features (PR [#222](https://github.com/MindWorkAI/AI-Studio/pull/222))~~
|
||||
- [x] ~~App: Configure embedding providers (PR [#224](https://github.com/MindWorkAI/AI-Studio/pull/224))~~
|
||||
- [x] ~~App: Implement an [ERI](https://github.com/MindWorkAI/ERI) server coding assistant (PR [#231](https://github.com/MindWorkAI/AI-Studio/pull/231))~~
|
||||
- [x] ~~App: Management of data sources (local & external data via [ERI](https://github.com/MindWorkAI/ERI)) (PR [#259](https://github.com/MindWorkAI/AI-Studio/pull/259), [#273](https://github.com/MindWorkAI/AI-Studio/pull/273))~~
|
||||
- [x] ~~Runtime: Extract data from txt / md / pdf / docx / xlsx files (PR [#374](https://github.com/MindWorkAI/AI-Studio/pull/374))~~
|
||||
- [ ] (*Optional*) Runtime: Implement internal embedding provider through [fastembed-rs](https://github.com/Anush008/fastembed-rs)
|
||||
- [x] ~~App: Implement dialog for checking & handling [pandoc](https://pandoc.org/) installation ([PR #393](https://github.com/MindWorkAI/AI-Studio/pull/393), [PR #487](https://github.com/MindWorkAI/AI-Studio/pull/487))~~
|
||||
- [ ] App: Implement external embedding providers
|
||||
- [ ] App: Implement the process to vectorize one local file using embeddings
|
||||
- [ ] Runtime: Integration of the vector database [LanceDB](https://github.com/lancedb/lancedb)
|
||||
- [ ] App: Implement the continuous process of vectorizing data
|
||||
- [x] ~~App: Define a common retrieval context interface for the integration of RAG processes in chats (PR [#281](https://github.com/MindWorkAI/AI-Studio/pull/281), [#284](https://github.com/MindWorkAI/AI-Studio/pull/284), [#286](https://github.com/MindWorkAI/AI-Studio/pull/286), [#287](https://github.com/MindWorkAI/AI-Studio/pull/287))~~
|
||||
- [x] ~~App: Define a common augmentation interface for the integration of RAG processes in chats (PR [#288](https://github.com/MindWorkAI/AI-Studio/pull/288), [#289](https://github.com/MindWorkAI/AI-Studio/pull/289))~~
|
||||
- [x] ~~App: Integrate data sources in chats (PR [#282](https://github.com/MindWorkAI/AI-Studio/pull/282))~~
|
||||
Since November 2024: Work on RAG (integration of your data and files) has begun. We will support the integration of local and external data sources. We need to implement the following runtime (Rust) and app (.NET) steps:
|
||||
|
||||
- [x] ~~Runtime: Restructuring the code into meaningful modules (PR [#192](https://github.com/MindWorkAI/AI-Studio/pull/192))~~
|
||||
- [x] ~~Define the [External Retrieval Interface (ERI)](https://github.com/MindWorkAI/ERI) as a contract for integrating arbitrary external data (PR [#1](https://github.com/MindWorkAI/ERI/pull/1))~~
|
||||
- [x] ~~App: Metadata for providers (which provider offers embeddings?) (PR [#205](https://github.com/MindWorkAI/AI-Studio/pull/205))~~
|
||||
- [x] ~~App: Add an option to show preview features (PR [#222](https://github.com/MindWorkAI/AI-Studio/pull/222))~~
|
||||
- [x] ~~App: Configure embedding providers (PR [#224](https://github.com/MindWorkAI/AI-Studio/pull/224))~~
|
||||
- [x] ~~App: Implement an [ERI](https://github.com/MindWorkAI/ERI) server coding assistant (PR [#231](https://github.com/MindWorkAI/AI-Studio/pull/231))~~
|
||||
- [x] ~~App: Management of data sources (local & external data via [ERI](https://github.com/MindWorkAI/ERI)) (PR [#259](https://github.com/MindWorkAI/AI-Studio/pull/259), [#273](https://github.com/MindWorkAI/AI-Studio/pull/273))~~
|
||||
- [x] ~~Runtime: Extract data from txt / md / pdf / docx / xlsx files (PR [#374](https://github.com/MindWorkAI/AI-Studio/pull/374))~~
|
||||
- [ ] (*Optional*) Runtime: Implement internal embedding provider through [fastembed-rs](https://github.com/Anush008/fastembed-rs)
|
||||
- [x] ~~App: Implement dialog for checking & handling [pandoc](https://pandoc.org/) installation ([PR #393](https://github.com/MindWorkAI/AI-Studio/pull/393), [PR #487](https://github.com/MindWorkAI/AI-Studio/pull/487))~~
|
||||
- [ ] App: Implement external embedding providers
|
||||
- [ ] App: Implement the process to vectorize one local file using embeddings
|
||||
- [ ] Runtime: Integration of the vector database [LanceDB](https://github.com/lancedb/lancedb)
|
||||
- [ ] App: Implement the continuous process of vectorizing data
|
||||
- [x] ~~App: Define a common retrieval context interface for the integration of RAG processes in chats (PR [#281](https://github.com/MindWorkAI/AI-Studio/pull/281), [#284](https://github.com/MindWorkAI/AI-Studio/pull/284), [#286](https://github.com/MindWorkAI/AI-Studio/pull/286), [#287](https://github.com/MindWorkAI/AI-Studio/pull/287))~~
|
||||
- [x] ~~App: Define a common augmentation interface for the integration of RAG processes in chats (PR [#288](https://github.com/MindWorkAI/AI-Studio/pull/288), [#289](https://github.com/MindWorkAI/AI-Studio/pull/289))~~
|
||||
- [x] ~~App: Integrate data sources in chats (PR [#282](https://github.com/MindWorkAI/AI-Studio/pull/282))~~
|
||||
|
||||
- Since September 2024: Experiments have been started on how we can work on long texts with AI Studio. Let's say you want to write a fantasy novel or create a complex project proposal and use LLM for support. The initial experiments were promising, but not yet satisfactory. We are testing further approaches until a satisfactory solution is found. The current state of our experiment is available as an experimental preview feature through your app configuration. Related PR: ~~[PR #167](https://github.com/MindWorkAI/AI-Studio/pull/167), [PR #226](https://github.com/MindWorkAI/AI-Studio/pull/226)~~, [PR #376](https://github.com/MindWorkAI/AI-Studio/pull/376).
|
||||
</details>
|
||||
|
||||
- Since March 2025: We have started developing the plugin system. There will be language plugins to offer AI Studio in other languages, configuration plugins to centrally manage certain providers and rules within an organization, and assistant plugins that allow anyone to develop their own assistants. We are using Lua as the plugin language:
|
||||
- [x] ~~Plan & implement the base plugin system ([PR #322](https://github.com/MindWorkAI/AI-Studio/pull/322))~~
|
||||
- [x] ~~Start the plugin system ([PR #372](https://github.com/MindWorkAI/AI-Studio/pull/372))~~
|
||||
- [x] ~~Added hot-reload support for plugins ([PR #377](https://github.com/MindWorkAI/AI-Studio/pull/377), [PR #391](https://github.com/MindWorkAI/AI-Studio/pull/391))~~
|
||||
- [x] ~~Add support for other languages (I18N) to AI Studio ([PR #381](https://github.com/MindWorkAI/AI-Studio/pull/381), [PR #400](https://github.com/MindWorkAI/AI-Studio/pull/400), [PR #404](https://github.com/MindWorkAI/AI-Studio/pull/404), [PR #429](https://github.com/MindWorkAI/AI-Studio/pull/429), [PR #446](https://github.com/MindWorkAI/AI-Studio/pull/446), [PR #451](https://github.com/MindWorkAI/AI-Studio/pull/451), [PR #455](https://github.com/MindWorkAI/AI-Studio/pull/455), [PR #458](https://github.com/MindWorkAI/AI-Studio/pull/458), [PR #462](https://github.com/MindWorkAI/AI-Studio/pull/462), [PR #469](https://github.com/MindWorkAI/AI-Studio/pull/469), [PR #486](https://github.com/MindWorkAI/AI-Studio/pull/486))~~
|
||||
- [x] ~~Add an I18N assistant to translate all AI Studio texts to a certain language & culture ([PR #422](https://github.com/MindWorkAI/AI-Studio/pull/422))~~
|
||||
- [x] ~~Provide MindWork AI Studio in German ([PR #430](https://github.com/MindWorkAI/AI-Studio/pull/430), [PR #446](https://github.com/MindWorkAI/AI-Studio/pull/446), [PR #451](https://github.com/MindWorkAI/AI-Studio/pull/451), [PR #455](https://github.com/MindWorkAI/AI-Studio/pull/455), [PR #458](https://github.com/MindWorkAI/AI-Studio/pull/458), [PR #462](https://github.com/MindWorkAI/AI-Studio/pull/462), [PR #469](https://github.com/MindWorkAI/AI-Studio/pull/469), [PR #486](https://github.com/MindWorkAI/AI-Studio/pull/486))~~
|
||||
- [x] ~~Add configuration plugins, which allow pre-defining some LLM providers in organizations ([PR #491](https://github.com/MindWorkAI/AI-Studio/pull/491), [PR #493](https://github.com/MindWorkAI/AI-Studio/pull/493), [PR #494](https://github.com/MindWorkAI/AI-Studio/pull/494), [PR #497](https://github.com/MindWorkAI/AI-Studio/pull/497))~~
|
||||
- [ ] Add an app store for plugins, showcasing community-contributed plugins from public GitHub and GitLab repositories. This will enable AI Studio users to discover, install, and update plugins directly within the platform.
|
||||
- [ ] Add assistant plugins
|
||||
<details>
|
||||
<summary>
|
||||
<h4 style="display:inline-block">
|
||||
Writer Mode
|
||||
</h4>
|
||||
</summary>
|
||||
|
||||
Other News:
|
||||
Since September 2024: Experiments have been started on how we can work on long texts with AI Studio. Let's say you want to write a fantasy novel or create a complex project proposal and use LLM for support. The initial experiments were promising, but not yet satisfactory. We are testing further approaches until a satisfactory solution is found. The current state of our experiment is available as an experimental preview feature through your app configuration. Related PR: ~~[PR #167](https://github.com/MindWorkAI/AI-Studio/pull/167), [PR #226](https://github.com/MindWorkAI/AI-Studio/pull/226)~~, [PR #376](https://github.com/MindWorkAI/AI-Studio/pull/376).
|
||||
|
||||
- April 2025: We have two active financial supporters: Peer `peerschuett` and Dominic `donework`. Thank you very much for your support. MindWork AI reinvests these donations by passing them on to our AI Studio dependencies ([see here](https://github.com/orgs/MindWorkAI/sponsoring)). In the event that we receive large donations, we will first sign the app ([#56](https://github.com/MindWorkAI/Planning/issues/56)). In case we receive more donations, we will look for and pay staff to develop features for AI Studio.
|
||||
</details>
|
||||
|
||||
- April 2025: The [German Aerospace Center (DLR)](https://en.wikipedia.org/wiki/German_Aerospace_Center) ([Website](https://www.dlr.de/en)) will use AI Studio at least within the scope of three projects and will also contribute to its further development. This is great news.
|
||||
<details>
|
||||
<summary>
|
||||
<h4 style="display:inline-block">
|
||||
Plugin System
|
||||
</h4>
|
||||
</summary>
|
||||
|
||||
Since March 2025: We have started developing the plugin system. There will be language plugins to offer AI Studio in other languages, configuration plugins to centrally manage certain providers and rules within an organization, and assistant plugins that allow anyone to develop their own assistants. We are using Lua as the plugin language:
|
||||
- [x] ~~Plan & implement the base plugin system ([PR #322](https://github.com/MindWorkAI/AI-Studio/pull/322))~~
|
||||
- [x] ~~Start the plugin system ([PR #372](https://github.com/MindWorkAI/AI-Studio/pull/372))~~
|
||||
- [x] ~~Added hot-reload support for plugins ([PR #377](https://github.com/MindWorkAI/AI-Studio/pull/377), [PR #391](https://github.com/MindWorkAI/AI-Studio/pull/391))~~
|
||||
- [x] ~~Add support for other languages (I18N) to AI Studio ([PR #381](https://github.com/MindWorkAI/AI-Studio/pull/381), [PR #400](https://github.com/MindWorkAI/AI-Studio/pull/400), [PR #404](https://github.com/MindWorkAI/AI-Studio/pull/404), [PR #429](https://github.com/MindWorkAI/AI-Studio/pull/429), [PR #446](https://github.com/MindWorkAI/AI-Studio/pull/446), [PR #451](https://github.com/MindWorkAI/AI-Studio/pull/451), [PR #455](https://github.com/MindWorkAI/AI-Studio/pull/455), [PR #458](https://github.com/MindWorkAI/AI-Studio/pull/458), [PR #462](https://github.com/MindWorkAI/AI-Studio/pull/462), [PR #469](https://github.com/MindWorkAI/AI-Studio/pull/469), [PR #486](https://github.com/MindWorkAI/AI-Studio/pull/486))~~
|
||||
- [x] ~~Add an I18N assistant to translate all AI Studio texts to a certain language & culture ([PR #422](https://github.com/MindWorkAI/AI-Studio/pull/422))~~
|
||||
- [x] ~~Provide MindWork AI Studio in German ([PR #430](https://github.com/MindWorkAI/AI-Studio/pull/430), [PR #446](https://github.com/MindWorkAI/AI-Studio/pull/446), [PR #451](https://github.com/MindWorkAI/AI-Studio/pull/451), [PR #455](https://github.com/MindWorkAI/AI-Studio/pull/455), [PR #458](https://github.com/MindWorkAI/AI-Studio/pull/458), [PR #462](https://github.com/MindWorkAI/AI-Studio/pull/462), [PR #469](https://github.com/MindWorkAI/AI-Studio/pull/469), [PR #486](https://github.com/MindWorkAI/AI-Studio/pull/486))~~
|
||||
- [x] ~~Add configuration plugins, which allow pre-defining some LLM providers in organizations ([PR #491](https://github.com/MindWorkAI/AI-Studio/pull/491), [PR #493](https://github.com/MindWorkAI/AI-Studio/pull/493), [PR #494](https://github.com/MindWorkAI/AI-Studio/pull/494), [PR #497](https://github.com/MindWorkAI/AI-Studio/pull/497))~~
|
||||
- [ ] Add an app store for plugins, showcasing community-contributed plugins from public GitHub and GitLab repositories. This will enable AI Studio users to discover, install, and update plugins directly within the platform.
|
||||
- [ ] Add assistant plugins
|
||||
|
||||
Features we have recently released:
|
||||
</details>
|
||||
</details>
|
||||
|
||||
<details open>
|
||||
<summary>
|
||||
<h3 style="display:inline-block">
|
||||
Features we have recently released
|
||||
</h3>
|
||||
</summary>
|
||||
|
||||
- v0.9.51: Added support for [Perplexity](https://www.perplexity.ai/); citations added so that LLMs can provide source references (e.g., some OpenAI models, Perplexity); added support for OpenAI's Responses API so that all text LLMs from OpenAI now work in MindWork AI Studio, including Deep Research models; web searches are now possible (some OpenAI models, Perplexity).
|
||||
- v0.9.50: Added support for self-hosted LLMs using [vLLM](https://blog.vllm.ai/2023/06/20/vllm.html).
|
||||
- v0.9.46: Released our plugin system, a German language plugin, early support for enterprise environments, and configuration plugins. Additionally, we added the Pandoc integration for future data processing and file generation.
|
||||
- v0.9.45: Added chat templates to AI Studio, allowing you to create and use a library of system prompts for your chats.
|
||||
@@ -58,6 +91,8 @@ Features we have recently released:
|
||||
- v0.9.26+: Added RAG for external data sources using our [ERI interface](https://mindworkai.org/#eri---external-retrieval-interface) as a preview feature.
|
||||
- v0.9.25: Added [xAI](https://x.ai/) as a new provider. xAI provides their Grok models for generating content.
|
||||
- v0.9.23: Added support for OpenAI `o` models (`o1`, `o1-mini`, `o3`, etc.); added also an [ERI](https://github.com/MindWorkAI/ERI) server coding assistant as a preview feature behind the RAG feature flag. Your own ERI server can be used to gain access to, e.g., your enterprise data from within AI Studio.
|
||||
|
||||
</details>
|
||||
|
||||
## What is AI Studio?
|
||||
|
||||
@@ -72,6 +107,7 @@ MindWork AI Studio is a free desktop app for macOS, Windows, and Linux. It provi
|
||||
- **Free of charge**: The app is free to use, both for personal and commercial purposes.
|
||||
- **Independence**: You are not tied to any single provider. Instead, you can choose the providers that best suit your needs. Right now, we support:
|
||||
- [OpenAI](https://openai.com/) (GPT5, GPT4.1, o1, o3, o4, etc.)
|
||||
- [Perplexity](https://www.perplexity.ai/)
|
||||
- [Mistral](https://mistral.ai/)
|
||||
- [Anthropic](https://www.anthropic.com/) (Claude)
|
||||
- [Google Gemini](https://gemini.google.com)
|
||||
@@ -92,7 +128,13 @@ MindWork AI Studio is a free desktop app for macOS, Windows, and Linux. It provi
|
||||
|
||||
## **Ready to get started 🤩?** [Download the appropriate setup for your operating system here](documentation/Setup.md).
|
||||
|
||||
## Support the Project
|
||||
<details>
|
||||
<summary>
|
||||
<h2 style="display:inline-block">
|
||||
Support the Project
|
||||
</h2>
|
||||
</summary>
|
||||
|
||||
Thank you for using MindWork AI Studio and considering supporting its development 😀. Your support helps keep the project alive and ensures continuous improvements and new features.
|
||||
|
||||
We offer various ways you can support the project:
|
||||
@@ -106,7 +148,15 @@ For companies, sponsoring MindWork AI Studio is not only a way to support innova
|
||||
To view all available tiers, please visit our [GitHub Sponsors page](https://github.com/sponsors/MindWorkAI).
|
||||
Your support, whether big or small, keeps the wheels turning and is deeply appreciated ❤️.
|
||||
|
||||
## Planned Features
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h2 style="display:inline-block">
|
||||
Planned Features
|
||||
</h2>
|
||||
</summary>
|
||||
|
||||
Here's an exciting look at some of the features we're planning to add to AI Studio in future releases:
|
||||
- **Integrating your data**: You'll be able to integrate your data into AI Studio, like your PDF or Office files, or your Markdown notes.
|
||||
- **Integration of enterprise data:** It will soon be possible to integrate data from the corporate network using a specified interface ([External Retrieval Interface](https://github.com/MindWorkAI/ERI), ERI for short). This will likely require development work by the organization in question.
|
||||
@@ -120,17 +170,45 @@ Here's an exciting look at some of the features we're planning to add to AI Stud
|
||||
|
||||
Stay tuned for more updates and enhancements to make MindWork AI Studio even more powerful and versatile 🤩.
|
||||
|
||||
## Building
|
||||
If you're interested in learning more about future plans, check out our [roadmap](https://github.com/orgs/MindWorkAI/projects/2/views/3) and our [planning issues](https://github.com/MindWorkAI/Planning/issues).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h2 style="display:inline-block">
|
||||
Building
|
||||
</h2>
|
||||
</summary>
|
||||
|
||||
You want to know how to build MindWork AI Studio from source? [Check out the instructions here](documentation/Build.md).
|
||||
|
||||
## Enterprise IT
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h2 style="display:inline-block">
|
||||
Enterprise IT
|
||||
</h2>
|
||||
</summary>
|
||||
|
||||
Do you want to manage AI Studio centrally from your IT department? Yes, that’s possible. [Here’s how it works.](documentation/Enterprise%20IT.md)
|
||||
|
||||
## License
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<h2 style="display:inline-block">
|
||||
License
|
||||
</h2>
|
||||
</summary>
|
||||
|
||||
MindWork AI Studio is licensed under the `FSL-1.1-MIT` license (functional source license). Here’s a simple rundown of what that means for you:
|
||||
- **Permitted Use**: Feel free to use, copy, modify, and share the software for your own projects, educational purposes, research, or even in professional services. The key is to use it in a way that doesn't compete with our offerings.
|
||||
- **Competing Use**: Our only request is that you don't create commercial products or services that replace or compete with MindWork AI Studio or any of our other offerings.
|
||||
- **No Warranties**: The software is provided "as is", without any promises from us about it working perfectly for your needs. While we strive to make it great, we can't guarantee it will be free of bugs or issues.
|
||||
- **Future License**: Good news! The license for each release of MindWork AI Studio will automatically convert to an MIT license two years from its release date. This makes it even easier for you to use the software in the future.
|
||||
|
||||
For more details, refer to the [LICENSE](LICENSE.md) file. This license structure ensures you have plenty of freedom to use and enjoy the software while protecting our work.
|
||||
For more details, refer to the [LICENSE](LICENSE.md) file. This license structure ensures you have plenty of freedom to use and enjoy the software while protecting our work.
|
||||
|
||||
</details>
|
||||
@@ -336,8 +336,9 @@ public sealed partial class UpdateMetadataCommands
|
||||
await File.WriteAllLinesAsync(pathMetadata, lines, Environment.UTF8_NO_BOM);
|
||||
Console.WriteLine(" done.");
|
||||
}
|
||||
|
||||
private async Task UpdateProjectCommitHash()
|
||||
|
||||
[Command("update-project-hash", Description = "Update the project commit hash")]
|
||||
public async Task UpdateProjectCommitHash()
|
||||
{
|
||||
const int COMMIT_HASH_INDEX = 8;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FNV/@EntryIndexedValue">FNV</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GWDG/@EntryIndexedValue">GWDG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HF/@EntryIndexedValue">HF</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IERI/@EntryIndexedValue">IERI</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LLM/@EntryIndexedValue">LLM</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LM/@EntryIndexedValue">LM</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MSG/@EntryIndexedValue">MSG</s:String>
|
||||
@@ -20,6 +21,7 @@
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=groq/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=gwdg/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=huggingface/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=ieri/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=mwais/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=ollama/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=tauri_0027s/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@@ -54,7 +54,7 @@ public abstract class AgentBase(ILogger<AgentBase> logger, SettingsManager setti
|
||||
|
||||
#region Implementation of IAgent
|
||||
|
||||
public abstract AIStudio.Settings.Provider? ProviderSettings { get; set; }
|
||||
public abstract AIStudio.Settings.Provider ProviderSettings { get; set; }
|
||||
|
||||
public abstract Task<ChatThread> ProcessContext(ChatThread chatThread, IDictionary<string, string> additionalData);
|
||||
|
||||
@@ -73,7 +73,6 @@ public abstract class AgentBase(ILogger<AgentBase> logger, SettingsManager setti
|
||||
WorkspaceId = Guid.Empty,
|
||||
ChatId = Guid.NewGuid(),
|
||||
Name = string.Empty,
|
||||
Seed = this.RNG.Next(),
|
||||
SystemPrompt = systemPrompt,
|
||||
Blocks = [],
|
||||
};
|
||||
@@ -103,10 +102,9 @@ public abstract class AgentBase(ILogger<AgentBase> logger, SettingsManager setti
|
||||
|
||||
protected async Task AddAIResponseAsync(ChatThread thread, IContent lastUserPrompt, DateTimeOffset time)
|
||||
{
|
||||
if(this.ProviderSettings is null)
|
||||
if(this.ProviderSettings == Settings.Provider.NONE)
|
||||
return;
|
||||
|
||||
var providerSettings = this.ProviderSettings.Value;
|
||||
var aiText = new ContentText
|
||||
{
|
||||
// We have to wait for the remote
|
||||
@@ -127,6 +125,6 @@ public abstract class AgentBase(ILogger<AgentBase> logger, SettingsManager setti
|
||||
// Use the selected provider to get the AI response.
|
||||
// By awaiting this line, we wait for the entire
|
||||
// content to be streamed.
|
||||
await aiText.CreateFromProviderAsync(providerSettings.CreateProvider(this.Logger), providerSettings.Model, lastUserPrompt, thread);
|
||||
await aiText.CreateFromProviderAsync(this.ProviderSettings.CreateProvider(), this.ProviderSettings.Model, lastUserPrompt, thread);
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ public sealed class AgentDataSourceSelection (ILogger<AgentDataSourceSelection>
|
||||
""";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Settings.Provider? ProviderSettings { get; set; }
|
||||
public override Settings.Provider ProviderSettings { get; set; } = Settings.Provider.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// The data source selection agent does not work with context. Use
|
||||
@@ -131,7 +131,7 @@ public sealed class AgentDataSourceSelection (ILogger<AgentDataSourceSelection>
|
||||
|
||||
#endregion
|
||||
|
||||
public async Task<List<SelectedDataSource>> PerformSelectionAsync(IProvider provider, IContent lastPrompt, ChatThread chatThread, AllowedSelectedDataSources dataSources, CancellationToken token = default)
|
||||
public async Task<List<SelectedDataSource>> PerformSelectionAsync(IProvider provider, IContent lastUserPrompt, ChatThread chatThread, AllowedSelectedDataSources dataSources, CancellationToken token = default)
|
||||
{
|
||||
logger.LogInformation("The AI should select the appropriate data sources.");
|
||||
|
||||
@@ -141,6 +141,11 @@ public sealed class AgentDataSourceSelection (ILogger<AgentDataSourceSelection>
|
||||
|
||||
// We start with the provider currently selected by the user:
|
||||
var agentProvider = this.SettingsManager.GetPreselectedProvider(Tools.Components.AGENT_DATA_SOURCE_SELECTION, provider.Id, true);
|
||||
if (agentProvider == Settings.Provider.NONE)
|
||||
{
|
||||
logger.LogWarning("No provider is selected for the agent. The agent cannot select data sources.");
|
||||
return [];
|
||||
}
|
||||
|
||||
// Assign the provider settings to the agent:
|
||||
logger.LogInformation($"The agent for the data source selection uses the provider '{agentProvider.InstanceName}' ({agentProvider.UsedLLMProvider.ToName()}, confidence={agentProvider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level.GetName()}).");
|
||||
@@ -149,7 +154,7 @@ public sealed class AgentDataSourceSelection (ILogger<AgentDataSourceSelection>
|
||||
//
|
||||
// 2. Prepare the current system and user prompts as input for the agent:
|
||||
//
|
||||
var lastPromptContent = lastPrompt switch
|
||||
var lastPromptContent = lastUserPrompt switch
|
||||
{
|
||||
ContentText text => text.Text,
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ public sealed class AgentRetrievalContextValidation (ILogger<AgentRetrievalConte
|
||||
""";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Settings.Provider? ProviderSettings { get; set; }
|
||||
public override Settings.Provider ProviderSettings { get; set; } = Settings.Provider.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// The retrieval context validation agent does not work with context. Use
|
||||
@@ -133,6 +133,11 @@ public sealed class AgentRetrievalContextValidation (ILogger<AgentRetrievalConte
|
||||
{
|
||||
// We start with the provider currently selected by the user:
|
||||
var agentProvider = this.SettingsManager.GetPreselectedProvider(Tools.Components.AGENT_RETRIEVAL_CONTEXT_VALIDATION, provider.Id, true);
|
||||
if (agentProvider == Settings.Provider.NONE)
|
||||
{
|
||||
logger.LogWarning("No provider is selected for the agent.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Assign the provider settings to the agent:
|
||||
logger.LogInformation($"The agent for the retrieval context validation uses the provider '{agentProvider.InstanceName}' ({agentProvider.UsedLLMProvider.ToName()}, confidence={agentProvider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level.GetName()}).");
|
||||
@@ -142,12 +147,12 @@ public sealed class AgentRetrievalContextValidation (ILogger<AgentRetrievalConte
|
||||
/// <summary>
|
||||
/// Validate all retrieval contexts against the last user and the system prompt.
|
||||
/// </summary>
|
||||
/// <param name="lastPrompt">The last user prompt.</param>
|
||||
/// <param name="lastUserPrompt">The last user prompt.</param>
|
||||
/// <param name="chatThread">The chat thread.</param>
|
||||
/// <param name="retrievalContexts">All retrieval contexts to validate.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The validation results.</returns>
|
||||
public async Task<IReadOnlyList<RetrievalContextValidationResult>> ValidateRetrievalContextsAsync(IContent lastPrompt, ChatThread chatThread, IReadOnlyList<IRetrievalContext> retrievalContexts, CancellationToken token = default)
|
||||
public async Task<IReadOnlyList<RetrievalContextValidationResult>> ValidateRetrievalContextsAsync(IContent lastUserPrompt, ChatThread chatThread, IReadOnlyList<IRetrievalContext> retrievalContexts, CancellationToken token = default)
|
||||
{
|
||||
// Check if the retrieval context validation is enabled:
|
||||
if (!this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.EnableRetrievalContextValidation)
|
||||
@@ -173,7 +178,7 @@ public sealed class AgentRetrievalContextValidation (ILogger<AgentRetrievalConte
|
||||
await semaphore.WaitAsync(token);
|
||||
|
||||
// Start the next validation task:
|
||||
validationTasks.Add(this.ValidateRetrievalContextAsync(lastPrompt, chatThread, retrievalContext, token, semaphore));
|
||||
validationTasks.Add(this.ValidateRetrievalContextAsync(lastUserPrompt, chatThread, retrievalContext, token, semaphore));
|
||||
}
|
||||
|
||||
// Wait for all validation tasks to complete:
|
||||
@@ -188,13 +193,13 @@ public sealed class AgentRetrievalContextValidation (ILogger<AgentRetrievalConte
|
||||
/// can call this method in parallel for each retrieval context. You might use
|
||||
/// the ValidateRetrievalContextsAsync method to validate all retrieval contexts.
|
||||
/// </remarks>
|
||||
/// <param name="lastPrompt">The last user prompt.</param>
|
||||
/// <param name="lastUserPrompt">The last user prompt.</param>
|
||||
/// <param name="chatThread">The chat thread.</param>
|
||||
/// <param name="retrievalContext">The retrieval context to validate.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <param name="semaphore">The optional semaphore to limit the number of parallel validations.</param>
|
||||
/// <returns>The validation result.</returns>
|
||||
public async Task<RetrievalContextValidationResult> ValidateRetrievalContextAsync(IContent lastPrompt, ChatThread chatThread, IRetrievalContext retrievalContext, CancellationToken token = default, SemaphoreSlim? semaphore = null)
|
||||
public async Task<RetrievalContextValidationResult> ValidateRetrievalContextAsync(IContent lastUserPrompt, ChatThread chatThread, IRetrievalContext retrievalContext, CancellationToken token = default, SemaphoreSlim? semaphore = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -209,7 +214,7 @@ public sealed class AgentRetrievalContextValidation (ILogger<AgentRetrievalConte
|
||||
//
|
||||
// 1. Prepare the current system and user prompts as input for the agent:
|
||||
//
|
||||
var lastPromptContent = lastPrompt switch
|
||||
var lastPromptContent = lastUserPrompt switch
|
||||
{
|
||||
ContentText text => text.Text,
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ public sealed class AgentTextContentCleaner(ILogger<AgentBase> logger, SettingsM
|
||||
|
||||
#region Overrides of AgentBase
|
||||
|
||||
public override AIStudio.Settings.Provider? ProviderSettings { get; set; }
|
||||
public override AIStudio.Settings.Provider ProviderSettings { get; set; } = AIStudio.Settings.Provider.NONE;
|
||||
|
||||
protected override Type Type => Type.SYSTEM;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ public interface IAgent
|
||||
/// <summary>
|
||||
/// The provider to use for this agent.
|
||||
/// </summary>
|
||||
public AIStudio.Settings.Provider? ProviderSettings { get; set; }
|
||||
public Settings.Provider ProviderSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Processes a chat thread (i.e., context) and returns the updated thread.
|
||||
|
||||
@@ -20,9 +20,6 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
|
||||
[Inject]
|
||||
protected IJSRuntime JsRuntime { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected ThreadSafeRandom RNG { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected ISnackbar Snackbar { get; init; } = null!;
|
||||
@@ -85,7 +82,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
|
||||
protected virtual IReadOnlyList<IButtonData> FooterButtons => [];
|
||||
|
||||
protected AIStudio.Settings.Provider providerSettings;
|
||||
protected AIStudio.Settings.Provider providerSettings = Settings.Provider.NONE;
|
||||
protected MudForm? form;
|
||||
protected bool inputIsValid;
|
||||
protected Profile currentProfile = Profile.NO_PROFILE;
|
||||
@@ -199,7 +196,6 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
WorkspaceId = Guid.Empty,
|
||||
ChatId = Guid.NewGuid(),
|
||||
Name = string.Format(this.TB("Assistant - {0}"), this.Title),
|
||||
Seed = this.RNG.Next(),
|
||||
Blocks = [],
|
||||
};
|
||||
}
|
||||
@@ -215,7 +211,6 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
WorkspaceId = workspaceId,
|
||||
ChatId = chatId,
|
||||
Name = name,
|
||||
Seed = this.RNG.Next(),
|
||||
Blocks = [],
|
||||
};
|
||||
|
||||
@@ -275,7 +270,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
// Use the selected provider to get the AI response.
|
||||
// By awaiting this line, we wait for the entire
|
||||
// content to be streamed.
|
||||
this.chatThread = await aiText.CreateFromProviderAsync(this.providerSettings.CreateProvider(this.Logger), this.providerSettings.Model, this.lastUserPrompt, this.chatThread, this.cancellationTokenSource!.Token);
|
||||
this.chatThread = await aiText.CreateFromProviderAsync(this.providerSettings.CreateProvider(), this.providerSettings.Model, this.lastUserPrompt, this.chatThread, this.cancellationTokenSource!.Token);
|
||||
|
||||
this.isProcessing = false;
|
||||
this.StateHasChanged();
|
||||
@@ -352,13 +347,15 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
private async Task InnerResetForm()
|
||||
{
|
||||
this.resultingContentBlock = null;
|
||||
this.providerSettings = default;
|
||||
this.providerSettings = Settings.Provider.NONE;
|
||||
|
||||
await this.JsRuntime.ClearDiv(RESULT_DIV_ID);
|
||||
await this.JsRuntime.ClearDiv(AFTER_RESULT_DIV_ID);
|
||||
|
||||
this.ResetForm();
|
||||
this.providerSettings = this.SettingsManager.GetPreselectedProvider(this.Component);
|
||||
this.currentProfile = this.SettingsManager.GetPreselectedProfile(this.Component);
|
||||
this.currentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(this.Component);
|
||||
|
||||
this.inputIsValid = false;
|
||||
this.inputIssues = [];
|
||||
@@ -384,7 +381,17 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
this.formChangeTimer.Dispose();
|
||||
try
|
||||
{
|
||||
this.formChangeTimer.Stop();
|
||||
this.formChangeTimer.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
base.DisposeResources();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -473,9 +473,9 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
||||
if(this.selectedERIServer is null)
|
||||
return;
|
||||
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Are you sure you want to delete the ERI server preset '{0}'?"), this.selectedERIServer.ServerName) },
|
||||
{ x => x.Message, string.Format(T("Are you sure you want to delete the ERI server preset '{0}'?"), this.selectedERIServer.ServerName) },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete ERI server preset"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -827,9 +827,9 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
||||
? string.Format(T("The embedding '{0}' is used in one or more retrieval processes. Are you sure you want to delete it?"), embeddingInfo.EmbeddingName)
|
||||
: string.Format(T("Are you sure you want to delete the embedding '{0}'?"), embeddingInfo.EmbeddingName);
|
||||
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", message },
|
||||
{ x => x.Message, message },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Embedding"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -890,9 +890,9 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
||||
|
||||
private async Task DeleteRetrievalProcess(RetrievalInfo retrievalInfo)
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Are you sure you want to delete the retrieval process '{0}'?"), retrievalInfo.Name) },
|
||||
{ x => x.Message, string.Format(T("Are you sure you want to delete the retrieval process '{0}'?"), retrievalInfo.Name) },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Retrieval Process"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
|
||||
@@ -1222,12 +1222,18 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::
|
||||
-- Target language
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T237828418"] = "Target language"
|
||||
|
||||
-- (Optional) Important Aspects
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T24391765"] = "(Optional) Important Aspects"
|
||||
|
||||
-- Summarize long text into a shorter version while retaining the main points. You might want to change the language of the summary to make it more readable. It is also possible to change the complexity of the summary to make it easy to understand.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T359929871"] = "Summarize long text into a shorter version while retaining the main points. You might want to change the language of the summary to make it more readable. It is also possible to change the complexity of the summary to make it easy to understand."
|
||||
|
||||
-- Please provide your field of expertise.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T3610378685"] = "Please provide your field of expertise."
|
||||
|
||||
-- (Optional) Specify aspects for the LLM to focus on when generating a summary, such as summary length or specific topics to emphasize.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T3830285347"] = "(Optional) Specify aspects for the LLM to focus on when generating a summary, such as summary length or specific topics to emphasize."
|
||||
|
||||
-- Custom target language
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T3848935911"] = "Custom target language"
|
||||
|
||||
@@ -1327,6 +1333,9 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1603883875"] = "Yes, re
|
||||
-- Yes, remove it
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1820166585"] = "Yes, remove it"
|
||||
|
||||
-- Number of sources
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1848978959"] = "Number of sources"
|
||||
|
||||
-- Do you really want to edit this message? In order to edit this message, the AI response will be deleted.
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T2018431076"] = "Do you really want to edit this message? In order to edit this message, the AI response will be deleted."
|
||||
|
||||
@@ -1354,6 +1363,9 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4070211974"] = "Remove
|
||||
-- No, keep it
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4188329028"] = "No, keep it"
|
||||
|
||||
-- Export Chat to Microsoft Word
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T861873672"] = "Export Chat to Microsoft Word"
|
||||
|
||||
-- Open Settings
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T1172211894"] = "Open Settings"
|
||||
|
||||
@@ -1654,6 +1666,12 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROFILESELECTION::T918741365"] = "You can
|
||||
-- Provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T900237532"] = "Provider"
|
||||
|
||||
-- Pandoc Installation
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T185447014"] = "Pandoc Installation"
|
||||
|
||||
-- Pandoc may be required for importing files.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T2596465560"] = "Pandoc may be required for importing files."
|
||||
|
||||
-- Images are not supported yet
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T298062956"] = "Images are not supported yet"
|
||||
|
||||
@@ -1705,6 +1723,24 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENT::T3825586228"] = "Please p
|
||||
-- Show web content options
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENT::T4249712357"] = "Show web content options"
|
||||
|
||||
-- Loading
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T1404011351"] = "Loading"
|
||||
|
||||
-- Start
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T182978943"] = "Start"
|
||||
|
||||
-- Done
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T2379421585"] = "Done"
|
||||
|
||||
-- Parsing
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T3151033983"] = "Parsing"
|
||||
|
||||
-- Cleaning
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T3420573362"] = "Cleaning"
|
||||
|
||||
-- n/a
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T907272257"] = "n/a"
|
||||
|
||||
-- Hide content
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SECRETINPUTFIELD::T1273315904"] = "Hide content"
|
||||
|
||||
@@ -1828,6 +1864,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1907446663"]
|
||||
-- Language behavior
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T2341504363"] = "Language behavior"
|
||||
|
||||
-- Update installation method
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T237706157"] = "Update installation method"
|
||||
|
||||
-- Language
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T2591284123"] = "Language"
|
||||
|
||||
@@ -1858,6 +1897,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T602293588"]
|
||||
-- Choose the color theme that best suits for you.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T654667432"] = "Choose the color theme that best suits for you."
|
||||
|
||||
-- Should updates be installed automatically or manually?
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T707880477"] = "Should updates be installed automatically or manually?"
|
||||
|
||||
-- Energy saving is enabled
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T71162186"] = "Energy saving is enabled"
|
||||
|
||||
@@ -2113,6 +2155,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1016188706"] = "Are you sure
|
||||
-- Move chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1133040906"] = "Move chat"
|
||||
|
||||
-- Unnamed workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1307384014"] = "Unnamed workspace"
|
||||
|
||||
-- Delete
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1469573738"] = "Delete"
|
||||
|
||||
@@ -2146,6 +2191,12 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2237618267"] = "Are you sure
|
||||
-- Delete Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2244038752"] = "Delete Chat"
|
||||
|
||||
-- Please enter a chat name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2301651387"] = "Please enter a chat name."
|
||||
|
||||
-- Workspace Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2446263209"] = "Workspace Name"
|
||||
|
||||
-- Move to workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2509305748"] = "Move to workspace"
|
||||
|
||||
@@ -2158,6 +2209,12 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3045856778"] = "Move Chat to
|
||||
-- Please enter a new or edit the name for your workspace '{0}':
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T323280982"] = "Please enter a new or edit the name for your workspace '{0}':"
|
||||
|
||||
-- Please enter a workspace name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3288132732"] = "Please enter a workspace name."
|
||||
|
||||
-- Unnamed chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3310482275"] = "Unnamed chat"
|
||||
|
||||
-- Rename
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3355849203"] = "Rename"
|
||||
|
||||
@@ -2170,6 +2227,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3555709365"] = "Load Chat"
|
||||
-- Add Workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3672981145"] = "Add Workspace"
|
||||
|
||||
-- Chat Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3891063690"] = "Chat Name"
|
||||
|
||||
-- Empty chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T4019509364"] = "Empty chat"
|
||||
|
||||
@@ -2923,6 +2983,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PANDOCDIALOG::T523908375"] = "Pandoc is dist
|
||||
-- Tell the AI what you want it to do for you. What are your goals or are you trying to achieve? Like having the AI address you informally.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T1458195391"] = "Tell the AI what you want it to do for you. What are your goals or are you trying to achieve? Like having the AI address you informally."
|
||||
|
||||
-- Please be aware that your profile info becomes part of the system prompt. This means it uses up context space — the “memory” the LLM uses to understand and respond to your request. If your profile is extremely long, the LLM may struggle to focus on your actual task.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T1717545317"] = "Please be aware that your profile info becomes part of the system prompt. This means it uses up context space — the “memory” the LLM uses to understand and respond to your request. If your profile is extremely long, the LLM may struggle to focus on your actual task."
|
||||
|
||||
-- Update
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T1847791252"] = "Update"
|
||||
|
||||
@@ -2935,18 +2998,12 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T2261456575"] = "What should
|
||||
-- Please enter a profile name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T2386844536"] = "Please enter a profile name."
|
||||
|
||||
-- The text must not exceed 256 characters.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T2560188276"] = "The text must not exceed 256 characters."
|
||||
|
||||
-- Add
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T2646845972"] = "Add"
|
||||
|
||||
-- The profile name must not exceed 40 characters.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3243902394"] = "The profile name must not exceed 40 characters."
|
||||
|
||||
-- The text must not exceed 444 characters.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3253349421"] = "The text must not exceed 444 characters."
|
||||
|
||||
-- Profile Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3392578705"] = "Profile Name"
|
||||
|
||||
@@ -2971,9 +3028,15 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T900713019"] = "Cancel"
|
||||
-- The profile name must be unique; the chosen name is already in use.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T911748898"] = "The profile name must be unique; the chosen name is already in use."
|
||||
|
||||
-- Please be aware: This section is for experts only. You are responsible for verifying the correctness of the additional parameters you provide to the API call. By default, AI Studio uses the OpenAI-compatible chat completions API, when that it is supported by the underlying service and model.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1017509792"] = "Please be aware: This section is for experts only. You are responsible for verifying the correctness of the additional parameters you provide to the API call. By default, AI Studio uses the OpenAI-compatible chat completions API, when that it is supported by the underlying service and model."
|
||||
|
||||
-- Hugging Face Inference Provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1085481431"] = "Hugging Face Inference Provider"
|
||||
|
||||
-- Hide Expert Settings
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1108876344"] = "Hide Expert Settings"
|
||||
|
||||
-- Failed to store the API key in the operating system. The message was: {0}. Please try again.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1122745046"] = "Failed to store the API key in the operating system. The message was: {0}. Please try again."
|
||||
|
||||
@@ -2986,6 +3049,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1356621346"] = "Create acco
|
||||
-- Load models
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T15352225"] = "Load models"
|
||||
|
||||
-- Add the parameters in proper JSON formatting, e.g., "temperature": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1689135032"] = "Add the parameters in proper JSON formatting, e.g., \"temperature\": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though."
|
||||
|
||||
-- Hostname
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1727440780"] = "Hostname"
|
||||
|
||||
@@ -3007,12 +3073,18 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2331453405"] = "(Optional)
|
||||
-- Add
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2646845972"] = "Add"
|
||||
|
||||
-- Additional API parameters
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2728244552"] = "Additional API parameters"
|
||||
|
||||
-- No models loaded or available.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2810182573"] = "No models loaded or available."
|
||||
|
||||
-- Instance Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2842060373"] = "Instance Name"
|
||||
|
||||
-- Show Expert Settings
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3361153305"] = "Show Expert Settings"
|
||||
|
||||
-- Show available models
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3763891899"] = "Show available models"
|
||||
|
||||
@@ -3415,6 +3487,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T32678
|
||||
-- Close
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3448155331"] = "Close"
|
||||
|
||||
-- This template is managed by your organization.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3576775249"] = "This template is managed by your organization."
|
||||
|
||||
-- Edit Chat Template
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3596030597"] = "Edit Chat Template"
|
||||
|
||||
@@ -3820,6 +3895,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T380451542
|
||||
-- Actions
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T3865031940"] = "Actions"
|
||||
|
||||
-- This profile is managed by your organization.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4058414654"] = "This profile is managed by your organization."
|
||||
|
||||
-- Store personal data about yourself in various profiles so that the AIs know your personal context. This saves you from having to explain your context each time, for example, in every chat. When you have different roles, you can create a profile for each role.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4125557797"] = "Store personal data about yourself in various profiles so that the AIs know your personal context. This saves you from having to explain your context each time, for example, in every chat. When you have different roles, you can create a profile for each role."
|
||||
|
||||
@@ -3958,12 +4036,18 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T364
|
||||
-- When enabled, the content cleaner agent is preselected. This is might be useful when you prefer to clean up the content before summarize it.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T3660434400"] = "When enabled, the content cleaner agent is preselected. This is might be useful when you prefer to clean up the content before summarize it."
|
||||
|
||||
-- Preselect important aspects
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T3705987833"] = "Preselect important aspects"
|
||||
|
||||
-- When enabled, you can preselect the text summarizer options. This is might be useful when you prefer a specific language, complexity, or LLM.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T3820844575"] = "When enabled, you can preselect the text summarizer options. This is might be useful when you prefer a specific language, complexity, or LLM."
|
||||
|
||||
-- Which summarizer complexity should be preselected?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T408530182"] = "Which summarizer complexity should be preselected?"
|
||||
|
||||
-- Preselect aspects for the LLM to focus on when generating a summary, such as summary length or specific topics to emphasize.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T414420518"] = "Preselect aspects for the LLM to focus on when generating a summary, such as summary length or specific topics to emphasize."
|
||||
|
||||
-- Preselect your expertise
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T51139714"] = "Preselect your expertise"
|
||||
|
||||
@@ -4117,8 +4201,11 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGWRITINGEMAILS::T3832
|
||||
-- Preselect one of your profiles?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGWRITINGEMAILS::T4004501229"] = "Preselect one of your profiles?"
|
||||
|
||||
-- Chat name
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T1746586282"] = "Chat name"
|
||||
-- Please enter a value.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T3576780391"] = "Please enter a value."
|
||||
|
||||
-- Your Input
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T4030229154"] = "Your Input"
|
||||
|
||||
-- Cancel
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T900713019"] = "Cancel"
|
||||
@@ -4573,6 +4660,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T1702902297"] = "Introduction"
|
||||
-- Vision
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T1892426825"] = "Vision"
|
||||
|
||||
-- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT5, o1, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using vLLM, llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2183503084"] = "You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT5, o1, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using vLLM, llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities."
|
||||
|
||||
-- Let's get started
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2331588413"] = "Let's get started"
|
||||
|
||||
@@ -4582,9 +4672,6 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2348849647"] = "Last Changelog"
|
||||
-- Choose the provider and model best suited for your current task.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2588488920"] = "Choose the provider and model best suited for your current task."
|
||||
|
||||
-- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT4o, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using vLLM, llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2900280782"] = "You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT4o, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using vLLM, llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities."
|
||||
|
||||
-- Quick Start Guide
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3002014720"] = "Quick Start Guide"
|
||||
|
||||
@@ -4744,8 +4831,8 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T1674355816"] = "Tried to com
|
||||
-- Tried to stream the LLM provider '{0}' answer. Was not able to read the stream. The message is: '{1}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T1856278860"] = "Tried to stream the LLM provider '{0}' answer. Was not able to read the stream. The message is: '{1}'"
|
||||
|
||||
-- Tried to communicate with the LLM provider '{0}'. Even after {1} retries, there were some problems with the request. The provider message is: '{2}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2249520705"] = "Tried to communicate with the LLM provider '{0}'. Even after {1} retries, there were some problems with the request. The provider message is: '{2}'"
|
||||
-- Tried to communicate with the LLM provider '{0}'. Even after {1} retries, there were some problems with the request. The provider message is: '{2}'.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2181034173"] = "Tried to communicate with the LLM provider '{0}'. Even after {1} retries, there were some problems with the request. The provider message is: '{2}'."
|
||||
|
||||
-- Tried to communicate with the LLM provider '{0}'. Something was not found. The provider message is: '{1}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2780552614"] = "Tried to communicate with the LLM provider '{0}'. Something was not found. The provider message is: '{1}'"
|
||||
@@ -4867,6 +4954,9 @@ UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T2128088682
|
||||
-- Navigation expands on mouse hover
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T2195945406"] = "Navigation expands on mouse hover"
|
||||
|
||||
-- Install updates manually
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T220653235"] = "Install updates manually"
|
||||
|
||||
-- Also show features ready for release; these should be stable
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T2301448762"] = "Also show features ready for release; these should be stable"
|
||||
|
||||
@@ -4906,6 +4996,9 @@ UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3137986690
|
||||
-- Delete disappearing chats older than 180 days
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3491430707"] = "Delete disappearing chats older than 180 days"
|
||||
|
||||
-- Install updates automatically
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3569059463"] = "Install updates automatically"
|
||||
|
||||
-- Disable workspaces
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3612390107"] = "Disable workspaces"
|
||||
|
||||
@@ -5281,6 +5374,18 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOC::T567205144"] = "It seems that Pandoc i
|
||||
-- The latest Pandoc version was not found, installing version {0} instead.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOC::T726914939"] = "The latest Pandoc version was not found, installing version {0} instead."
|
||||
|
||||
-- Pandoc is required for Microsoft Word export.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T1473115556"] = "Pandoc is required for Microsoft Word export."
|
||||
|
||||
-- Pandoc Installation
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T185447014"] = "Pandoc Installation"
|
||||
|
||||
-- Error during Microsoft Word export
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T3290596792"] = "Error during Microsoft Word export"
|
||||
|
||||
-- Microsoft Word export successful
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T4256043333"] = "Microsoft Word export successful"
|
||||
|
||||
-- The table AUTHORS does not exist or is using an invalid syntax.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINBASE::T1068328139"] = "The table AUTHORS does not exist or is using an invalid syntax."
|
||||
|
||||
@@ -5440,9 +5545,6 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T1148682011
|
||||
-- The CONFIG table does not exist or is not a valid table.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T3331620576"] = "The CONFIG table does not exist or is not a valid table."
|
||||
|
||||
-- The LLM_PROVIDERS table does not exist or is not a valid table.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T806592324"] = "The LLM_PROVIDERS table does not exist or is not a valid table."
|
||||
|
||||
-- The field IETF_TAG does not exist or is not a valid string.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINLANGUAGE::T1796010240"] = "The field IETF_TAG does not exist or is not a valid string."
|
||||
|
||||
@@ -5569,6 +5671,15 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::SECRETS::T4007657575"]
|
||||
-- No update found.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::UPDATESERVICE::T1015418291"] = "No update found."
|
||||
|
||||
-- Failed to install update automatically. Please try again manually.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::UPDATESERVICE::T3709709946"] = "Failed to install update automatically. Please try again manually."
|
||||
|
||||
-- Sources provided by the data providers
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SOURCEEXTENSIONS::T4174900468"] = "Sources provided by the data providers"
|
||||
|
||||
-- Sources provided by the AI
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SOURCEEXTENSIONS::T4261248356"] = "Sources provided by the AI"
|
||||
|
||||
-- The hostname is not a valid HTTP(S) URL.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::DATASOURCEVALIDATION::T1013354736"] = "The hostname is not a valid HTTP(S) URL."
|
||||
|
||||
@@ -5671,5 +5782,8 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::PROVIDERVALIDATION::T497939286"] =
|
||||
-- Please select a model.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::PROVIDERVALIDATION::T818893091"] = "Please select a model."
|
||||
|
||||
-- Unnamed workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T1307384014"] = "Unnamed workspace"
|
||||
|
||||
-- Delete Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T2244038752"] = "Delete Chat"
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
@if (!this.SettingsManager.ConfigurationData.LegalCheck.HideWebContentReader)
|
||||
{
|
||||
<ReadWebContent @bind-Content="@this.inputLegalDocument" ProviderSettings="@this.providerSettings" @bind-AgentIsRunning="@this.isAgentRunning" Preselect="@(this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions && this.SettingsManager.ConfigurationData.LegalCheck.PreselectWebContentReader)" PreselectContentCleanerAgent="@(this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions && this.SettingsManager.ConfigurationData.LegalCheck.PreselectContentCleanerAgent)"/>
|
||||
<ReadWebContent @bind-Content="@this.inputLegalDocument" ProviderSettings="@this.providerSettings" @bind-AgentIsRunning="@this.isAgentRunning" @bind-Preselect="@this.showWebContentReader" @bind-PreselectContentCleanerAgent="@this.useContentCleanerAgent"/>
|
||||
}
|
||||
|
||||
<ReadFileContent @bind-FileContent="@this.inputLegalDocument"/>
|
||||
|
||||
@@ -37,11 +37,27 @@ public partial class AssistantLegalCheck : AssistantBaseCore<SettingsDialogLegal
|
||||
{
|
||||
this.inputLegalDocument = string.Empty;
|
||||
this.inputQuestions = string.Empty;
|
||||
this.MightPreselectValues();
|
||||
if (!this.MightPreselectValues())
|
||||
{
|
||||
this.showWebContentReader = false;
|
||||
this.useContentCleanerAgent = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MightPreselectValues() => false;
|
||||
|
||||
protected override bool MightPreselectValues()
|
||||
{
|
||||
if (this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)
|
||||
{
|
||||
this.showWebContentReader = this.SettingsManager.ConfigurationData.LegalCheck.PreselectWebContentReader;
|
||||
this.useContentCleanerAgent = this.SettingsManager.ConfigurationData.LegalCheck.PreselectContentCleanerAgent;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool showWebContentReader;
|
||||
private bool useContentCleanerAgent;
|
||||
private bool isAgentRunning;
|
||||
private string inputLegalDocument = string.Empty;
|
||||
private string inputQuestions = string.Empty;
|
||||
|
||||
@@ -85,7 +85,7 @@ public partial class AssistantMyTasks : AssistantBaseCore<SettingsDialogMyTasks>
|
||||
|
||||
private string? ValidateProfile(Profile profile)
|
||||
{
|
||||
if(profile == default || profile == Profile.NO_PROFILE)
|
||||
if(profile == Profile.NO_PROFILE)
|
||||
return T("Please select one of your profiles.");
|
||||
|
||||
return null;
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
|
||||
@if (!this.SettingsManager.ConfigurationData.TextSummarizer.HideWebContentReader)
|
||||
{
|
||||
<ReadWebContent @bind-Content="@this.inputText" ProviderSettings="@this.providerSettings" @bind-AgentIsRunning="@this.isAgentRunning" Preselect="@(this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions && this.SettingsManager.ConfigurationData.TextSummarizer.PreselectWebContentReader)" PreselectContentCleanerAgent="@(this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions && this.SettingsManager.ConfigurationData.TextSummarizer.PreselectContentCleanerAgent)"/>
|
||||
<ReadWebContent @bind-Content="@this.inputText" ProviderSettings="@this.providerSettings" @bind-AgentIsRunning="@this.isAgentRunning" @bind-Preselect="@this.showWebContentReader" @bind-PreselectContentCleanerAgent="@this.useContentCleanerAgent"/>
|
||||
}
|
||||
|
||||
<ReadFileContent @bind-FileContent="@this.inputText"/>
|
||||
<MudTextField T="string" Disabled="@this.isAgentRunning" @bind-Text="@this.inputText" Validation="@this.ValidatingText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="@T("Your input")" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.Name())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="@T("Target language")" AllowOther="@true" @bind-OtherInput="@this.customTargetLanguage" OtherValue="CommonLanguages.OTHER" LabelOther="@T("Custom target language")" ValidateOther="@this.ValidateCustomLanguage" />
|
||||
<EnumSelection T="Complexity" NameFunc="@(complexity => complexity.Name())" @bind-Value="@this.selectedComplexity" Icon="@Icons.Material.Filled.Layers" Label="@T("Target complexity")" AllowOther="@true" @bind-OtherInput="@this.expertInField" OtherValue="Complexity.SCIENTIFIC_LANGUAGE_OTHER_EXPERTS" LabelOther="@T("Your expertise")" ValidateOther="@this.ValidateExpertInField" />
|
||||
<MudTextField T="string" AutoGrow="true" Lines="2" @bind-Text="@this.importantAspects" class="mb-3" Label="@T("(Optional) Important Aspects")" HelperText="@T("(Optional) Specify aspects for the LLM to focus on when generating a summary, such as summary length or specific topics to emphasize.")" ShrinkLabel="true" Variant="Variant.Outlined" AdornmentIcon="@Icons.Material.Filled.List" Adornment="Adornment.Start"/>
|
||||
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
|
||||
@@ -12,13 +12,13 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
|
||||
protected override string Description => T("Summarize long text into a shorter version while retaining the main points. You might want to change the language of the summary to make it more readable. It is also possible to change the complexity of the summary to make it easy to understand.");
|
||||
|
||||
protected override string SystemPrompt =>
|
||||
"""
|
||||
You get a long text as input. The user wants to get a summary of the text.
|
||||
The user might want to change the language of the summary. In this case,
|
||||
you should provide a summary in the requested language. Eventually, the user
|
||||
want to change the complexity of the text. In this case, you should provide
|
||||
a summary with the requested complexity. In any case, do not add any information.
|
||||
""";
|
||||
$"""
|
||||
You get a long text as input. The text is marked with ```. The user wants to get a summary of the text.
|
||||
{this.selectedTargetLanguage.PromptSummarizing(this.customTargetLanguage)}
|
||||
{this.selectedComplexity.Prompt(this.expertInField)}
|
||||
{this.PromptImportantAspects()}
|
||||
In any case, only use information that is provided in the text for the summary.
|
||||
""";
|
||||
|
||||
protected override bool AllowProfiles => false;
|
||||
|
||||
@@ -40,10 +40,13 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
|
||||
this.inputText = string.Empty;
|
||||
if(!this.MightPreselectValues())
|
||||
{
|
||||
this.showWebContentReader = false;
|
||||
this.useContentCleanerAgent = false;
|
||||
this.selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
this.customTargetLanguage = string.Empty;
|
||||
this.selectedComplexity = Complexity.NO_CHANGE;
|
||||
this.expertInField = string.Empty;
|
||||
this.importantAspects = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,22 +54,28 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
|
||||
{
|
||||
if (this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions)
|
||||
{
|
||||
this.showWebContentReader = this.SettingsManager.ConfigurationData.TextSummarizer.PreselectWebContentReader;
|
||||
this.useContentCleanerAgent = this.SettingsManager.ConfigurationData.TextSummarizer.PreselectContentCleanerAgent;
|
||||
this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedTargetLanguage;
|
||||
this.customTargetLanguage = this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedOtherLanguage;
|
||||
this.selectedComplexity = this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedComplexity;
|
||||
this.expertInField = this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedExpertInField;
|
||||
this.importantAspects = this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedImportantAspects;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool showWebContentReader;
|
||||
private bool useContentCleanerAgent;
|
||||
private string inputText = string.Empty;
|
||||
private bool isAgentRunning;
|
||||
private CommonLanguages selectedTargetLanguage;
|
||||
private string customTargetLanguage = string.Empty;
|
||||
private Complexity selectedComplexity;
|
||||
private string expertInField = string.Empty;
|
||||
private string importantAspects = string.Empty;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
@@ -105,6 +114,17 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
|
||||
return null;
|
||||
}
|
||||
|
||||
private string PromptImportantAspects()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(this.importantAspects))
|
||||
return string.Empty;
|
||||
|
||||
return $"""
|
||||
Emphasize the following aspects in your summary:
|
||||
{this.importantAspects}
|
||||
""";
|
||||
}
|
||||
|
||||
private async Task SummarizeText()
|
||||
{
|
||||
await this.form!.Validate();
|
||||
@@ -114,11 +134,7 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
|
||||
this.CreateChatThread();
|
||||
var time = this.AddUserRequest(
|
||||
$"""
|
||||
{this.selectedTargetLanguage.PromptSummarizing(this.customTargetLanguage)}
|
||||
{this.selectedComplexity.Prompt(this.expertInField)}
|
||||
|
||||
Please summarize the following text:
|
||||
|
||||
```
|
||||
{this.inputText}
|
||||
```
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
@if (!this.SettingsManager.ConfigurationData.Translation.HideWebContentReader)
|
||||
{
|
||||
<ReadWebContent @bind-Content="@this.inputText" ProviderSettings="@this.providerSettings" @bind-AgentIsRunning="@this.isAgentRunning" Preselect="@(this.SettingsManager.ConfigurationData.Translation.PreselectOptions && this.SettingsManager.ConfigurationData.Translation.PreselectWebContentReader)" PreselectContentCleanerAgent="@(this.SettingsManager.ConfigurationData.Translation.PreselectOptions && this.SettingsManager.ConfigurationData.Translation.PreselectContentCleanerAgent)"/>
|
||||
<ReadWebContent @bind-Content="@this.inputText" ProviderSettings="@this.providerSettings" @bind-AgentIsRunning="@this.isAgentRunning" @bind-Preselect="@this.showWebContentReader" @bind-PreselectContentCleanerAgent="@this.useContentCleanerAgent"/>
|
||||
}
|
||||
|
||||
<ReadFileContent @bind-FileContent="@this.inputText"/>
|
||||
|
||||
@@ -40,6 +40,8 @@ public partial class AssistantTranslation : AssistantBaseCore<SettingsDialogTran
|
||||
this.inputTextLastTranslation = string.Empty;
|
||||
if (!this.MightPreselectValues())
|
||||
{
|
||||
this.showWebContentReader = false;
|
||||
this.useContentCleanerAgent = false;
|
||||
this.liveTranslation = false;
|
||||
this.selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
this.customTargetLanguage = string.Empty;
|
||||
@@ -50,6 +52,8 @@ public partial class AssistantTranslation : AssistantBaseCore<SettingsDialogTran
|
||||
{
|
||||
if (this.SettingsManager.ConfigurationData.Translation.PreselectOptions)
|
||||
{
|
||||
this.showWebContentReader = this.SettingsManager.ConfigurationData.Translation.PreselectWebContentReader;
|
||||
this.useContentCleanerAgent = this.SettingsManager.ConfigurationData.Translation.PreselectContentCleanerAgent;
|
||||
this.liveTranslation = this.SettingsManager.ConfigurationData.Translation.PreselectLiveTranslation;
|
||||
this.selectedTargetLanguage = this.SettingsManager.ConfigurationData.Translation.PreselectedTargetLanguage;
|
||||
this.customTargetLanguage = this.SettingsManager.ConfigurationData.Translation.PreselectOtherLanguage;
|
||||
@@ -59,6 +63,8 @@ public partial class AssistantTranslation : AssistantBaseCore<SettingsDialogTran
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool showWebContentReader;
|
||||
private bool useContentCleanerAgent;
|
||||
private bool liveTranslation;
|
||||
private bool isAgentRunning;
|
||||
private string inputText = string.Empty;
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace AIStudio.Chat;
|
||||
/// </summary>
|
||||
public sealed record ChatThread
|
||||
{
|
||||
private static readonly ILogger<ChatThread> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ChatThread>();
|
||||
|
||||
/// <summary>
|
||||
/// The unique identifier of the chat thread.
|
||||
/// </summary>
|
||||
@@ -59,11 +61,6 @@ public sealed record ChatThread
|
||||
/// The name of the chat thread. Usually generated by an AI model or manually edited by the user.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The seed for the chat thread. Some providers use this to generate deterministic results.
|
||||
/// </summary>
|
||||
public int Seed { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The current system prompt for the chat thread.
|
||||
@@ -87,9 +84,8 @@ public sealed record ChatThread
|
||||
/// </remarks>
|
||||
/// <param name="settingsManager">The settings manager instance to use.</param>
|
||||
/// <param name="chatThread">The chat thread to prepare the system prompt for.</param>
|
||||
/// <param name="logger">The logger instance to use.</param>
|
||||
/// <returns>The prepared system prompt.</returns>
|
||||
public string PrepareSystemPrompt(SettingsManager settingsManager, ChatThread chatThread, ILogger logger)
|
||||
public string PrepareSystemPrompt(SettingsManager settingsManager, ChatThread chatThread)
|
||||
{
|
||||
//
|
||||
// Use the information from the chat template, if provided. Otherwise, use the default system prompt
|
||||
@@ -109,7 +105,7 @@ public sealed record ChatThread
|
||||
else
|
||||
{
|
||||
var chatTemplate = settingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatThread.SelectedChatTemplate);
|
||||
if(chatTemplate == default)
|
||||
if(chatTemplate == null)
|
||||
systemPromptTextWithChatTemplate = chatThread.SystemPrompt;
|
||||
else
|
||||
{
|
||||
@@ -126,7 +122,7 @@ public sealed record ChatThread
|
||||
// default system prompt:
|
||||
chatThread = chatThread with { SystemPrompt = systemPromptTextWithChatTemplate };
|
||||
|
||||
logger.LogInformation(logMessage);
|
||||
LOGGER.LogInformation(logMessage);
|
||||
|
||||
//
|
||||
// Add augmented data, if available:
|
||||
@@ -144,9 +140,9 @@ public sealed record ChatThread
|
||||
};
|
||||
|
||||
if(isAugmentedDataAvailable)
|
||||
logger.LogInformation("Augmented data is available for the chat thread.");
|
||||
LOGGER.LogInformation("Augmented data is available for the chat thread.");
|
||||
else
|
||||
logger.LogInformation("No augmented data is available for the chat thread.");
|
||||
LOGGER.LogInformation("No augmented data is available for the chat thread.");
|
||||
|
||||
|
||||
//
|
||||
@@ -182,7 +178,7 @@ public sealed record ChatThread
|
||||
}
|
||||
}
|
||||
|
||||
logger.LogInformation(logMessage);
|
||||
LOGGER.LogInformation(logMessage);
|
||||
return systemPromptText;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
@using MudBlazor
|
||||
@using AIStudio.Components
|
||||
@inherits AIStudio.Components.MSGComponentBase
|
||||
|
||||
<MudCard Class="@this.CardClasses" Outlined="@true">
|
||||
<MudCardHeader>
|
||||
<CardHeaderAvatar>
|
||||
@@ -15,6 +16,14 @@
|
||||
</MudText>
|
||||
</CardHeaderContent>
|
||||
<CardHeaderActions>
|
||||
@if (this.Content.Sources.Count > 0)
|
||||
{
|
||||
<MudTooltip Text="@T("Number of sources")" Placement="Placement.Bottom">
|
||||
<MudBadge Content="@this.Content.Sources.Count" Color="Color.Primary" Overlap="true" BadgeClass="sources-card-header">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Link" />
|
||||
</MudBadge>
|
||||
</MudTooltip>
|
||||
}
|
||||
@if (this.IsSecondToLastBlock && this.Role is ChatRole.USER && this.EditLastUserBlockFunc is not null)
|
||||
{
|
||||
<MudTooltip Text="@T("Edit")" Placement="Placement.Bottom">
|
||||
@@ -39,6 +48,13 @@
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error" OnClick="@this.RemoveBlock"/>
|
||||
</MudTooltip>
|
||||
}
|
||||
|
||||
@if (this.Role is ChatRole.AI)
|
||||
{
|
||||
<MudTooltip Text="@T("Export Chat to Microsoft Word")" Placement="Placement.Bottom">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Save" OnClick="@this.ExportToWord"/>
|
||||
</MudTooltip>
|
||||
}
|
||||
<MudCopyClipboardButton Content="@this.Content" Type="@this.Type" Size="Size.Medium"/>
|
||||
</CardHeaderActions>
|
||||
</MudCardHeader>
|
||||
@@ -72,6 +88,10 @@
|
||||
else
|
||||
{
|
||||
<MudMarkdown Value="@textContent.Text.RemoveThinkTags().Trim()" Props="Markdown.DefaultConfig" Styling="@this.MarkdownStyling" />
|
||||
@if (textContent.Sources.Count > 0)
|
||||
{
|
||||
<MudMarkdown Value="@textContent.Sources.ToMarkdown()" Props="Markdown.DefaultConfig" Styling="@this.MarkdownStyling" />
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using AIStudio.Components;
|
||||
|
||||
using AIStudio.Tools.Services;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Chat;
|
||||
@@ -63,6 +63,9 @@ public partial class ContentBlockComponent : MSGComponentBase
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
private bool HideContent { get; set; }
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
@@ -133,6 +136,11 @@ public partial class ContentBlockComponent : MSGComponentBase
|
||||
await this.RemoveBlockFunc(this.Content);
|
||||
}
|
||||
|
||||
private async Task ExportToWord()
|
||||
{
|
||||
await PandocExport.ToMicrosoftWord(this.RustService, this.DialogService, T("Export Chat to Microsoft Word"), this.Content);
|
||||
}
|
||||
|
||||
private async Task RegenerateBlock()
|
||||
{
|
||||
if (this.RegenerateFunc is null)
|
||||
@@ -179,4 +187,5 @@ public partial class ContentBlockComponent : MSGComponentBase
|
||||
if (edit.HasValue && edit.Value)
|
||||
await this.EditLastUserBlockFunc(this.Content);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,7 +28,10 @@ public sealed class ContentImage : IContent, IImageSource
|
||||
public Func<Task> StreamingEvent { get; set; } = () => Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastPrompt, ChatThread? chatChatThread, CancellationToken token = default)
|
||||
public List<Source> Sources { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastUserPrompt, ChatThread? chatChatThread, CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace AIStudio.Chat;
|
||||
/// </summary>
|
||||
public sealed class ContentText : IContent
|
||||
{
|
||||
private static readonly ILogger<ContentText> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ContentText>();
|
||||
|
||||
/// <summary>
|
||||
/// The minimum time between two streaming events, when the user
|
||||
/// enables the energy saving mode.
|
||||
@@ -24,7 +26,7 @@ public sealed class ContentText : IContent
|
||||
public bool InitialRemoteWait { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
// [JsonIgnore]
|
||||
[JsonIgnore]
|
||||
public bool IsStreaming { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -36,30 +38,31 @@ public sealed class ContentText : IContent
|
||||
public Func<Task> StreamingEvent { get; set; } = () => Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastPrompt, ChatThread? chatThread, CancellationToken token = default)
|
||||
public List<Source> Sources { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastUserPrompt, ChatThread? chatThread, CancellationToken token = default)
|
||||
{
|
||||
if(chatThread is null)
|
||||
return new();
|
||||
|
||||
if(!chatThread.IsLLMProviderAllowed(provider))
|
||||
{
|
||||
var logger = Program.SERVICE_PROVIDER.GetService<ILogger<ContentText>>()!;
|
||||
logger.LogError("The provider is not allowed for this chat thread due to data security reasons. Skipping the AI process.");
|
||||
LOGGER.LogError("The provider is not allowed for this chat thread due to data security reasons. Skipping the AI process.");
|
||||
return chatThread;
|
||||
}
|
||||
|
||||
// Call the RAG process. Right now, we only have one RAG process:
|
||||
if (lastPrompt is not null)
|
||||
if (lastUserPrompt is not null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var rag = new AISrcSelWithRetCtxVal();
|
||||
chatThread = await rag.ProcessAsync(provider, lastPrompt, chatThread, token);
|
||||
chatThread = await rag.ProcessAsync(provider, lastUserPrompt, chatThread, token);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var logger = Program.SERVICE_PROVIDER.GetService<ILogger<ContentText>>()!;
|
||||
logger.LogError(e, "Skipping the RAG process due to an error.");
|
||||
LOGGER.LogError(e, "Skipping the RAG process due to an error.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +83,7 @@ public sealed class ContentText : IContent
|
||||
this.InitialRemoteWait = true;
|
||||
|
||||
// Iterate over the responses from the AI:
|
||||
await foreach (var deltaText in provider.StreamChatCompletion(chatModel, chatThread, settings, token))
|
||||
await foreach (var contentStreamChunk in provider.StreamChatCompletion(chatModel, chatThread, settings, token))
|
||||
{
|
||||
// When the user cancels the request, we stop the loop:
|
||||
if (token.IsCancellationRequested)
|
||||
@@ -91,7 +94,10 @@ public sealed class ContentText : IContent
|
||||
this.IsStreaming = true;
|
||||
|
||||
// Add the response to the text:
|
||||
this.Text += deltaText;
|
||||
this.Text += contentStreamChunk;
|
||||
|
||||
// Merge the sources:
|
||||
this.Sources.MergeSources(contentStreamChunk.Sources);
|
||||
|
||||
// Notify the UI that the content has changed,
|
||||
// depending on the energy saving mode:
|
||||
|
||||
@@ -37,11 +37,21 @@ public interface IContent
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public Func<Task> StreamingDone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The provided sources, if any.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We cannot use ISource here because System.Text.Json does not support
|
||||
/// interface serialization. So we have to use a concrete class.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public List<Source> Sources { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Uses the provider to create the content.
|
||||
/// </summary>
|
||||
public Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastPrompt, ChatThread? chatChatThread, CancellationToken token = default);
|
||||
public Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastUserPrompt, ChatThread? chatChatThread, CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep copy
|
||||
|
||||
@@ -13,6 +13,9 @@ public partial class Changelog
|
||||
|
||||
public static readonly Log[] LOGS =
|
||||
[
|
||||
new (228, "v0.9.53, build 228 (2025-11-14 13:14 UTC)", "v0.9.53.md"),
|
||||
new (227, "v0.9.52, build 227 (2025-10-24 06:00 UTC)", "v0.9.52.md"),
|
||||
new (226, "v0.9.51, build 226 (2025-09-04 18:02 UTC)", "v0.9.51.md"),
|
||||
new (225, "v0.9.50, build 225 (2025-08-10 16:40 UTC)", "v0.9.50.md"),
|
||||
new (224, "v0.9.49, build 224 (2025-07-02 12:12 UTC)", "v0.9.49.md"),
|
||||
new (223, "v0.9.48, build 223 (2025-06-10 13:15 UTC)", "v0.9.48.md"),
|
||||
|
||||
@@ -18,9 +18,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<ChatThread?> ChatThreadChanged { get; set; }
|
||||
|
||||
|
||||
[Parameter]
|
||||
public AIStudio.Settings.Provider Provider { get; set; }
|
||||
public AIStudio.Settings.Provider Provider { get; set; } = AIStudio.Settings.Provider.NONE;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<AIStudio.Settings.Provider> ProviderChanged { get; set; }
|
||||
@@ -34,9 +34,6 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
[Inject]
|
||||
private ILogger<ChatComponent> Logger { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ThreadSafeRandom RNG { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
@@ -327,7 +324,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
private async Task ChatTemplateWasChanged(ChatTemplate chatTemplate)
|
||||
{
|
||||
this.currentChatTemplate = chatTemplate;
|
||||
this.userInput = this.currentChatTemplate.PredefinedUserPrompt;
|
||||
if(!string.IsNullOrWhiteSpace(this.currentChatTemplate.PredefinedUserPrompt))
|
||||
this.userInput = this.currentChatTemplate.PredefinedUserPrompt;
|
||||
|
||||
if(this.ChatThread is null)
|
||||
return;
|
||||
|
||||
@@ -434,8 +433,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
ChatId = Guid.NewGuid(),
|
||||
DataSourceOptions = this.earlyDataSourceOptions,
|
||||
Name = this.ExtractThreadName(this.userInput),
|
||||
Seed = this.RNG.Next(),
|
||||
Blocks = this.currentChatTemplate == default ? [] : this.currentChatTemplate.ExampleConversation.Select(x => x.DeepClone()).ToList(),
|
||||
Blocks = this.currentChatTemplate == ChatTemplate.NO_CHAT_TEMPLATE ? [] : this.currentChatTemplate.ExampleConversation.Select(x => x.DeepClone()).ToList(),
|
||||
};
|
||||
|
||||
await this.ChatThreadChanged.InvokeAsync(this.ChatThread);
|
||||
@@ -530,7 +528,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
// Use the selected provider to get the AI response.
|
||||
// By awaiting this line, we wait for the entire
|
||||
// content to be streamed.
|
||||
this.ChatThread = await aiText.CreateFromProviderAsync(this.Provider.CreateProvider(this.Logger), this.Provider.Model, lastUserPrompt, this.ChatThread, this.cancellationTokenSource.Token);
|
||||
this.ChatThread = await aiText.CreateFromProviderAsync(this.Provider.CreateProvider(), this.Provider.Model, lastUserPrompt, this.ChatThread, this.cancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
this.cancellationTokenSource = null;
|
||||
@@ -585,9 +583,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
//
|
||||
if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_MANUALLY && this.hasUnsavedChanges)
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", "Are you sure you want to start a new chat? All unsaved changes will be lost." },
|
||||
{ x => x.Message, "Are you sure you want to start a new chat? All unsaved changes will be lost." },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Delete Chat", dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -632,7 +630,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
|
||||
default:
|
||||
case AddChatProviderBehavior.ADDED_CHATS_USE_LATEST_PROVIDER:
|
||||
if(this.Provider == default)
|
||||
if(this.Provider == AIStudio.Settings.Provider.NONE)
|
||||
{
|
||||
this.Provider = this.SettingsManager.GetPreselectedProvider(Tools.Components.CHAT);
|
||||
await this.ProviderChanged.InvokeAsync(this.Provider);
|
||||
@@ -672,8 +670,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
WorkspaceId = this.currentWorkspaceId,
|
||||
ChatId = Guid.NewGuid(),
|
||||
Name = string.Empty,
|
||||
Seed = this.RNG.Next(),
|
||||
Blocks = this.currentChatTemplate == default ? [] : this.currentChatTemplate.ExampleConversation.Select(x => x.DeepClone()).ToList(),
|
||||
Blocks = this.currentChatTemplate == ChatTemplate.NO_CHAT_TEMPLATE ? [] : this.currentChatTemplate.ExampleConversation.Select(x => x.DeepClone()).ToList(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -693,9 +690,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
|
||||
if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_MANUALLY && this.hasUnsavedChanges)
|
||||
{
|
||||
var confirmationDialogParameters = new DialogParameters
|
||||
var confirmationDialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", T("Are you sure you want to move this chat? All unsaved changes will be lost.") },
|
||||
{ x => x.Message, T("Are you sure you want to move this chat? All unsaved changes will be lost.") },
|
||||
};
|
||||
|
||||
var confirmationDialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Unsaved Changes", confirmationDialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -704,11 +701,11 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<WorkspaceSelectionDialog>
|
||||
{
|
||||
{ "Message", T("Please select the workspace where you want to move the chat to.") },
|
||||
{ "SelectedWorkspace", this.ChatThread?.WorkspaceId },
|
||||
{ "ConfirmText", T("Move chat") },
|
||||
{ x => x.Message, T("Please select the workspace where you want to move the chat to.") },
|
||||
{ x => x.SelectedWorkspace, this.ChatThread?.WorkspaceId ?? Guid.Empty },
|
||||
{ x => x.ConfirmText, T("Move chat") },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<WorkspaceSelectionDialog>(T("Move Chat to Workspace"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -795,7 +792,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
break;
|
||||
|
||||
case LoadingChatProviderBehavior.ALWAYS_USE_LATEST_CHAT_PROVIDER:
|
||||
if(this.Provider == default)
|
||||
if(this.Provider == AIStudio.Settings.Provider.NONE)
|
||||
this.Provider = this.SettingsManager.GetPreselectedProvider(Tools.Components.CHAT);
|
||||
break;
|
||||
}
|
||||
@@ -804,18 +801,13 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
|
||||
// Try to select the profile:
|
||||
if (!string.IsNullOrWhiteSpace(chatProfile))
|
||||
{
|
||||
this.currentProfile = this.SettingsManager.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == chatProfile);
|
||||
if(this.currentProfile == default)
|
||||
this.currentProfile = Profile.NO_PROFILE;
|
||||
}
|
||||
this.currentProfile = this.SettingsManager.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == chatProfile) ?? Profile.NO_PROFILE;
|
||||
|
||||
// Try to select the chat template:
|
||||
if (!string.IsNullOrWhiteSpace(chatChatTemplate))
|
||||
{
|
||||
this.currentChatTemplate = this.SettingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatChatTemplate);
|
||||
if(this.currentChatTemplate == default)
|
||||
this.currentChatTemplate = ChatTemplate.NO_CHAT_TEMPLATE;
|
||||
var selectedTemplate = this.SettingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatChatTemplate);
|
||||
this.currentChatTemplate = selectedTemplate ?? ChatTemplate.NO_CHAT_TEMPLATE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,10 @@ public partial class ConfigurationMultiSelect<TData> : ConfigurationBaseCore
|
||||
/// <inheritdoc />
|
||||
protected override bool Stretch => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Variant Variant => Variant.Outlined;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override string Label => this.OptionDescription;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -42,6 +42,9 @@ public partial class ConfigurationProviderSelection : MSGComponentBase
|
||||
foreach (var providerId in this.Data)
|
||||
{
|
||||
var provider = this.SettingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == providerId.Value);
|
||||
if (provider is null)
|
||||
continue;
|
||||
|
||||
if (provider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level >= minimumLevel)
|
||||
yield return providerId;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ public partial class ConfigurationSelect<TConfig> : ConfigurationBaseCore
|
||||
/// <inheritdoc />
|
||||
protected override string Label => this.OptionDescription;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Variant Variant => Variant.Outlined;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -92,4 +92,23 @@ public partial class ConfigurationText : ConfigurationBaseCore
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.InformAboutChange();
|
||||
}
|
||||
|
||||
#region Overrides of MSGComponentBase
|
||||
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.timer.Stop();
|
||||
this.timer.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
base.DisposeResources();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using Timer = System.Timers.Timer;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class DebouncedTextField : MudComponentBase
|
||||
public partial class DebouncedTextField : MudComponentBase, IDisposable
|
||||
{
|
||||
[Parameter]
|
||||
public string Label { get; set; } = string.Empty;
|
||||
@@ -50,12 +50,15 @@ public partial class DebouncedTextField : MudComponentBase
|
||||
|
||||
private readonly Timer debounceTimer = new();
|
||||
private string text = string.Empty;
|
||||
private string lastParameterText = string.Empty;
|
||||
private bool isInitialized;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
this.text = this.Text;
|
||||
this.lastParameterText = this.Text;
|
||||
this.debounceTimer.AutoReset = false;
|
||||
this.debounceTimer.Interval = this.DebounceTime.TotalMilliseconds;
|
||||
this.debounceTimer.Elapsed += (_, _) =>
|
||||
@@ -66,8 +69,31 @@ public partial class DebouncedTextField : MudComponentBase
|
||||
this.InvokeAsync(() => this.WhenTextCanged(this.text));
|
||||
};
|
||||
|
||||
this.isInitialized = true;
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
// Ensure the timer uses the latest debouncing interval:
|
||||
if (!this.isInitialized)
|
||||
return;
|
||||
|
||||
if(Math.Abs(this.debounceTimer.Interval - this.DebounceTime.TotalMilliseconds) > 1)
|
||||
this.debounceTimer.Interval = this.DebounceTime.TotalMilliseconds;
|
||||
|
||||
// Only sync when the parent's parameter actually changed since the last change:
|
||||
if (this.Text != this.lastParameterText)
|
||||
{
|
||||
this.text = this.Text;
|
||||
this.lastParameterText = this.Text;
|
||||
}
|
||||
|
||||
this.debounceTimer.Stop();
|
||||
this.debounceTimer.Start();
|
||||
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -77,4 +103,21 @@ public partial class DebouncedTextField : MudComponentBase
|
||||
this.debounceTimer.Stop();
|
||||
this.debounceTimer.Start();
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.debounceTimer.Stop();
|
||||
this.debounceTimer.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
<MudDivider/>
|
||||
@foreach (var profile in this.SettingsManager.ConfigurationData.Profiles.GetAllProfiles())
|
||||
{
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Person4" OnClick="() => this.SelectionChanged(profile)">
|
||||
<MudMenuItem Icon="@this.ProfileIcon(profile)" OnClick="() => this.SelectionChanged(profile)">
|
||||
@profile.Name
|
||||
</MudMenuItem>
|
||||
}
|
||||
|
||||
@@ -38,6 +38,14 @@ public partial class ProfileSelection : MSGComponentBase
|
||||
|
||||
private string MarginClass => $"{this.MarginLeft} {this.MarginRight}";
|
||||
|
||||
private string ProfileIcon(Profile profile)
|
||||
{
|
||||
if (profile.IsEnterpriseConfiguration)
|
||||
return Icons.Material.Filled.Business;
|
||||
|
||||
return Icons.Material.Filled.Person4;
|
||||
}
|
||||
|
||||
private async Task SelectionChanged(Profile profile)
|
||||
{
|
||||
this.CurrentProfile = profile;
|
||||
|
||||
@@ -11,9 +11,9 @@ public partial class ProviderSelection : MSGComponentBase
|
||||
{
|
||||
[CascadingParameter]
|
||||
public AssistantBase<NoComponent>? AssistantBase { get; set; }
|
||||
|
||||
|
||||
[Parameter]
|
||||
public AIStudio.Settings.Provider ProviderSettings { get; set; }
|
||||
public AIStudio.Settings.Provider ProviderSettings { get; set; } = AIStudio.Settings.Provider.NONE;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<AIStudio.Settings.Provider> ProviderSettingsChanged { get; set; }
|
||||
@@ -32,7 +32,8 @@ public partial class ProviderSelection : MSGComponentBase
|
||||
{
|
||||
var minimumLevel = this.SettingsManager.GetMinimumConfidenceLevel(this.AssistantBase?.Component ?? Tools.Components.NONE);
|
||||
foreach (var provider in this.SettingsManager.ConfigurationData.Providers)
|
||||
if (provider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level >= minimumLevel)
|
||||
yield return provider;
|
||||
if (provider.UsedLLMProvider != LLMProviders.NONE)
|
||||
if (provider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level >= minimumLevel)
|
||||
yield return provider;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
using AIStudio.Dialogs;
|
||||
using AIStudio.Tools.Rust;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class ReadFileContent : MSGComponentBase
|
||||
@@ -16,6 +19,12 @@ public partial class ReadFileContent : MSGComponentBase
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ILogger<ReadFileContent> Logger { get; init; } = null!;
|
||||
|
||||
private async Task SelectFile()
|
||||
{
|
||||
var selectedFile = await this.RustService.SelectFile(T("Select file to read its content"));
|
||||
@@ -38,6 +47,26 @@ public partial class ReadFileContent : MSGComponentBase
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure that Pandoc is installed and ready:
|
||||
var pandocState = await Pandoc.CheckAvailabilityAsync(this.RustService, showSuccessMessage: false);
|
||||
if (!pandocState.IsAvailable)
|
||||
{
|
||||
var dialogParameters = new DialogParameters<PandocDialog>
|
||||
{
|
||||
{ x => x.ShowInitialResultInSnackbar, false },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<PandocDialog>(T("Pandoc Installation"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
await dialogReference.Result;
|
||||
|
||||
pandocState = await Pandoc.CheckAvailabilityAsync(this.RustService, showSuccessMessage: true);
|
||||
if (!pandocState.IsAvailable)
|
||||
{
|
||||
this.Logger.LogError("Pandoc is not available after installation attempt.");
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Cancel, T("Pandoc may be required for importing files.")));
|
||||
}
|
||||
}
|
||||
|
||||
var fileContent = await this.RustService.ReadArbitraryFileData(selectedFile.SelectedFilePath, int.MaxValue);
|
||||
await this.FileContentChanged.InvokeAsync(fileContent);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
@inherits MSGComponentBase
|
||||
<MudPaper Class="pa-3 mb-3 border-dashed border rounded-lg">
|
||||
<MudTextSwitch Label="@T("Read content from web?")" Disabled="@this.AgentIsRunning" @bind-Value="@this.showWebContentReader" LabelOn="@T("Show web content options")" LabelOff="@T("Hide web content options")" />
|
||||
@if (this.showWebContentReader)
|
||||
<MudTextSwitch Label="@T("Read content from web?")" Disabled="@this.AgentIsRunning" Value="@this.Preselect" ValueChanged="@this.ShowWebContentReaderChanged" LabelOn="@T("Show web content options")" LabelOff="@T("Hide web content options")" />
|
||||
@if (this.Preselect)
|
||||
{
|
||||
<MudTextSwitch Label="@T("Cleanup content by using an LLM agent?")" @bind-Value="@this.useContentCleanerAgent" Validation="@this.ValidateProvider" Disabled="@this.AgentIsRunning" LabelOn="@T("The content is cleaned using an LLM agent: the main content is extracted, advertisements and other irrelevant things are attempted to be removed; relative links are attempted to be converted into absolute links so that they can be used.")" LabelOff="@T("No content cleaning")" />
|
||||
<MudTextSwitch Label="@T("Cleanup content by using an LLM agent?")" Value="@this.PreselectContentCleanerAgent" ValueChanged="@this.UseContentCleanerAgentChanged" Validation="@this.ValidateProvider" Disabled="@this.AgentIsRunning" LabelOn="@T("The content is cleaned using an LLM agent: the main content is extracted, advertisements and other irrelevant things are attempted to be removed; relative links are attempted to be converted into absolute links so that they can be used.")" LabelOff="@T("No content cleaning")" />
|
||||
<MudStack Row="@true" AlignItems="@AlignItems.Baseline" Class="mb-3">
|
||||
<MudTextField T="string" Label="@T("URL from which to load the content")" @bind-Value="@this.providedURL" Validation="@this.ValidateURL" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Link" Placeholder="https://..." HelperText="@T("Loads the content from your URL. Does not work when the content is hidden behind a paywall.")" Variant="Variant.Outlined" Immediate="@true" Disabled="@this.AgentIsRunning"/>
|
||||
<MudButton Disabled="@(!this.IsReady || this.AgentIsRunning)" Variant="Variant.Filled" Size="Size.Large" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Download" OnClick="() => this.LoadFromWeb()">
|
||||
|
||||
@@ -20,7 +20,7 @@ public partial class ReadWebContent : MSGComponentBase
|
||||
public EventCallback<string> ContentChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public AIStudio.Settings.Provider ProviderSettings { get; set; }
|
||||
public AIStudio.Settings.Provider ProviderSettings { get; set; } = AIStudio.Settings.Provider.NONE;
|
||||
|
||||
[Parameter]
|
||||
public bool AgentIsRunning { get; set; }
|
||||
@@ -31,31 +31,32 @@ public partial class ReadWebContent : MSGComponentBase
|
||||
[Parameter]
|
||||
public bool Preselect { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> PreselectChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool PreselectContentCleanerAgent { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> PreselectContentCleanerAgentChanged { get; set; }
|
||||
|
||||
private Process<ReadWebContentSteps> process = Process<ReadWebContentSteps>.INSTANCE;
|
||||
private readonly Process<ReadWebContentSteps> process = Process<ReadWebContentSteps>.INSTANCE;
|
||||
private ProcessStepValue processStep;
|
||||
|
||||
private bool showWebContentReader;
|
||||
private bool useContentCleanerAgent;
|
||||
private string providedURL = string.Empty;
|
||||
private bool urlIsValid;
|
||||
private bool isProviderValid;
|
||||
|
||||
private AIStudio.Settings.Provider providerSettings;
|
||||
private AIStudio.Settings.Provider providerSettings = AIStudio.Settings.Provider.NONE;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if(this.Preselect)
|
||||
this.showWebContentReader = true;
|
||||
|
||||
if(this.PreselectContentCleanerAgent)
|
||||
this.useContentCleanerAgent = true;
|
||||
|
||||
this.ProviderSettings = this.SettingsManager.GetPreselectedProvider(Tools.Components.AGENT_TEXT_CONTENT_CLEANER, this.ProviderSettings.Id, true);
|
||||
this.providerSettings = this.ProviderSettings;
|
||||
this.ValidateProvider(this.PreselectContentCleanerAgent);
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
@@ -64,6 +65,7 @@ public partial class ReadWebContent : MSGComponentBase
|
||||
if (!this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions)
|
||||
this.providerSettings = this.ProviderSettings;
|
||||
|
||||
this.ValidateProvider(this.PreselectContentCleanerAgent);
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
@@ -86,7 +88,7 @@ public partial class ReadWebContent : MSGComponentBase
|
||||
this.StateHasChanged();
|
||||
markdown = this.HTMLParser.ParseToMarkdown(html);
|
||||
|
||||
if (this.useContentCleanerAgent)
|
||||
if (this.PreselectContentCleanerAgent && this.providerSettings != AIStudio.Settings.Provider.NONE)
|
||||
{
|
||||
this.AgentTextContentCleaner.ProviderSettings = this.providerSettings;
|
||||
var additionalData = new Dictionary<string, string>
|
||||
@@ -140,16 +142,26 @@ public partial class ReadWebContent : MSGComponentBase
|
||||
if(!this.urlIsValid)
|
||||
return false;
|
||||
|
||||
if(this.useContentCleanerAgent && !this.isProviderValid)
|
||||
if(this.PreselectContentCleanerAgent && !this.isProviderValid)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ShowWebContentReaderChanged(bool state)
|
||||
{
|
||||
await this.PreselectChanged.InvokeAsync(state);
|
||||
}
|
||||
|
||||
private async Task UseContentCleanerAgentChanged(bool state)
|
||||
{
|
||||
await this.PreselectContentCleanerAgentChanged.InvokeAsync(state);
|
||||
}
|
||||
|
||||
private string? ValidateProvider(bool shouldUseAgent)
|
||||
{
|
||||
if(shouldUseAgent && this.providerSettings == default)
|
||||
if(shouldUseAgent && this.providerSettings == AIStudio.Settings.Provider.NONE)
|
||||
{
|
||||
this.isProviderValid = false;
|
||||
return T("Please select a provider to use the cleanup agent.");
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public static class ReadWebContentStepsExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ReadWebContentStepsExtensions).Namespace, nameof(ReadWebContentStepsExtensions));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text representation of a given ReadWebContentSteps enum value.
|
||||
/// </summary>
|
||||
/// <param name="step">The ReadWebContentSteps enum value.</param>
|
||||
/// <returns>>The text representation of the process step.</returns>
|
||||
public static string GetText(this ReadWebContentSteps step) => step switch
|
||||
{
|
||||
ReadWebContentSteps.START => TB("Start"),
|
||||
ReadWebContentSteps.LOADING => TB("Loading"),
|
||||
ReadWebContentSteps.PARSING => TB("Parsing"),
|
||||
ReadWebContentSteps.CLEANING => TB("Cleaning"),
|
||||
ReadWebContentSteps.DONE => TB("Done"),
|
||||
|
||||
_ => TB("n/a")
|
||||
};
|
||||
}
|
||||
@@ -13,19 +13,20 @@
|
||||
<ConfigurationSelect OptionDescription="@T("Color theme")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreferredTheme)" Data="@ConfigurationSelectDataFactory.GetThemesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreferredTheme = selectedValue)" OptionHelp="@T("Choose the color theme that best suits for you.")"/>
|
||||
<ConfigurationOption OptionDescription="@T("Save energy?")" LabelOn="@T("Energy saving is enabled")" LabelOff="@T("Energy saving is disabled")" State="@(() => this.SettingsManager.ConfigurationData.App.IsSavingEnergy)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.IsSavingEnergy = updatedState)" OptionHelp="@T("When enabled, streamed content from the AI is updated once every third second. When disabled, streamed content will be updated as soon as it is available.")"/>
|
||||
<ConfigurationOption OptionDescription="@T("Enable spellchecking?")" LabelOn="@T("Spellchecking is enabled")" LabelOff="@T("Spellchecking is disabled")" State="@(() => this.SettingsManager.ConfigurationData.App.EnableSpellchecking)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.EnableSpellchecking = updatedState)" OptionHelp="@T("When enabled, spellchecking will be active in all input fields. Depending on your operating system, errors may not be visually highlighted, but right-clicking may still offer possible corrections.")"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Check for updates")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.UpdateBehavior)" Data="@ConfigurationSelectDataFactory.GetUpdateBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UpdateBehavior = selectedValue)" OptionHelp="@T("How often should we check for app updates?")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.UpdateBehavior, out var meta) && meta.IsLocked"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Check for updates")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.UpdateInterval)" Data="@ConfigurationSelectDataFactory.GetUpdateIntervalData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UpdateInterval = selectedValue)" OptionHelp="@T("How often should we check for app updates?")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.UpdateInterval, out var meta) && meta.IsLocked"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Update installation method")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.UpdateInstallation)" Data="@ConfigurationSelectDataFactory.GetUpdateBehaviourData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UpdateInstallation = selectedValue)" OptionHelp="@T("Should updates be installed automatically or manually?")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.UpdateInstallation, out var meta) && meta.IsLocked"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Navigation bar behavior")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.NavigationBehavior)" Data="@ConfigurationSelectDataFactory.GetNavBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.NavigationBehavior = selectedValue)" OptionHelp="@T("Select the desired behavior for the navigation bar.")"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Preview feature visibility")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreviewVisibility)" Data="@ConfigurationSelectDataFactory.GetPreviewVisibility()" SelectionUpdate="@this.UpdatePreviewFeatures" OptionHelp="@T("Do you want to show preview features in the app?")"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Preview feature visibility")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreviewVisibility)" Data="@ConfigurationSelectDataFactory.GetPreviewVisibility()" SelectionUpdate="@this.UpdatePreviewFeatures" OptionHelp="@T("Do you want to show preview features in the app?")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.PreviewVisibility, out var meta) && meta.IsLocked"/>
|
||||
|
||||
@if (this.SettingsManager.ConfigurationData.App.PreviewVisibility > PreviewVisibility.NONE)
|
||||
{
|
||||
var availablePreviewFeatures = ConfigurationSelectDataFactory.GetPreviewFeaturesData(this.SettingsManager).ToList();
|
||||
if (availablePreviewFeatures.Count > 0)
|
||||
{
|
||||
<ConfigurationMultiSelect OptionDescription="@T("Select preview features")" SelectedValues="@(() => this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures.Where(x => !x.IsReleased()).ToHashSet())" Data="@availablePreviewFeatures" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures = selectedValue)" OptionHelp="@T("Which preview features would you like to enable?")"/>
|
||||
<ConfigurationMultiSelect OptionDescription="@T("Select preview features")" SelectedValues="@(() => this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures.Where(x => !x.IsReleased()).ToHashSet())" Data="@availablePreviewFeatures" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures = selectedValue)" OptionHelp="@T("Which preview features would you like to enable?")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.EnabledPreviewFeatures, out var meta) && meta.IsLocked"/>
|
||||
}
|
||||
}
|
||||
|
||||
<ConfigurationProviderSelection Component="Components.APP_SETTINGS" Data="@this.AvailableLLMProvidersFunc()" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProvider = selectedValue)" HelpText="@(() => T("Would you like to set one provider as the default for the entire app? When you configure a different provider for an assistant, it will always take precedence."))"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Preselect one of your profiles?")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProfile = selectedValue)" OptionHelp="@T("Would you like to set one of your profiles as the default for the entire app? When you configure a different profile for an assistant, it will always take precedence.")"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Preselect one of your profiles?")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProfile = selectedValue)" OptionHelp="@T("Would you like to set one of your profiles as the default for the entire app? When you configure a different profile for an assistant, it will always take precedence.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.PreselectedProfile, out var meta) && meta.IsLocked"/>
|
||||
</ExpansionPanel>
|
||||
@@ -90,9 +90,9 @@ public partial class SettingsPanelEmbeddings : SettingsPanelBase
|
||||
|
||||
private async Task DeleteEmbeddingProvider(EmbeddingProvider provider)
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Are you sure you want to delete the embedding provider '{0}'?"), provider.Name) },
|
||||
{ x => x.Message, string.Format(T("Are you sure you want to delete the embedding provider '{0}'?"), provider.Name) },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Embedding Provider"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
|
||||
@@ -54,6 +54,12 @@ public partial class SettingsPanelProviders : SettingsPanelBase
|
||||
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
|
||||
private async Task EditLLMProvider(AIStudio.Settings.Provider provider)
|
||||
{
|
||||
if(provider == AIStudio.Settings.Provider.NONE)
|
||||
return;
|
||||
|
||||
if (provider.IsEnterpriseConfiguration)
|
||||
return;
|
||||
|
||||
var dialogParameters = new DialogParameters<ProviderDialog>
|
||||
{
|
||||
{ x => x.DataNum, provider.Num },
|
||||
@@ -66,6 +72,7 @@ public partial class SettingsPanelProviders : SettingsPanelBase
|
||||
{ x => x.IsEditing, true },
|
||||
{ x => x.DataHost, provider.Host },
|
||||
{ x => x.HFInferenceProviderId, provider.HFInferenceProvider },
|
||||
{ x => x.AdditionalJsonApiParameters, provider.AdditionalJsonApiParameters },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ProviderDialog>(T("Edit LLM Provider"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -90,9 +97,9 @@ public partial class SettingsPanelProviders : SettingsPanelBase
|
||||
[SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")]
|
||||
private async Task DeleteLLMProvider(AIStudio.Settings.Provider provider)
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Are you sure you want to delete the provider '{0}'?"), provider.InstanceName) },
|
||||
{ x => x.Message, string.Format(T("Are you sure you want to delete the provider '{0}'?"), provider.InstanceName) },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete LLM Provider"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -108,9 +115,9 @@ public partial class SettingsPanelProviders : SettingsPanelBase
|
||||
}
|
||||
else
|
||||
{
|
||||
var issueDialogParameters = new DialogParameters
|
||||
var issueDialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Couldn't delete the provider '{0}'. The issue: {1}. We can ignore this issue and delete the provider anyway. Do you want to ignore it and delete this provider?"), provider.InstanceName, deleteSecretResponse.Issue) },
|
||||
{ x => x.Message, string.Format(T("Couldn't delete the provider '{0}'. The issue: {1}. We can ignore this issue and delete the provider anyway. Do you want to ignore it and delete this provider?"), provider.InstanceName, deleteSecretResponse.Issue) },
|
||||
};
|
||||
|
||||
var issueDialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete LLM Provider"), issueDialogParameters, DialogOptions.FULLSCREEN);
|
||||
|
||||
@@ -16,9 +16,6 @@ public partial class Workspaces : MSGComponentBase
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ThreadSafeRandom RNG { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ILogger<Workspaces> Logger { get; init; } = null!;
|
||||
|
||||
@@ -112,9 +109,30 @@ public partial class Workspaces : MSGComponentBase
|
||||
// Enumerate the chat directories:
|
||||
foreach (var tempChatDirPath in Directory.EnumerateDirectories(temporaryDirectories))
|
||||
{
|
||||
// Read the `name` file:
|
||||
// Read or create the `name` file (self-heal):
|
||||
var chatNamePath = Path.Join(tempChatDirPath, "name");
|
||||
var chatName = await File.ReadAllTextAsync(chatNamePath, Encoding.UTF8);
|
||||
string chatName;
|
||||
try
|
||||
{
|
||||
if (!File.Exists(chatNamePath))
|
||||
{
|
||||
chatName = T("Unnamed chat");
|
||||
await File.WriteAllTextAsync(chatNamePath, chatName, Encoding.UTF8);
|
||||
}
|
||||
else
|
||||
{
|
||||
chatName = await File.ReadAllTextAsync(chatNamePath, Encoding.UTF8);
|
||||
if (string.IsNullOrWhiteSpace(chatName))
|
||||
{
|
||||
chatName = T("Unnamed chat");
|
||||
await File.WriteAllTextAsync(chatNamePath, chatName, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
chatName = T("Unnamed chat");
|
||||
}
|
||||
|
||||
// Read the last change time of the chat:
|
||||
var chatThreadPath = Path.Join(tempChatDirPath, "thread.json");
|
||||
@@ -158,9 +176,30 @@ public partial class Workspaces : MSGComponentBase
|
||||
// Enumerate the workspace directories:
|
||||
foreach (var workspaceDirPath in Directory.EnumerateDirectories(workspaceDirectories))
|
||||
{
|
||||
// Read the `name` file:
|
||||
// Read or create the `name` file (self-heal):
|
||||
var workspaceNamePath = Path.Join(workspaceDirPath, "name");
|
||||
var workspaceName = await File.ReadAllTextAsync(workspaceNamePath, Encoding.UTF8);
|
||||
string workspaceName;
|
||||
try
|
||||
{
|
||||
if (!File.Exists(workspaceNamePath))
|
||||
{
|
||||
workspaceName = T("Unnamed workspace");
|
||||
await File.WriteAllTextAsync(workspaceNamePath, workspaceName, Encoding.UTF8);
|
||||
}
|
||||
else
|
||||
{
|
||||
workspaceName = await File.ReadAllTextAsync(workspaceNamePath, Encoding.UTF8);
|
||||
if (string.IsNullOrWhiteSpace(workspaceName))
|
||||
{
|
||||
workspaceName = T("Unnamed workspace");
|
||||
await File.WriteAllTextAsync(workspaceNamePath, workspaceName, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
workspaceName = T("Unnamed workspace");
|
||||
}
|
||||
|
||||
workspaces.Add(new TreeItemData<ITreeItem>
|
||||
{
|
||||
@@ -194,9 +233,30 @@ public partial class Workspaces : MSGComponentBase
|
||||
// Enumerate the workspace directory:
|
||||
foreach (var chatPath in Directory.EnumerateDirectories(workspacePath))
|
||||
{
|
||||
// Read the `name` file:
|
||||
// Read or create the `name` file (self-heal):
|
||||
var chatNamePath = Path.Join(chatPath, "name");
|
||||
var chatName = await File.ReadAllTextAsync(chatNamePath, Encoding.UTF8);
|
||||
string chatName;
|
||||
try
|
||||
{
|
||||
if (!File.Exists(chatNamePath))
|
||||
{
|
||||
chatName = T("Unnamed chat");
|
||||
await File.WriteAllTextAsync(chatNamePath, chatName, Encoding.UTF8);
|
||||
}
|
||||
else
|
||||
{
|
||||
chatName = await File.ReadAllTextAsync(chatNamePath, Encoding.UTF8);
|
||||
if (string.IsNullOrWhiteSpace(chatName))
|
||||
{
|
||||
chatName = T("Unnamed chat");
|
||||
await File.WriteAllTextAsync(chatNamePath, chatName, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
chatName = T("Unnamed chat");
|
||||
}
|
||||
|
||||
// Read the last change time of the chat:
|
||||
var chatThreadPath = Path.Join(chatPath, "thread.json");
|
||||
@@ -252,9 +312,9 @@ public partial class Workspaces : MSGComponentBase
|
||||
// Check if the chat has unsaved changes:
|
||||
if (switchToChat && await MessageBus.INSTANCE.SendMessageUseFirstResult<bool, bool>(this, Event.HAS_CHAT_UNSAVED_CHANGES))
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", T("Are you sure you want to load another chat? All unsaved changes will be lost.") },
|
||||
{ x => x.Message, T("Are you sure you want to load another chat? All unsaved changes will be lost.") },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Load Chat"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -293,10 +353,10 @@ public partial class Workspaces : MSGComponentBase
|
||||
if (askForConfirmation)
|
||||
{
|
||||
var workspaceName = await WorkspaceBehaviour.LoadWorkspaceName(chat.WorkspaceId);
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{
|
||||
"Message", (chat.WorkspaceId == Guid.Empty) switch
|
||||
x => x.Message, (chat.WorkspaceId == Guid.Empty) switch
|
||||
{
|
||||
true => string.Format(T("Are you sure you want to delete the temporary chat '{0}'?"), chat.Name),
|
||||
false => string.Format(T("Are you sure you want to delete the chat '{0}' in the workspace '{1}'?"), chat.Name, workspaceName),
|
||||
@@ -333,12 +393,15 @@ public partial class Workspaces : MSGComponentBase
|
||||
if (chat is null)
|
||||
return;
|
||||
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<SingleInputDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Please enter a new or edit the name for your chat '{0}':"), chat.Name) },
|
||||
{ "UserInput", chat.Name },
|
||||
{ "ConfirmText", T("Rename") },
|
||||
{ "ConfirmColor", Color.Info },
|
||||
{ x => x.Message, string.Format(T("Please enter a new or edit the name for your chat '{0}':"), chat.Name) },
|
||||
{ x => x.InputHeaderText, T("Chat Name") },
|
||||
{ x => x.UserInput, chat.Name },
|
||||
{ x => x.ConfirmText, T("Rename") },
|
||||
{ x => x.ConfirmColor, Color.Info },
|
||||
{ x => x.AllowEmptyInput, false },
|
||||
{ x => x.EmptyInputErrorMessage, T("Please enter a chat name.") },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<SingleInputDialog>(T("Rename Chat"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -365,12 +428,15 @@ public partial class Workspaces : MSGComponentBase
|
||||
|
||||
var workspaceId = Guid.Parse(Path.GetFileName(workspacePath));
|
||||
var workspaceName = await WorkspaceBehaviour.LoadWorkspaceName(workspaceId);
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<SingleInputDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Please enter a new or edit the name for your workspace '{0}':"), workspaceName) },
|
||||
{ "UserInput", workspaceName },
|
||||
{ "ConfirmText", T("Rename") },
|
||||
{ "ConfirmColor", Color.Info },
|
||||
{ x => x.Message, string.Format(T("Please enter a new or edit the name for your workspace '{0}':"), workspaceName) },
|
||||
{ x => x.InputHeaderText, T("Workspace Name") },
|
||||
{ x => x.UserInput, workspaceName },
|
||||
{ x => x.ConfirmText, T("Rename") },
|
||||
{ x => x.ConfirmColor, Color.Info },
|
||||
{ x => x.AllowEmptyInput, false },
|
||||
{ x => x.EmptyInputErrorMessage, T("Please enter a workspace name.") },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<SingleInputDialog>(T("Rename Workspace"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -386,12 +452,15 @@ public partial class Workspaces : MSGComponentBase
|
||||
|
||||
private async Task AddWorkspace()
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<SingleInputDialog>
|
||||
{
|
||||
{ "Message", T("Please name your workspace:") },
|
||||
{ "UserInput", string.Empty },
|
||||
{ "ConfirmText", T("Add workspace") },
|
||||
{ "ConfirmColor", Color.Info },
|
||||
{ x => x.Message, T("Please name your workspace:") },
|
||||
{ x => x.InputHeaderText, T("Workspace Name") },
|
||||
{ x => x.UserInput, string.Empty },
|
||||
{ x => x.ConfirmText, T("Add workspace") },
|
||||
{ x => x.ConfirmColor, Color.Info },
|
||||
{ x => x.AllowEmptyInput, false },
|
||||
{ x => x.EmptyInputErrorMessage, T("Please enter a workspace name.") },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<SingleInputDialog>(T("Add Workspace"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -420,9 +489,9 @@ public partial class Workspaces : MSGComponentBase
|
||||
// Determine how many chats are in the workspace:
|
||||
var chatCount = Directory.EnumerateDirectories(workspacePath).Count();
|
||||
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Are you sure you want to delete the workspace '{0}'? This will also delete {1} chat(s) in this workspace."), workspaceName, chatCount) },
|
||||
{ x => x.Message, string.Format(T("Are you sure you want to delete the workspace '{0}'? This will also delete {1} chat(s) in this workspace."), workspaceName, chatCount) },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Workspace"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -440,11 +509,11 @@ public partial class Workspaces : MSGComponentBase
|
||||
if (chat is null)
|
||||
return;
|
||||
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<WorkspaceSelectionDialog>
|
||||
{
|
||||
{ "Message", T("Please select the workspace where you want to move the chat to.") },
|
||||
{ "SelectedWorkspace", chat.WorkspaceId },
|
||||
{ "ConfirmText", T("Move chat") },
|
||||
{ x => x.Message, T("Please select the workspace where you want to move the chat to.") },
|
||||
{ x => x.SelectedWorkspace, chat.WorkspaceId },
|
||||
{ x => x.ConfirmText, T("Move chat") },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<WorkspaceSelectionDialog>(T("Move Chat to Workspace"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -487,9 +556,9 @@ public partial class Workspaces : MSGComponentBase
|
||||
// Check if the chat has unsaved changes:
|
||||
if (await MessageBus.INSTANCE.SendMessageUseFirstResult<bool, bool>(this, Event.HAS_CHAT_UNSAVED_CHANGES))
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", T("Are you sure you want to create a another chat? All unsaved changes will be lost.") },
|
||||
{ x => x.Message, T("Are you sure you want to create a another chat? All unsaved changes will be lost.") },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Create Chat"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
@@ -504,7 +573,6 @@ public partial class Workspaces : MSGComponentBase
|
||||
WorkspaceId = workspaceId,
|
||||
ChatId = Guid.NewGuid(),
|
||||
Name = string.Empty,
|
||||
Seed = this.RNG.Next(),
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
Blocks = [],
|
||||
};
|
||||
|
||||
@@ -129,6 +129,9 @@ public partial class ChatTemplateDialog : MSGComponentBase
|
||||
PredefinedUserPrompt = this.PredefinedUserPrompt,
|
||||
ExampleConversation = this.dataExampleConversation,
|
||||
AllowProfileUsage = this.AllowProfileUsage,
|
||||
|
||||
EnterpriseConfigurationPluginId = Guid.Empty,
|
||||
IsEnterpriseConfiguration = false,
|
||||
};
|
||||
|
||||
private void RemoveMessage(ContentBlock item)
|
||||
|
||||
@@ -69,9 +69,6 @@ public partial class EmbeddingProviderDialog : MSGComponentBase, ISecretId
|
||||
[Parameter]
|
||||
public bool IsEditing { get; init; }
|
||||
|
||||
[Inject]
|
||||
private ILogger<ProviderDialog> Logger { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
@@ -244,7 +241,7 @@ public partial class EmbeddingProviderDialog : MSGComponentBase, ISecretId
|
||||
private async Task ReloadModels()
|
||||
{
|
||||
var currentEmbeddingProviderSettings = this.CreateEmbeddingProviderSettings();
|
||||
var provider = currentEmbeddingProviderSettings.CreateProvider(this.Logger);
|
||||
var provider = currentEmbeddingProviderSettings.CreateProvider();
|
||||
if(provider is NoProvider)
|
||||
return;
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ public partial class PandocDialog : MSGComponentBase
|
||||
[CascadingParameter]
|
||||
private IMudDialogInstance MudDialog { get; set; } = null!;
|
||||
|
||||
private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger("PandocDialog");
|
||||
private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger(nameof(PandocDialog));
|
||||
private static readonly string LICENCE_URI = "https://raw.githubusercontent.com/jgm/pandoc/refs/heads/main/COPYING.md";
|
||||
private static string LATEST_PANDOC_VERSION = string.Empty;
|
||||
|
||||
@@ -83,9 +83,9 @@ public partial class PandocDialog : MSGComponentBase
|
||||
private async Task RejectLicense()
|
||||
{
|
||||
var message = T("Pandoc is open-source and free, but if you reject its license, you can't install it and some MindWork AI Studio features will be limited (like the integration of Office files) or unavailable (like the generation of Office files). You can change your decision anytime. Are you sure you want to reject the license?");
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", message },
|
||||
{ x => x.Message, message },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Reject Pandoc's Licence"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
|
||||
@@ -42,12 +42,10 @@
|
||||
Lines="6"
|
||||
AutoGrow="@true"
|
||||
MaxLines="12"
|
||||
MaxLength="444"
|
||||
Counter="444"
|
||||
Class="mb-3"
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
HelperText="@T("Tell the AI something about yourself. What is your profession? How experienced are you in this profession? Which technologies do you like?")"
|
||||
/>
|
||||
<ReadFileContent @bind-FileContent="@this.DataNeedToKnow"/>
|
||||
|
||||
<MudTextField
|
||||
T="string"
|
||||
@@ -61,13 +59,16 @@
|
||||
Lines="6"
|
||||
AutoGrow="@true"
|
||||
MaxLines="12"
|
||||
MaxLength="256"
|
||||
Counter="256"
|
||||
Class="mb-3"
|
||||
Class="mt-10"
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
HelperText="@T("Tell the AI what you want it to do for you. What are your goals or are you trying to achieve? Like having the AI address you informally.")"
|
||||
/>
|
||||
<ReadFileContent @bind-FileContent="@this.DataActions"/>
|
||||
|
||||
<MudJustifiedText Typo="Typo.body2" Class="mb-3 mt-3">
|
||||
@T("Please be aware that your profile info becomes part of the system prompt. This means it uses up context space — the “memory” the LLM uses to understand and respond to your request. If your profile is extremely long, the LLM may struggle to focus on your actual task.")
|
||||
</MudJustifiedText>
|
||||
|
||||
</MudForm>
|
||||
<Issues IssuesData="@this.dataIssues"/>
|
||||
</DialogContent>
|
||||
|
||||
@@ -67,10 +67,12 @@ public partial class ProfileDialog : MSGComponentBase
|
||||
{
|
||||
Num = this.DataNum,
|
||||
Id = this.DataId,
|
||||
|
||||
Name = this.DataName,
|
||||
NeedToKnow = this.DataNeedToKnow,
|
||||
Actions = this.DataActions,
|
||||
|
||||
EnterpriseConfigurationPluginId = Guid.Empty,
|
||||
IsEnterpriseConfiguration = false,
|
||||
};
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
@@ -129,9 +131,6 @@ public partial class ProfileDialog : MSGComponentBase
|
||||
if (string.IsNullOrWhiteSpace(this.DataNeedToKnow) && string.IsNullOrWhiteSpace(this.DataActions))
|
||||
return T("Please enter what the LLM should know about you and/or what actions it should take.");
|
||||
|
||||
if(text.Length > 444)
|
||||
return T("The text must not exceed 444 characters.");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -140,9 +139,6 @@ public partial class ProfileDialog : MSGComponentBase
|
||||
if (string.IsNullOrWhiteSpace(this.DataNeedToKnow) && string.IsNullOrWhiteSpace(this.DataActions))
|
||||
return T("Please enter what the LLM should know about you and/or what actions it should take.");
|
||||
|
||||
if(text.Length > 256)
|
||||
return T("The text must not exceed 256 characters.");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -130,6 +130,18 @@
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
/>
|
||||
|
||||
<MudStack>
|
||||
<MudButton OnClick="@this.ToggleExpertSettings">
|
||||
@(this.showExpertSettings ? T("Hide Expert Settings") : T("Show Expert Settings"))
|
||||
</MudButton>
|
||||
<MudDivider />
|
||||
<MudCollapse Expanded="@this.showExpertSettings" Class="@this.GetExpertStyles">
|
||||
<MudJustifiedText Class="mb-5">
|
||||
@T("Please be aware: This section is for experts only. You are responsible for verifying the correctness of the additional parameters you provide to the API call. By default, AI Studio uses the OpenAI-compatible chat completions API, when that it is supported by the underlying service and model.")
|
||||
</MudJustifiedText>
|
||||
<MudTextField T="string" Label=@T("Additional API parameters") Variant="Variant.Outlined" Lines="4" AutoGrow="true" MaxLines="10" HelperText=@T("""Add the parameters in proper JSON formatting, e.g., "temperature": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though.""") Placeholder="@GetPlaceholderExpertSettings" @bind-Value="@this.AdditionalJsonApiParameters" OnBlur="@this.OnInputChangeExpertSettings"/>
|
||||
</MudCollapse>
|
||||
</MudStack>
|
||||
</MudForm>
|
||||
<Issues IssuesData="@this.dataIssues"/>
|
||||
</DialogContent>
|
||||
|
||||
@@ -78,8 +78,8 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
[Parameter]
|
||||
public bool IsEditing { get; init; }
|
||||
|
||||
[Inject]
|
||||
private ILogger<ProviderDialog> Logger { get; init; } = null!;
|
||||
[Parameter]
|
||||
public string AdditionalJsonApiParameters { get; set; } = string.Empty;
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
@@ -97,6 +97,7 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
private string dataManuallyModel = string.Empty;
|
||||
private string dataAPIKeyStorageIssue = string.Empty;
|
||||
private string dataEditingPreviousInstanceName = string.Empty;
|
||||
private bool showExpertSettings;
|
||||
|
||||
// We get the form reference from Blazor code to validate it manually:
|
||||
private MudForm form = null!;
|
||||
@@ -126,17 +127,19 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
Id = this.DataId,
|
||||
InstanceName = this.DataInstanceName,
|
||||
UsedLLMProvider = this.DataLLMProvider,
|
||||
|
||||
Model = this.DataLLMProvider switch
|
||||
{
|
||||
LLMProviders.FIREWORKS => new Model(this.dataManuallyModel, null),
|
||||
LLMProviders.HUGGINGFACE => new Model(this.dataManuallyModel, null),
|
||||
LLMProviders.FIREWORKS or LLMProviders.HUGGINGFACE => new Model(this.dataManuallyModel, null),
|
||||
_ => this.DataModel
|
||||
},
|
||||
|
||||
IsSelfHosted = this.DataLLMProvider is LLMProviders.SELF_HOSTED,
|
||||
IsEnterpriseConfiguration = false,
|
||||
Hostname = cleanedHostname.EndsWith('/') ? cleanedHostname[..^1] : cleanedHostname,
|
||||
Host = this.DataHost,
|
||||
HFInferenceProvider = this.HFInferenceProviderId,
|
||||
AdditionalJsonApiParameters = this.AdditionalJsonApiParameters,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -151,6 +154,8 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
#pragma warning disable MWAIS0001
|
||||
this.UsedInstanceNames = this.SettingsManager.ConfigurationData.Providers.Select(x => x.InstanceName.ToLowerInvariant()).ToList();
|
||||
#pragma warning restore MWAIS0001
|
||||
|
||||
this.showExpertSettings = !string.IsNullOrWhiteSpace(this.AdditionalJsonApiParameters);
|
||||
|
||||
// When editing, we need to load the data:
|
||||
if(this.IsEditing)
|
||||
@@ -158,7 +163,7 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
this.dataEditingPreviousInstanceName = this.DataInstanceName.ToLowerInvariant();
|
||||
|
||||
// When using Fireworks or Hugging Face, we must copy the model name:
|
||||
if (this.DataLLMProvider is LLMProviders.FIREWORKS or LLMProviders.HUGGINGFACE)
|
||||
if (this.DataLLMProvider.IsLLMModelProvidedManually())
|
||||
this.dataManuallyModel = this.DataModel.Id;
|
||||
|
||||
//
|
||||
@@ -241,7 +246,7 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
|
||||
private string? ValidateManuallyModel(string manuallyModel)
|
||||
{
|
||||
if ((this.DataLLMProvider is LLMProviders.FIREWORKS or LLMProviders.HUGGINGFACE) && string.IsNullOrWhiteSpace(manuallyModel))
|
||||
if (this.DataLLMProvider.IsLLMModelProvidedManually() && string.IsNullOrWhiteSpace(manuallyModel))
|
||||
return T("Please enter a model name.");
|
||||
|
||||
return null;
|
||||
@@ -252,7 +257,7 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
private async Task ReloadModels()
|
||||
{
|
||||
var currentProviderSettings = this.CreateProviderSettings();
|
||||
var provider = currentProviderSettings.CreateProvider(this.Logger);
|
||||
var provider = currentProviderSettings.CreateProvider();
|
||||
if(provider is NoProvider)
|
||||
return;
|
||||
|
||||
@@ -270,4 +275,20 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
LLMProviders.SELF_HOSTED => T("(Optional) API Key"),
|
||||
_ => T("API Key"),
|
||||
};
|
||||
|
||||
private void ToggleExpertSettings() => this.showExpertSettings = !this.showExpertSettings;
|
||||
|
||||
private void OnInputChangeExpertSettings()
|
||||
{
|
||||
this.AdditionalJsonApiParameters = this.AdditionalJsonApiParameters.Trim().TrimEnd(',', ' ');
|
||||
}
|
||||
|
||||
private string GetExpertStyles => this.showExpertSettings ? "border-2 border-dashed rounded pa-2" : string.Empty;
|
||||
|
||||
private static string GetPlaceholderExpertSettings =>
|
||||
"""
|
||||
"temperature": 0.5,
|
||||
"top_p": 0.9,
|
||||
"frequency_penalty": 0.0
|
||||
""";
|
||||
}
|
||||
@@ -8,9 +8,9 @@ public partial class SettingsDialogAssistantBias : SettingsDialogBase
|
||||
|
||||
private async Task ResetBiasOfTheDayHistory()
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", T("Are you sure you want to reset your bias-of-the-day statistics? The system will no longer remember which biases you already know. As a result, biases you are already familiar with may be addressed again.") },
|
||||
{ x => x.Message, T("Are you sure you want to reset your bias-of-the-day statistics? The system will no longer remember which biases you already know. As a result, biases you are already familiar with may be addressed again.") },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Reset your bias-of-the-day statistics"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
|
||||
@@ -31,14 +31,23 @@
|
||||
<MudTd>@context.Num</MudTd>
|
||||
<MudTd>@context.Name</MudTd>
|
||||
<MudTd>
|
||||
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
|
||||
<MudTooltip Text="@T("Edit")">
|
||||
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Edit" OnClick="() => this.EditChatTemplate(context)"/>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="@T("Delete")">
|
||||
<MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteChatTemplate(context)"/>
|
||||
</MudTooltip>
|
||||
</MudStack>
|
||||
@if (context.IsEnterpriseConfiguration)
|
||||
{
|
||||
<MudTooltip Text="@T("This template is managed by your organization.")">
|
||||
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Business" Disabled="true"/>
|
||||
</MudTooltip>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
|
||||
<MudTooltip Text="@T("Edit")">
|
||||
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Edit" OnClick="() => this.EditChatTemplate(context)"/>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="@T("Delete")">
|
||||
<MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteChatTemplate(context)"/>
|
||||
</MudTooltip>
|
||||
</MudStack>
|
||||
}
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
|
||||
@@ -53,6 +53,9 @@ public partial class SettingsDialogChatTemplate : SettingsDialogBase
|
||||
|
||||
private async Task EditChatTemplate(ChatTemplate chatTemplate)
|
||||
{
|
||||
if (chatTemplate == ChatTemplate.NO_CHAT_TEMPLATE || chatTemplate.IsEnterpriseConfiguration)
|
||||
return;
|
||||
|
||||
var dialogParameters = new DialogParameters<ChatTemplateDialog>
|
||||
{
|
||||
{ x => x.DataNum, chatTemplate.Num },
|
||||
@@ -79,9 +82,9 @@ public partial class SettingsDialogChatTemplate : SettingsDialogBase
|
||||
|
||||
private async Task DeleteChatTemplate(ChatTemplate chatTemplate)
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Are you sure you want to delete the chat template '{0}'?"), chatTemplate.Name) },
|
||||
{ x => x.Message, string.Format(T("Are you sure you want to delete the chat template '{0}'?"), chatTemplate.Name) },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Chat Template"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
|
||||
@@ -151,9 +151,9 @@ public partial class SettingsDialogDataSources : SettingsDialogBase
|
||||
|
||||
private async Task DeleteDataSource(IDataSource dataSource)
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Are you sure you want to delete the data source '{0}' of type {1}?"), dataSource.Name, dataSource.Type.GetDisplayName()) },
|
||||
{ x => x.Message, string.Format(T("Are you sure you want to delete the data source '{0}' of type {1}?"), dataSource.Name, dataSource.Type.GetDisplayName()) },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Data Source"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
|
||||
@@ -30,14 +30,23 @@
|
||||
<MudTd>@context.Num</MudTd>
|
||||
<MudTd>@context.Name</MudTd>
|
||||
<MudTd>
|
||||
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
|
||||
<MudTooltip Text="@T("Edit")">
|
||||
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Edit" OnClick="() => this.EditProfile(context)"/>
|
||||
@if (context.IsEnterpriseConfiguration)
|
||||
{
|
||||
<MudTooltip Text="@T("This profile is managed by your organization.")">
|
||||
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Business" Disabled="true"/>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="@T("Delete")">
|
||||
<MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteProfile(context)"/>
|
||||
</MudTooltip>
|
||||
</MudStack>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudStack Row="true" Class="mb-2 mt-2" Wrap="Wrap.Wrap">
|
||||
<MudTooltip Text="@T("Edit")">
|
||||
<MudIconButton Color="Color.Info" Icon="@Icons.Material.Filled.Edit" OnClick="() => this.EditProfile(context)"/>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="@T("Delete")">
|
||||
<MudIconButton Color="Color.Error" Icon="@Icons.Material.Filled.Delete" OnClick="() => this.DeleteProfile(context)"/>
|
||||
</MudTooltip>
|
||||
</MudStack>
|
||||
}
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
|
||||
@@ -51,9 +51,9 @@ public partial class SettingsDialogProfiles : SettingsDialogBase
|
||||
|
||||
private async Task DeleteProfile(Profile profile)
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", string.Format(T("Are you sure you want to delete the profile '{0}'?"), profile.Name) },
|
||||
{ x => x.Message, string.Format(T("Are you sure you want to delete the profile '{0}'?"), profile.Name) },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Profile"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
{
|
||||
<ConfigurationText OptionDescription="@T("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="@T("Preselect important aspects")" Disabled="@(() => !this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions)" Text="@(() => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedImportantAspects)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedImportantAspects = updatedText)" NumLines="2" OptionHelp="@T("Preselect aspects for the LLM to focus on when generating a summary, such as summary length or specific topics to emphasize.")" Icon="@Icons.Material.Filled.List"/>
|
||||
<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>
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
<MudText Typo="Typo.body1">
|
||||
@this.Message
|
||||
</MudText>
|
||||
<MudTextField T="string" @bind-Text="@this.UserInput" Variant="Variant.Outlined" AutoGrow="@false" Lines="1" Label="@T("Chat name")" AutoFocus="@true" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
|
||||
<MudForm @ref="this.form" Class="mt-4">
|
||||
<MudTextField T="string" @bind-Text="@this.UserInput" Variant="Variant.Outlined" AutoGrow="@false" Lines="1" Label="@this.GetInputHeaderText" AutoFocus="@true" UserAttributes="@USER_INPUT_ATTRIBUTES" Validation="@this.ValidateUserInput" />
|
||||
</MudForm>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">
|
||||
|
||||
@@ -20,9 +20,20 @@ public partial class SingleInputDialog : MSGComponentBase
|
||||
|
||||
[Parameter]
|
||||
public Color ConfirmColor { get; set; } = Color.Error;
|
||||
|
||||
[Parameter]
|
||||
public bool AllowEmptyInput { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string InputHeaderText { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public string EmptyInputErrorMessage { get; set; } = string.Empty;
|
||||
|
||||
private static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();
|
||||
|
||||
private MudForm form = null!;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
@@ -34,7 +45,24 @@ public partial class SingleInputDialog : MSGComponentBase
|
||||
|
||||
#endregion
|
||||
|
||||
private string GetInputHeaderText => string.IsNullOrWhiteSpace(this.InputHeaderText) ? T("Your Input") : this.InputHeaderText;
|
||||
|
||||
private string? ValidateUserInput(string? value)
|
||||
{
|
||||
if (!this.AllowEmptyInput && string.IsNullOrWhiteSpace(value))
|
||||
return string.IsNullOrWhiteSpace(this.EmptyInputErrorMessage) ? T("Please enter a value.") : this.EmptyInputErrorMessage;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void Cancel() => this.MudDialog.Cancel();
|
||||
|
||||
private void Confirm() => this.MudDialog.Close(DialogResult.Ok(this.UserInput));
|
||||
private async Task Confirm()
|
||||
{
|
||||
await this.form.Validate();
|
||||
if(!this.form.IsValid)
|
||||
return;
|
||||
|
||||
this.MudDialog.Close(DialogResult.Ok(this.UserInput));
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
[
|
||||
Event.UPDATE_AVAILABLE, Event.CONFIGURATION_CHANGED, Event.COLOR_THEME_CHANGED, Event.SHOW_ERROR,
|
||||
Event.SHOW_ERROR, Event.SHOW_WARNING, Event.SHOW_SUCCESS, Event.STARTUP_PLUGIN_SYSTEM,
|
||||
Event.PLUGINS_RELOADED
|
||||
Event.PLUGINS_RELOADED, Event.INSTALL_UPDATE,
|
||||
]);
|
||||
|
||||
// Set the snackbar for the update service:
|
||||
@@ -143,6 +143,11 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
{
|
||||
switch (triggeredEvent)
|
||||
{
|
||||
case Event.INSTALL_UPDATE:
|
||||
this.performingUpdate = true;
|
||||
this.StateHasChanged();
|
||||
break;
|
||||
|
||||
case Event.UPDATE_AVAILABLE:
|
||||
if (data is UpdateResponse updateResponse)
|
||||
{
|
||||
@@ -301,9 +306,9 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
{
|
||||
if (await MessageBus.INSTANCE.SendMessageUseFirstResult<bool, bool>(this, Event.HAS_CHAT_UNSAVED_CHANGES))
|
||||
{
|
||||
var dialogParameters = new DialogParameters
|
||||
var dialogParameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ "Message", T("Are you sure you want to leave the chat page? All unsaved changes will be lost.") },
|
||||
{ x => x.Message, T("Are you sure you want to leave the chat page? All unsaved changes will be lost.") },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Leave Chat Page"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
|
||||
@@ -47,12 +47,12 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CodeBeam.MudBlazor.Extensions" Version="8.2.4" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.12.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.8" />
|
||||
<PackageReference Include="MudBlazor" Version="8.11.0" />
|
||||
<PackageReference Include="CodeBeam.MudBlazor.Extensions" Version="8.2.5" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.12.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.11" />
|
||||
<PackageReference Include="MudBlazor" Version="8.12.0" />
|
||||
<PackageReference Include="MudBlazor.Markdown" Version="8.11.0" />
|
||||
<PackageReference Include="ReverseMarkdown" Version="4.7.0" />
|
||||
<PackageReference Include="ReverseMarkdown" Version="4.7.1" />
|
||||
<PackageReference Include="LuaCSharp" Version="0.4.2" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ public partial class Chat : MSGComponentBase
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
private ChatThread? chatThread;
|
||||
private AIStudio.Settings.Provider providerSettings;
|
||||
private AIStudio.Settings.Provider providerSettings = AIStudio.Settings.Provider.NONE;
|
||||
private bool workspaceOverlayVisible;
|
||||
private string currentWorkspaceName = string.Empty;
|
||||
private Workspaces? workspaces;
|
||||
@@ -100,6 +100,23 @@ public partial class Chat : MSGComponentBase
|
||||
|
||||
#region Overrides of MSGComponentBase
|
||||
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.splitterSaveTimer.Stop();
|
||||
this.splitterSaveTimer.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
base.DisposeResources();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected override Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||
{
|
||||
switch (triggeredEvent)
|
||||
@@ -111,6 +128,4 @@ public partial class Chat : MSGComponentBase
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -31,7 +31,7 @@ public partial class Home : MSGComponentBase
|
||||
{
|
||||
this.itemsAdvantages = [
|
||||
new(this.T("Free of charge"), this.T("The app is free to use, both for personal and commercial purposes.")),
|
||||
new(this.T("Independence"), this.T("You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT4o, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using vLLM, llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.")),
|
||||
new(this.T("Independence"), this.T("You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT5, o1, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using vLLM, llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.")),
|
||||
new(this.T("Assistants"), this.T("You just want to quickly translate a text? AI Studio has so-called assistants for such and other tasks. No prompting is necessary when working with these assistants.")),
|
||||
new(this.T("Unrestricted usage"), this.T("Unlike services like ChatGPT, which impose limits after intensive use, MindWork AI Studio offers unlimited usage through the providers API.")),
|
||||
new(this.T("Cost-effective"), this.T("You only pay for what you use, which can be cheaper than monthly subscription services like ChatGPT Plus, especially if used infrequently. But beware, here be dragons: For extremely intensive usage, the API costs can be significantly higher. Unfortunately, providers currently do not offer a way to display current costs in the app. Therefore, check your account with the respective provider to see how your costs are developing. When available, use prepaid and set a cost limit.")),
|
||||
|
||||
@@ -2,7 +2,6 @@ using AIStudio.Chat;
|
||||
using AIStudio.Components;
|
||||
using AIStudio.Provider;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
|
||||
using Timer = System.Timers.Timer;
|
||||
@@ -11,14 +10,11 @@ namespace AIStudio.Pages;
|
||||
|
||||
public partial class Writer : MSGComponentBase
|
||||
{
|
||||
[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 AIStudio.Settings.Provider providerSettings = AIStudio.Settings.Provider.NONE;
|
||||
private ChatThread? chatThread;
|
||||
private bool isStreaming;
|
||||
private string userInput = string.Empty;
|
||||
@@ -77,7 +73,6 @@ public partial class Writer : MSGComponentBase
|
||||
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
|
||||
@@ -122,7 +117,7 @@ public partial class Writer : MSGComponentBase
|
||||
this.isStreaming = true;
|
||||
this.StateHasChanged();
|
||||
|
||||
this.chatThread = await aiText.CreateFromProviderAsync(this.providerSettings.CreateProvider(this.Logger), this.providerSettings.Model, lastUserPrompt, this.chatThread);
|
||||
this.chatThread = await aiText.CreateFromProviderAsync(this.providerSettings.CreateProvider(), this.providerSettings.Model, lastUserPrompt, this.chatThread);
|
||||
this.suggestion = aiText.Text;
|
||||
|
||||
this.isStreaming = false;
|
||||
@@ -152,4 +147,23 @@ public partial class Writer : MSGComponentBase
|
||||
this.suggestion = string.Join(' ', words.Skip(1));
|
||||
this.StateHasChanged();
|
||||
}
|
||||
|
||||
#region Overrides of MSGComponentBase
|
||||
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.typeTimer.Stop();
|
||||
this.typeTimer.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
base.DisposeResources();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -47,25 +47,92 @@ DEPRECATION_MESSAGE = ""
|
||||
CONFIG = {}
|
||||
CONFIG["LLM_PROVIDERS"] = {}
|
||||
|
||||
-- An example of a configuration for a self-hosted ollama server:
|
||||
-- An example of a configuration for a self-hosted server:
|
||||
CONFIG["LLM_PROVIDERS"][#CONFIG["LLM_PROVIDERS"]+1] = {
|
||||
["Id"] = "00000000-0000-0000-0000-000000000000",
|
||||
["InstanceName"] = "<user-friendly name for the combination of server and model>",
|
||||
["UsedLLMProvider"] = "SELF_HOSTED",
|
||||
|
||||
-- Allowed values for Host are: LM_STUDIO, LLAMACPP, OLLAMA, and VLLM
|
||||
["Host"] = "OLLAMA",
|
||||
["Hostname"] = "<https address of the ollama server>",
|
||||
["Hostname"] = "<https address of the server>",
|
||||
|
||||
-- Optional: Additional parameters for the API.
|
||||
-- Please refer to the documentation of the selected host for details.
|
||||
-- Might be something like ... \"temperature\": 0.5 ... for one parameter.
|
||||
-- Could be something like ... \"temperature\": 0.5, \"max_tokens\": 1000 ... for multiple parameters.
|
||||
-- Please do not add the enclosing curly braces {} here. Also, no trailing comma is allowed.
|
||||
["AdditionalJsonApiParameters"] = "",
|
||||
["Model"] = {
|
||||
["Id"] = "<the ollama model ID>",
|
||||
["Id"] = "<the model ID>",
|
||||
["DisplayName"] = "<user-friendly name of the model>",
|
||||
}
|
||||
}
|
||||
|
||||
CONFIG["SETTINGS"] = {}
|
||||
|
||||
-- Configure the update behavior:
|
||||
-- Configure the update check interval:
|
||||
-- Allowed values are: NO_CHECK, ONCE_STARTUP, HOURLY, DAILY, WEEKLY
|
||||
-- CONFIG["SETTINGS"]["DataApp.UpdateBehavior"] = "NO_CHECK"
|
||||
-- CONFIG["SETTINGS"]["DataApp.UpdateInterval"] = "NO_CHECK"
|
||||
|
||||
-- Configure how updates are installed:
|
||||
-- Allowed values are: MANUAL, AUTOMATIC
|
||||
-- CONFIG["SETTINGS"]["DataApp.UpdateInstallation"] = "MANUAL"
|
||||
|
||||
-- Configure the user permission to add providers:
|
||||
-- Allowed values are: true, false
|
||||
-- CONFIG["SETTINGS"]["DataApp.AllowUserToAddProvider"] = false
|
||||
-- CONFIG["SETTINGS"]["DataApp.AllowUserToAddProvider"] = false
|
||||
|
||||
-- Configure the visibility of preview features:
|
||||
-- Allowed values are: NONE, RELEASE_CANDIDATE, BETA, ALPHA, PROTOTYPE, EXPERIMENTAL
|
||||
-- Please note:
|
||||
-- I: that this setting does not hide features that are already enabled.
|
||||
-- II: lower levels include all features of the higher levels. E.g. BETA includes RELEASE_CANDIDATE features.
|
||||
-- CONFIG["SETTINGS"]["DataApp.PreviewVisibility"] = "NONE"
|
||||
|
||||
-- Configure the enabled preview features:
|
||||
-- Allowed values are can be found in https://github.com/MindWorkAI/AI-Studio/app/MindWork%20AI%20Studio/Settings/DataModel/PreviewFeatures.cs
|
||||
-- Examples are PRE_WRITER_MODE_2024, PRE_RAG_2024, PRE_DOCUMENT_ANALYSIS_2025.
|
||||
-- CONFIG["SETTINGS"]["DataApp.EnabledPreviewFeatures"] = { "PRE_RAG_2024", "PRE_DOCUMENT_ANALYSIS_2025" }
|
||||
|
||||
-- Configure the preselected profile.
|
||||
-- It must be one of the profile IDs defined in CONFIG["PROFILES"].
|
||||
-- Be aware that the ID must be using the same casing as defined in the profile.
|
||||
-- When the ID is using upper case letters, but using lower case letters in this
|
||||
-- setting, the preselection will not work.
|
||||
-- CONFIG["SETTINGS"]["DataApp.PreselectedProfile"] = "00000000-0000-0000-0000-000000000000"
|
||||
|
||||
-- Example chat templates for this configuration:
|
||||
CONFIG["CHAT_TEMPLATES"] = {}
|
||||
|
||||
-- A simple example chat template:
|
||||
CONFIG["CHAT_TEMPLATES"][#CONFIG["CHAT_TEMPLATES"]+1] = {
|
||||
["Id"] = "00000000-0000-0000-0000-000000000000",
|
||||
["Name"] = "<user-friendly name of the chat template>",
|
||||
["SystemPrompt"] = "You are <Company Name>'s helpful AI assistant for <Department Name>. Your task is ...",
|
||||
["PredefinedUserPrompt"] = "Please help me with ...",
|
||||
["AllowProfileUsage"] = true,
|
||||
["ExampleConversation"] = {
|
||||
{
|
||||
-- Allowed values are: USER, AI, SYSTEM
|
||||
["Role"] = "USER",
|
||||
["Content"] = "Hello! Can you help me with a quick task?"
|
||||
},
|
||||
{
|
||||
-- Allowed values are: USER, AI, SYSTEM
|
||||
["Role"] = "AI",
|
||||
["Content"] = "Of course. What do you need?"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-- Profiles for this configuration:
|
||||
CONFIG["PROFILES"] = {}
|
||||
|
||||
-- A simple profile template:
|
||||
CONFIG["PROFILES"][#CONFIG["PROFILES"]+1] = {
|
||||
["Id"] = "00000000-0000-0000-0000-000000000000",
|
||||
["Name"] = "<user-friendly name of the profile>",
|
||||
["NeedToKnow"] = "I like to cook in my free time. My favorite meal is ...",
|
||||
["Actions"] = "Please always ensure the portion size is ..."
|
||||
}
|
||||
+145
-31
@@ -1224,12 +1224,18 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::
|
||||
-- Target language
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T237828418"] = "Zielsprache"
|
||||
|
||||
-- (Optional) Important Aspects
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T24391765"] = "(Optional) Wichtige Aspekte"
|
||||
|
||||
-- Summarize long text into a shorter version while retaining the main points. You might want to change the language of the summary to make it more readable. It is also possible to change the complexity of the summary to make it easy to understand.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T359929871"] = "Fasse einen langen Text zu einer kürzeren Version zusammen und behalte dabei die wichtigsten Punkte bei. Sie können die Sprache des Textes anpassen, um die Zusammenfassung verständlicher zu machen. Außerdem ist es möglich, die Zusammenfassung einfacher zu formulieren, damit sie leichter zu verstehen ist."
|
||||
|
||||
-- Please provide your field of expertise.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T3610378685"] = "Bitte geben Sie Ihr Fachgebiet an."
|
||||
|
||||
-- (Optional) Specify aspects for the LLM to focus on when generating a summary, such as summary length or specific topics to emphasize.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T3830285347"] = "(Optional) Spezifizieren Sie Aspekte, auf die das LLM bei der Erstellung einer Zusammenfassung den Fokus legen soll, wie z.B. die Länge der Zusammenfassung oder bestimmte Themen, die hervorgehoben werden sollen."
|
||||
|
||||
-- Custom target language
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T3848935911"] = "Benutzerdefinierte Zielsprache"
|
||||
|
||||
@@ -1329,6 +1335,9 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1603883875"] = "Ja, neu
|
||||
-- Yes, remove it
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1820166585"] = "Ja, entferne es"
|
||||
|
||||
-- Number of sources
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1848978959"] = "Anzahl der Quellen"
|
||||
|
||||
-- Do you really want to edit this message? In order to edit this message, the AI response will be deleted.
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T2018431076"] = "Möchten Sie diese Nachricht wirklich bearbeiten? Um die Nachricht zu bearbeiten, wird die Antwort der KI gelöscht."
|
||||
|
||||
@@ -1356,6 +1365,9 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4070211974"] = "Nachric
|
||||
-- No, keep it
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4188329028"] = "Nein, behalten"
|
||||
|
||||
-- Export Chat to Microsoft Word
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T861873672"] = "Chat in Microsoft Word exportieren"
|
||||
|
||||
-- Open Settings
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T1172211894"] = "Einstellungen öffnen"
|
||||
|
||||
@@ -1656,6 +1668,12 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROFILESELECTION::T918741365"] = "Hier k
|
||||
-- Provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T900237532"] = "Anbieter"
|
||||
|
||||
-- Pandoc Installation
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T185447014"] = "Pandoc-Installation"
|
||||
|
||||
-- Pandoc may be required for importing files.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T2596465560"] = "Pandoc wird möglicherweise zum Importieren von Dateien benötigt."
|
||||
|
||||
-- Images are not supported yet
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T298062956"] = "Bilder werden derzeit nicht unterstützt"
|
||||
|
||||
@@ -1693,7 +1711,7 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENT::T2927391091"] = "Inhalte
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENT::T2939928117"] = "Inhalte mit einem LLM-Agenten bereinigen?"
|
||||
|
||||
-- Hide web content options
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENT::T3031774728"] = "Web-Inhaltsoptionen ausblenden"
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENT::T3031774728"] = "Optionen für Webinhalte ausblenden"
|
||||
|
||||
-- Please provide a valid HTTP or HTTPS URL.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENT::T307442288"] = "Bitte geben Sie eine gültige HTTP- oder HTTPS-URL ein."
|
||||
@@ -1705,7 +1723,25 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENT::T3588401674"] = "Keine In
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENT::T3825586228"] = "Bitte geben Sie eine gültige URL ein."
|
||||
|
||||
-- Show web content options
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENT::T4249712357"] = "Web-Inhalte anzeigen"
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENT::T4249712357"] = "Optionen für Webinhalte anzeigen"
|
||||
|
||||
-- Loading
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T1404011351"] = "Laden"
|
||||
|
||||
-- Start
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T182978943"] = "Start"
|
||||
|
||||
-- Done
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T2379421585"] = "Fertig"
|
||||
|
||||
-- Parsing
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T3151033983"] = "Zerlegen"
|
||||
|
||||
-- Cleaning
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T3420573362"] = "Bereinigen"
|
||||
|
||||
-- n/a
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T907272257"] = "n/a"
|
||||
|
||||
-- Hide content
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SECRETINPUTFIELD::T1273315904"] = "Inhalt ausblenden"
|
||||
@@ -1830,6 +1866,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1907446663"]
|
||||
-- Language behavior
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T2341504363"] = "Sprachverhalten"
|
||||
|
||||
-- Update installation method
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T237706157"] = "Installationsmethode für Updates"
|
||||
|
||||
-- Language
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T2591284123"] = "Sprache"
|
||||
|
||||
@@ -1860,6 +1899,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T602293588"]
|
||||
-- Choose the color theme that best suits for you.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T654667432"] = "Wählen Sie das Farbschema, das am besten zu Ihnen passt."
|
||||
|
||||
-- Should updates be installed automatically or manually?
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T707880477"] = "Sollen Updates automatisch oder manuell installiert werden?"
|
||||
|
||||
-- Energy saving is enabled
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T71162186"] = "Energiesparmodus ist aktiviert"
|
||||
|
||||
@@ -2115,6 +2157,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1016188706"] = "Möchten Sie
|
||||
-- Move chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1133040906"] = "Chat verschieben"
|
||||
|
||||
-- Unnamed workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1307384014"] = "Unbenannter Arbeitsbereich"
|
||||
|
||||
-- Delete
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1469573738"] = "Löschen"
|
||||
|
||||
@@ -2148,6 +2193,12 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2237618267"] = "Möchten Sie
|
||||
-- Delete Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2244038752"] = "Chat löschen"
|
||||
|
||||
-- Please enter a chat name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2301651387"] = "Bitte geben Sie einen Namen für diesen Chat ein."
|
||||
|
||||
-- Workspace Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2446263209"] = "Name des Arbeitsbereichs"
|
||||
|
||||
-- Move to workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2509305748"] = "In einen Arbeitsbereich verschieben"
|
||||
|
||||
@@ -2160,6 +2211,12 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3045856778"] = "Chat in den
|
||||
-- Please enter a new or edit the name for your workspace '{0}':
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T323280982"] = "Bitte geben Sie einen neuen Namen für ihren Arbeitsbereich „{0}“ ein oder bearbeiten Sie ihn:"
|
||||
|
||||
-- Please enter a workspace name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3288132732"] = "Bitte geben Sie einen Namen für diesen Arbeitsbereich ein."
|
||||
|
||||
-- Unnamed chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3310482275"] = "Unbenannter Chat"
|
||||
|
||||
-- Rename
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3355849203"] = "Umbenennen"
|
||||
|
||||
@@ -2172,6 +2229,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3555709365"] = "Chat laden"
|
||||
-- Add Workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3672981145"] = "Arbeitsbereich hinzufügen"
|
||||
|
||||
-- Chat Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3891063690"] = "Name des Chat"
|
||||
|
||||
-- Empty chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T4019509364"] = "Leerer Chat"
|
||||
|
||||
@@ -2925,6 +2985,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PANDOCDIALOG::T523908375"] = "Pandoc wird un
|
||||
-- Tell the AI what you want it to do for you. What are your goals or are you trying to achieve? Like having the AI address you informally.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T1458195391"] = "Teilen Sie der KI mit, was sie machen soll. Was sind ihre Ziele oder was möchten Sie erreichen? Zum Beispiel, dass die KI Sie duzt."
|
||||
|
||||
-- Please be aware that your profile info becomes part of the system prompt. This means it uses up context space — the “memory” the LLM uses to understand and respond to your request. If your profile is extremely long, the LLM may struggle to focus on your actual task.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T1717545317"] = "Bitte beachten Sie, dass Ihre Profilinformationen Teil des System-Prompts werden. Das bedeutet, sie belegen einen Teil des Kontexts – den „Speicher“, den das LLM nutzt, um Ihre Anfrage zu verstehen und darauf zu antworten. Wenn Ihr Profil extrem lang ist, kann das LLM Schwierigkeiten haben, die Aufgabe auszuführen."
|
||||
|
||||
-- Update
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T1847791252"] = "Aktualisieren"
|
||||
|
||||
@@ -2937,18 +3000,12 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T2261456575"] = "Was soll die
|
||||
-- Please enter a profile name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T2386844536"] = "Bitte geben Sie einen Profilnamen ein."
|
||||
|
||||
-- The text must not exceed 256 characters.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T2560188276"] = "Der Text darf 256 Zeichen nicht überschreiten."
|
||||
|
||||
-- Add
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T2646845972"] = "Hinzufügen"
|
||||
|
||||
-- The profile name must not exceed 40 characters.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3243902394"] = "Der Profilname darf nicht länger als 40 Zeichen sein."
|
||||
|
||||
-- The text must not exceed 444 characters.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3253349421"] = "Der Text darf 444 Zeichen nicht überschreiten."
|
||||
|
||||
-- Profile Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3392578705"] = "Profilname"
|
||||
|
||||
@@ -2973,9 +3030,15 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T900713019"] = "Abbrechen"
|
||||
-- The profile name must be unique; the chosen name is already in use.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T911748898"] = "Der Profilname muss eindeutig sein; der ausgewählte Name wird bereits verwendet."
|
||||
|
||||
-- Please be aware: This section is for experts only. You are responsible for verifying the correctness of the additional parameters you provide to the API call. By default, AI Studio uses the OpenAI-compatible chat completions API, when that it is supported by the underlying service and model.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1017509792"] = "Bitte beachten Sie: Dieser Bereich ist nur für Expertinnen und Experten. Sie sind dafür verantwortlich, die Korrektheit der zusätzlichen Parameter zu überprüfen, die Sie beim API‑Aufruf angeben. Standardmäßig verwendet AI Studio die OpenAI‑kompatible Chat Completions-API, sofern diese vom zugrunde liegenden Dienst und Modell unterstützt wird."
|
||||
|
||||
-- Hugging Face Inference Provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1085481431"] = "Hugging Face Inferenz-Anbieter"
|
||||
|
||||
-- Hide Expert Settings
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1108876344"] = "Experten-Einstellungen ausblenden"
|
||||
|
||||
-- Failed to store the API key in the operating system. The message was: {0}. Please try again.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1122745046"] = "Der API-Schlüssel konnte nicht im Betriebssystem gespeichert werden. Die Meldung war: {0}. Bitte versuchen Sie es erneut."
|
||||
|
||||
@@ -2988,6 +3051,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1356621346"] = "Konto erste
|
||||
-- Load models
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T15352225"] = "Modelle laden"
|
||||
|
||||
-- Add the parameters in proper JSON formatting, e.g., "temperature": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1689135032"] = "Fügen Sie die Parameter in korrekter JSON-Formatierung hinzu, z. B. \"temperature\": 0.5. Entfernen Sie abschließende Kommas. Die üblichen äußeren geschweiften Klammern {} dürfen dabei jedoch nicht verwendet werden."
|
||||
|
||||
-- Hostname
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1727440780"] = "Hostname"
|
||||
|
||||
@@ -3009,12 +3075,18 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2331453405"] = "(Optional)
|
||||
-- Add
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2646845972"] = "Hinzufügen"
|
||||
|
||||
-- Additional API parameters
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2728244552"] = "Zusätzliche API-Parameter"
|
||||
|
||||
-- No models loaded or available.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2810182573"] = "Keine Modelle geladen oder verfügbar."
|
||||
|
||||
-- Instance Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2842060373"] = "Instanzname"
|
||||
|
||||
-- Show Expert Settings
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3361153305"] = "Experten-Einstellungen anzeigen"
|
||||
|
||||
-- Show available models
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3763891899"] = "Verfügbare Modelle anzeigen"
|
||||
|
||||
@@ -3417,6 +3489,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T32678
|
||||
-- Close
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3448155331"] = "Schließen"
|
||||
|
||||
-- This template is managed by your organization.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3576775249"] = "Diesee Vorlage wird von Ihrer Organisation verwaltet."
|
||||
|
||||
-- Edit Chat Template
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3596030597"] = "Chat-Vorlage bearbeiten"
|
||||
|
||||
@@ -3721,7 +3796,7 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGLEGALCHECK::T1591931
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGLEGALCHECK::T1633101895"] = "Wenn diese Option aktiviert ist, wird der Web-Content-Reader ausgeblendet und kann nicht verwendet werden. Dadurch wird die Benutzeroberfläche etwas einfacher zu bedienen."
|
||||
|
||||
-- Web content reader is not preselected
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGLEGALCHECK::T1701127912"] = "Tool zum Lesen von Webinhalten ist nicht vorausgewählt"
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGLEGALCHECK::T1701127912"] = "Der Web-Content-Reader zum Lesen von Webinhalten ist nicht vorausgewählt"
|
||||
|
||||
-- Content cleaner agent is not preselected
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGLEGALCHECK::T1969816694"] = "Agent zur Inhaltsbereinigung ist nicht vorausgewählt"
|
||||
@@ -3742,13 +3817,13 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGLEGALCHECK::T2529161
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGLEGALCHECK::T2746583995"] = "Wenn aktiviert, ist der Content Cleaner Agent vorausgewählt. Das kann nützlich sein, wenn Sie den rechtlichen Inhalt bereinigen möchten, bevor Sie ihn übersetzen."
|
||||
|
||||
-- Web content reader is hidden
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGLEGALCHECK::T2799795311"] = "Tool zum Lesen von Webinhalten ist ausgeblendet"
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGLEGALCHECK::T2799795311"] = "Der Web-Content-Reader zum Lesen von Webinhalten ist ausgeblendet"
|
||||
|
||||
-- Close
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGLEGALCHECK::T3448155331"] = "Schließen"
|
||||
|
||||
-- Web content reader is preselected
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGLEGALCHECK::T3641773985"] = "Tool zum Lesen von Webinhalten ist vorausgewählt"
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGLEGALCHECK::T3641773985"] = "Der Web-Content-Reader zum Lesen von Webinhalten ist vorausgewählt"
|
||||
|
||||
-- Preselect the content cleaner agent?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGLEGALCHECK::T3649428096"] = "Assistent zur Inhaltsbereinigungs vorauswählen?"
|
||||
@@ -3760,7 +3835,7 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGLEGALCHECK::T4004501
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGLEGALCHECK::T4033382756"] = "Assistent: Optionen für rechtliche Prüfung"
|
||||
|
||||
-- Preselect the web content reader?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGLEGALCHECK::T629158142"] = "Tool zum Lesen von Webinhalten vorauswählen?"
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGLEGALCHECK::T629158142"] = "Den Web-Content-Reader zum Lesen von Webinhalten vorauswählen?"
|
||||
|
||||
-- Would you like to preselect one of your profiles?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGMYTASKS::T2221665527"] = "Möchten Sie eines ihrer Profile vorauswählen?"
|
||||
@@ -3822,6 +3897,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T380451542
|
||||
-- Actions
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T3865031940"] = "Aktionen"
|
||||
|
||||
-- This profile is managed by your organization.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4058414654"] = "Dieses Profil wird von Ihrer Organisation verwaltet."
|
||||
|
||||
-- Store personal data about yourself in various profiles so that the AIs know your personal context. This saves you from having to explain your context each time, for example, in every chat. When you have different roles, you can create a profile for each role.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4125557797"] = "Speichern Sie persönliche Daten über sich in verschiedenen Profilen, damit die KIs ihren persönlichen Kontext kennen. So müssen Sie den Kontext nicht jedes Mal erneut erklären, zum Beispiel in jedem Chat. Wenn Sie verschiedene Rollen haben, können Sie für jede Rolle ein eigenes Profil anlegen."
|
||||
|
||||
@@ -3904,7 +3982,7 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSYNONYMS::T417092184
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T1013787967"] = "Agent zur Inhaltsbereinigung ist vorausgewählt"
|
||||
|
||||
-- Web content reader is shown
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T1030372436"] = "Tool zum Lesen von Webinhalten wird angezeigt"
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T1030372436"] = "Der Web-Content-Reader zum Lesen von Webinhalten wird angezeigt"
|
||||
|
||||
-- Preselect the summarizer complexity
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T104409170"] = "Wähle die Sprachkomplexität der Zusammenfassungs aus"
|
||||
@@ -3922,7 +4000,7 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T146
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T1633101895"] = "Wenn aktiviert, wird der Web-Content-Reader ausgeblendet und kann nicht verwendet werden. Dadurch wird die Benutzeroberfläche etwas einfacher zu bedienen."
|
||||
|
||||
-- Web content reader is not preselected
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T1701127912"] = "Das Tool zum Lesen von Webinhalten ist nicht vorab selektiert"
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T1701127912"] = "Der Web-Content-Reader zum Lesen von Webinhalten ist nicht vorab selektiert"
|
||||
|
||||
-- Assistant: Text Summarizer Options
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T1767527569"] = "Assistent: Optionen zur Textzusammenfassung"
|
||||
@@ -3952,7 +4030,7 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T344
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T3547337928"] = "Welche Zielsprache soll vorausgewählt werden?"
|
||||
|
||||
-- Web content reader is preselected
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T3641773985"] = "Tool zum Lesen von Webinhalten ist vorausgewählt"
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T3641773985"] = "Der Web-Content-Reader zum Lesen von Webinhalten ist vorausgewählt"
|
||||
|
||||
-- Preselect the content cleaner agent?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T3649428096"] = "Den Agenten zur Inhaltsbereinigungs vorauswählen?"
|
||||
@@ -3960,17 +4038,23 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T364
|
||||
-- When enabled, the content cleaner agent is preselected. This is might be useful when you prefer to clean up the content before summarize it.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T3660434400"] = "Wenn diese Option aktiviert ist, wird der Content Cleaner-Agent automatisch vorausgewählt. Das kann nützlich sein, wenn Sie den Inhalt bereinigen möchten, bevor Sie ihn zusammenfassen."
|
||||
|
||||
-- Preselect important aspects
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T3705987833"] = "Vorauswahl der Aspekte"
|
||||
|
||||
-- When enabled, you can preselect the text summarizer options. This is might be useful when you prefer a specific language, complexity, or LLM.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T3820844575"] = "Wenn aktiviert, können Sie die Optionen für die Textzusammenfassung im Voraus auswählen. Das kann hilfreich sein, wenn Sie eine bestimmte Sprache, einen bestimmten Schwierigkeitsgrad oder ein bestimmtes LLM bevorzugen."
|
||||
|
||||
-- Which summarizer complexity should be preselected?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T408530182"] = "Welche Komplexität der Zusammenfassung soll vorausgewählt werden?"
|
||||
|
||||
-- Preselect aspects for the LLM to focus on when generating a summary, such as summary length or specific topics to emphasize.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T414420518"] = "Vorausgewählte Aspekte, auf die sich das LLM beim Erstellen einer Zusammenfassung fokussieren soll, wie z.B. die Länge der Zusammenfassung oder bestimmte zu betonende Themen."
|
||||
|
||||
-- Preselect your expertise
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T51139714"] = "Wählen Sie Ihr Fachgebiet aus"
|
||||
|
||||
-- Preselect the web content reader?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T629158142"] = "Tool zum Lesen von Webinhalten vorauswählen?"
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T629158142"] = "Den Web-Content-Reader zum Lesen von Webinhalten vorauswählen?"
|
||||
|
||||
-- Content cleaner agent is preselected
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T1013787967"] = "Agent zur Inhaltsbereinigung ist vorausgewählt"
|
||||
@@ -3979,7 +4063,7 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T101378
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T1016384269"] = "Assistent: Übersetzer-Optionen"
|
||||
|
||||
-- Web content reader is shown
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T1030372436"] = "Tool zum Lesen von Webinhalten wird angezeigt"
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T1030372436"] = "Der Web-Content-Reader zum Lesen von Webinhalten wird angezeigt"
|
||||
|
||||
-- When enabled, you can preselect the translator options. This is might be useful when you prefer a specific target language or LLM model.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T1111006275"] = "Wenn diese Option aktiviert ist, können Sie die Übersetzungsoptionen im Voraus auswählen. Das ist nützlich, wenn Sie eine bestimmte Zielsprache oder ein bestimmtes LLM-Modell bevorzugen."
|
||||
@@ -3997,7 +4081,7 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T146229
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T1633101895"] = "Wenn aktiviert, wird der Web-Content-Reader ausgeblendet und kann nicht verwendet werden. Dadurch wird die Benutzeroberfläche etwas einfacher zu bedienen."
|
||||
|
||||
-- Web content reader is not preselected
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T1701127912"] = "Tool zum Lesen von Webinhalten ist nicht vorausgewählt"
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T1701127912"] = "Der Web-Content-Reader zum Lesen von Webinhalten ist nicht vorausgewählt"
|
||||
|
||||
-- Live translation is not preselected
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T1825690873"] = "Live-Übersetzung ist nicht vorausgewählt"
|
||||
@@ -4018,7 +4102,7 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T223453
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T2435743076"] = "Live-Übersetzung ist vorausgewählt"
|
||||
|
||||
-- Web content reader is hidden
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T2799795311"] = "Tool zum Lesen von Webinhalten ist ausgeblendet"
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T2799795311"] = "Der Web-Content-Reader zum Lesen von Webinhalten ist ausgeblendet"
|
||||
|
||||
-- No translator options are preselected
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T2866358796"] = "Keine Übersetzungseinstellungen sind vorausgewählt"
|
||||
@@ -4033,13 +4117,13 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T344815
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T3547337928"] = "Welche Zielsprache soll vorausgewählt werden?"
|
||||
|
||||
-- Web content reader is preselected
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T3641773985"] = "Tool zum Lesen von Webinhalten ist vorausgewählt"
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T3641773985"] = "Der Web-Content-Reader zum Lesen von Webinhalten ist vorausgewählt"
|
||||
|
||||
-- Preselect the content cleaner agent?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T3649428096"] = "Agent zur Inhaltsbereinigung vorauswählen?"
|
||||
|
||||
-- Preselect the web content reader?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T629158142"] = "Tool zum Lesen von Webinhalten vorauswählen?"
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T629158142"] = "Den Web-Content-Reader zum Lesen von Webinhalten vorauswählen?"
|
||||
|
||||
-- How fast should the live translation react?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTRANSLATION::T884246296"] = "Wie schnell soll die Live-Übersetzung reagieren?"
|
||||
@@ -4119,8 +4203,11 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGWRITINGEMAILS::T3832
|
||||
-- Preselect one of your profiles?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGWRITINGEMAILS::T4004501229"] = "Eines ihrer Profile vorauswählen?"
|
||||
|
||||
-- Chat name
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T1746586282"] = "Chat-Name"
|
||||
-- Please enter a value.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T3576780391"] = "Bitte geben Sie einen Wert ein."
|
||||
|
||||
-- Your Input
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T4030229154"] = "Ihre Eingabe"
|
||||
|
||||
-- Cancel
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T900713019"] = "Abbrechen"
|
||||
@@ -4575,6 +4662,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T1702902297"] = "Einführung"
|
||||
-- Vision
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T1892426825"] = "Vision"
|
||||
|
||||
-- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT5, o1, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using vLLM, llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2183503084"] = "Sie sind an keinen einzelnen Anbieter gebunden. Stattdessen können Sie den Anbieter wählen, der am besten zu ihren Bedürfnissen passt. Derzeit unterstützen wir OpenAI (GPT5, o1, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face und selbst gehostete Modelle mit vLLM, llama.cpp, ollama, LM Studio, Groq oder Fireworks. Für Wissenschaftler und Mitarbeiter von Forschungseinrichtungen unterstützen wir auch die KI-Dienste von Helmholtz und GWDG. Diese sind über föderierte Anmeldungen wie eduGAIN für alle 18 Helmholtz-Zentren, die Max-Planck-Gesellschaft, die meisten deutschen und viele internationale Universitäten verfügbar."
|
||||
|
||||
-- Let's get started
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2331588413"] = "Los geht's"
|
||||
|
||||
@@ -4584,9 +4674,6 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2348849647"] = "Letztes Änderungsproto
|
||||
-- Choose the provider and model best suited for your current task.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2588488920"] = "Wählen Sie den Anbieter und das Modell aus, die am besten zu ihrer aktuellen Aufgabe passen."
|
||||
|
||||
-- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT4o, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using vLLM, llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2900280782"] = "Sie sind an keinen einzelnen Anbieter gebunden. Stattdessen können Sie den Anbieter wählen, der am besten zu ihren Bedürfnissen passt. Derzeit unterstützen wir OpenAI (GPT5, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face und selbst gehostete Modelle mit vLLM, llama.cpp, ollama, LM Studio, Groq oder Fireworks. Für Wissenschaftler und Mitarbeiter von Forschungseinrichtungen unterstützen wir auch die KI-Dienste von Helmholtz und GWDG. Diese sind über föderierte Anmeldungen wie eduGAIN für alle 18 Helmholtz-Zentren, die Max-Planck-Gesellschaft, die meisten deutschen und viele internationale Universitäten verfügbar."
|
||||
|
||||
-- Quick Start Guide
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3002014720"] = "Schnellstart-Anleitung"
|
||||
|
||||
@@ -4746,8 +4833,8 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T1674355816"] = "Es wurde ver
|
||||
-- Tried to stream the LLM provider '{0}' answer. Was not able to read the stream. The message is: '{1}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T1856278860"] = "Der Versuch, die Antwort des LLM-Anbieters '{0}' zu streamen, ist fehlgeschlagen. Der Stream konnte nicht gelesen werden. Die Meldung lautet: '{1}'"
|
||||
|
||||
-- Tried to communicate with the LLM provider '{0}'. Even after {1} retries, there were some problems with the request. The provider message is: '{2}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2249520705"] = "Es wurde versucht, mit dem LLM-Anbieter '{0}' zu kommunizieren. Auch nach {1} Versuchen gab es Probleme mit der Anfrage. Die Nachricht des Anbieters lautet: '{2}'"
|
||||
-- Tried to communicate with the LLM provider '{0}'. Even after {1} retries, there were some problems with the request. The provider message is: '{2}'.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2181034173"] = "Versuchte, mit dem LLM-Anbieter '{0}' zu kommunizieren. Auch nach {1} Wiederholungsversuchen gab es Probleme mit der Anfrage. Die Meldung des Anbieters lautet: '{2}'."
|
||||
|
||||
-- Tried to communicate with the LLM provider '{0}'. Something was not found. The provider message is: '{1}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2780552614"] = "Es wurde versucht, mit dem LLM-Anbieter '{0}' zu kommunizieren. Etwas wurde nicht gefunden. Die Meldung des Anbieters lautet: '{1}'"
|
||||
@@ -4869,6 +4956,9 @@ UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T2128088682
|
||||
-- Navigation expands on mouse hover
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T2195945406"] = "Navigationsleiste erweitert sich, wenn sich die Maus darüber befindet"
|
||||
|
||||
-- Install updates manually
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T220653235"] = "Updates manuell installieren"
|
||||
|
||||
-- Also show features ready for release; these should be stable
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T2301448762"] = "Auch Funktionen anzeigen, die bereit für die Veröffentlichung sind; diese sollten stabil sein."
|
||||
|
||||
@@ -4908,6 +4998,9 @@ UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3137986690
|
||||
-- Disappearing chats: delete chats older than 180 days
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3491430707"] = "Selbstlöschende Chats: lösche Chats die älter als 180 Tage sind"
|
||||
|
||||
-- Install updates automatically
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3569059463"] = "Updates automatisch installieren"
|
||||
|
||||
-- Disable workspaces
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3612390107"] = "Arbeitsbereiche deaktivieren"
|
||||
|
||||
@@ -5283,6 +5376,18 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOC::T567205144"] = "Es scheint, dass Pando
|
||||
-- The latest Pandoc version was not found, installing version {0} instead.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOC::T726914939"] = "Die neueste Pandoc-Version wurde nicht gefunden, stattdessen wird Version {0} installiert."
|
||||
|
||||
-- Pandoc is required for Microsoft Word export.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T1473115556"] = "Pandoc wird für den Export nach Microsoft Word benötigt."
|
||||
|
||||
-- Pandoc Installation
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T185447014"] = "Pandoc-Installation"
|
||||
|
||||
-- Error during Microsoft Word export
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T3290596792"] = "Fehler beim Exportieren nach Microsoft Word"
|
||||
|
||||
-- Microsoft Word export successful
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T4256043333"] = "Export nach Microsoft Word erfolgreich"
|
||||
|
||||
-- The table AUTHORS does not exist or is using an invalid syntax.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINBASE::T1068328139"] = "Die Tabelle AUTHORS existiert nicht oder verwendet eine ungültige Syntax."
|
||||
|
||||
@@ -5442,9 +5547,6 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T1148682011
|
||||
-- The CONFIG table does not exist or is not a valid table.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T3331620576"] = "Die Tabelle CONFIG existiert nicht oder ist keine gültige Tabelle."
|
||||
|
||||
-- The LLM_PROVIDERS table does not exist or is not a valid table.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T806592324"] = "Die Tabelle LLM_PROVIDERS existiert nicht oder ist keine gültige Tabelle."
|
||||
|
||||
-- The field IETF_TAG does not exist or is not a valid string.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINLANGUAGE::T1796010240"] = "Das Feld IETF_TAG existiert nicht oder ist keine gültige Zeichenkette."
|
||||
|
||||
@@ -5571,6 +5673,15 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::SECRETS::T4007657575"]
|
||||
-- No update found.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::UPDATESERVICE::T1015418291"] = "Kein Update gefunden."
|
||||
|
||||
-- Failed to install update automatically. Please try again manually.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::UPDATESERVICE::T3709709946"] = "Fehler bei der automatischen Installation des Updates. Bitte versuchen Sie es manuell erneut."
|
||||
|
||||
-- Sources provided by the data providers
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SOURCEEXTENSIONS::T4174900468"] = "Von den Datenanbietern bereitgestellte Quellen"
|
||||
|
||||
-- Sources provided by the AI
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SOURCEEXTENSIONS::T4261248356"] = "Von der KI bereitgestellte Quellen"
|
||||
|
||||
-- The hostname is not a valid HTTP(S) URL.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::DATASOURCEVALIDATION::T1013354736"] = "Der Hostname ist keine gültige HTTP(S)-URL."
|
||||
|
||||
@@ -5673,5 +5784,8 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::PROVIDERVALIDATION::T497939286"] =
|
||||
-- Please select a model.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::PROVIDERVALIDATION::T818893091"] = "Bitte wählen Sie ein Modell aus."
|
||||
|
||||
-- Unnamed workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T1307384014"] = "Unbenannter Arbeitsbereich"
|
||||
|
||||
-- Delete Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T2244038752"] = "Chat löschen"
|
||||
+130
-16
@@ -1224,12 +1224,18 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::
|
||||
-- Target language
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T237828418"] = "Target language"
|
||||
|
||||
-- (Optional) Important Aspects
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T24391765"] = "(Optional) Important Aspects"
|
||||
|
||||
-- Summarize long text into a shorter version while retaining the main points. You might want to change the language of the summary to make it more readable. It is also possible to change the complexity of the summary to make it easy to understand.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T359929871"] = "Summarize long text into a shorter version while retaining the main points. You might want to change the language of the summary to make it more readable. It is also possible to change the complexity of the summary to make it easy to understand."
|
||||
|
||||
-- Please provide your field of expertise.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T3610378685"] = "Please provide your field of expertise."
|
||||
|
||||
-- (Optional) Specify aspects for the LLM to focus on when generating a summary, such as summary length or specific topics to emphasize.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T3830285347"] = "(Optional) Specify aspects for the LLM to focus on when generating a summary, such as summary length or specific topics to emphasize."
|
||||
|
||||
-- Custom target language
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::TEXTSUMMARIZER::ASSISTANTTEXTSUMMARIZER::T3848935911"] = "Custom target language"
|
||||
|
||||
@@ -1329,6 +1335,9 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1603883875"] = "Yes, re
|
||||
-- Yes, remove it
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1820166585"] = "Yes, remove it"
|
||||
|
||||
-- Number of sources
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1848978959"] = "Number of sources"
|
||||
|
||||
-- Do you really want to edit this message? In order to edit this message, the AI response will be deleted.
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T2018431076"] = "Do you really want to edit this message? In order to edit this message, the AI response will be deleted."
|
||||
|
||||
@@ -1356,6 +1365,9 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4070211974"] = "Remove
|
||||
-- No, keep it
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4188329028"] = "No, keep it"
|
||||
|
||||
-- Export Chat to Microsoft Word
|
||||
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T861873672"] = "Export Chat to Microsoft Word"
|
||||
|
||||
-- Open Settings
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T1172211894"] = "Open Settings"
|
||||
|
||||
@@ -1656,6 +1668,12 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROFILESELECTION::T918741365"] = "You can
|
||||
-- Provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T900237532"] = "Provider"
|
||||
|
||||
-- Pandoc Installation
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T185447014"] = "Pandoc Installation"
|
||||
|
||||
-- Pandoc may be required for importing files.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T2596465560"] = "Pandoc may be required for importing files."
|
||||
|
||||
-- Images are not supported yet
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T298062956"] = "Images are not supported yet"
|
||||
|
||||
@@ -1707,6 +1725,24 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENT::T3825586228"] = "Please p
|
||||
-- Show web content options
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENT::T4249712357"] = "Show web content options"
|
||||
|
||||
-- Loading
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T1404011351"] = "Loading"
|
||||
|
||||
-- Start
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T182978943"] = "Start"
|
||||
|
||||
-- Done
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T2379421585"] = "Done"
|
||||
|
||||
-- Parsing
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T3151033983"] = "Parsing"
|
||||
|
||||
-- Cleaning
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T3420573362"] = "Cleaning"
|
||||
|
||||
-- n/a
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENTSTEPSEXTENSIONS::T907272257"] = "n/a"
|
||||
|
||||
-- Hide content
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SECRETINPUTFIELD::T1273315904"] = "Hide content"
|
||||
|
||||
@@ -1830,6 +1866,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1907446663"]
|
||||
-- Language behavior
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T2341504363"] = "Language behavior"
|
||||
|
||||
-- Update installation method
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T237706157"] = "Update installation method"
|
||||
|
||||
-- Language
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T2591284123"] = "Language"
|
||||
|
||||
@@ -1860,6 +1899,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T602293588"]
|
||||
-- Choose the color theme that best suits for you.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T654667432"] = "Choose the color theme that best suits for you."
|
||||
|
||||
-- Should updates be installed automatically or manually?
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T707880477"] = "Should updates be installed automatically or manually?"
|
||||
|
||||
-- Energy saving is enabled
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T71162186"] = "Energy saving is enabled"
|
||||
|
||||
@@ -2115,6 +2157,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1016188706"] = "Are you sure
|
||||
-- Move chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1133040906"] = "Move chat"
|
||||
|
||||
-- Unnamed workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1307384014"] = "Unnamed workspace"
|
||||
|
||||
-- Delete
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1469573738"] = "Delete"
|
||||
|
||||
@@ -2148,6 +2193,12 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2237618267"] = "Are you sure
|
||||
-- Delete Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2244038752"] = "Delete Chat"
|
||||
|
||||
-- Please enter a chat name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2301651387"] = "Please enter a chat name."
|
||||
|
||||
-- Workspace Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2446263209"] = "Workspace Name"
|
||||
|
||||
-- Move to workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T2509305748"] = "Move to workspace"
|
||||
|
||||
@@ -2160,6 +2211,12 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3045856778"] = "Move Chat to
|
||||
-- Please enter a new or edit the name for your workspace '{0}':
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T323280982"] = "Please enter a new or edit the name for your workspace '{0}':"
|
||||
|
||||
-- Please enter a workspace name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3288132732"] = "Please enter a workspace name."
|
||||
|
||||
-- Unnamed chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3310482275"] = "Unnamed chat"
|
||||
|
||||
-- Rename
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3355849203"] = "Rename"
|
||||
|
||||
@@ -2172,6 +2229,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3555709365"] = "Load Chat"
|
||||
-- Add Workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3672981145"] = "Add Workspace"
|
||||
|
||||
-- Chat Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3891063690"] = "Chat Name"
|
||||
|
||||
-- Empty chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T4019509364"] = "Empty chat"
|
||||
|
||||
@@ -2925,6 +2985,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PANDOCDIALOG::T523908375"] = "Pandoc is dist
|
||||
-- Tell the AI what you want it to do for you. What are your goals or are you trying to achieve? Like having the AI address you informally.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T1458195391"] = "Tell the AI what you want it to do for you. What are your goals or are you trying to achieve? Like having the AI address you informally."
|
||||
|
||||
-- Please be aware that your profile info becomes part of the system prompt. This means it uses up context space — the “memory” the LLM uses to understand and respond to your request. If your profile is extremely long, the LLM may struggle to focus on your actual task.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T1717545317"] = "Please be aware that your profile info becomes part of the system prompt. This means it uses up context space — the “memory” the LLM uses to understand and respond to your request. If your profile is extremely long, the LLM may struggle to focus on your actual task."
|
||||
|
||||
-- Update
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T1847791252"] = "Update"
|
||||
|
||||
@@ -2937,18 +3000,12 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T2261456575"] = "What should
|
||||
-- Please enter a profile name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T2386844536"] = "Please enter a profile name."
|
||||
|
||||
-- The text must not exceed 256 characters.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T2560188276"] = "The text must not exceed 256 characters."
|
||||
|
||||
-- Add
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T2646845972"] = "Add"
|
||||
|
||||
-- The profile name must not exceed 40 characters.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3243902394"] = "The profile name must not exceed 40 characters."
|
||||
|
||||
-- The text must not exceed 444 characters.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3253349421"] = "The text must not exceed 444 characters."
|
||||
|
||||
-- Profile Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T3392578705"] = "Profile Name"
|
||||
|
||||
@@ -2973,9 +3030,15 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T900713019"] = "Cancel"
|
||||
-- The profile name must be unique; the chosen name is already in use.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T911748898"] = "The profile name must be unique; the chosen name is already in use."
|
||||
|
||||
-- Please be aware: This section is for experts only. You are responsible for verifying the correctness of the additional parameters you provide to the API call. By default, AI Studio uses the OpenAI-compatible chat completions API, when that it is supported by the underlying service and model.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1017509792"] = "Please be aware: This section is for experts only. You are responsible for verifying the correctness of the additional parameters you provide to the API call. By default, AI Studio uses the OpenAI-compatible chat completions API, when that it is supported by the underlying service and model."
|
||||
|
||||
-- Hugging Face Inference Provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1085481431"] = "Hugging Face Inference Provider"
|
||||
|
||||
-- Hide Expert Settings
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1108876344"] = "Hide Expert Settings"
|
||||
|
||||
-- Failed to store the API key in the operating system. The message was: {0}. Please try again.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1122745046"] = "Failed to store the API key in the operating system. The message was: {0}. Please try again."
|
||||
|
||||
@@ -2988,6 +3051,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1356621346"] = "Create acco
|
||||
-- Load models
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T15352225"] = "Load models"
|
||||
|
||||
-- Add the parameters in proper JSON formatting, e.g., "temperature": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1689135032"] = "Add the parameters in proper JSON formatting, e.g., \"temperature\": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though."
|
||||
|
||||
-- Hostname
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1727440780"] = "Hostname"
|
||||
|
||||
@@ -3009,12 +3075,18 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2331453405"] = "(Optional)
|
||||
-- Add
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2646845972"] = "Add"
|
||||
|
||||
-- Additional API parameters
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2728244552"] = "Additional API parameters"
|
||||
|
||||
-- No models loaded or available.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2810182573"] = "No models loaded or available."
|
||||
|
||||
-- Instance Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2842060373"] = "Instance Name"
|
||||
|
||||
-- Show Expert Settings
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3361153305"] = "Show Expert Settings"
|
||||
|
||||
-- Show available models
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3763891899"] = "Show available models"
|
||||
|
||||
@@ -3417,6 +3489,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T32678
|
||||
-- Close
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3448155331"] = "Close"
|
||||
|
||||
-- This template is managed by your organization.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3576775249"] = "This template is managed by your organization."
|
||||
|
||||
-- Edit Chat Template
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGCHATTEMPLATE::T3596030597"] = "Edit Chat Template"
|
||||
|
||||
@@ -3822,6 +3897,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T380451542
|
||||
-- Actions
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T3865031940"] = "Actions"
|
||||
|
||||
-- This profile is managed by your organization.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4058414654"] = "This profile is managed by your organization."
|
||||
|
||||
-- Store personal data about yourself in various profiles so that the AIs know your personal context. This saves you from having to explain your context each time, for example, in every chat. When you have different roles, you can create a profile for each role.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGPROFILES::T4125557797"] = "Store personal data about yourself in various profiles so that the AIs know your personal context. This saves you from having to explain your context each time, for example, in every chat. When you have different roles, you can create a profile for each role."
|
||||
|
||||
@@ -3960,12 +4038,18 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T364
|
||||
-- When enabled, the content cleaner agent is preselected. This is might be useful when you prefer to clean up the content before summarize it.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T3660434400"] = "When enabled, the content cleaner agent is preselected. This is might be useful when you prefer to clean up the content before summarize it."
|
||||
|
||||
-- Preselect important aspects
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T3705987833"] = "Preselect important aspects"
|
||||
|
||||
-- When enabled, you can preselect the text summarizer options. This is might be useful when you prefer a specific language, complexity, or LLM.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T3820844575"] = "When enabled, you can preselect the text summarizer options. This is might be useful when you prefer a specific language, complexity, or LLM."
|
||||
|
||||
-- Which summarizer complexity should be preselected?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T408530182"] = "Which summarizer complexity should be preselected?"
|
||||
|
||||
-- Preselect aspects for the LLM to focus on when generating a summary, such as summary length or specific topics to emphasize.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T414420518"] = "Preselect aspects for the LLM to focus on when generating a summary, such as summary length or specific topics to emphasize."
|
||||
|
||||
-- Preselect your expertise
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGTEXTSUMMARIZER::T51139714"] = "Preselect your expertise"
|
||||
|
||||
@@ -4119,8 +4203,11 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGWRITINGEMAILS::T3832
|
||||
-- Preselect one of your profiles?
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGWRITINGEMAILS::T4004501229"] = "Preselect one of your profiles?"
|
||||
|
||||
-- Chat name
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T1746586282"] = "Chat name"
|
||||
-- Please enter a value.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T3576780391"] = "Please enter a value."
|
||||
|
||||
-- Your Input
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T4030229154"] = "Your Input"
|
||||
|
||||
-- Cancel
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T900713019"] = "Cancel"
|
||||
@@ -4575,6 +4662,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T1702902297"] = "Introduction"
|
||||
-- Vision
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T1892426825"] = "Vision"
|
||||
|
||||
-- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT5, o1, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using vLLM, llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2183503084"] = "You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT5, o1, etc.), Perplexity, Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using vLLM, llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities."
|
||||
|
||||
-- Let's get started
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2331588413"] = "Let's get started"
|
||||
|
||||
@@ -4584,9 +4674,6 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2348849647"] = "Last Changelog"
|
||||
-- Choose the provider and model best suited for your current task.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2588488920"] = "Choose the provider and model best suited for your current task."
|
||||
|
||||
-- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT4o, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using vLLM, llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T2900280782"] = "You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (GPT5, o1, etc.), Mistral, Anthropic (Claude), Google Gemini, xAI (Grok), DeepSeek, Alibaba Cloud (Qwen), Hugging Face, and self-hosted models using vLLM, llama.cpp, ollama, LM Studio, Groq, or Fireworks. For scientists and employees of research institutions, we also support Helmholtz and GWDG AI services. These are available through federated logins like eduGAIN to all 18 Helmholtz Centers, the Max Planck Society, most German, and many international universities."
|
||||
|
||||
-- Quick Start Guide
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3002014720"] = "Quick Start Guide"
|
||||
|
||||
@@ -4746,8 +4833,8 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T1674355816"] = "Tried to com
|
||||
-- Tried to stream the LLM provider '{0}' answer. Was not able to read the stream. The message is: '{1}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T1856278860"] = "Tried to stream the LLM provider '{0}' answer. Was not able to read the stream. The message is: '{1}'"
|
||||
|
||||
-- Tried to communicate with the LLM provider '{0}'. Even after {1} retries, there were some problems with the request. The provider message is: '{2}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2249520705"] = "Tried to communicate with the LLM provider '{0}'. Even after {1} retries, there were some problems with the request. The provider message is: '{2}'"
|
||||
-- Tried to communicate with the LLM provider '{0}'. Even after {1} retries, there were some problems with the request. The provider message is: '{2}'.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2181034173"] = "Tried to communicate with the LLM provider '{0}'. Even after {1} retries, there were some problems with the request. The provider message is: '{2}'."
|
||||
|
||||
-- Tried to communicate with the LLM provider '{0}'. Something was not found. The provider message is: '{1}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T2780552614"] = "Tried to communicate with the LLM provider '{0}'. Something was not found. The provider message is: '{1}'"
|
||||
@@ -4869,6 +4956,9 @@ UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T2128088682
|
||||
-- Navigation expands on mouse hover
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T2195945406"] = "Navigation expands on mouse hover"
|
||||
|
||||
-- Install updates manually
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T220653235"] = "Install updates manually"
|
||||
|
||||
-- Also show features ready for release; these should be stable
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T2301448762"] = "Also show features ready for release; these should be stable"
|
||||
|
||||
@@ -4908,6 +4998,9 @@ UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3137986690
|
||||
-- Disappearing chats: delete chats older than 180 days
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3491430707"] = "Disappearing chats: delete chats older than 180 days"
|
||||
|
||||
-- Install updates automatically
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3569059463"] = "Install updates automatically"
|
||||
|
||||
-- Disable workspaces
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3612390107"] = "Disable workspaces"
|
||||
|
||||
@@ -5283,6 +5376,18 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOC::T567205144"] = "It seems that Pandoc i
|
||||
-- The latest Pandoc version was not found, installing version {0} instead.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOC::T726914939"] = "The latest Pandoc version was not found, installing version {0} instead."
|
||||
|
||||
-- Pandoc is required for Microsoft Word export.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T1473115556"] = "Pandoc is required for Microsoft Word export."
|
||||
|
||||
-- Pandoc Installation
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T185447014"] = "Pandoc Installation"
|
||||
|
||||
-- Error during Microsoft Word export
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T3290596792"] = "Error during Microsoft Word export"
|
||||
|
||||
-- Microsoft Word export successful
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T4256043333"] = "Microsoft Word export successful"
|
||||
|
||||
-- The table AUTHORS does not exist or is using an invalid syntax.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINBASE::T1068328139"] = "The table AUTHORS does not exist or is using an invalid syntax."
|
||||
|
||||
@@ -5442,9 +5547,6 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T1148682011
|
||||
-- The CONFIG table does not exist or is not a valid table.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T3331620576"] = "The CONFIG table does not exist or is not a valid table."
|
||||
|
||||
-- The LLM_PROVIDERS table does not exist or is not a valid table.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINCONFIGURATION::T806592324"] = "The LLM_PROVIDERS table does not exist or is not a valid table."
|
||||
|
||||
-- The field IETF_TAG does not exist or is not a valid string.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::PLUGINLANGUAGE::T1796010240"] = "The field IETF_TAG does not exist or is not a valid string."
|
||||
|
||||
@@ -5571,6 +5673,15 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::SECRETS::T4007657575"]
|
||||
-- No update found.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::UPDATESERVICE::T1015418291"] = "No update found."
|
||||
|
||||
-- Failed to install update automatically. Please try again manually.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::UPDATESERVICE::T3709709946"] = "Failed to install update automatically. Please try again manually."
|
||||
|
||||
-- Sources provided by the data providers
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SOURCEEXTENSIONS::T4174900468"] = "Sources provided by the data providers"
|
||||
|
||||
-- Sources provided by the AI
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SOURCEEXTENSIONS::T4261248356"] = "Sources provided by the AI"
|
||||
|
||||
-- The hostname is not a valid HTTP(S) URL.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::DATASOURCEVALIDATION::T1013354736"] = "The hostname is not a valid HTTP(S) URL."
|
||||
|
||||
@@ -5673,5 +5784,8 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::PROVIDERVALIDATION::T497939286"] =
|
||||
-- Please select a model.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::PROVIDERVALIDATION::T818893091"] = "Please select a model."
|
||||
|
||||
-- Unnamed workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T1307384014"] = "Unnamed workspace"
|
||||
|
||||
-- Delete Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T2244038752"] = "Delete Chat"
|
||||
@@ -9,8 +9,9 @@ using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider.AlibabaCloud;
|
||||
|
||||
public sealed class ProviderAlibabaCloud(ILogger logger) : BaseProvider("https://dashscope-intl.aliyuncs.com/compatible-mode/v1/", logger)
|
||||
public sealed class ProviderAlibabaCloud() : BaseProvider("https://dashscope-intl.aliyuncs.com/compatible-mode/v1/", LOGGER)
|
||||
{
|
||||
private static readonly ILogger<ProviderAlibabaCloud> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ProviderAlibabaCloud>();
|
||||
|
||||
#region Implementation of IProvider
|
||||
|
||||
@@ -21,7 +22,7 @@ public sealed class ProviderAlibabaCloud(ILogger logger) : BaseProvider("https:/
|
||||
public override string InstanceName { get; set; } = "AlibabaCloud";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
// Get the API key:
|
||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||
@@ -32,11 +33,14 @@ public sealed class ProviderAlibabaCloud(ILogger logger) : BaseProvider("https:/
|
||||
var systemPrompt = new Message
|
||||
{
|
||||
Role = "system",
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread, this.logger),
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
|
||||
};
|
||||
|
||||
// Parse the API parameters:
|
||||
var apiParameters = this.ParseAdditionalApiParameters();
|
||||
|
||||
// Prepare the AlibabaCloud HTTP chat request:
|
||||
var alibabaCloudChatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||
var alibabaCloudChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
||||
{
|
||||
Model = chatModel.Id,
|
||||
|
||||
@@ -62,6 +66,7 @@ public sealed class ProviderAlibabaCloud(ILogger logger) : BaseProvider("https:/
|
||||
}
|
||||
}).ToList()],
|
||||
Stream = true,
|
||||
AdditionalApiParameters = apiParameters
|
||||
}, JSON_SERIALIZER_OPTIONS);
|
||||
|
||||
async Task<HttpRequestMessage> RequestBuilder()
|
||||
@@ -77,7 +82,7 @@ public sealed class ProviderAlibabaCloud(ILogger logger) : BaseProvider("https:/
|
||||
return request;
|
||||
}
|
||||
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine>("AlibabaCloud", RequestBuilder, token))
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>("AlibabaCloud", RequestBuilder, token))
|
||||
yield return content;
|
||||
}
|
||||
|
||||
@@ -140,76 +145,6 @@ public sealed class ProviderAlibabaCloud(ILogger logger) : BaseProvider("https:/
|
||||
return this.LoadModels(["text-embedding-"], token, apiKeyProvisional).ContinueWith(t => t.Result.Concat(additionalModels).OrderBy(x => x.Id).AsEnumerable(), token);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IReadOnlyCollection<Capability> GetModelCapabilities(Model model)
|
||||
{
|
||||
var modelName = model.Id.ToLowerInvariant().AsSpan();
|
||||
|
||||
// Qwen models:
|
||||
if (modelName.StartsWith("qwen"))
|
||||
{
|
||||
// Check for omni models:
|
||||
if (modelName.IndexOf("omni") is not -1)
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.AUDIO_INPUT, Capability.SPEECH_INPUT,
|
||||
Capability.VIDEO_INPUT,
|
||||
|
||||
Capability.TEXT_OUTPUT, Capability.SPEECH_OUTPUT
|
||||
];
|
||||
|
||||
// Check for Qwen 3:
|
||||
if(modelName.StartsWith("qwen3"))
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING
|
||||
];
|
||||
|
||||
if(modelName.IndexOf("-vl-") is not -1)
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
];
|
||||
}
|
||||
|
||||
// QwQ models:
|
||||
if (modelName.StartsWith("qwq"))
|
||||
{
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING
|
||||
];
|
||||
}
|
||||
|
||||
// QVQ models:
|
||||
if (modelName.StartsWith("qvq"))
|
||||
{
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.ALWAYS_REASONING
|
||||
];
|
||||
}
|
||||
|
||||
// Default to text input and output:
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING
|
||||
];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using AIStudio.Provider.OpenAI;
|
||||
|
||||
namespace AIStudio.Provider.Anthropic;
|
||||
@@ -16,4 +17,9 @@ public readonly record struct ChatRequest(
|
||||
int MaxTokens,
|
||||
bool Stream,
|
||||
string System
|
||||
);
|
||||
)
|
||||
{
|
||||
// Attention: The "required" modifier is not supported for [JsonExtensionData].
|
||||
[JsonExtensionData]
|
||||
public IDictionary<string, object> AdditionalApiParameters { get; init; } = new Dictionary<string, object>();
|
||||
}
|
||||
@@ -9,8 +9,10 @@ using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider.Anthropic;
|
||||
|
||||
public sealed class ProviderAnthropic(ILogger logger) : BaseProvider("https://api.anthropic.com/v1/", logger)
|
||||
public sealed class ProviderAnthropic() : BaseProvider("https://api.anthropic.com/v1/", LOGGER)
|
||||
{
|
||||
private static readonly ILogger<ProviderAnthropic> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ProviderAnthropic>();
|
||||
|
||||
#region Implementation of IProvider
|
||||
|
||||
public override string Id => LLMProviders.ANTHROPIC.ToName();
|
||||
@@ -18,12 +20,15 @@ public sealed class ProviderAnthropic(ILogger logger) : BaseProvider("https://ap
|
||||
public override string InstanceName { get; set; } = "Anthropic";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
// Get the API key:
|
||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||
if(!requestedSecret.Success)
|
||||
yield break;
|
||||
|
||||
// Parse the API parameters:
|
||||
var apiParameters = this.ParseAdditionalApiParameters("system");
|
||||
|
||||
// Prepare the Anthropic HTTP chat request:
|
||||
var chatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||
@@ -49,11 +54,12 @@ public sealed class ProviderAnthropic(ILogger logger) : BaseProvider("https://ap
|
||||
}
|
||||
}).ToList()],
|
||||
|
||||
System = chatThread.PrepareSystemPrompt(settingsManager, chatThread, this.logger),
|
||||
MaxTokens = 4_096,
|
||||
System = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
|
||||
MaxTokens = apiParameters.TryGetValue("max_tokens", out var value) && value is int intValue ? intValue : 4_096,
|
||||
|
||||
// Right now, we only support streaming completions:
|
||||
Stream = true,
|
||||
AdditionalApiParameters = apiParameters
|
||||
}, JSON_SERIALIZER_OPTIONS);
|
||||
|
||||
async Task<HttpRequestMessage> RequestBuilder()
|
||||
@@ -72,7 +78,7 @@ public sealed class ProviderAnthropic(ILogger logger) : BaseProvider("https://ap
|
||||
return request;
|
||||
}
|
||||
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine>("Anthropic", RequestBuilder, token))
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine, NoChatCompletionAnnotationStreamLine>("Anthropic", RequestBuilder, token))
|
||||
yield return content;
|
||||
}
|
||||
|
||||
@@ -111,41 +117,6 @@ public sealed class ProviderAnthropic(ILogger logger) : BaseProvider("https://ap
|
||||
{
|
||||
return Task.FromResult(Enumerable.Empty<Model>());
|
||||
}
|
||||
|
||||
public override IReadOnlyCollection<Capability> GetModelCapabilities(Model model)
|
||||
{
|
||||
var modelName = model.Id.ToLowerInvariant().AsSpan();
|
||||
|
||||
// Claude 4.x models:
|
||||
if(modelName.StartsWith("claude-opus-4") || modelName.StartsWith("claude-sonnet-4"))
|
||||
return [
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING];
|
||||
|
||||
// Claude 3.7 is able to do reasoning:
|
||||
if(modelName.StartsWith("claude-3-7"))
|
||||
return [
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING];
|
||||
|
||||
// All other 3.x models are able to process text and images as input:
|
||||
if(modelName.StartsWith("claude-3-"))
|
||||
return [
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING];
|
||||
|
||||
// Any other model is able to process text only:
|
||||
return [
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
Capability.FUNCTION_CALLING];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -13,7 +13,22 @@ public readonly record struct ResponseStreamLine(string Type, int Index, Delta D
|
||||
public bool ContainsContent() => this != default && !string.IsNullOrWhiteSpace(this.Delta.Text);
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetContent() => this.Delta.Text;
|
||||
public ContentStreamChunk GetContent() => new(this.Delta.Text, []);
|
||||
|
||||
#region Implementation of IAnnotationStreamLine
|
||||
|
||||
//
|
||||
// Please note: Anthropic's API does not currently support sources in their
|
||||
// OpenAI-compatible response stream.
|
||||
//
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ContainsSources() => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IList<ISource> GetSources() => [];
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Provider.OpenAI;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
using AIStudio.Tools.Services;
|
||||
@@ -24,7 +25,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
/// <summary>
|
||||
/// The logger to use.
|
||||
/// </summary>
|
||||
protected readonly ILogger logger;
|
||||
private readonly ILogger logger;
|
||||
|
||||
static BaseProvider()
|
||||
{
|
||||
@@ -39,16 +40,18 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
protected static readonly JsonSerializerOptions JSON_SERIALIZER_OPTIONS = new()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
|
||||
Converters = { new AnnotationConverter() },
|
||||
AllowTrailingCommas = false
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for the base provider.
|
||||
/// </summary>
|
||||
/// <param name="url">The base URL for the provider.</param>
|
||||
/// <param name="loggerService">The logger service to use.</param>
|
||||
protected BaseProvider(string url, ILogger loggerService)
|
||||
/// <param name="logger">The logger to use.</param>
|
||||
protected BaseProvider(string url, ILogger logger)
|
||||
{
|
||||
this.logger = loggerService;
|
||||
this.logger = logger;
|
||||
|
||||
// Set the base URL:
|
||||
this.httpClient.BaseAddress = new(url);
|
||||
@@ -61,9 +64,12 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract string InstanceName { get; set; }
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, CancellationToken token = default);
|
||||
public string AdditionalJsonApiParameters { get; init; } = string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, CancellationToken token = default);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract IAsyncEnumerable<ImageURL> StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, CancellationToken token = default);
|
||||
@@ -76,9 +82,6 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract Task<IEnumerable<Model>> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract IReadOnlyCollection<Capability> GetModelCapabilities(Model model);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -96,7 +99,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
/// <param name="requestBuilder">A function that builds the request.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The status object of the request.</returns>
|
||||
protected async Task<HttpRateLimitedStreamResult> SendRequest(Func<Task<HttpRequestMessage>> requestBuilder, CancellationToken token = default)
|
||||
private async Task<HttpRateLimitedStreamResult> SendRequest(Func<Task<HttpRequestMessage>> requestBuilder, CancellationToken token = default)
|
||||
{
|
||||
const int MAX_RETRIES = 6;
|
||||
const double RETRY_DELAY_SECONDS = 4;
|
||||
@@ -123,10 +126,11 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
break;
|
||||
}
|
||||
|
||||
var errorBody = await nextResponse.Content.ReadAsStringAsync(token);
|
||||
if (nextResponse.StatusCode is HttpStatusCode.Forbidden)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Block, string.Format(TB("Tried to communicate with the LLM provider '{0}'. You might not be able to use this provider from your location. The provider message is: '{1}'"), this.InstanceName, nextResponse.ReasonPhrase)));
|
||||
this.logger.LogError($"Failed request with status code {nextResponse.StatusCode} (message = '{nextResponse.ReasonPhrase}').");
|
||||
this.logger.LogError("Failed request with status code {ResposeStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
||||
errorMessage = nextResponse.ReasonPhrase;
|
||||
break;
|
||||
}
|
||||
@@ -134,7 +138,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
if(nextResponse.StatusCode is HttpStatusCode.BadRequest)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("Tried to communicate with the LLM provider '{0}'. The required message format might be changed. The provider message is: '{1}'"), this.InstanceName, nextResponse.ReasonPhrase)));
|
||||
this.logger.LogError($"Failed request with status code {nextResponse.StatusCode} (message = '{nextResponse.ReasonPhrase}').");
|
||||
this.logger.LogError("Failed request with status code {ResposeStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
||||
errorMessage = nextResponse.ReasonPhrase;
|
||||
break;
|
||||
}
|
||||
@@ -142,7 +146,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
if(nextResponse.StatusCode is HttpStatusCode.NotFound)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("Tried to communicate with the LLM provider '{0}'. Something was not found. The provider message is: '{1}'"), this.InstanceName, nextResponse.ReasonPhrase)));
|
||||
this.logger.LogError($"Failed request with status code {nextResponse.StatusCode} (message = '{nextResponse.ReasonPhrase}').");
|
||||
this.logger.LogError("Failed request with status code {ResposeStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
||||
errorMessage = nextResponse.ReasonPhrase;
|
||||
break;
|
||||
}
|
||||
@@ -150,7 +154,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
if(nextResponse.StatusCode is HttpStatusCode.Unauthorized)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Key, string.Format(TB("Tried to communicate with the LLM provider '{0}'. The API key might be invalid. The provider message is: '{1}'"), this.InstanceName, nextResponse.ReasonPhrase)));
|
||||
this.logger.LogError($"Failed request with status code {nextResponse.StatusCode} (message = '{nextResponse.ReasonPhrase}').");
|
||||
this.logger.LogError("Failed request with status code {ResposeStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
||||
errorMessage = nextResponse.ReasonPhrase;
|
||||
break;
|
||||
}
|
||||
@@ -158,7 +162,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
if(nextResponse.StatusCode is HttpStatusCode.InternalServerError)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("Tried to communicate with the LLM provider '{0}'. The server might be down or having issues. The provider message is: '{1}'"), this.InstanceName, nextResponse.ReasonPhrase)));
|
||||
this.logger.LogError($"Failed request with status code {nextResponse.StatusCode} (message = '{nextResponse.ReasonPhrase}').");
|
||||
this.logger.LogError("Failed request with status code {ResposeStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
||||
errorMessage = nextResponse.ReasonPhrase;
|
||||
break;
|
||||
}
|
||||
@@ -166,7 +170,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
if(nextResponse.StatusCode is HttpStatusCode.ServiceUnavailable)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("Tried to communicate with the LLM provider '{0}'. The provider is overloaded. The message is: '{1}'"), this.InstanceName, nextResponse.ReasonPhrase)));
|
||||
this.logger.LogError($"Failed request with status code {nextResponse.StatusCode} (message = '{nextResponse.ReasonPhrase}').");
|
||||
this.logger.LogError("Failed request with status code {ResposeStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
|
||||
errorMessage = nextResponse.ReasonPhrase;
|
||||
break;
|
||||
}
|
||||
@@ -176,21 +180,33 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
if(timeSeconds > 90)
|
||||
timeSeconds = 90;
|
||||
|
||||
this.logger.LogDebug($"Failed request with status code {nextResponse.StatusCode} (message = '{errorMessage}'). Retrying in {timeSeconds:0.00} seconds.");
|
||||
this.logger.LogDebug("Failed request with status code {ResponseStatusCode} (message = '{ErrorMessage}'). Retrying in {TimeSeconds:0.00} seconds.", nextResponse.StatusCode, errorMessage, timeSeconds);
|
||||
await Task.Delay(TimeSpan.FromSeconds(timeSeconds), token);
|
||||
}
|
||||
|
||||
if(retry >= MAX_RETRIES || !string.IsNullOrWhiteSpace(errorMessage))
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("Tried to communicate with the LLM provider '{0}'. Even after {1} retries, there were some problems with the request. The provider message is: '{2}'"), this.InstanceName, MAX_RETRIES, errorMessage)));
|
||||
await MessageBus.INSTANCE.SendError(new DataErrorMessage(Icons.Material.Filled.CloudOff, string.Format(TB("Tried to communicate with the LLM provider '{0}'. Even after {1} retries, there were some problems with the request. The provider message is: '{2}'."), this.InstanceName, MAX_RETRIES, errorMessage)));
|
||||
return new HttpRateLimitedStreamResult(false, true, errorMessage ?? $"Failed after {MAX_RETRIES} retries; no provider message available", response);
|
||||
}
|
||||
|
||||
return new HttpRateLimitedStreamResult(true, false, string.Empty, response);
|
||||
}
|
||||
|
||||
protected async IAsyncEnumerable<string> StreamChatCompletionInternal<T>(string providerName, Func<Task<HttpRequestMessage>> requestBuilder, [EnumeratorCancellation] CancellationToken token = default) where T : struct, IResponseStreamLine
|
||||
/// <summary>
|
||||
/// Streams the chat completion from the provider using the Chat Completion API.
|
||||
/// </summary>
|
||||
/// <param name="providerName">The name of the provider.</param>
|
||||
/// <param name="requestBuilder">A function that builds the request.</param>
|
||||
/// <param name="token">The cancellation token to use.</param>
|
||||
/// <typeparam name="TDelta">The type of the delta lines inside the stream.</typeparam>
|
||||
/// <typeparam name="TAnnotation">The type of the annotation lines inside the stream.</typeparam>
|
||||
/// <returns>The stream of content chunks.</returns>
|
||||
protected async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletionInternal<TDelta, TAnnotation>(string providerName, Func<Task<HttpRequestMessage>> requestBuilder, [EnumeratorCancellation] CancellationToken token = default) where TDelta : IResponseStreamLine where TAnnotation : IAnnotationStreamLine
|
||||
{
|
||||
// Check if annotations are supported:
|
||||
var annotationSupported = typeof(TAnnotation) != typeof(NoResponsesAnnotationStreamLine) && typeof(TAnnotation) != typeof(NoChatCompletionAnnotationStreamLine);
|
||||
|
||||
StreamReader? streamReader = null;
|
||||
try
|
||||
{
|
||||
@@ -217,7 +233,9 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
if (streamReader is null)
|
||||
yield break;
|
||||
|
||||
//
|
||||
// Read the stream, line by line:
|
||||
//
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
@@ -240,7 +258,9 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
yield break;
|
||||
}
|
||||
|
||||
//
|
||||
// Read the next line:
|
||||
//
|
||||
string? line;
|
||||
try
|
||||
{
|
||||
@@ -266,30 +286,297 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
if (line.StartsWith("data: [DONE]", StringComparison.InvariantCulture))
|
||||
yield break;
|
||||
|
||||
T providerResponse;
|
||||
try
|
||||
//
|
||||
// Process annotation lines:
|
||||
//
|
||||
if (annotationSupported && line.Contains("""
|
||||
"annotations":[
|
||||
""", StringComparison.InvariantCulture))
|
||||
{
|
||||
// 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..];
|
||||
TAnnotation? providerResponse;
|
||||
|
||||
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:
|
||||
providerResponse = JsonSerializer.Deserialize<T>(jsonData, JSON_SERIALIZER_OPTIONS);
|
||||
// Deserialize the JSON data:
|
||||
providerResponse = JsonSerializer.Deserialize<TAnnotation>(jsonData, JSON_SERIALIZER_OPTIONS);
|
||||
|
||||
if (providerResponse is null)
|
||||
continue;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Skip invalid JSON data:
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip empty responses:
|
||||
if (!providerResponse.ContainsSources())
|
||||
continue;
|
||||
|
||||
// Yield the response:
|
||||
yield return new(string.Empty, providerResponse.GetSources());
|
||||
}
|
||||
catch
|
||||
|
||||
//
|
||||
// Process delta lines:
|
||||
//
|
||||
else
|
||||
{
|
||||
// Skip invalid JSON data:
|
||||
continue;
|
||||
TDelta? providerResponse;
|
||||
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:
|
||||
providerResponse = JsonSerializer.Deserialize<TDelta>(jsonData, JSON_SERIALIZER_OPTIONS);
|
||||
|
||||
if (providerResponse is null)
|
||||
continue;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Skip invalid JSON data:
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip empty responses:
|
||||
if (!providerResponse.ContainsContent())
|
||||
continue;
|
||||
|
||||
// Yield the response:
|
||||
yield return providerResponse.GetContent();
|
||||
}
|
||||
|
||||
// Skip empty responses:
|
||||
if (!providerResponse.ContainsContent())
|
||||
continue;
|
||||
|
||||
// Yield the response:
|
||||
yield return providerResponse.GetContent();
|
||||
}
|
||||
|
||||
streamReader.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Streams the chat completion from the provider using the Responses API.
|
||||
/// </summary>
|
||||
/// <param name="providerName">The name of the provider.</param>
|
||||
/// <param name="requestBuilder">A function that builds the request.</param>
|
||||
/// <param name="token">The cancellation token to use.</param>
|
||||
/// <typeparam name="TDelta">The type of the delta lines inside the stream.</typeparam>
|
||||
/// <typeparam name="TAnnotation">The type of the annotation lines inside the stream.</typeparam>
|
||||
/// <returns>The stream of content chunks.</returns>
|
||||
protected async IAsyncEnumerable<ContentStreamChunk> StreamResponsesInternal<TDelta, TAnnotation>(string providerName, Func<Task<HttpRequestMessage>> requestBuilder, [EnumeratorCancellation] CancellationToken token = default) where TDelta : IResponseStreamLine where TAnnotation : IAnnotationStreamLine
|
||||
{
|
||||
// Check if annotations are supported:
|
||||
var annotationSupported = typeof(TAnnotation) != typeof(NoResponsesAnnotationStreamLine) && typeof(TAnnotation) != typeof(NoChatCompletionAnnotationStreamLine);
|
||||
|
||||
StreamReader? streamReader = null;
|
||||
try
|
||||
{
|
||||
// Send the request using exponential backoff:
|
||||
var responseData = await this.SendRequest(requestBuilder, token);
|
||||
if(responseData.IsFailedAfterAllRetries)
|
||||
{
|
||||
this.logger.LogError($"The {providerName} responses call failed: {responseData.ErrorMessage}");
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Open the response stream:
|
||||
var providerStream = await responseData.Response!.Content.ReadAsStreamAsync(token);
|
||||
|
||||
// Add a stream reader to read the stream, line by line:
|
||||
streamReader = new StreamReader(providerStream);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Stream, string.Format(TB("Tried to communicate with the LLM provider '{0}'. There were some problems with the request. The provider message is: '{1}'"), this.InstanceName, e.Message)));
|
||||
this.logger.LogError($"Failed to stream responses from {providerName} '{this.InstanceName}': {e.Message}");
|
||||
}
|
||||
|
||||
if (streamReader is null)
|
||||
yield break;
|
||||
|
||||
//
|
||||
// Read the stream, line by line:
|
||||
//
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(streamReader.EndOfStream)
|
||||
break;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Stream, string.Format(TB("Tried to stream the LLM provider '{0}' answer. There were some problems with the stream. The message is: '{1}'"), this.InstanceName, e.Message)));
|
||||
this.logger.LogWarning($"Failed to read the end-of-stream state from {providerName} '{this.InstanceName}': {e.Message}");
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if the token is canceled:
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
this.logger.LogWarning($"The user canceled the responses for {providerName} '{this.InstanceName}'.");
|
||||
streamReader.Close();
|
||||
yield break;
|
||||
}
|
||||
|
||||
//
|
||||
// Read the next line:
|
||||
//
|
||||
string? line;
|
||||
try
|
||||
{
|
||||
line = await streamReader.ReadLineAsync(token);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Stream, string.Format(TB("Tried to stream the LLM provider '{0}' answer. Was not able to read the stream. The message is: '{1}'"), this.InstanceName, e.Message)));
|
||||
this.logger.LogError($"Failed to read the stream from {providerName} '{this.InstanceName}': {e.Message}");
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip empty lines:
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
continue;
|
||||
|
||||
// Check if the line is the end of the stream:
|
||||
if (line.StartsWith("event: response.completed", StringComparison.InvariantCulture))
|
||||
yield break;
|
||||
|
||||
//
|
||||
// Find delta lines:
|
||||
//
|
||||
if (line.StartsWith("""
|
||||
data: {"type":"response.output_text.delta"
|
||||
""", StringComparison.InvariantCulture))
|
||||
{
|
||||
TDelta? providerResponse;
|
||||
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:
|
||||
providerResponse = JsonSerializer.Deserialize<TDelta>(jsonData, JSON_SERIALIZER_OPTIONS);
|
||||
|
||||
if (providerResponse is null)
|
||||
continue;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Skip invalid JSON data:
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip empty responses:
|
||||
if (!providerResponse.ContainsContent())
|
||||
continue;
|
||||
|
||||
// Yield the response:
|
||||
yield return providerResponse.GetContent();
|
||||
}
|
||||
|
||||
//
|
||||
// Find annotation added lines:
|
||||
//
|
||||
else if (annotationSupported && line.StartsWith(
|
||||
"""
|
||||
data: {"type":"response.output_text.annotation.added"
|
||||
""", StringComparison.InvariantCulture))
|
||||
{
|
||||
TAnnotation? providerResponse;
|
||||
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:
|
||||
providerResponse = JsonSerializer.Deserialize<TAnnotation>(jsonData, JSON_SERIALIZER_OPTIONS);
|
||||
|
||||
if (providerResponse is null)
|
||||
continue;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Skip invalid JSON data:
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip empty responses:
|
||||
if (!providerResponse.ContainsSources())
|
||||
continue;
|
||||
|
||||
// Yield the response:
|
||||
yield return new(string.Empty, providerResponse.GetSources());
|
||||
}
|
||||
}
|
||||
|
||||
streamReader.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse and convert API parameters from a provided JSON string into a dictionary,
|
||||
/// optionally merging additional parameters and removing specific keys.
|
||||
/// </summary>
|
||||
/// <param name="keysToRemove">Optional list of keys to remove from the final dictionary
|
||||
/// (case-insensitive). The parameters stream, model, and messages are removed by default.</param>
|
||||
protected IDictionary<string, object> ParseAdditionalApiParameters(
|
||||
params List<string> keysToRemove)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(this.AdditionalJsonApiParameters))
|
||||
return new Dictionary<string, object>();
|
||||
|
||||
try
|
||||
{
|
||||
// Wrap the user-provided parameters in curly brackets to form a valid JSON object:
|
||||
var json = $"{{{this.AdditionalJsonApiParameters}}}";
|
||||
var jsonDoc = JsonSerializer.Deserialize<JsonElement>(json, JSON_SERIALIZER_OPTIONS);
|
||||
var dict = ConvertToDictionary(jsonDoc);
|
||||
|
||||
// Some keys are always removed because we set them:
|
||||
keysToRemove.Add("stream");
|
||||
keysToRemove.Add("model");
|
||||
keysToRemove.Add("messages");
|
||||
|
||||
// Remove the specified keys (case-insensitive):
|
||||
var removeSet = new HashSet<string>(keysToRemove, StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var key in removeSet)
|
||||
dict.Remove(key);
|
||||
|
||||
return dict;
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
this.logger.LogError("Failed to parse additional API parameters: {ExceptionMessage}", ex.Message);
|
||||
return new Dictionary<string, object>();
|
||||
}
|
||||
}
|
||||
|
||||
private static IDictionary<string, object> ConvertToDictionary(JsonElement element)
|
||||
{
|
||||
return element.EnumerateObject()
|
||||
.ToDictionary<JsonProperty, string, object>(
|
||||
p => p.Name,
|
||||
p => ConvertJsonValue(p.Value) ?? string.Empty
|
||||
);
|
||||
}
|
||||
|
||||
private static object? ConvertJsonValue(JsonElement element) => element.ValueKind switch
|
||||
{
|
||||
JsonValueKind.String => element.GetString(),
|
||||
JsonValueKind.Number => element.TryGetInt32(out var i) ? i :
|
||||
element.TryGetInt64(out var l) ? l :
|
||||
element.TryGetDouble(out var d) ? d :
|
||||
element.GetDecimal(),
|
||||
JsonValueKind.True or JsonValueKind.False => element.GetBoolean(),
|
||||
JsonValueKind.Null => string.Empty,
|
||||
JsonValueKind.Object => ConvertToDictionary(element),
|
||||
JsonValueKind.Array => element.EnumerateArray().Select(ConvertJsonValue).ToList(),
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
@@ -94,4 +94,19 @@ public enum Capability
|
||||
/// The AI model can perform function calling, such as invoking APIs or executing functions.
|
||||
/// </summary>
|
||||
FUNCTION_CALLING,
|
||||
|
||||
/// <summary>
|
||||
/// The AI model can perform web search to retrieve information from the internet.
|
||||
/// </summary>
|
||||
WEB_SEARCH,
|
||||
|
||||
/// <summary>
|
||||
/// The AI model is used via the Chat Completion API.
|
||||
/// </summary>
|
||||
CHAT_COMPLETION_API,
|
||||
|
||||
/// <summary>
|
||||
/// The AI model is used via the Responses API.
|
||||
/// </summary>
|
||||
RESPONSES_API,
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace AIStudio.Provider;
|
||||
|
||||
/// <summary>
|
||||
/// A chunk of content from a content stream, along with its associated sources.
|
||||
/// </summary>
|
||||
/// <param name="Content">The text content of the chunk.</param>
|
||||
/// <param name="Sources">The list of sources associated with the chunk.</param>
|
||||
public sealed record ContentStreamChunk(string Content, IList<ISource> Sources)
|
||||
{
|
||||
/// <summary>
|
||||
/// Implicit conversion to string.
|
||||
/// </summary>
|
||||
/// <param name="chunk">The content stream chunk.</param>
|
||||
/// <returns>The text content of the chunk.</returns>
|
||||
public static implicit operator string(ContentStreamChunk chunk) => chunk.Content;
|
||||
}
|
||||
@@ -9,8 +9,10 @@ using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider.DeepSeek;
|
||||
|
||||
public sealed class ProviderDeepSeek(ILogger logger) : BaseProvider("https://api.deepseek.com/", logger)
|
||||
public sealed class ProviderDeepSeek() : BaseProvider("https://api.deepseek.com/", LOGGER)
|
||||
{
|
||||
private static readonly ILogger<ProviderDeepSeek> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ProviderDeepSeek>();
|
||||
|
||||
#region Implementation of IProvider
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -20,7 +22,7 @@ public sealed class ProviderDeepSeek(ILogger logger) : BaseProvider("https://api
|
||||
public override string InstanceName { get; set; } = "DeepSeek";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
// Get the API key:
|
||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||
@@ -31,11 +33,14 @@ public sealed class ProviderDeepSeek(ILogger logger) : BaseProvider("https://api
|
||||
var systemPrompt = new Message
|
||||
{
|
||||
Role = "system",
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread, this.logger),
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
|
||||
};
|
||||
|
||||
// Parse the API parameters:
|
||||
var apiParameters = this.ParseAdditionalApiParameters();
|
||||
|
||||
// Prepare the DeepSeek HTTP chat request:
|
||||
var deepSeekChatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||
var deepSeekChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
||||
{
|
||||
Model = chatModel.Id,
|
||||
|
||||
@@ -61,6 +66,7 @@ public sealed class ProviderDeepSeek(ILogger logger) : BaseProvider("https://api
|
||||
}
|
||||
}).ToList()],
|
||||
Stream = true,
|
||||
AdditionalApiParameters = apiParameters
|
||||
}, JSON_SERIALIZER_OPTIONS);
|
||||
|
||||
async Task<HttpRequestMessage> RequestBuilder()
|
||||
@@ -76,7 +82,7 @@ public sealed class ProviderDeepSeek(ILogger logger) : BaseProvider("https://api
|
||||
return request;
|
||||
}
|
||||
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine>("DeepSeek", RequestBuilder, token))
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>("DeepSeek", RequestBuilder, token))
|
||||
yield return content;
|
||||
}
|
||||
|
||||
@@ -106,26 +112,6 @@ public sealed class ProviderDeepSeek(ILogger logger) : BaseProvider("https://api
|
||||
return Task.FromResult(Enumerable.Empty<Model>());
|
||||
}
|
||||
|
||||
public override IReadOnlyCollection<Capability> GetModelCapabilities(Model model)
|
||||
{
|
||||
var modelName = model.Id.ToLowerInvariant().AsSpan();
|
||||
|
||||
if(modelName.IndexOf("reasoner") is not -1)
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.ALWAYS_REASONING,
|
||||
];
|
||||
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AIStudio.Provider.Fireworks;
|
||||
|
||||
/// <summary>
|
||||
@@ -10,4 +12,9 @@ public readonly record struct ChatRequest(
|
||||
string Model,
|
||||
IList<Message> Messages,
|
||||
bool Stream
|
||||
);
|
||||
)
|
||||
{
|
||||
// Attention: The "required" modifier is not supported for [JsonExtensionData].
|
||||
[JsonExtensionData]
|
||||
public IDictionary<string, object> AdditionalApiParameters { get; init; } = new Dictionary<string, object>();
|
||||
}
|
||||
@@ -4,12 +4,15 @@ using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Provider.OpenAI;
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider.Fireworks;
|
||||
|
||||
public class ProviderFireworks(ILogger logger) : BaseProvider("https://api.fireworks.ai/inference/v1/", logger)
|
||||
public class ProviderFireworks() : BaseProvider("https://api.fireworks.ai/inference/v1/", LOGGER)
|
||||
{
|
||||
private static readonly ILogger<ProviderFireworks> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ProviderFireworks>();
|
||||
|
||||
#region Implementation of IProvider
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -19,7 +22,7 @@ public class ProviderFireworks(ILogger logger) : BaseProvider("https://api.firew
|
||||
public override string InstanceName { get; set; } = "Fireworks.ai";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
// Get the API key:
|
||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||
@@ -30,9 +33,12 @@ public class ProviderFireworks(ILogger logger) : BaseProvider("https://api.firew
|
||||
var systemPrompt = new Message
|
||||
{
|
||||
Role = "system",
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread, this.logger),
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
|
||||
};
|
||||
|
||||
// Parse the API parameters:
|
||||
var apiParameters = this.ParseAdditionalApiParameters();
|
||||
|
||||
// Prepare the Fireworks HTTP chat request:
|
||||
var fireworksChatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||
{
|
||||
@@ -62,6 +68,7 @@ public class ProviderFireworks(ILogger logger) : BaseProvider("https://api.firew
|
||||
|
||||
// Right now, we only support streaming completions:
|
||||
Stream = true,
|
||||
AdditionalApiParameters = apiParameters
|
||||
}, JSON_SERIALIZER_OPTIONS);
|
||||
|
||||
async Task<HttpRequestMessage> RequestBuilder()
|
||||
@@ -77,7 +84,7 @@ public class ProviderFireworks(ILogger logger) : BaseProvider("https://api.firew
|
||||
return request;
|
||||
}
|
||||
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine>("Fireworks", RequestBuilder, token))
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine, ChatCompletionAnnotationStreamLine>("Fireworks", RequestBuilder, token))
|
||||
yield return content;
|
||||
}
|
||||
|
||||
@@ -107,7 +114,5 @@ public class ProviderFireworks(ILogger logger) : BaseProvider("https://api.firew
|
||||
return Task.FromResult(Enumerable.Empty<Model>());
|
||||
}
|
||||
|
||||
public override IReadOnlyCollection<Capability> GetModelCapabilities(Model model) => CapabilitiesOpenSource.GetCapabilities(model);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -14,7 +14,21 @@ public readonly record struct ResponseStreamLine(string Id, string Object, uint
|
||||
public bool ContainsContent() => this != default && this.Choices.Count > 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetContent() => this.Choices[0].Delta.Content;
|
||||
public ContentStreamChunk GetContent() => new(this.Choices[0].Delta.Content, []);
|
||||
|
||||
#region Implementation of IAnnotationStreamLine
|
||||
|
||||
//
|
||||
// Currently, Fireworks does not provide source citations in their response stream.
|
||||
//
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ContainsSources() => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IList<ISource> GetSources() => [];
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -9,8 +9,10 @@ using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider.GWDG;
|
||||
|
||||
public sealed class ProviderGWDG(ILogger logger) : BaseProvider("https://chat-ai.academiccloud.de/v1/", logger)
|
||||
public sealed class ProviderGWDG() : BaseProvider("https://chat-ai.academiccloud.de/v1/", LOGGER)
|
||||
{
|
||||
private static readonly ILogger<ProviderGWDG> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ProviderGWDG>();
|
||||
|
||||
#region Implementation of IProvider
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -20,7 +22,7 @@ public sealed class ProviderGWDG(ILogger logger) : BaseProvider("https://chat-ai
|
||||
public override string InstanceName { get; set; } = "GWDG SAIA";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
// Get the API key:
|
||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||
@@ -31,11 +33,14 @@ public sealed class ProviderGWDG(ILogger logger) : BaseProvider("https://chat-ai
|
||||
var systemPrompt = new Message
|
||||
{
|
||||
Role = "system",
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread, this.logger),
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
|
||||
};
|
||||
|
||||
// Parse the API parameters:
|
||||
var apiParameters = this.ParseAdditionalApiParameters();
|
||||
|
||||
// Prepare the GWDG HTTP chat request:
|
||||
var gwdgChatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||
var gwdgChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
||||
{
|
||||
Model = chatModel.Id,
|
||||
|
||||
@@ -61,6 +66,7 @@ public sealed class ProviderGWDG(ILogger logger) : BaseProvider("https://chat-ai
|
||||
}
|
||||
}).ToList()],
|
||||
Stream = true,
|
||||
AdditionalApiParameters = apiParameters
|
||||
}, JSON_SERIALIZER_OPTIONS);
|
||||
|
||||
async Task<HttpRequestMessage> RequestBuilder()
|
||||
@@ -76,7 +82,7 @@ public sealed class ProviderGWDG(ILogger logger) : BaseProvider("https://chat-ai
|
||||
return request;
|
||||
}
|
||||
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine>("GWDG", RequestBuilder, token))
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>("GWDG", RequestBuilder, token))
|
||||
yield return content;
|
||||
}
|
||||
|
||||
@@ -108,8 +114,6 @@ public sealed class ProviderGWDG(ILogger logger) : BaseProvider("https://chat-ai
|
||||
return models.Where(model => model.Id.StartsWith("e5-", StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
|
||||
public override IReadOnlyCollection<Capability> GetModelCapabilities(Model model) => CapabilitiesOpenSource.GetCapabilities(model);
|
||||
|
||||
#endregion
|
||||
|
||||
private async Task<IEnumerable<Model>> LoadModels(CancellationToken token, string? apiKeyProvisional = null)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using AIStudio.Provider.OpenAI;
|
||||
|
||||
namespace AIStudio.Provider.Google;
|
||||
@@ -12,4 +13,9 @@ public readonly record struct ChatRequest(
|
||||
string Model,
|
||||
IList<Message> Messages,
|
||||
bool Stream
|
||||
);
|
||||
)
|
||||
{
|
||||
// Attention: The "required" modifier is not supported for [JsonExtensionData].
|
||||
[JsonExtensionData]
|
||||
public IDictionary<string, object> AdditionalApiParameters { get; init; } = new Dictionary<string, object>();
|
||||
}
|
||||
@@ -9,8 +9,10 @@ using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider.Google;
|
||||
|
||||
public class ProviderGoogle(ILogger logger) : BaseProvider("https://generativelanguage.googleapis.com/v1beta/", logger)
|
||||
public class ProviderGoogle() : BaseProvider("https://generativelanguage.googleapis.com/v1beta/", LOGGER)
|
||||
{
|
||||
private static readonly ILogger<ProviderGoogle> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ProviderGoogle>();
|
||||
|
||||
#region Implementation of IProvider
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -20,7 +22,7 @@ public class ProviderGoogle(ILogger logger) : BaseProvider("https://generativela
|
||||
public override string InstanceName { get; set; } = "Google Gemini";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async IAsyncEnumerable<string> StreamChatCompletion(Provider.Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Provider.Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
// Get the API key:
|
||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||
@@ -31,9 +33,12 @@ public class ProviderGoogle(ILogger logger) : BaseProvider("https://generativela
|
||||
var systemPrompt = new Message
|
||||
{
|
||||
Role = "system",
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread, this.logger),
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
|
||||
};
|
||||
|
||||
// Parse the API parameters:
|
||||
var apiParameters = this.ParseAdditionalApiParameters();
|
||||
|
||||
// Prepare the Google HTTP chat request:
|
||||
var geminiChatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||
{
|
||||
@@ -63,6 +68,7 @@ public class ProviderGoogle(ILogger logger) : BaseProvider("https://generativela
|
||||
|
||||
// Right now, we only support streaming completions:
|
||||
Stream = true,
|
||||
AdditionalApiParameters = apiParameters
|
||||
}, JSON_SERIALIZER_OPTIONS);
|
||||
|
||||
async Task<HttpRequestMessage> RequestBuilder()
|
||||
@@ -78,7 +84,7 @@ public class ProviderGoogle(ILogger logger) : BaseProvider("https://generativela
|
||||
return request;
|
||||
}
|
||||
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine>("Google", RequestBuilder, token))
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>("Google", RequestBuilder, token))
|
||||
yield return content;
|
||||
}
|
||||
|
||||
@@ -120,87 +126,6 @@ public class ProviderGoogle(ILogger logger) : BaseProvider("https://generativela
|
||||
.Select(n => new Provider.Model(n.Name.Replace("models/", string.Empty), n.DisplayName));
|
||||
}
|
||||
|
||||
public override IReadOnlyCollection<Capability> GetModelCapabilities(Provider.Model model)
|
||||
{
|
||||
var modelName = model.Id.ToLowerInvariant().AsSpan();
|
||||
|
||||
if (modelName.IndexOf("gemini-") is not -1)
|
||||
{
|
||||
// Reasoning models:
|
||||
if (modelName.IndexOf("gemini-2.5") is not -1)
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, Capability.AUDIO_INPUT,
|
||||
Capability.SPEECH_INPUT, Capability.VIDEO_INPUT,
|
||||
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING,
|
||||
];
|
||||
|
||||
// Image generation:
|
||||
if(modelName.IndexOf("-2.0-flash-preview-image-") is not -1)
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, Capability.AUDIO_INPUT,
|
||||
Capability.SPEECH_INPUT, Capability.VIDEO_INPUT,
|
||||
|
||||
Capability.TEXT_OUTPUT, Capability.IMAGE_OUTPUT,
|
||||
];
|
||||
|
||||
// Realtime model:
|
||||
if(modelName.IndexOf("-2.0-flash-live-") is not -1)
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.AUDIO_INPUT, Capability.SPEECH_INPUT,
|
||||
Capability.VIDEO_INPUT,
|
||||
|
||||
Capability.TEXT_OUTPUT, Capability.SPEECH_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING,
|
||||
];
|
||||
|
||||
// The 2.0 flash models cannot call functions:
|
||||
if(modelName.IndexOf("-2.0-flash-") is not -1)
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, Capability.AUDIO_INPUT,
|
||||
Capability.SPEECH_INPUT, Capability.VIDEO_INPUT,
|
||||
|
||||
Capability.TEXT_OUTPUT,
|
||||
];
|
||||
|
||||
// The old 1.0 pro vision model:
|
||||
if(modelName.IndexOf("pro-vision") is not -1)
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
|
||||
Capability.TEXT_OUTPUT,
|
||||
];
|
||||
|
||||
// Default to all other Gemini models:
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, Capability.AUDIO_INPUT,
|
||||
Capability.SPEECH_INPUT, Capability.VIDEO_INPUT,
|
||||
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING,
|
||||
];
|
||||
}
|
||||
|
||||
// Default for all other models:
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING,
|
||||
];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using AIStudio.Provider.OpenAI;
|
||||
|
||||
namespace AIStudio.Provider.Groq;
|
||||
@@ -14,4 +15,9 @@ public readonly record struct ChatRequest(
|
||||
IList<Message> Messages,
|
||||
bool Stream,
|
||||
int Seed
|
||||
);
|
||||
)
|
||||
{
|
||||
// Attention: The "required" modifier is not supported for [JsonExtensionData].
|
||||
[JsonExtensionData]
|
||||
public IDictionary<string, object> AdditionalApiParameters { get; init; } = new Dictionary<string, object>();
|
||||
}
|
||||
@@ -9,8 +9,10 @@ using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider.Groq;
|
||||
|
||||
public class ProviderGroq(ILogger logger) : BaseProvider("https://api.groq.com/openai/v1/", logger)
|
||||
public class ProviderGroq() : BaseProvider("https://api.groq.com/openai/v1/", LOGGER)
|
||||
{
|
||||
private static readonly ILogger<ProviderGroq> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ProviderGroq>();
|
||||
|
||||
#region Implementation of IProvider
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -20,7 +22,7 @@ public class ProviderGroq(ILogger logger) : BaseProvider("https://api.groq.com/o
|
||||
public override string InstanceName { get; set; } = "Groq";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
// Get the API key:
|
||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||
@@ -31,9 +33,12 @@ public class ProviderGroq(ILogger logger) : BaseProvider("https://api.groq.com/o
|
||||
var systemPrompt = new Message
|
||||
{
|
||||
Role = "system",
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread, this.logger),
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
|
||||
};
|
||||
|
||||
// Parse the API parameters:
|
||||
var apiParameters = this.ParseAdditionalApiParameters();
|
||||
|
||||
// Prepare the OpenAI HTTP chat request:
|
||||
var groqChatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||
{
|
||||
@@ -60,11 +65,10 @@ public class ProviderGroq(ILogger logger) : BaseProvider("https://api.groq.com/o
|
||||
_ => string.Empty,
|
||||
}
|
||||
}).ToList()],
|
||||
|
||||
Seed = chatThread.Seed,
|
||||
|
||||
// Right now, we only support streaming completions:
|
||||
Stream = true,
|
||||
AdditionalApiParameters = apiParameters
|
||||
}, JSON_SERIALIZER_OPTIONS);
|
||||
|
||||
async Task<HttpRequestMessage> RequestBuilder()
|
||||
@@ -80,7 +84,7 @@ public class ProviderGroq(ILogger logger) : BaseProvider("https://api.groq.com/o
|
||||
return request;
|
||||
}
|
||||
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine>("Groq", RequestBuilder, token))
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>("Groq", RequestBuilder, token))
|
||||
yield return content;
|
||||
}
|
||||
|
||||
@@ -110,8 +114,6 @@ public class ProviderGroq(ILogger logger) : BaseProvider("https://api.groq.com/o
|
||||
return Task.FromResult(Enumerable.Empty<Model>());
|
||||
}
|
||||
|
||||
public override IReadOnlyCollection<Capability> GetModelCapabilities(Model model) => CapabilitiesOpenSource.GetCapabilities(model);
|
||||
|
||||
#endregion
|
||||
|
||||
private async Task<IEnumerable<Model>> LoadModels(CancellationToken token, string? apiKeyProvisional = null)
|
||||
|
||||
@@ -9,8 +9,10 @@ using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider.Helmholtz;
|
||||
|
||||
public sealed class ProviderHelmholtz(ILogger logger) : BaseProvider("https://api.helmholtz-blablador.fz-juelich.de/v1/", logger)
|
||||
public sealed class ProviderHelmholtz() : BaseProvider("https://api.helmholtz-blablador.fz-juelich.de/v1/", LOGGER)
|
||||
{
|
||||
private static readonly ILogger<ProviderHelmholtz> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ProviderHelmholtz>();
|
||||
|
||||
#region Implementation of IProvider
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -20,7 +22,7 @@ public sealed class ProviderHelmholtz(ILogger logger) : BaseProvider("https://ap
|
||||
public override string InstanceName { get; set; } = "Helmholtz Blablador";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
// Get the API key:
|
||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||
@@ -31,11 +33,14 @@ public sealed class ProviderHelmholtz(ILogger logger) : BaseProvider("https://ap
|
||||
var systemPrompt = new Message
|
||||
{
|
||||
Role = "system",
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread, this.logger),
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
|
||||
};
|
||||
|
||||
// Parse the API parameters:
|
||||
var apiParameters = this.ParseAdditionalApiParameters();
|
||||
|
||||
// Prepare the Helmholtz HTTP chat request:
|
||||
var helmholtzChatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||
var helmholtzChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
||||
{
|
||||
Model = chatModel.Id,
|
||||
|
||||
@@ -61,6 +66,7 @@ public sealed class ProviderHelmholtz(ILogger logger) : BaseProvider("https://ap
|
||||
}
|
||||
}).ToList()],
|
||||
Stream = true,
|
||||
AdditionalApiParameters = apiParameters
|
||||
}, JSON_SERIALIZER_OPTIONS);
|
||||
|
||||
async Task<HttpRequestMessage> RequestBuilder()
|
||||
@@ -76,7 +82,7 @@ public sealed class ProviderHelmholtz(ILogger logger) : BaseProvider("https://ap
|
||||
return request;
|
||||
}
|
||||
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine>("Helmholtz", RequestBuilder, token))
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>("Helmholtz", RequestBuilder, token))
|
||||
yield return content;
|
||||
}
|
||||
|
||||
@@ -112,8 +118,6 @@ public sealed class ProviderHelmholtz(ILogger logger) : BaseProvider("https://ap
|
||||
model.Id.Contains("gritlm", StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
|
||||
public override IReadOnlyCollection<Capability> GetModelCapabilities(Model model) => CapabilitiesOpenSource.GetCapabilities(model);
|
||||
|
||||
#endregion
|
||||
|
||||
private async Task<IEnumerable<Model>> LoadModels(CancellationToken token, string? apiKeyProvisional = null)
|
||||
|
||||
@@ -11,9 +11,11 @@ namespace AIStudio.Provider.HuggingFace;
|
||||
|
||||
public sealed class ProviderHuggingFace : BaseProvider
|
||||
{
|
||||
public ProviderHuggingFace(ILogger logger, HFInferenceProvider hfProvider, Model model) : base($"https://router.huggingface.co/{hfProvider.Endpoints(model)}", logger)
|
||||
private static readonly ILogger<ProviderHuggingFace> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ProviderHuggingFace>();
|
||||
|
||||
public ProviderHuggingFace(HFInferenceProvider hfProvider, Model model) : base($"https://router.huggingface.co/{hfProvider.Endpoints(model)}", LOGGER)
|
||||
{
|
||||
logger.LogInformation($"We use the inferende provider '{hfProvider}'. Thus we use the base URL 'https://router.huggingface.co/{hfProvider.Endpoints(model)}'.");
|
||||
LOGGER.LogInformation($"We use the inferende provider '{hfProvider}'. Thus we use the base URL 'https://router.huggingface.co/{hfProvider.Endpoints(model)}'.");
|
||||
}
|
||||
|
||||
#region Implementation of IProvider
|
||||
@@ -25,7 +27,7 @@ public sealed class ProviderHuggingFace : BaseProvider
|
||||
public override string InstanceName { get; set; } = "HuggingFace";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
// Get the API key:
|
||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||
@@ -36,11 +38,14 @@ public sealed class ProviderHuggingFace : BaseProvider
|
||||
var systemPrompt = new Message
|
||||
{
|
||||
Role = "system",
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread, this.logger),
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
|
||||
};
|
||||
|
||||
// Parse the API parameters:
|
||||
var apiParameters = this.ParseAdditionalApiParameters();
|
||||
|
||||
// Prepare the HuggingFace HTTP chat request:
|
||||
var huggingfaceChatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||
var huggingfaceChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
||||
{
|
||||
Model = chatModel.Id,
|
||||
|
||||
@@ -66,6 +71,7 @@ public sealed class ProviderHuggingFace : BaseProvider
|
||||
}
|
||||
}).ToList()],
|
||||
Stream = true,
|
||||
AdditionalApiParameters = apiParameters
|
||||
}, JSON_SERIALIZER_OPTIONS);
|
||||
|
||||
async Task<HttpRequestMessage> RequestBuilder()
|
||||
@@ -81,7 +87,7 @@ public sealed class ProviderHuggingFace : BaseProvider
|
||||
return request;
|
||||
}
|
||||
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine>("HuggingFace", RequestBuilder, token))
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>("HuggingFace", RequestBuilder, token))
|
||||
yield return content;
|
||||
}
|
||||
|
||||
@@ -111,7 +117,5 @@ public sealed class ProviderHuggingFace : BaseProvider
|
||||
return Task.FromResult(Enumerable.Empty<Model>());
|
||||
}
|
||||
|
||||
public override IReadOnlyCollection<Capability> GetModelCapabilities(Model model) => CapabilitiesOpenSource.GetCapabilities(model);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace AIStudio.Provider;
|
||||
|
||||
/// <summary>
|
||||
/// A contract for a line in a response stream that can provide annotations such as sources.
|
||||
/// </summary>
|
||||
public interface IAnnotationStreamLine
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the response line contains any sources.
|
||||
/// </summary>
|
||||
/// <returns>True when the response line contains sources, false otherwise.</returns>
|
||||
public bool ContainsSources();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sources of the response line.
|
||||
/// </summary>
|
||||
/// <returns>The sources of the response line.</returns>
|
||||
public IList<ISource> GetSources();
|
||||
}
|
||||
@@ -19,6 +19,11 @@ public interface IProvider
|
||||
/// </summary>
|
||||
public string InstanceName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The additional API parameters.
|
||||
/// </summary>
|
||||
public string AdditionalJsonApiParameters { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Starts a chat completion stream.
|
||||
/// </summary>
|
||||
@@ -27,7 +32,7 @@ public interface IProvider
|
||||
/// <param name="settingsManager">The settings manager instance to use.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The chat completion stream.</returns>
|
||||
public IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, CancellationToken token = default);
|
||||
public IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// Starts an image completion stream.
|
||||
@@ -64,10 +69,4 @@ public interface IProvider
|
||||
/// <returns>The list of embedding models.</returns>
|
||||
public Task<IEnumerable<Model>> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get the capabilities of a model.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to get the capabilities for.</param>
|
||||
/// <returns>The capabilities of the model.</returns>
|
||||
public IReadOnlyCollection<Capability> GetModelCapabilities(Model model);
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
namespace AIStudio.Provider;
|
||||
|
||||
public interface IResponseStreamLine
|
||||
/// <summary>
|
||||
/// A contract for a streamed response line that may contain content and annotations.
|
||||
/// </summary>
|
||||
public interface IResponseStreamLine : IAnnotationStreamLine
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the response line contains any content.
|
||||
@@ -12,5 +15,5 @@ public interface IResponseStreamLine
|
||||
/// Gets the content of the response line.
|
||||
/// </summary>
|
||||
/// <returns>The content of the response line.</returns>
|
||||
public string GetContent();
|
||||
public ContentStreamChunk GetContent();
|
||||
}
|
||||
@@ -14,6 +14,7 @@ public enum LLMProviders
|
||||
X = 8,
|
||||
DEEP_SEEK = 11,
|
||||
ALIBABA_CLOUD = 12,
|
||||
PERPLEXITY = 14,
|
||||
|
||||
FIREWORKS = 5,
|
||||
GROQ = 6,
|
||||
|
||||
@@ -9,6 +9,7 @@ using AIStudio.Provider.Helmholtz;
|
||||
using AIStudio.Provider.HuggingFace;
|
||||
using AIStudio.Provider.Mistral;
|
||||
using AIStudio.Provider.OpenAI;
|
||||
using AIStudio.Provider.Perplexity;
|
||||
using AIStudio.Provider.SelfHosted;
|
||||
using AIStudio.Provider.X;
|
||||
using AIStudio.Settings;
|
||||
@@ -20,6 +21,8 @@ namespace AIStudio.Provider;
|
||||
|
||||
public static class LLMProvidersExtensions
|
||||
{
|
||||
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(nameof(LLMProvidersExtensions));
|
||||
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(LLMProvidersExtensions).Namespace, nameof(LLMProvidersExtensions));
|
||||
|
||||
/// <summary>
|
||||
@@ -38,6 +41,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.X => "xAI",
|
||||
LLMProviders.DEEP_SEEK => "DeepSeek",
|
||||
LLMProviders.ALIBABA_CLOUD => "Alibaba Cloud",
|
||||
LLMProviders.PERPLEXITY => "Perplexity",
|
||||
|
||||
LLMProviders.GROQ => "Groq",
|
||||
LLMProviders.FIREWORKS => "Fireworks.ai",
|
||||
@@ -86,6 +90,8 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.DEEP_SEEK => Confidence.CHINA_NO_TRAINING.WithRegion("Asia").WithSources("https://cdn.deepseek.com/policies/en-US/deepseek-open-platform-terms-of-service.html").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||
|
||||
LLMProviders.ALIBABA_CLOUD => Confidence.CHINA_NO_TRAINING.WithRegion("Asia").WithSources("https://www.alibabacloud.com/help/en/model-studio/support/faq-about-alibaba-cloud-model-studio").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||
|
||||
LLMProviders.PERPLEXITY => Confidence.USA_NO_TRAINING.WithRegion("America, U.S.").WithSources("https://www.perplexity.ai/hub/legal/perplexity-api-terms-of-service").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||
|
||||
LLMProviders.SELF_HOSTED => Confidence.SELF_HOSTED.WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||
|
||||
@@ -121,6 +127,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.GWDG => false,
|
||||
LLMProviders.DEEP_SEEK => false,
|
||||
LLMProviders.HUGGINGFACE => false,
|
||||
LLMProviders.PERPLEXITY => false,
|
||||
|
||||
//
|
||||
// Self-hosted providers are treated as a special case anyway.
|
||||
@@ -134,53 +141,52 @@ public static class LLMProvidersExtensions
|
||||
/// 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 AIStudio.Settings.Provider providerSettings, ILogger logger)
|
||||
public static IProvider CreateProvider(this AIStudio.Settings.Provider providerSettings)
|
||||
{
|
||||
return providerSettings.UsedLLMProvider.CreateProvider(providerSettings.InstanceName, providerSettings.Host, providerSettings.Hostname, providerSettings.Model, providerSettings.HFInferenceProvider ,logger);
|
||||
return providerSettings.UsedLLMProvider.CreateProvider(providerSettings.InstanceName, providerSettings.Host, providerSettings.Hostname, providerSettings.Model, providerSettings.HFInferenceProvider, providerSettings.AdditionalJsonApiParameters);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
public static IProvider CreateProvider(this EmbeddingProvider embeddingProviderSettings)
|
||||
{
|
||||
return embeddingProviderSettings.UsedLLMProvider.CreateProvider(embeddingProviderSettings.Name, embeddingProviderSettings.Host, embeddingProviderSettings.Hostname, embeddingProviderSettings.Model, HFInferenceProvider.NONE,logger);
|
||||
return embeddingProviderSettings.UsedLLMProvider.CreateProvider(embeddingProviderSettings.Name, embeddingProviderSettings.Host, embeddingProviderSettings.Hostname, embeddingProviderSettings.Model, HFInferenceProvider.NONE);
|
||||
}
|
||||
|
||||
private static IProvider CreateProvider(this LLMProviders provider, string instanceName, Host host, string hostname, Model model, HFInferenceProvider inferenceProvider , ILogger logger)
|
||||
private static IProvider CreateProvider(this LLMProviders provider, string instanceName, Host host, string hostname, Model model, HFInferenceProvider inferenceProvider, string expertProviderApiParameter = "")
|
||||
{
|
||||
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.X => new ProviderX(logger) { InstanceName = instanceName },
|
||||
LLMProviders.DEEP_SEEK => new ProviderDeepSeek(logger) { InstanceName = instanceName },
|
||||
LLMProviders.ALIBABA_CLOUD => new ProviderAlibabaCloud(logger) { InstanceName = instanceName },
|
||||
LLMProviders.OPEN_AI => new ProviderOpenAI { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
|
||||
LLMProviders.ANTHROPIC => new ProviderAnthropic { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
|
||||
LLMProviders.MISTRAL => new ProviderMistral { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
|
||||
LLMProviders.GOOGLE => new ProviderGoogle { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
|
||||
LLMProviders.X => new ProviderX { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
|
||||
LLMProviders.DEEP_SEEK => new ProviderDeepSeek { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
|
||||
LLMProviders.ALIBABA_CLOUD => new ProviderAlibabaCloud { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
|
||||
LLMProviders.PERPLEXITY => new ProviderPerplexity { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
|
||||
|
||||
LLMProviders.GROQ => new ProviderGroq(logger) { InstanceName = instanceName },
|
||||
LLMProviders.FIREWORKS => new ProviderFireworks(logger) { InstanceName = instanceName },
|
||||
LLMProviders.HUGGINGFACE => new ProviderHuggingFace(logger, inferenceProvider, model) { InstanceName = instanceName },
|
||||
LLMProviders.GROQ => new ProviderGroq { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
|
||||
LLMProviders.FIREWORKS => new ProviderFireworks { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
|
||||
LLMProviders.HUGGINGFACE => new ProviderHuggingFace(inferenceProvider, model) { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
|
||||
|
||||
LLMProviders.SELF_HOSTED => new ProviderSelfHosted(logger, host, hostname) { InstanceName = instanceName },
|
||||
LLMProviders.SELF_HOSTED => new ProviderSelfHosted(host, hostname) { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
|
||||
|
||||
LLMProviders.HELMHOLTZ => new ProviderHelmholtz(logger) { InstanceName = instanceName },
|
||||
LLMProviders.GWDG => new ProviderGWDG(logger) { InstanceName = instanceName },
|
||||
LLMProviders.HELMHOLTZ => new ProviderHelmholtz { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
|
||||
LLMProviders.GWDG => new ProviderGWDG { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
|
||||
|
||||
_ => new NoProvider(),
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError($"Failed to create provider: {e.Message}");
|
||||
LOGGER.LogError($"Failed to create provider: {e.Message}");
|
||||
return new NoProvider();
|
||||
}
|
||||
}
|
||||
@@ -194,6 +200,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.X => "https://accounts.x.ai/sign-up",
|
||||
LLMProviders.DEEP_SEEK => "https://platform.deepseek.com/sign_up",
|
||||
LLMProviders.ALIBABA_CLOUD => "https://account.alibabacloud.com/register/intl_register.htm",
|
||||
LLMProviders.PERPLEXITY => "https://www.perplexity.ai/account/api",
|
||||
|
||||
LLMProviders.GROQ => "https://console.groq.com/",
|
||||
LLMProviders.FIREWORKS => "https://fireworks.ai/login",
|
||||
@@ -216,6 +223,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.FIREWORKS => "https://fireworks.ai/account/billing",
|
||||
LLMProviders.DEEP_SEEK => "https://platform.deepseek.com/usage",
|
||||
LLMProviders.ALIBABA_CLOUD => "https://usercenter2-intl.aliyun.com/billing",
|
||||
LLMProviders.PERPLEXITY => "https://www.perplexity.ai/account/api/",
|
||||
LLMProviders.HUGGINGFACE => "https://huggingface.co/settings/billing",
|
||||
|
||||
_ => string.Empty,
|
||||
@@ -232,6 +240,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.GOOGLE => true,
|
||||
LLMProviders.DEEP_SEEK => true,
|
||||
LLMProviders.ALIBABA_CLOUD => true,
|
||||
LLMProviders.PERPLEXITY => true,
|
||||
LLMProviders.HUGGINGFACE => true,
|
||||
|
||||
_ => false,
|
||||
@@ -278,6 +287,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.X => true,
|
||||
LLMProviders.DEEP_SEEK => true,
|
||||
LLMProviders.ALIBABA_CLOUD => true,
|
||||
LLMProviders.PERPLEXITY => true,
|
||||
|
||||
LLMProviders.GROQ => true,
|
||||
LLMProviders.FIREWORKS => true,
|
||||
@@ -299,6 +309,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.X => true,
|
||||
LLMProviders.DEEP_SEEK => true,
|
||||
LLMProviders.ALIBABA_CLOUD => true,
|
||||
LLMProviders.PERPLEXITY => true,
|
||||
|
||||
LLMProviders.GROQ => true,
|
||||
LLMProviders.FIREWORKS => true,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AIStudio.Provider.Mistral;
|
||||
|
||||
/// <summary>
|
||||
@@ -14,4 +16,9 @@ public readonly record struct ChatRequest(
|
||||
bool Stream,
|
||||
int RandomSeed,
|
||||
bool SafePrompt = false
|
||||
);
|
||||
)
|
||||
{
|
||||
// Attention: The "required" modifier is not supported for [JsonExtensionData].
|
||||
[JsonExtensionData]
|
||||
public IDictionary<string, object> AdditionalApiParameters { get; init; } = new Dictionary<string, object>();
|
||||
}
|
||||
@@ -9,8 +9,10 @@ using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider.Mistral;
|
||||
|
||||
public sealed class ProviderMistral(ILogger logger) : BaseProvider("https://api.mistral.ai/v1/", logger)
|
||||
public sealed class ProviderMistral() : BaseProvider("https://api.mistral.ai/v1/", LOGGER)
|
||||
{
|
||||
private static readonly ILogger<ProviderMistral> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ProviderMistral>();
|
||||
|
||||
#region Implementation of IProvider
|
||||
|
||||
public override string Id => LLMProviders.MISTRAL.ToName();
|
||||
@@ -18,7 +20,7 @@ public sealed class ProviderMistral(ILogger logger) : BaseProvider("https://api.
|
||||
public override string InstanceName { get; set; } = "Mistral";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async IAsyncEnumerable<string> StreamChatCompletion(Provider.Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Provider.Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
// Get the API key:
|
||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||
@@ -29,9 +31,12 @@ public sealed class ProviderMistral(ILogger logger) : BaseProvider("https://api.
|
||||
var systemPrompt = new RegularMessage
|
||||
{
|
||||
Role = "system",
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread, this.logger),
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
|
||||
};
|
||||
|
||||
// Parse the API parameters:
|
||||
var apiParameters = this.ParseAdditionalApiParameters();
|
||||
|
||||
// Prepare the Mistral HTTP chat request:
|
||||
var mistralChatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||
{
|
||||
@@ -58,12 +63,11 @@ public sealed class ProviderMistral(ILogger logger) : BaseProvider("https://api.
|
||||
_ => string.Empty,
|
||||
}
|
||||
}).ToList()],
|
||||
|
||||
RandomSeed = chatThread.Seed,
|
||||
|
||||
// Right now, we only support streaming completions:
|
||||
Stream = true,
|
||||
SafePrompt = false,
|
||||
SafePrompt = apiParameters.TryGetValue("safe_prompt", out var value) && value is true,
|
||||
AdditionalApiParameters = apiParameters
|
||||
}, JSON_SERIALIZER_OPTIONS);
|
||||
|
||||
async Task<HttpRequestMessage> RequestBuilder()
|
||||
@@ -79,7 +83,7 @@ public sealed class ProviderMistral(ILogger logger) : BaseProvider("https://api.
|
||||
return request;
|
||||
}
|
||||
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine>("Mistral", RequestBuilder, token))
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>("Mistral", RequestBuilder, token))
|
||||
yield return content;
|
||||
}
|
||||
|
||||
@@ -122,52 +126,6 @@ public sealed class ProviderMistral(ILogger logger) : BaseProvider("https://api.
|
||||
return Task.FromResult(Enumerable.Empty<Provider.Model>());
|
||||
}
|
||||
|
||||
public override IReadOnlyCollection<Capability> GetModelCapabilities(Provider.Model model)
|
||||
{
|
||||
var modelName = model.Id.ToLowerInvariant().AsSpan();
|
||||
|
||||
// Pixtral models are able to do process images:
|
||||
if (modelName.IndexOf("pixtral") is not -1)
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING,
|
||||
];
|
||||
|
||||
// Mistral medium:
|
||||
if (modelName.IndexOf("mistral-medium-") is not -1)
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING,
|
||||
];
|
||||
|
||||
// Mistral small:
|
||||
if (modelName.IndexOf("mistral-small-") is not -1)
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING,
|
||||
];
|
||||
|
||||
// Mistral saba:
|
||||
if (modelName.IndexOf("mistral-saba-") is not -1)
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
];
|
||||
|
||||
// Default:
|
||||
return CapabilitiesOpenSource.GetCapabilities(model);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private async Task<ModelsResponse> LoadModelList(string? apiKeyProvisional, CancellationToken token)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace AIStudio.Provider;
|
||||
|
||||
/// <summary>
|
||||
/// A marker record indicating that no chat completion annotation line is expected in that stream.
|
||||
/// </summary>
|
||||
public sealed record NoChatCompletionAnnotationStreamLine : IAnnotationStreamLine
|
||||
{
|
||||
#region Implementation of IAnnotationStreamLine
|
||||
|
||||
public bool ContainsSources() => false;
|
||||
|
||||
public IList<ISource> GetSources() => [];
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -13,13 +13,16 @@ public class NoProvider : IProvider
|
||||
|
||||
public string InstanceName { get; set; } = "None";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string AdditionalJsonApiParameters { get; init; } = string.Empty;
|
||||
|
||||
public Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default) => Task.FromResult<IEnumerable<Model>>([]);
|
||||
|
||||
public Task<IEnumerable<Model>> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default) => Task.FromResult<IEnumerable<Model>>([]);
|
||||
|
||||
public Task<IEnumerable<Model>> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default) => Task.FromResult<IEnumerable<Model>>([]);
|
||||
|
||||
public async IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatChatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatChatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
await Task.FromResult(0);
|
||||
yield break;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace AIStudio.Provider;
|
||||
|
||||
/// <summary>
|
||||
/// A marker record indicating that no annotation line is expected in that Responses API stream.
|
||||
/// </summary>
|
||||
public sealed record NoResponsesAnnotationStreamLine : IAnnotationStreamLine
|
||||
{
|
||||
#region Implementation of IAnnotationStreamLine
|
||||
|
||||
public bool ContainsSources() => false;
|
||||
|
||||
public IList<ISource> GetSources() => [];
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an unknown annotation type.
|
||||
/// </summary>
|
||||
/// <param name="Type">The type of the unknown annotation.</param>
|
||||
public sealed record AnnotatingUnknown(string Type) : Annotation(Type);
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for different types of annotations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We use this base class to represent various annotation types for all types of LLM providers.
|
||||
/// </remarks>
|
||||
/// <param name="Type">The type of the annotation.</param>
|
||||
public abstract record Annotation(string Type);
|
||||
@@ -0,0 +1,62 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// Custom JSON converter for the annotation class to handle polymorphic deserialization.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We use this converter for chat completion API and responses API annotation deserialization.
|
||||
/// </remarks>
|
||||
public sealed class AnnotationConverter : JsonConverter<Annotation>
|
||||
{
|
||||
public override Annotation? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
using var doc = JsonDocument.ParseValue(ref reader);
|
||||
var root = doc.RootElement;
|
||||
|
||||
if (!root.TryGetProperty("type", out var typeElement))
|
||||
return null;
|
||||
|
||||
var type = typeElement.GetString();
|
||||
var rawText = root.GetRawText();
|
||||
|
||||
Annotation? annotation;
|
||||
switch (type)
|
||||
{
|
||||
case "url_citation":
|
||||
|
||||
// Let's check the responses API data type first:
|
||||
var responsesAnnotation = JsonSerializer.Deserialize<ResponsesAnnotatingUrlCitationData>(rawText, options);
|
||||
|
||||
// If it fails, let's try the chat completion API data type:
|
||||
if(responsesAnnotation is null || string.IsNullOrWhiteSpace(responsesAnnotation.Title) || string.IsNullOrWhiteSpace(responsesAnnotation.URL))
|
||||
{
|
||||
// Try chat completion API data type:
|
||||
var chatCompletionAnnotation = JsonSerializer.Deserialize<ChatCompletionAnnotatingURL>(rawText, options);
|
||||
|
||||
// If both fail, we return the unknown type:
|
||||
if(chatCompletionAnnotation is null)
|
||||
annotation = new AnnotatingUnknown(type);
|
||||
else
|
||||
annotation = chatCompletionAnnotation;
|
||||
}
|
||||
else
|
||||
annotation = responsesAnnotation;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
annotation = new AnnotatingUnknown(type ?? "unknown");
|
||||
break;
|
||||
}
|
||||
|
||||
return annotation;
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, Annotation value, JsonSerializerOptions options)
|
||||
{
|
||||
JsonSerializer.Serialize(writer, value, value.GetType(), options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// The OpenAI's legacy chat completion 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 record ChatCompletionAPIRequest(
|
||||
string Model,
|
||||
IList<Message> Messages,
|
||||
bool Stream
|
||||
)
|
||||
{
|
||||
public ChatCompletionAPIRequest() : this(string.Empty, [], true)
|
||||
{
|
||||
}
|
||||
|
||||
// Attention: The "required" modifier is not supported for [JsonExtensionData].
|
||||
[JsonExtensionData]
|
||||
public IDictionary<string, object> AdditionalApiParameters { get; init; } = new Dictionary<string, object>();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// Data structure for URL annotation in chat completions.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Although this class is not directly intended for the Responses API, it is
|
||||
/// used there as a fallback solution. One day, one of the open source LLM
|
||||
/// drivers may use this data structure for their responses API.
|
||||
/// </remarks>
|
||||
/// <param name="Type">The type of annotation, typically "url_citation".</param>
|
||||
/// <param name="UrlCitation">The URL citation details.</param>
|
||||
public sealed record ChatCompletionAnnotatingURL(
|
||||
string Type,
|
||||
ChatCompletionUrlCitationData UrlCitation
|
||||
) : Annotation(Type);
|
||||
Loaded 100 of 205 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user