mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-20 07:38:00 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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>
|
||||
@@ -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."
|
||||
|
||||
@@ -1705,6 +1714,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 +1855,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 +1888,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 +2146,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 +2182,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 +2200,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 +2218,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"
|
||||
|
||||
@@ -3415,6 +3466,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"
|
||||
|
||||
@@ -3958,12 +4012,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 +4177,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 +4636,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 +4648,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"
|
||||
|
||||
@@ -4867,6 +4930,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 +4972,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"
|
||||
|
||||
@@ -5440,9 +5509,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 +5635,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 +5746,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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,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">
|
||||
@@ -72,6 +80,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" />
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,8 @@ public partial class Changelog
|
||||
|
||||
public static readonly Log[] LOGS =
|
||||
[
|
||||
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;
|
||||
}
|
||||
@@ -813,9 +810,8 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
// Try to select the chat template:
|
||||
if (!string.IsNullOrWhiteSpace(chatChatTemplate))
|
||||
{
|
||||
this.currentChatTemplate = this.SettingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatChatTemplate);
|
||||
if(this.currentChatTemplate == default)
|
||||
this.currentChatTemplate = ChatTemplate.NO_CHAT_TEMPLATE;
|
||||
var selectedTemplate = this.SettingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatChatTemplate);
|
||||
this.currentChatTemplate = selectedTemplate ?? ChatTemplate.NO_CHAT_TEMPLATE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,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,16 +13,17 @@
|
||||
<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"/>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 },
|
||||
@@ -90,9 +96,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 +114,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);
|
||||
|
||||
@@ -78,9 +78,6 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
[Parameter]
|
||||
public bool IsEditing { get; init; }
|
||||
|
||||
[Inject]
|
||||
private ILogger<ProviderDialog> Logger { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
@@ -126,12 +123,13 @@ 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,
|
||||
@@ -158,7 +156,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 +239,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 +250,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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -49,8 +49,8 @@
|
||||
<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="Microsoft.Extensions.FileProviders.Embedded" Version="9.0.10" />
|
||||
<PackageReference Include="MudBlazor" Version="8.12.0" />
|
||||
<PackageReference Include="MudBlazor.Markdown" Version="8.11.0" />
|
||||
<PackageReference Include="ReverseMarkdown" Version="4.7.0" />
|
||||
<PackageReference Include="LuaCSharp" Version="0.4.2" />
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -62,10 +62,50 @@ CONFIG["LLM_PROVIDERS"][#CONFIG["LLM_PROVIDERS"]+1] = {
|
||||
|
||||
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" }
|
||||
|
||||
-- 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?"
|
||||
}
|
||||
}
|
||||
}
|
||||
+101
-23
@@ -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."
|
||||
|
||||
@@ -1693,7 +1702,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 +1714,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 +1857,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 +1890,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 +2148,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 +2184,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 +2202,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 +2220,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"
|
||||
|
||||
@@ -3417,6 +3468,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 +3775,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 +3796,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 +3814,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?"
|
||||
@@ -3904,7 +3958,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 +3976,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 +4006,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 +4014,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 +4039,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 +4057,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 +4078,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 +4093,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 +4179,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 +4638,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 +4650,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"
|
||||
|
||||
@@ -4869,6 +4932,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 +4974,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"
|
||||
|
||||
@@ -5442,9 +5511,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 +5637,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 +5748,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"
|
||||
+86
-8
@@ -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."
|
||||
|
||||
@@ -1707,6 +1716,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 +1857,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 +1890,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 +2148,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 +2184,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 +2202,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 +2220,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"
|
||||
|
||||
@@ -3417,6 +3468,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"
|
||||
|
||||
@@ -3960,12 +4014,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 +4179,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 +4638,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 +4650,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"
|
||||
|
||||
@@ -4869,6 +4932,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 +4974,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"
|
||||
|
||||
@@ -5442,9 +5511,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 +5637,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 +5748,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,11 @@ 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),
|
||||
};
|
||||
|
||||
// Prepare the AlibabaCloud HTTP chat request:
|
||||
var alibabaCloudChatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||
var alibabaCloudChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
||||
{
|
||||
Model = chatModel.Id,
|
||||
|
||||
@@ -77,7 +78,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;
|
||||
}
|
||||
|
||||
@@ -156,7 +157,9 @@ public sealed class ProviderAlibabaCloud(ILogger logger) : BaseProvider("https:/
|
||||
Capability.AUDIO_INPUT, Capability.SPEECH_INPUT,
|
||||
Capability.VIDEO_INPUT,
|
||||
|
||||
Capability.TEXT_OUTPUT, Capability.SPEECH_OUTPUT
|
||||
Capability.TEXT_OUTPUT, Capability.SPEECH_OUTPUT,
|
||||
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
// Check for Qwen 3:
|
||||
@@ -166,7 +169,8 @@ public sealed class ProviderAlibabaCloud(ILogger logger) : BaseProvider("https:/
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING
|
||||
Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
if(modelName.IndexOf("-vl-") is not -1)
|
||||
@@ -174,6 +178,8 @@ public sealed class ProviderAlibabaCloud(ILogger logger) : BaseProvider("https:/
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -185,7 +191,8 @@ public sealed class ProviderAlibabaCloud(ILogger logger) : BaseProvider("https:/
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING
|
||||
Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -197,7 +204,8 @@ public sealed class ProviderAlibabaCloud(ILogger logger) : BaseProvider("https:/
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.ALWAYS_REASONING
|
||||
Capability.ALWAYS_REASONING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -207,7 +215,8 @@ public sealed class ProviderAlibabaCloud(ILogger logger) : BaseProvider("https:/
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING
|
||||
Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -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,7 +20,7 @@ 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);
|
||||
@@ -49,7 +51,7 @@ public sealed class ProviderAnthropic(ILogger logger) : BaseProvider("https://ap
|
||||
}
|
||||
}).ToList()],
|
||||
|
||||
System = chatThread.PrepareSystemPrompt(settingsManager, chatThread, this.logger),
|
||||
System = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
|
||||
MaxTokens = 4_096,
|
||||
|
||||
// Right now, we only support streaming completions:
|
||||
@@ -72,7 +74,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;
|
||||
}
|
||||
|
||||
@@ -122,7 +124,9 @@ public sealed class ProviderAnthropic(ILogger logger) : BaseProvider("https://ap
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING];
|
||||
Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
// Claude 3.7 is able to do reasoning:
|
||||
if(modelName.StartsWith("claude-3-7"))
|
||||
@@ -130,7 +134,9 @@ public sealed class ProviderAnthropic(ILogger logger) : BaseProvider("https://ap
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING];
|
||||
Capability.OPTIONAL_REASONING, Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
// All other 3.x models are able to process text and images as input:
|
||||
if(modelName.StartsWith("claude-3-"))
|
||||
@@ -138,13 +144,17 @@ public sealed class ProviderAnthropic(ILogger logger) : BaseProvider("https://ap
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING];
|
||||
Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
// Any other model is able to process text only:
|
||||
return [
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
Capability.FUNCTION_CALLING];
|
||||
Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
}
|
||||
|
||||
#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,17 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
protected static readonly JsonSerializerOptions JSON_SERIALIZER_OPTIONS = new()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
|
||||
Converters = { new AnnotationConverter() }
|
||||
};
|
||||
|
||||
/// <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);
|
||||
@@ -63,7 +65,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
public abstract string InstanceName { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, CancellationToken token = default);
|
||||
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);
|
||||
@@ -96,7 +98,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 +125,12 @@ 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.LogDebug($"Error body: {errorBody}");
|
||||
errorMessage = nextResponse.ReasonPhrase;
|
||||
break;
|
||||
}
|
||||
@@ -135,6 +139,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
{
|
||||
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.LogDebug($"Error body: {errorBody}");
|
||||
errorMessage = nextResponse.ReasonPhrase;
|
||||
break;
|
||||
}
|
||||
@@ -143,6 +148,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
{
|
||||
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.LogDebug($"Error body: {errorBody}");
|
||||
errorMessage = nextResponse.ReasonPhrase;
|
||||
break;
|
||||
}
|
||||
@@ -151,6 +157,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
{
|
||||
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.LogDebug($"Error body: {errorBody}");
|
||||
errorMessage = nextResponse.ReasonPhrase;
|
||||
break;
|
||||
}
|
||||
@@ -159,6 +166,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
{
|
||||
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.LogDebug($"Error body: {errorBody}");
|
||||
errorMessage = nextResponse.ReasonPhrase;
|
||||
break;
|
||||
}
|
||||
@@ -167,6 +175,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
{
|
||||
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.LogDebug($"Error body: {errorBody}");
|
||||
errorMessage = nextResponse.ReasonPhrase;
|
||||
break;
|
||||
}
|
||||
@@ -189,8 +198,20 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
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 +238,9 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
if (streamReader is null)
|
||||
yield break;
|
||||
|
||||
//
|
||||
// Read the stream, line by line:
|
||||
//
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
@@ -240,7 +263,9 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
yield break;
|
||||
}
|
||||
|
||||
//
|
||||
// Read the next line:
|
||||
//
|
||||
string? line;
|
||||
try
|
||||
{
|
||||
@@ -266,28 +291,233 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
if (line.StartsWith("data: [DONE]", StringComparison.InvariantCulture))
|
||||
yield break;
|
||||
|
||||
T providerResponse;
|
||||
//
|
||||
// Process annotation lines:
|
||||
//
|
||||
if (annotationSupported && line.Contains("""
|
||||
"annotations":[
|
||||
""", 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());
|
||||
}
|
||||
|
||||
//
|
||||
// Process delta lines:
|
||||
//
|
||||
else
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
// 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);
|
||||
if(streamReader.EndOfStream)
|
||||
break;
|
||||
}
|
||||
catch
|
||||
catch (Exception e)
|
||||
{
|
||||
// Skip invalid JSON data:
|
||||
continue;
|
||||
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;
|
||||
}
|
||||
|
||||
// Skip empty responses:
|
||||
if (!providerResponse.ContainsContent())
|
||||
continue;
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Yield the response:
|
||||
yield return providerResponse.GetContent();
|
||||
// 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();
|
||||
|
||||
@@ -34,11 +34,17 @@ public static class CapabilitiesOpenSource
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
// The old vision models cannot do function calling:
|
||||
if (modelName.IndexOf("vision") is not -1)
|
||||
return [Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT, Capability.TEXT_OUTPUT];
|
||||
return [
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
//
|
||||
// All models >= 3.1 are able to do function calling:
|
||||
@@ -53,10 +59,14 @@ public static class CapabilitiesOpenSource
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
// All other llama models can only do text input and output:
|
||||
return [Capability.TEXT_INPUT, Capability.TEXT_OUTPUT];
|
||||
return [
|
||||
Capability.TEXT_INPUT, Capability.TEXT_OUTPUT,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
}
|
||||
|
||||
//
|
||||
@@ -66,9 +76,16 @@ public static class CapabilitiesOpenSource
|
||||
{
|
||||
if(modelName.IndexOf("deepseek-r1") is not -1 ||
|
||||
modelName.IndexOf("deepseek r1") is not -1)
|
||||
return [Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, Capability.ALWAYS_REASONING];
|
||||
return [
|
||||
Capability.TEXT_INPUT, Capability.TEXT_OUTPUT,
|
||||
Capability.ALWAYS_REASONING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
return [Capability.TEXT_INPUT, Capability.TEXT_OUTPUT];
|
||||
return [
|
||||
Capability.TEXT_INPUT, Capability.TEXT_OUTPUT,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
}
|
||||
|
||||
//
|
||||
@@ -77,9 +94,16 @@ public static class CapabilitiesOpenSource
|
||||
if (modelName.IndexOf("qwen") is not -1 || modelName.IndexOf("qwq") is not -1)
|
||||
{
|
||||
if (modelName.IndexOf("qwq") is not -1)
|
||||
return [Capability.TEXT_INPUT, Capability.TEXT_OUTPUT, Capability.ALWAYS_REASONING];
|
||||
return [
|
||||
Capability.TEXT_INPUT, Capability.TEXT_OUTPUT,
|
||||
Capability.ALWAYS_REASONING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
return [Capability.TEXT_INPUT, Capability.TEXT_OUTPUT];
|
||||
return [
|
||||
Capability.TEXT_INPUT, Capability.TEXT_OUTPUT,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
}
|
||||
|
||||
//
|
||||
@@ -93,7 +117,8 @@ public static class CapabilitiesOpenSource
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
Capability.FUNCTION_CALLING
|
||||
Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
if (modelName.IndexOf("3.1") is not -1)
|
||||
@@ -101,7 +126,8 @@ public static class CapabilitiesOpenSource
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
Capability.FUNCTION_CALLING
|
||||
Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
// Default:
|
||||
@@ -109,7 +135,8 @@ public static class CapabilitiesOpenSource
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
Capability.FUNCTION_CALLING
|
||||
Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -123,6 +150,7 @@ public static class CapabilitiesOpenSource
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
if(modelName.StartsWith("grok-3-mini"))
|
||||
@@ -132,6 +160,7 @@ public static class CapabilitiesOpenSource
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
if(modelName.StartsWith("grok-3"))
|
||||
@@ -141,10 +170,41 @@ public static class CapabilitiesOpenSource
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
}
|
||||
|
||||
//
|
||||
// OpenAI models:
|
||||
//
|
||||
if (modelName.IndexOf("gpt-oss") is not -1 ||
|
||||
modelName.IndexOf("gpt-3.5") is not -1)
|
||||
{
|
||||
if(modelName.IndexOf("gpt-oss") is not -1)
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING,
|
||||
Capability.WEB_SEARCH,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
if(modelName.IndexOf("gpt-3.5") is not -1)
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
}
|
||||
|
||||
// Default:
|
||||
return [Capability.TEXT_INPUT, Capability.TEXT_OUTPUT];
|
||||
return [
|
||||
Capability.TEXT_INPUT, Capability.TEXT_OUTPUT,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -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,11 @@ 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),
|
||||
};
|
||||
|
||||
// Prepare the DeepSeek HTTP chat request:
|
||||
var deepSeekChatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||
var deepSeekChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
||||
{
|
||||
Model = chatModel.Id,
|
||||
|
||||
@@ -76,7 +78,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;
|
||||
}
|
||||
|
||||
@@ -117,12 +119,14 @@ public sealed class ProviderDeepSeek(ILogger logger) : BaseProvider("https://api
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.ALWAYS_REASONING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -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,7 +33,7 @@ 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),
|
||||
};
|
||||
|
||||
// Prepare the Fireworks HTTP chat request:
|
||||
@@ -77,7 +80,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,11 @@ 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),
|
||||
};
|
||||
|
||||
// Prepare the GWDG HTTP chat request:
|
||||
var gwdgChatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||
var gwdgChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
||||
{
|
||||
Model = chatModel.Id,
|
||||
|
||||
@@ -76,7 +78,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,7 +33,7 @@ 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),
|
||||
};
|
||||
|
||||
// Prepare the Google HTTP chat request:
|
||||
@@ -78,7 +80,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;
|
||||
}
|
||||
|
||||
@@ -136,6 +138,7 @@ public class ProviderGoogle(ILogger logger) : BaseProvider("https://generativela
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
// Image generation:
|
||||
@@ -146,6 +149,7 @@ public class ProviderGoogle(ILogger logger) : BaseProvider("https://generativela
|
||||
Capability.SPEECH_INPUT, Capability.VIDEO_INPUT,
|
||||
|
||||
Capability.TEXT_OUTPUT, Capability.IMAGE_OUTPUT,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
// Realtime model:
|
||||
@@ -158,6 +162,7 @@ public class ProviderGoogle(ILogger logger) : BaseProvider("https://generativela
|
||||
Capability.TEXT_OUTPUT, Capability.SPEECH_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
// The 2.0 flash models cannot call functions:
|
||||
@@ -168,6 +173,7 @@ public class ProviderGoogle(ILogger logger) : BaseProvider("https://generativela
|
||||
Capability.SPEECH_INPUT, Capability.VIDEO_INPUT,
|
||||
|
||||
Capability.TEXT_OUTPUT,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
// The old 1.0 pro vision model:
|
||||
@@ -177,6 +183,7 @@ public class ProviderGoogle(ILogger logger) : BaseProvider("https://generativela
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
|
||||
Capability.TEXT_OUTPUT,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
// Default to all other Gemini models:
|
||||
@@ -188,6 +195,7 @@ public class ProviderGoogle(ILogger logger) : BaseProvider("https://generativela
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -199,6 +207,7 @@ public class ProviderGoogle(ILogger logger) : BaseProvider("https://generativela
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -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,7 +33,7 @@ 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),
|
||||
};
|
||||
|
||||
// Prepare the OpenAI HTTP chat request:
|
||||
@@ -60,8 +62,6 @@ 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,
|
||||
@@ -80,7 +80,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,11 @@ 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),
|
||||
};
|
||||
|
||||
// Prepare the Helmholtz HTTP chat request:
|
||||
var helmholtzChatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||
var helmholtzChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
||||
{
|
||||
Model = chatModel.Id,
|
||||
|
||||
@@ -76,7 +78,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,11 @@ public sealed class ProviderHuggingFace : BaseProvider
|
||||
var systemPrompt = new Message
|
||||
{
|
||||
Role = "system",
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread, this.logger),
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
|
||||
};
|
||||
|
||||
// Prepare the HuggingFace HTTP chat request:
|
||||
var huggingfaceChatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||
var huggingfaceChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
||||
{
|
||||
Model = chatModel.Id,
|
||||
|
||||
@@ -81,7 +83,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -27,7 +27,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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
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 },
|
||||
LLMProviders.ANTHROPIC => new ProviderAnthropic { InstanceName = instanceName },
|
||||
LLMProviders.MISTRAL => new ProviderMistral { InstanceName = instanceName },
|
||||
LLMProviders.GOOGLE => new ProviderGoogle { InstanceName = instanceName },
|
||||
LLMProviders.X => new ProviderX { InstanceName = instanceName },
|
||||
LLMProviders.DEEP_SEEK => new ProviderDeepSeek { InstanceName = instanceName },
|
||||
LLMProviders.ALIBABA_CLOUD => new ProviderAlibabaCloud { InstanceName = instanceName },
|
||||
LLMProviders.PERPLEXITY => new ProviderPerplexity { InstanceName = instanceName },
|
||||
|
||||
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 },
|
||||
LLMProviders.FIREWORKS => new ProviderFireworks { InstanceName = instanceName },
|
||||
LLMProviders.HUGGINGFACE => new ProviderHuggingFace(inferenceProvider, model) { InstanceName = instanceName },
|
||||
|
||||
LLMProviders.SELF_HOSTED => new ProviderSelfHosted(logger, host, hostname) { InstanceName = instanceName },
|
||||
LLMProviders.SELF_HOSTED => new ProviderSelfHosted(host, hostname) { InstanceName = instanceName },
|
||||
|
||||
LLMProviders.HELMHOLTZ => new ProviderHelmholtz(logger) { InstanceName = instanceName },
|
||||
LLMProviders.GWDG => new ProviderGWDG(logger) { InstanceName = instanceName },
|
||||
LLMProviders.HELMHOLTZ => new ProviderHelmholtz { InstanceName = instanceName },
|
||||
LLMProviders.GWDG => new ProviderGWDG { InstanceName = instanceName },
|
||||
|
||||
_ => 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,
|
||||
|
||||
@@ -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,7 +31,7 @@ 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),
|
||||
};
|
||||
|
||||
// Prepare the Mistral HTTP chat request:
|
||||
@@ -58,8 +60,6 @@ public sealed class ProviderMistral(ILogger logger) : BaseProvider("https://api.
|
||||
_ => string.Empty,
|
||||
}
|
||||
}).ToList()],
|
||||
|
||||
RandomSeed = chatThread.Seed,
|
||||
|
||||
// Right now, we only support streaming completions:
|
||||
Stream = true,
|
||||
@@ -79,7 +79,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;
|
||||
}
|
||||
|
||||
@@ -134,6 +134,7 @@ public sealed class ProviderMistral(ILogger logger) : BaseProvider("https://api.
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
// Mistral medium:
|
||||
@@ -144,6 +145,7 @@ public sealed class ProviderMistral(ILogger logger) : BaseProvider("https://api.
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
// Mistral small:
|
||||
@@ -154,6 +156,7 @@ public sealed class ProviderMistral(ILogger logger) : BaseProvider("https://api.
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
// Mistral saba:
|
||||
@@ -162,6 +165,7 @@ public sealed class ProviderMistral(ILogger logger) : BaseProvider("https://api.
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
// Default:
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -19,7 +19,7 @@ public class NoProvider : IProvider
|
||||
|
||||
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,18 @@
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// Data structure representing a choice in a chat completion annotation response.
|
||||
/// </summary>
|
||||
/// <param name="Index">The index of the choice.</param>
|
||||
/// <param name="Delta">The delta information for the choice.</param>
|
||||
public record ChatCompletionAnnotationChoice(int Index, ChatCompletionAnnotationDelta Delta);
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// Data structure representing annotation deltas in chat completions.
|
||||
/// </summary>
|
||||
/// <param name="Annotations">The list of annotations, which can be null.</param>
|
||||
public record ChatCompletionAnnotationDelta(IList<Annotation>? Annotations);
|
||||
@@ -0,0 +1,57 @@
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a line of a chat completion annotation stream.
|
||||
/// </summary>
|
||||
/// <param name="Id">The unique identifier of the chat completion.</param>
|
||||
/// <param name="Object">The type of object returned, typically "chat.completion".</param>
|
||||
/// <param name="Created">The creation timestamp of the chat completion in Unix epoch format.</param>
|
||||
/// <param name="Model">The model used for the chat completion.</param>
|
||||
/// <param name="SystemFingerprint">The system fingerprint associated with the chat completion.</param>
|
||||
/// <param name="Choices">The list of choices returned in the chat completion.</param>
|
||||
public record ChatCompletionAnnotationStreamLine(string Id, string Object, uint Created, string Model, string SystemFingerprint, IList<ChatCompletionAnnotationChoice> Choices) : IAnnotationStreamLine
|
||||
{
|
||||
#region Implementation of IAnnotationStreamLine
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ContainsSources() => this.Choices.Any(choice => choice.Delta.Annotations is not null && choice.Delta.Annotations.Any(annotation => annotation is not AnnotatingUnknown));
|
||||
|
||||
/// <inheritdoc />
|
||||
public IList<ISource> GetSources()
|
||||
{
|
||||
var sources = new List<ISource>();
|
||||
foreach (var choice in this.Choices)
|
||||
{
|
||||
if (choice.Delta.Annotations is null)
|
||||
continue;
|
||||
|
||||
// Iterate through all annotations:
|
||||
foreach (var annotation in choice.Delta.Annotations)
|
||||
{
|
||||
// Check if the annotation is of the expected type and extract the source information:
|
||||
if (annotation is ChatCompletionAnnotatingURL urlAnnotation)
|
||||
sources.Add(new Source(urlAnnotation.UrlCitation.Title, urlAnnotation.UrlCitation.URL, SourceOrigin.LLM));
|
||||
|
||||
//
|
||||
// Check for the unexpected annotation type of the Responses API.
|
||||
//
|
||||
// This seems weird at first. But there are two possibilities why this could happen:
|
||||
// - Anyone of the open source providers such as ollama, LM Studio, etc. could
|
||||
// implement & use the Responses API data structures for annotations in their
|
||||
// chat completion endpoint.
|
||||
//
|
||||
// - Our custom JSON converter checks for the Responses API data type first. If it
|
||||
// fails, it checks for the chat completion API data type. So, when the Responses
|
||||
// API data type is valid, it will be deserialized into that type, even though
|
||||
// we are calling the chat completion endpoint.
|
||||
//
|
||||
if (annotation is ResponsesAnnotatingUrlCitationData citationData)
|
||||
sources.Add(new Source(citationData.Title, citationData.URL, SourceOrigin.LLM));
|
||||
}
|
||||
}
|
||||
|
||||
return sources;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// Data model for a choice made by the AI.
|
||||
/// </summary>
|
||||
/// <param name="Index">The index of the choice.</param>
|
||||
/// <param name="Delta">The delta text of the choice.</param>
|
||||
public record ChatCompletionChoice(int Index, ChatCompletionDelta Delta)
|
||||
{
|
||||
public ChatCompletionChoice() : this(0, new (string.Empty))
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// The delta text of a choice.
|
||||
/// </summary>
|
||||
/// <param name="Content">The content of the delta text.</param>
|
||||
public record ChatCompletionDelta(string Content)
|
||||
{
|
||||
public ChatCompletionDelta() : this(string.Empty)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// Data model for a delta line in the chat completion response stream.
|
||||
/// </summary>
|
||||
/// <param name="Id">The id of the response.</param>
|
||||
/// <param name="Object">The object describing the response.</param>
|
||||
/// <param name="Created">The timestamp of the response.</param>
|
||||
/// <param name="Model">The model used for the response.</param>
|
||||
/// <param name="SystemFingerprint">The system fingerprint; together with the seed, this allows you to reproduce the response.</param>
|
||||
/// <param name="Choices">The choices made by the AI.</param>
|
||||
public record ChatCompletionDeltaStreamLine(string Id, string Object, uint Created, string Model, string SystemFingerprint, IList<ChatCompletionChoice> Choices) : IResponseStreamLine
|
||||
{
|
||||
public ChatCompletionDeltaStreamLine() : this(string.Empty, string.Empty, 0, string.Empty, string.Empty, [])
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ContainsContent() => this.Choices.Count > 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ContentStreamChunk GetContent() => new(this.Choices[0].Delta.Content, []);
|
||||
|
||||
#region Implementation of IAnnotationStreamLine
|
||||
|
||||
//
|
||||
// Please note that there are multiple options where LLM providers might stream sources:
|
||||
//
|
||||
// - As part of the delta content while streaming. That would be part of this class.
|
||||
// - By using a dedicated stream event and data structure. That would be another class implementing IResponseStreamLine.
|
||||
//
|
||||
// Right now, OpenAI uses the latter approach, so we don't have any sources here. And
|
||||
// because no other provider does it yet, we don't have any implementation here either.
|
||||
//
|
||||
// One example where sources are part of the delta content is the Perplexity provider.
|
||||
//
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ContainsSources() => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IList<ISource> GetSources() => [];
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// Represents citation data for a URL in a chat completion response.
|
||||
/// </summary>
|
||||
/// <param name="EndIndex">The end index of the citation in the response text.</param>
|
||||
/// <param name="StartIndex">The start index of the citation in the response text.</param>
|
||||
/// <param name="Title">The title of the cited source.</param>
|
||||
/// <param name="URL">The URL of the cited source.</param>
|
||||
public sealed record ChatCompletionUrlCitationData(
|
||||
int EndIndex,
|
||||
int StartIndex,
|
||||
string Title,
|
||||
string URL);
|
||||
@@ -1,21 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// The OpenAI chat request model.
|
||||
/// </summary>
|
||||
/// <param name="Model">Which model to use for chat completion.</param>
|
||||
/// <param name="Messages">The chat messages.</param>
|
||||
/// <param name="Stream">Whether to stream the chat completion.</param>
|
||||
/// <param name="Seed">The seed for the chat completion.</param>
|
||||
/// <param name="FrequencyPenalty">The frequency penalty for the chat completion.</param>
|
||||
public readonly record struct ChatRequest(
|
||||
string Model,
|
||||
IList<Message> Messages,
|
||||
bool Stream,
|
||||
int Seed,
|
||||
|
||||
[Range(-2.0f, 2.0f)]
|
||||
float FrequencyPenalty
|
||||
);
|
||||
@@ -11,8 +11,10 @@ namespace AIStudio.Provider.OpenAI;
|
||||
/// <summary>
|
||||
/// The OpenAI provider.
|
||||
/// </summary>
|
||||
public sealed class ProviderOpenAI(ILogger logger) : BaseProvider("https://api.openai.com/v1/", logger)
|
||||
public sealed class ProviderOpenAI() : BaseProvider("https://api.openai.com/v1/", LOGGER)
|
||||
{
|
||||
private static readonly ILogger<ProviderOpenAI> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ProviderOpenAI>();
|
||||
|
||||
#region Implementation of IProvider
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -22,7 +24,7 @@ public sealed class ProviderOpenAI(ILogger logger) : BaseProvider("https://api.o
|
||||
public override string InstanceName { get; set; } = "OpenAI";
|
||||
|
||||
/// <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,16 +32,20 @@ public sealed class ProviderOpenAI(ILogger logger) : BaseProvider("https://api.o
|
||||
yield break;
|
||||
|
||||
// Unfortunately, OpenAI changed the name of the system prompt based on the model.
|
||||
// All models that start with "o" (the omni aka reasoning models) and all GPT4o models
|
||||
// have the system prompt named "developer". All other models have the system prompt
|
||||
// named "system". We need to check this to get the correct system prompt.
|
||||
// All models that start with "o" (the omni aka reasoning models), all GPT4o models,
|
||||
// and all newer models have the system prompt named "developer". All other models
|
||||
// have the system prompt named "system". We need to check this to get the correct
|
||||
// system prompt.
|
||||
//
|
||||
// To complicate it even more: The early versions of reasoning models, which are released
|
||||
// before the 17th of December 2024, have no system prompt at all. We need to check this
|
||||
// as well.
|
||||
|
||||
// Apply the basic rule first:
|
||||
var systemPromptRole = chatModel.Id.StartsWith('o') || chatModel.Id.Contains("4o") ? "developer" : "system";
|
||||
var systemPromptRole =
|
||||
chatModel.Id.StartsWith('o') ||
|
||||
chatModel.Id.StartsWith("gpt-5", StringComparison.Ordinal) ||
|
||||
chatModel.Id.Contains("4o") ? "developer" : "system";
|
||||
|
||||
// Check if the model is an early version of the reasoning models:
|
||||
systemPromptRole = chatModel.Id switch
|
||||
@@ -51,53 +57,113 @@ public sealed class ProviderOpenAI(ILogger logger) : BaseProvider("https://api.o
|
||||
|
||||
_ => systemPromptRole,
|
||||
};
|
||||
|
||||
this.logger.LogInformation($"Using the system prompt role '{systemPromptRole}' for model '{chatModel.Id}'.");
|
||||
|
||||
// Read the model capabilities:
|
||||
var modelCapabilities = this.GetModelCapabilities(chatModel);
|
||||
|
||||
// Check if we are using the Responses API or the Chat Completion API:
|
||||
var usingResponsesAPI = modelCapabilities.Contains(Capability.RESPONSES_API);
|
||||
|
||||
// Prepare the request path based on the API we are using:
|
||||
var requestPath = usingResponsesAPI ? "responses" : "chat/completions";
|
||||
|
||||
LOGGER.LogInformation("Using the system prompt role '{SystemPromptRole}' and the '{RequestPath}' API for model '{ChatModelId}'.", systemPromptRole, requestPath, chatModel.Id);
|
||||
|
||||
// Prepare the system prompt:
|
||||
var systemPrompt = new Message
|
||||
{
|
||||
Role = systemPromptRole,
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread, this.logger),
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
|
||||
};
|
||||
|
||||
//
|
||||
// Prepare the tools we want to use:
|
||||
//
|
||||
IList<Tool> tools = modelCapabilities.Contains(Capability.WEB_SEARCH) switch
|
||||
{
|
||||
true => [ Tools.WEB_SEARCH ],
|
||||
_ => []
|
||||
};
|
||||
|
||||
// Prepare the OpenAI HTTP chat request:
|
||||
var openAIChatRequest = JsonSerializer.Serialize(new ChatRequest
|
||||
//
|
||||
// Create the request: either for the Responses API or the Chat Completion API
|
||||
//
|
||||
var openAIChatRequest = usingResponsesAPI switch
|
||||
{
|
||||
Model = chatModel.Id,
|
||||
|
||||
// Build the messages:
|
||||
// - First of all the system prompt
|
||||
// - Then none-empty user and AI messages
|
||||
Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message
|
||||
// Chat Completion API request:
|
||||
false => JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
||||
{
|
||||
Role = n.Role switch
|
||||
{
|
||||
ChatRole.USER => "user",
|
||||
ChatRole.AI => "assistant",
|
||||
ChatRole.AGENT => "assistant",
|
||||
ChatRole.SYSTEM => systemPromptRole,
|
||||
|
||||
_ => "user",
|
||||
},
|
||||
|
||||
Content = n.Content switch
|
||||
{
|
||||
ContentText text => text.Text,
|
||||
_ => string.Empty,
|
||||
}
|
||||
}).ToList()],
|
||||
|
||||
Seed = chatThread.Seed,
|
||||
Model = chatModel.Id,
|
||||
|
||||
// Right now, we only support streaming completions:
|
||||
Stream = true,
|
||||
}, JSON_SERIALIZER_OPTIONS);
|
||||
// Build the messages:
|
||||
// - First of all the system prompt
|
||||
// - Then none-empty user and AI messages
|
||||
Messages = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message
|
||||
{
|
||||
Role = n.Role switch
|
||||
{
|
||||
ChatRole.USER => "user",
|
||||
ChatRole.AI => "assistant",
|
||||
ChatRole.AGENT => "assistant",
|
||||
ChatRole.SYSTEM => systemPromptRole,
|
||||
|
||||
_ => "user",
|
||||
},
|
||||
|
||||
Content = n.Content switch
|
||||
{
|
||||
ContentText text => text.Text,
|
||||
_ => string.Empty,
|
||||
}
|
||||
}).ToList()],
|
||||
|
||||
// Right now, we only support streaming completions:
|
||||
Stream = true,
|
||||
}, JSON_SERIALIZER_OPTIONS),
|
||||
|
||||
// Responses API request:
|
||||
true => JsonSerializer.Serialize(new ResponsesAPIRequest
|
||||
{
|
||||
Model = chatModel.Id,
|
||||
|
||||
// Build the messages:
|
||||
// - First of all the system prompt
|
||||
// - Then none-empty user and AI messages
|
||||
Input = [systemPrompt, ..chatThread.Blocks.Where(n => n.ContentType is ContentType.TEXT && !string.IsNullOrWhiteSpace((n.Content as ContentText)?.Text)).Select(n => new Message
|
||||
{
|
||||
Role = n.Role switch
|
||||
{
|
||||
ChatRole.USER => "user",
|
||||
ChatRole.AI => "assistant",
|
||||
ChatRole.AGENT => "assistant",
|
||||
ChatRole.SYSTEM => systemPromptRole,
|
||||
|
||||
_ => "user",
|
||||
},
|
||||
|
||||
Content = n.Content switch
|
||||
{
|
||||
ContentText text => text.Text,
|
||||
_ => string.Empty,
|
||||
}
|
||||
}).ToList()],
|
||||
|
||||
// Right now, we only support streaming completions:
|
||||
Stream = true,
|
||||
|
||||
// We do not want to store any data on OpenAI's servers:
|
||||
Store = false,
|
||||
|
||||
// Tools we want to use:
|
||||
Tools = tools,
|
||||
|
||||
}, JSON_SERIALIZER_OPTIONS),
|
||||
};
|
||||
|
||||
async Task<HttpRequestMessage> RequestBuilder()
|
||||
{
|
||||
// Build the HTTP post request:
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, requestPath);
|
||||
|
||||
// Set the authorization header:
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
||||
@@ -106,29 +172,35 @@ public sealed class ProviderOpenAI(ILogger logger) : BaseProvider("https://api.o
|
||||
request.Content = new StringContent(openAIChatRequest, Encoding.UTF8, "application/json");
|
||||
return request;
|
||||
}
|
||||
|
||||
if (usingResponsesAPI)
|
||||
await foreach (var content in this.StreamResponsesInternal<ResponsesDeltaStreamLine, ResponsesAnnotationStreamLine>("OpenAI", RequestBuilder, token))
|
||||
yield return content;
|
||||
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine>("OpenAI", RequestBuilder, token))
|
||||
yield return content;
|
||||
else
|
||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>("OpenAI", RequestBuilder, token))
|
||||
yield return content;
|
||||
}
|
||||
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async IAsyncEnumerable<ImageURL> StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
{
|
||||
var models = await this.LoadModels(["gpt-", "o1-", "o3-", "o4-"], token, apiKeyProvisional);
|
||||
var models = await this.LoadModels(["chatgpt-", "gpt-", "o1-", "o3-", "o4-"], token, apiKeyProvisional);
|
||||
return models.Where(model => !model.Id.Contains("image", StringComparison.OrdinalIgnoreCase) &&
|
||||
!model.Id.Contains("realtime", StringComparison.OrdinalIgnoreCase) &&
|
||||
!model.Id.Contains("audio", StringComparison.OrdinalIgnoreCase) &&
|
||||
!model.Id.Contains("tts", StringComparison.OrdinalIgnoreCase) &&
|
||||
!model.Id.Contains("transcribe", StringComparison.OrdinalIgnoreCase) &&
|
||||
!model.Id.Contains("o1-pro", StringComparison.OrdinalIgnoreCase));
|
||||
!model.Id.Contains("transcribe", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -147,6 +219,26 @@ public sealed class ProviderOpenAI(ILogger logger) : BaseProvider("https://api.o
|
||||
{
|
||||
var modelName = model.Id.ToLowerInvariant().AsSpan();
|
||||
|
||||
if (modelName is "gpt-4o-search-preview")
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.WEB_SEARCH,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
if (modelName is "gpt-4o-mini-search-preview")
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.WEB_SEARCH,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
if (modelName.StartsWith("o1-mini"))
|
||||
return
|
||||
[
|
||||
@@ -154,32 +246,63 @@ public sealed class ProviderOpenAI(ILogger logger) : BaseProvider("https://api.o
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.ALWAYS_REASONING,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
if(modelName is "gpt-3.5-turbo")
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
Capability.RESPONSES_API,
|
||||
];
|
||||
|
||||
if(modelName.StartsWith("gpt-3.5"))
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
];
|
||||
|
||||
if (modelName.StartsWith("chatgpt-4o-"))
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
Capability.RESPONSES_API,
|
||||
];
|
||||
|
||||
if (modelName.StartsWith("o3-mini"))
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING
|
||||
Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING,
|
||||
Capability.RESPONSES_API,
|
||||
];
|
||||
|
||||
if (modelName.StartsWith("o4-mini") || modelName.StartsWith("o1") || modelName.StartsWith("o3"))
|
||||
if (modelName.StartsWith("o4-mini") || modelName.StartsWith("o3"))
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING
|
||||
Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING,
|
||||
Capability.WEB_SEARCH,
|
||||
Capability.RESPONSES_API,
|
||||
];
|
||||
|
||||
if(modelName.StartsWith("gpt-3.5"))
|
||||
if (modelName.StartsWith("o1"))
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
];
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.ALWAYS_REASONING, Capability.FUNCTION_CALLING,
|
||||
Capability.RESPONSES_API,
|
||||
];
|
||||
|
||||
if(modelName.StartsWith("gpt-4-turbo"))
|
||||
return
|
||||
@@ -187,7 +310,8 @@ public sealed class ProviderOpenAI(ILogger logger) : BaseProvider("https://api.o
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING
|
||||
Capability.FUNCTION_CALLING,
|
||||
Capability.RESPONSES_API,
|
||||
];
|
||||
|
||||
if(modelName is "gpt-4" || modelName.StartsWith("gpt-4-"))
|
||||
@@ -195,14 +319,37 @@ public sealed class ProviderOpenAI(ILogger logger) : BaseProvider("https://api.o
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
Capability.RESPONSES_API,
|
||||
];
|
||||
|
||||
if(modelName.StartsWith("gpt-5-nano"))
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING, Capability.ALWAYS_REASONING,
|
||||
Capability.RESPONSES_API,
|
||||
];
|
||||
|
||||
if(modelName is "gpt-5" || modelName.StartsWith("gpt-5-"))
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING, Capability.ALWAYS_REASONING,
|
||||
Capability.WEB_SEARCH,
|
||||
Capability.RESPONSES_API,
|
||||
];
|
||||
|
||||
return
|
||||
[
|
||||
Capability.TEXT_INPUT, Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
|
||||
Capability.FUNCTION_CALLING,
|
||||
Capability.RESPONSES_API,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// The request body for the Responses API.
|
||||
/// </summary>
|
||||
/// <param name="Model">Which model to use.</param>
|
||||
/// <param name="Input">The chat messages.</param>
|
||||
/// <param name="Stream">Whether to stream the response.</param>
|
||||
/// <param name="Store">Whether to store the response on the server (usually OpenAI's infrastructure).</param>
|
||||
/// <param name="Tools">The tools to use for the request.</param>
|
||||
public record ResponsesAPIRequest(
|
||||
string Model,
|
||||
IList<Message> Input,
|
||||
bool Stream,
|
||||
bool Store,
|
||||
IList<Tool> Tools)
|
||||
{
|
||||
public ResponsesAPIRequest() : this(string.Empty, [], true, false, [])
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// Data structure for URL citation annotations in the OpenAI Responses API.
|
||||
/// </summary>
|
||||
/// <param name="Type">The type of annotation, typically "url_citation".</param>
|
||||
/// <param name="EndIndex">The end index of the annotated text in the response.</param>
|
||||
/// <param name="StartIndex">The start index of the annotated text in the response.</param>
|
||||
/// <param name="Title">The title of the cited URL.</param>
|
||||
/// <param name="URL">The URL being cited.</param>
|
||||
public sealed record ResponsesAnnotatingUrlCitationData(
|
||||
string Type,
|
||||
int EndIndex,
|
||||
int StartIndex,
|
||||
string Title,
|
||||
string URL) : Annotation(Type);
|
||||
@@ -0,0 +1,45 @@
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// Data structure for a line in the response stream of the Responses API, containing an annotation.
|
||||
/// </summary>
|
||||
/// <param name="Type">The type of the annotation.</param>
|
||||
/// <param name="AnnotationIndex">The continuous index of the annotation in the response.</param>
|
||||
/// <param name="Annotation">The annotation details.</param>
|
||||
public sealed record ResponsesAnnotationStreamLine(string Type, int AnnotationIndex, Annotation Annotation) : IAnnotationStreamLine
|
||||
{
|
||||
#region Implementation of IAnnotationStreamLine
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ContainsSources()
|
||||
{
|
||||
return this.Annotation is not AnnotatingUnknown;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IList<ISource> GetSources()
|
||||
{
|
||||
//
|
||||
// Check for the unexpected annotation type of the chat completion API.
|
||||
//
|
||||
// This seems weird at first. But there are two possibilities why this could happen:
|
||||
// - Anyone of the open source providers such as ollama, LM Studio, etc. could
|
||||
// implement and use the chat completion API data structures for annotations in their
|
||||
// Responses API endpoint.
|
||||
//
|
||||
// - Our custom JSON converter checks for all possible annotation data types. So,
|
||||
// when the streamed data is valid for any annotation type, it will be deserialized
|
||||
// into that type, even though we are calling the Responses API endpoint.
|
||||
//
|
||||
if (this.Annotation is ChatCompletionAnnotatingURL urlAnnotation)
|
||||
return [new Source(urlAnnotation.UrlCitation.Title, urlAnnotation.UrlCitation.URL, SourceOrigin.LLM)];
|
||||
|
||||
// Check for the expected annotation type of the Responses API:
|
||||
if (this.Annotation is ResponsesAnnotatingUrlCitationData urlCitationData)
|
||||
return [new Source(urlCitationData.Title, urlCitationData.URL, SourceOrigin.LLM)];
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// Data model for a delta line in the Response API chat completion stream.
|
||||
/// </summary>
|
||||
/// <param name="Type">The type of the response.</param>
|
||||
/// <param name="Delta">The delta content of the response.</param>
|
||||
public record ResponsesDeltaStreamLine(
|
||||
string Type,
|
||||
string Delta) : IResponseStreamLine
|
||||
{
|
||||
#region Implementation of IResponseStreamLine
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ContainsContent() => !string.IsNullOrWhiteSpace(this.Delta);
|
||||
|
||||
/// <inheritdoc />
|
||||
public ContentStreamChunk GetContent() => new(this.Delta, this.GetSources());
|
||||
|
||||
//
|
||||
// Please note that there are multiple options where LLM providers might stream sources:
|
||||
//
|
||||
// - As part of the delta content while streaming. That would be part of this class.
|
||||
// - By using a dedicated stream event and data structure. That would be another class implementing IResponseStreamLine.
|
||||
//
|
||||
// Right now, OpenAI uses the latter approach, so we don't have any sources here. And
|
||||
// because no other provider does it yet, we don't have any implementation here either.
|
||||
//
|
||||
// One example where sources are part of the delta content is the Perplexity provider.
|
||||
//
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ContainsSources() => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IList<ISource> GetSources() => [];
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a tool used by the AI model.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Right now, only our OpenAI provider is using tools. Thus, this class is located in the
|
||||
/// OpenAI namespace. In the future, when other providers also support tools, this class can
|
||||
/// be moved into the provider namespace.
|
||||
/// </remarks>
|
||||
/// <param name="Type">The type of the tool.</param>
|
||||
public record Tool(string Type);
|
||||
Loaded 100 of 176 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user