diff --git a/app/MindWork AI Studio/Pages/Home.razor.cs b/app/MindWork AI Studio/Pages/Home.razor.cs
index bdd46e06..7facd6e3 100644
--- a/app/MindWork AI Studio/Pages/Home.razor.cs
+++ b/app/MindWork AI Studio/Pages/Home.razor.cs
@@ -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 (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("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), OpenRouter, 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.")),
diff --git a/app/MindWork AI Studio/Pages/About.razor b/app/MindWork AI Studio/Pages/Information.razor
similarity index 99%
rename from app/MindWork AI Studio/Pages/About.razor
rename to app/MindWork AI Studio/Pages/Information.razor
index b442a833..acaf8567 100644
--- a/app/MindWork AI Studio/Pages/About.razor
+++ b/app/MindWork AI Studio/Pages/Information.razor
@@ -4,7 +4,7 @@
- @T("About MindWork AI Studio")
+ @T("Information about MindWork AI Studio")
diff --git a/app/MindWork AI Studio/Pages/About.razor.cs b/app/MindWork AI Studio/Pages/Information.razor.cs
similarity index 98%
rename from app/MindWork AI Studio/Pages/About.razor.cs
rename to app/MindWork AI Studio/Pages/Information.razor.cs
index 5f3c2fce..42349201 100644
--- a/app/MindWork AI Studio/Pages/About.razor.cs
+++ b/app/MindWork AI Studio/Pages/Information.razor.cs
@@ -16,14 +16,14 @@ using DialogOptions = AIStudio.Dialogs.DialogOptions;
namespace AIStudio.Pages;
-public partial class About : MSGComponentBase
+public partial class Information : MSGComponentBase
{
[Inject]
private RustService RustService { get; init; } = null!;
-
+
[Inject]
private IDialogService DialogService { get; init; } = null!;
-
+
[Inject]
private ISnackbar Snackbar { get; init; } = null!;
@@ -36,7 +36,7 @@ public partial class About : MSGComponentBase
private static readonly MetaDataLibrariesAttribute META_DATA_LIBRARIES = ASSEMBLY.GetCustomAttribute()!;
private static readonly MetaDataDatabasesAttribute META_DATA_DATABASES = ASSEMBLY.GetCustomAttribute()!;
- private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(About).Namespace, nameof(About));
+ private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(Information).Namespace, nameof(Information));
private string osLanguage = string.Empty;
@@ -211,7 +211,7 @@ public partial class About : MSGComponentBase
## Notice
- Copyright 2025 Thorsten Sommer
+ Copyright 2026 Thorsten Sommer
## Terms and Conditions
diff --git a/app/MindWork AI Studio/Pages/Settings.razor b/app/MindWork AI Studio/Pages/Settings.razor
index da1f837d..70201807 100644
--- a/app/MindWork AI Studio/Pages/Settings.razor
+++ b/app/MindWork AI Studio/Pages/Settings.razor
@@ -12,18 +12,23 @@
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
{
-
+
}
-
+ @if (PreviewFeatures.PRE_SPEECH_TO_TEXT_2026.IsEnabled(this.SettingsManager))
+ {
+
+ }
+
+
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
{
-
-
+
+
}
-
+
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Pages/Settings.razor.cs b/app/MindWork AI Studio/Pages/Settings.razor.cs
index 89c7338b..561ebb46 100644
--- a/app/MindWork AI Studio/Pages/Settings.razor.cs
+++ b/app/MindWork AI Studio/Pages/Settings.razor.cs
@@ -9,6 +9,7 @@ public partial class Settings : MSGComponentBase
{
private List> availableLLMProviders = new();
private List> availableEmbeddingProviders = new();
+ private List> availableTranscriptionProviders = new();
#region Overrides of ComponentBase
diff --git a/app/MindWork AI Studio/Plugins/configuration/plugin.lua b/app/MindWork AI Studio/Plugins/configuration/plugin.lua
index c4e5f08c..9708c666 100644
--- a/app/MindWork AI Studio/Plugins/configuration/plugin.lua
+++ b/app/MindWork AI Studio/Plugins/configuration/plugin.lua
@@ -53,11 +53,11 @@ CONFIG["LLM_PROVIDERS"][#CONFIG["LLM_PROVIDERS"]+1] = {
["Id"] = "00000000-0000-0000-0000-000000000000",
["InstanceName"] = "",
["UsedLLMProvider"] = "SELF_HOSTED",
-
+
-- Allowed values for Host are: LM_STUDIO, LLAMACPP, OLLAMA, and VLLM
["Host"] = "OLLAMA",
["Hostname"] = "",
-
+
-- Optional: Additional parameters for the API.
-- Please refer to the documentation of the selected host for details.
-- Might be something like ... \"temperature\": 0.5 ... for one parameter.
@@ -70,6 +70,42 @@ CONFIG["LLM_PROVIDERS"][#CONFIG["LLM_PROVIDERS"]+1] = {
}
}
+-- Transcription providers for voice-to-text functionality:
+CONFIG["TRANSCRIPTION_PROVIDERS"] = {}
+
+-- An example of a transcription provider configuration:
+-- CONFIG["TRANSCRIPTION_PROVIDERS"][#CONFIG["TRANSCRIPTION_PROVIDERS"]+1] = {
+-- ["Id"] = "00000000-0000-0000-0000-000000000000",
+-- ["Name"] = "",
+-- ["UsedLLMProvider"] = "SELF_HOSTED",
+--
+-- -- Allowed values for Host are: LM_STUDIO, LLAMACPP, OLLAMA, VLLM, and WHISPER_CPP
+-- ["Host"] = "WHISPER_CPP",
+-- ["Hostname"] = "",
+-- ["Model"] = {
+-- ["Id"] = "",
+-- ["DisplayName"] = "",
+-- }
+-- }
+
+-- Embedding providers for local RAG (Retrieval-Augmented Generation) functionality:
+CONFIG["EMBEDDING_PROVIDERS"] = {}
+
+-- An example of an embedding provider configuration:
+-- CONFIG["EMBEDDING_PROVIDERS"][#CONFIG["EMBEDDING_PROVIDERS"]+1] = {
+-- ["Id"] = "00000000-0000-0000-0000-000000000000",
+-- ["Name"] = "",
+-- ["UsedLLMProvider"] = "SELF_HOSTED",
+--
+-- -- Allowed values for Host are: LM_STUDIO, LLAMACPP, OLLAMA, and VLLM
+-- ["Host"] = "OLLAMA",
+-- ["Hostname"] = "",
+-- ["Model"] = {
+-- ["Id"] = "",
+-- ["DisplayName"] = "",
+-- }
+-- }
+
CONFIG["SETTINGS"] = {}
-- Configure the update check interval:
@@ -101,6 +137,22 @@ CONFIG["SETTINGS"] = {}
-- Please note: using an empty string ("") will lock the preselected profile selection, even though no valid preselected profile is found.
-- CONFIG["SETTINGS"]["DataApp.PreselectedProfile"] = "00000000-0000-0000-0000-000000000000"
+-- Configure the transcription provider for voice-to-text functionality.
+-- It must be one of the transcription provider IDs defined in CONFIG["TRANSCRIPTION_PROVIDERS"].
+-- Without a selected transcription provider, dictation and transcription features will be disabled.
+-- Please note: using an empty string ("") will lock the selection and disable dictation/transcription.
+-- CONFIG["SETTINGS"]["DataApp.UseTranscriptionProvider"] = "00000000-0000-0000-0000-000000000000"
+
+-- Configure which assistants should be hidden from the UI.
+-- Allowed values are:
+-- GRAMMAR_SPELLING_ASSISTANT, ICON_FINDER_ASSISTANT, REWRITE_ASSISTANT,
+-- TRANSLATION_ASSISTANT, AGENDA_ASSISTANT, CODING_ASSISTANT,
+-- TEXT_SUMMARIZER_ASSISTANT, EMAIL_ASSISTANT, LEGAL_CHECK_ASSISTANT,
+-- SYNONYMS_ASSISTANT, MY_TASKS_ASSISTANT, JOB_POSTING_ASSISTANT,
+-- BIAS_DAY_ASSISTANT, ERI_ASSISTANT, DOCUMENT_ANALYSIS_ASSISTANT,
+-- I18N_ASSISTANT
+-- CONFIG["SETTINGS"]["DataApp.HiddenAssistants"] = { "ERI_ASSISTANT", "I18N_ASSISTANT" }
+
-- Example chat templates for this configuration:
CONFIG["CHAT_TEMPLATES"] = {}
@@ -125,6 +177,33 @@ CONFIG["CHAT_TEMPLATES"][#CONFIG["CHAT_TEMPLATES"]+1] = {
}
}
+-- An example chat template with file attachments:
+-- This template automatically attaches specified files when the user selects it.
+CONFIG["CHAT_TEMPLATES"][#CONFIG["CHAT_TEMPLATES"]+1] = {
+ ["Id"] = "00000000-0000-0000-0000-000000000001",
+ ["Name"] = "Document Analysis Template",
+ ["SystemPrompt"] = "You are an expert document analyst. Please analyze the attached documents and provide insights.",
+ ["PredefinedUserPrompt"] = "Please analyze the attached company guidelines and summarize the key points.",
+ ["AllowProfileUsage"] = true,
+ -- Optional: Pre-attach files that will be automatically included when using this template.
+ -- These files will be loaded when the user selects this chat template.
+ -- Note: File paths must be absolute paths and accessible to all users.
+ ["FileAttachments"] = {
+ "G:\\Company\\Documents\\Guidelines.pdf",
+ "G:\\Company\\Documents\\CompanyPolicies.docx"
+ },
+ ["ExampleConversation"] = {
+ {
+ ["Role"] = "USER",
+ ["Content"] = "I have attached the company documents for analysis."
+ },
+ {
+ ["Role"] = "AI",
+ ["Content"] = "Thank you. I'll analyze the documents and provide a comprehensive summary."
+ }
+ }
+}
+
-- Profiles for this configuration:
CONFIG["PROFILES"] = {}
diff --git a/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua b/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua
index e6f60d7d..ebab2f8c 100644
--- a/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua
+++ b/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua
@@ -49,7 +49,7 @@ LANG_NAME = "Deutsch (Deutschland)"
UI_TEXT_CONTENT = {}
-- Objective
-UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::AGENDA::ASSISTANTAGENDA::T1121586136"] = "Ziel"
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::AGENDA::ASSISTANTAGENDA::T1121586136"] = "Zielsetzung"
-- Describe the topic of the meeting, seminar, etc. Is it about quantum computing, software engineering, or is it a general business meeting?
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::AGENDA::ASSISTANTAGENDA::T12079368"] = "Beschreiben Sie das Thema des Treffens, Seminars usw. Geht es um Quantencomputing, Softwareentwicklung oder handelt es sich um ein allgemeines Geschäftstreffen?"
@@ -1044,21 +1044,36 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T1214535771"] = "Ent
-- Added Content ({0} entries)
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T1258080997"] = "Hinzugefügte Inhalte ({0} Einträge)"
+-- No Lua code generated yet.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T1365137848"] = "Es wurde kein Lua-Code generiert."
+
-- Localized Content ({0} entries of {1})
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T1492071634"] = "Lokalisierte Inhalte ({0} von {1} Einträgen)"
-- Select the language plugin used for comparision.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T1523568309"] = "Wählen Sie das Sprach-Plugin für den Vergleich aus."
+-- Successfully updated plugin file.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T1524590750"] = "Plugin-Datei erfolgreich aktualisiert."
+
-- Was not able to load the language plugin for comparison ({0}). Please select a valid, loaded & running language plugin.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T1893011391"] = "Das Sprach-Plugin für den Vergleich konnte nicht geladen werden ({0}). Bitte wählen Sie ein gültiges, geladenes und laufendes Sprach-Plugin."
+-- No language plugin selected.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T237325294"] = "Kein Sprach-Plugin ausgewählt."
+
-- Target language
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T237828418"] = "Zielsprache"
+-- Write Lua code to language plugin file
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T253827221"] = "Lua-Code für Sprach-Plugin-Datei schreiben"
+
-- Language plugin used for comparision
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T263317578"] = "Sprach-Plugin für den Vergleich"
+-- Plugin file not found.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T2938065913"] = "Plugin-Datei nicht gefunden."
+
-- Localize AI Studio & generate the Lua code
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T3055634395"] = "Lokalisiere AI Studio & generiere den Lua-Code"
@@ -1089,6 +1104,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T453060723"] = "Die
-- The selected language plugin for comparison uses the IETF tag '{0}' which does not match the selected target language '{1}'. Please select a valid, loaded & running language plugin which matches the target language.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T458999393"] = "Das ausgewählte Sprach-Plugin für den Vergleich verwendet das IETF-Tag „{0}“, das nicht mit der ausgewählten Zielsprache „{1}“ übereinstimmt. Bitte wähle ein gültiges, geladenes und laufendes Sprach-Plugin aus, das mit der Zielsprache übereinstimmt."
+-- Could not find 'UI_TEXT_CONTENT = {}' marker in plugin file.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T628596031"] = "Konnte den 'UI_TEXT_CONTENT = {}'-Marker in der Plugin-Datei nicht finden."
+
-- Please provide a custom language.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T656744944"] = "Bitte geben Sie eine benutzerdefinierte Sprache an."
@@ -1098,6 +1116,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T851515643"] = "Bitt
-- Localization
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T897888480"] = "Lokalisierung"
+-- Error writing to plugin file.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T948564909"] = "Fehler beim Schreiben in die Plugin-Datei."
+
-- Your icon source
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ICONFINDER::ASSISTANTICONFINDER::T1302165948"] = "Ihre Icons-Quelle"
@@ -1494,36 +1515,45 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4188329028"] = "Nein, b
-- Export Chat to Microsoft Word
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T861873672"] = "Chat in Microsoft Word exportieren"
+-- The local image file does not exist. Skipping the image.
+UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T255679918"] = "Die lokale Bilddatei existiert nicht. Das Bild wird übersprungen."
+
+-- Failed to download the image from the URL. Skipping the image.
+UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T2996654916"] = "Das Bild konnte nicht von der URL heruntergeladen werden. Das Bild wird übersprungen."
+
+-- The local image file is too large (>10 MB). Skipping the image.
+UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T3219823625"] = "Die lokale Bilddatei ist zu groß (>10 MB). Das Bild wird übersprungen."
+
+-- The image at the URL is too large (>10 MB). Skipping the image.
+UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T349928509"] = "Das Bild unter der URL ist zu groß (>10 MB). Das Bild wird übersprungen."
+
-- Open Settings
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T1172211894"] = "Einstellungen öffnen"
+-- Click the paperclip to attach files, or click the number to see your attached files.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T1358313858"] = "Klicken Sie auf die Büroklammer, um Dateien anzuhängen, oder klicken Sie auf die Zahl, um Ihre angehängten Dateien anzuzeigen."
+
+-- Drop files here to attach them.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T143112277"] = "Dateien hier ablegen, um sie anzuhängen."
+
+-- Click here to attach files.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T1875575968"] = "Klicken Sie hier, um Dateien anzuhängen."
+
-- Drag and drop files into the marked area or click here to attach documents:
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T230755331"] = "Ziehen Sie Dateien in den markierten Bereich oder klicken Sie hier, um Dokumente anzuhängen:"
+-- Select files to attach
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T2495931372"] = "Dateien zum Anhängen auswählen"
+
-- Document Preview
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T285154968"] = "Dokumentenvorschau"
--- Videos are not supported yet
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T2928927510"] = "Videos werden noch nicht unterstützt."
-
--- Images are not supported yet
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T298062956"] = "Bilder werden noch nicht unterstützt."
-
--- Click to attach files
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T3521845090"] = "Klicken, um Dateien anzuhängen"
-
-- Clear file list
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T3759696136"] = "Dateiliste löschen"
-- Add file
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T4014053962"] = "Datei hinzufügen"
--- Executables are not allowed
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T4167762413"] = "Ausführbare Dateien sind nicht erlaubt"
-
--- Select a file to attach
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T595772870"] = "Datei zum Anhängen auswählen"
-
-- Changelog
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHANGELOG::T3017574265"] = "Änderungsprotokoll"
@@ -1824,21 +1854,12 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T900237532"] = "Anbiet
-- Failed to load file content
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T1989554334"] = "Laden des Dateiinhalts fehlgeschlagen"
--- Videos are not supported yet
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T2928927510"] = "Videos werden noch nicht unterstützt."
-
--- Images are not supported yet
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T298062956"] = "Bilder werden derzeit nicht unterstützt"
-
-- Use file content as input
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T3499386973"] = "Dokumenteninhalt als Eingabe verwenden"
-- Select file to read its content
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T354817589"] = "Datei auswählen, um den Inhalt zu lesen"
--- Executables are not allowed
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T4167762413"] = "Ausführbare Dateien sind nicht erlaubt"
-
-- The content is cleaned using an LLM agent: the main content is extracted, advertisements and other irrelevant things are attempted to be removed; relative links are attempted to be converted into absolute links so that they can be used.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENT::T1164201762"] = "Der Inhalt wird mithilfe eines LLM-Agents bereinigt: Der Hauptinhalt wird extrahiert, Werbung und andere irrelevante Elemente werden nach Möglichkeit entfernt. Relative Links werden nach Möglichkeit in absolute Links umgewandelt, damit sie verwendet werden können."
@@ -2004,6 +2025,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1599198973"]
-- Would you like to set one of your profiles as the default for the entire app? When you configure a different profile for an assistant, it will always take precedence.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1666052109"] = "Möchten Sie eines ihrer Profile als Standard für die gesamte App festlegen? Wenn Sie einem Assistenten ein anderes Profil zuweisen, hat dieses immer Vorrang."
+-- Select a transcription provider for transcribing your voice. Without a selected provider, dictation and transcription features will be disabled.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1834486728"] = "Wählen Sie für die Transkription Ihrer Stimme einen Anbieter für Transkriptionen aus. Ohne einen ausgewählten Anbieter wird die Diktier- und Transkriptions-Funktion deaktiviert."
+
-- Select the language behavior for the app. The default is to use the system language. You might want to choose a language manually?
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T186780842"] = "Wählen Sie das Sprachverhalten für die App aus. Standardmäßig wird die Systemsprache verwendet. Möchten Sie die Sprache manuell einstellen?"
@@ -2016,6 +2040,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1898060643"]
-- Select the language for the app.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1907446663"] = "Wählen Sie die Sprache für die App aus."
+-- Disable dictation and transcription
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T215381891"] = "Diktieren und Transkribieren deaktivieren"
+
-- Language behavior
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T2341504363"] = "Sprachverhalten"
@@ -2046,6 +2073,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T4004501229"]
-- 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.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T4067492921"] = "Wenn aktiviert, ist die Rechtschreibprüfung in allen Eingabefeldern aktiv. Je nach Betriebssystem werden Fehler möglicherweise nicht visuell hervorgehoben, aber ein Rechtsklick kann dennoch Korrekturvorschläge anzeigen."
+-- Select a transcription provider
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T4174666315"] = "Wählen Sie einen Transkriptionsanbieter aus"
+
-- Navigation bar behavior
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T602293588"] = "Verhalten der Navigationsleiste"
@@ -2088,24 +2118,30 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T24199
-- Name
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T266367750"] = "Name"
+-- Configured Embedding Providers
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T305753126"] = "Konfigurierte Anbieter für Einbettungen"
+
-- This helps AI Studio understand and compare things in a way that's similar to how humans do. When you're working on something, AI Studio can automatically identify related documents and data by comparing their digital fingerprints. For instance, if you're writing about customer service, AI Studio can instantly find other documents in your data that discuss similar topics or experiences, even if they use different words.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3251217940"] = "Dies hilft AI Studio, Dinge auf eine Art und Weise zu verstehen und zu vergleichen, die der menschlichen Denkweise ähnelt. Wenn Sie an etwas arbeiten, kann AI Studio automatisch verwandte Dokumente und Daten erkennen, indem es ihre digitalen Fingerabdrücke vergleicht. Wenn Sie zum Beispiel über Kundenservice schreiben, kann AI Studio sofort andere Dokumente in ihren Daten finden, die über ähnliche Themen oder Erfahrungen sprechen – selbst wenn sie andere Begriffe verwenden."
-- Edit
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3267849393"] = "Bearbeiten"
--- Configured Embeddings
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3526613453"] = "Konfigurierte Einbettungen"
-
-- Actions
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3865031940"] = "Aktionen"
+-- This embedding provider is managed by your organization.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T4062656589"] = "Dieser Einbettungsanbieter wird von Ihrer Organisation verwaltet."
+
-- No embeddings configured yet.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T4068015588"] = "Es wurden bislang keine Einbettungen konfiguriert."
-- Edit Embedding Provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T4264602229"] = "Einbettungsanbieter bearbeiten"
+-- Configure Embedding Providers
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T488419116"] = "Anbieter für Einbettungen konfigurieren"
+
-- Delete Embedding Provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T511304264"] = "Einbettungsanbieter löschen"
@@ -2115,9 +2151,6 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T78223
-- Provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T900237532"] = "Anbieter"
--- Configure Embeddings
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T970042679"] = "Einbettungen konfigurieren"
-
-- Show provider's confidence level?
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1052533048"] = "Anzeigen, wie sicher sich der Anbieter ist?"
@@ -2136,6 +2169,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T172585
-- Add Provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1806589097"] = "Anbieter hinzufügen"
+-- Configure LLM Providers
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1810190350"] = "Anbieter für LLM konfigurieren"
+
-- Edit LLM Provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1868766523"] = "LLM-Anbieter bearbeiten"
@@ -2169,8 +2205,8 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T284206
-- No providers configured yet.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T2911731076"] = "Noch keine Anbieter konfiguriert."
--- Configure Providers
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T3027859089"] = "Anbieter konfigurieren"
+-- Configured LLM Providers
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T3019870540"] = "Konfigurierte Anbieter für LLM"
-- as selected by provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T3082210376"] = "wie vom Anbieter ausgewählt"
@@ -2193,9 +2229,6 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T361241
-- No, do not enforce a minimum confidence level
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T3642102079"] = "Nein, kein Mindestvertrauensniveau erzwingen"
--- Configured Providers
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T3850871263"] = "Konfigurierte Anbieter"
-
-- Actions
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T3865031940"] = "Aktionen"
@@ -2223,6 +2256,57 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T853225
-- Provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T900237532"] = "Anbieter"
+-- No transcription provider configured yet.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T1079350363"] = "Es ist bisher kein Anbieter für Transkriptionen konfiguriert."
+
+-- Edit Transcription Provider
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T1317362918"] = "Anbieter für Transkriptionen bearbeiten"
+
+-- Delete
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T1469573738"] = "Löschen"
+
+-- Add transcription provider
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T1645238629"] = "Anbieter für Transkriptionen hinzufügen"
+
+-- Add Transcription Provider
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T2066315685"] = "Anbieter für Transkriptionen hinzufügen"
+
+-- Model
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T2189814010"] = "Modell"
+
+-- Name
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T266367750"] = "Name"
+
+-- Edit
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T3267849393"] = "Bearbeiten"
+
+-- Delete Transcription Provider
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T370103955"] = "Anbieter für Transkriptionen löschen"
+
+-- Actions
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T3865031940"] = "Aktionen"
+
+-- Configure Transcription Providers
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T4073110625"] = "Anbieter für Transkriptionen konfigurieren"
+
+-- Configured Transcription Providers
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T4210863523"] = "Konfigurierte Anbieter für Transkriptionen"
+
+-- This transcription provider is managed by your organization.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T756131076"] = "Dieser Anbieter für Transkriptionen wird von Ihrer Organisation verwaltet."
+
+-- Open Dashboard
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T78223861"] = "Dashboard öffnen"
+
+-- Are you sure you want to delete the transcription provider '{0}'?
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T789660305"] = "Möchten Sie den Anbieter für Transkriptionen „{0}“ wirklich löschen?"
+
+-- With the support of transcription models, MindWork AI Studio can convert human speech into text. This is useful, for example, when you need to dictate text. You can choose from dedicated transcription models, but not multimodal LLMs (large language models) that can handle both speech and text. The configuration of multimodal models is done in the \"Configure providers\" section.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T584860404"] = "Mit Unterstützung von Modellen für Transkriptionen kann MindWork AI Studio menschliche Sprache in Text umwandeln. Das ist zum Beispiel hilfreich, wenn Sie Texte diktieren möchten. Sie können aus speziellen Modellen für Transkriptionen wählen, jedoch nicht aus multimodalen LLMs (Large Language Models), die sowohl Sprache als auch Text verarbeiten können. Die Einrichtung multimodaler Modelle erfolgt im Abschnitt „Anbieter für LLM konfigurieren“."
+
+-- Provider
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T900237532"] = "Anbieter"
+
-- Copy {0} to the clipboard
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TEXTINFOLINE::T2206391442"] = "Kopiere {0} in die Zwischenablage"
@@ -2304,6 +2388,33 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VISION::T428040679"] = "Erstellung von In
-- Useful assistants
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VISION::T586430036"] = "Nützliche Assistenten"
+-- Failed to create the transcription provider.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VOICERECORDER::T1689988905"] = "Der Anbieter für die Transkription konnte nicht erstellt werden."
+
+-- Stop recording and start transcription
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VOICERECORDER::T224155287"] = "Aufnahme beenden und Transkription starten"
+
+-- Start recording your voice for a transcription
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VOICERECORDER::T2372624045"] = "Beginnen Sie mit der Aufnahme Ihrer Stimme für eine Transkription"
+
+-- Transcription in progress...
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VOICERECORDER::T2851219233"] = "Transkription läuft …"
+
+-- The configured transcription provider was not found.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VOICERECORDER::T331613105"] = "Der konfigurierte Anbieter für die Transkription wurde nicht gefunden."
+
+-- The configured transcription provider does not meet the minimum confidence level.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VOICERECORDER::T3834149033"] = "Der konfigurierte Anbieter für die Transkription erfüllt nicht das erforderliche Mindestmaß an Vertrauenswürdigkeit."
+
+-- An error occurred during transcription.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VOICERECORDER::T588743762"] = "Während der Transkription ist ein Fehler aufgetreten."
+
+-- No transcription provider is configured.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VOICERECORDER::T663630295"] = "Es ist kein Anbieter für die Transkription konfiguriert."
+
+-- The transcription result is empty.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VOICERECORDER::T974954792"] = "Das Ergebnis der Transkription ist leer."
+
-- Are you sure you want to delete the chat '{0}' in the workspace '{1}'?
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1016188706"] = "Möchten Sie den Chat „{0}“ im Arbeitsbereich „{1}“ wirklich löschen?"
@@ -2424,6 +2535,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2147062613"] = "Profiln
-- Add messages of an example conversation (user prompt followed by assistant prompt) to demonstrate the desired interaction pattern. These examples help the AI understand your expectations by showing it the correct format, style, and content of responses before it receives actual user inputs.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2292424657"] = "Fügen Sie Nachrichten einer Beispiel-Konversation hinzu (Nutzereingabe, gefolgt von einer Antwort des Assistenten), um das gewünschte Interaktionsmuster zu demonstrieren. Diese Beispiele helfen der KI, ihre Erwartungen zu verstehen, indem Sie das korrekte Format, den Stil und den Inhalt von Antworten zeigen, bevor tatsächliche Nutzereingaben erfolgen."
+-- File Attachments
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2294745309"] = "Dateianhänge"
+
-- Role
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2418769465"] = "Rolle"
@@ -2463,6 +2577,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3016903701"] = "Der Nam
-- Image content
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3094908719"] = "Bildinhalt"
+-- You can attach files that will be automatically included when using this chat template. These files will be added to the first message sent in any chat using this template.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3108503534"] = "Sie können Dateien anhängen, die bei Verwendung dieser Chat-Vorlage automatisch einbezogen werden. Diese Dateien werden der ersten Nachricht hinzugefügt, die in einem Chat gesendet wird, der diese Vorlage verwendet."
+
-- Are you unsure which system prompt to use? You might start with the default system prompt that AI Studio uses for all chats.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3127437308"] = "Sind Sie unsicher, welchen System-Prompt Sie verwenden sollen? Sie können mit dem Standard-System-Prompt beginnen, den AI Studio für alle Chats verwendet."
@@ -2955,6 +3072,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTCHECKDIALOG::T1373123357"] = "Markdo
-- Load file
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTCHECKDIALOG::T2129302565"] = "Datei laden"
+-- Image View
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTCHECKDIALOG::T2199753423"] = "Bildansicht"
+
-- See how we load your file. Review the content before we process it further.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTCHECKDIALOG::T3271853346"] = "So wird Ihre Datei geladen. Überprüfen Sie den Inhalt, bevor wir ihn weiterverarbeiten."
@@ -2973,6 +3093,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTCHECKDIALOG::T652739927"] = "Dies is
-- File Path
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTCHECKDIALOG::T729508546"] = "Dateipfad"
+-- The specified file could not be found. The file have been moved, deleted, renamed, or is otherwise inaccessible.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTCHECKDIALOG::T973777830"] = "Die angegebene Datei konnte nicht gefunden werden. Die Datei wurde möglicherweise verschoben, gelöscht, umbenannt oder ist anderweitig nicht zugänglich."
+
-- Embedding Name
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGMETHODDIALOG::T1427271797"] = "Name der Einbettung"
@@ -3069,9 +3192,6 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T2189814010"] = "Mo
-- (Optional) API Key
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T2331453405"] = "(Optional) API-Schlüssel"
--- Currently, we cannot query the embedding models of self-hosted systems. Therefore, enter the model name manually.
-UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T2615586687"] = "Derzeit können wir die Einbettungs-Modelle von selbst gehosteten Systemen nicht abfragen. Bitte geben Sie daher den Modellnamen manuell ein."
-
-- Add
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T2646845972"] = "Hinzufügen"
@@ -3081,6 +3201,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T2810182573"] = "Ke
-- Instance Name
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T2842060373"] = "Instanzname"
+-- Currently, we cannot query the embedding models for the selected provider and/or host. Therefore, please enter the model name manually.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T290547799"] = "Derzeit können wir die Einbettungs-Modelle für den ausgewählten Anbieter und/oder Host nicht abfragen. Bitte geben Sie daher den Modellnamen manuell ein."
+
-- Model selection
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T416738168"] = "Modellauswahl"
@@ -3291,6 +3414,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3361153305"] = "Experten-Ei
-- Show available models
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3763891899"] = "Verfügbare Modelle anzeigen"
+-- Currently, we cannot query the models for the selected provider and/or host. Therefore, please enter the model name manually.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T4116737656"] = "Derzeit können wir die Modelle für den ausgewählten Anbieter und/oder Host nicht abfragen. Bitte geben Sie daher den Modellnamen manuell ein."
+
-- Model selection
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T416738168"] = "Modellauswahl"
@@ -3417,6 +3543,33 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::RETRIEVALPROCESSDIALOG::T900713019"] = "Abbr
-- Embeddings
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::RETRIEVALPROCESSDIALOG::T951463987"] = "Einbettungen"
+-- Here you can see all attached files. Files that can no longer be found (deleted, renamed, or moved) are marked with a warning icon and a strikethrough name. You can remove any attachment using the trash can icon.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T1746160064"] = "Hier sehen Sie alle angehängten Dateien. Dateien, die nicht mehr gefunden werden können (gelöscht, umbenannt oder verschoben), sind mit einem Warnsymbol und einem durchgestrichenen Namen markiert. Sie können jeden Anhang über das Papierkorbsymbol entfernen."
+
+-- There aren't any file attachments right now.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T2111340711"] = "Derzeit sind keine Dateianhänge vorhanden."
+
+-- Document Preview
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T285154968"] = "Dokumentvorschau"
+
+-- The file was deleted, renamed, or moved.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T3083729256"] = "Die Datei wurde gelöscht, umbenannt oder verschoben."
+
+-- Your attached file.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T3154198222"] = "Ihre angehängte Datei."
+
+-- Preview what we send to the AI.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T3160778981"] = "Vorschau dessen, was wir an die KI senden."
+
+-- Close
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T3448155331"] = "Schließen"
+
+-- Your attached files
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T3909191077"] = "Ihre angehängten Dateien"
+
+-- Remove this attachment.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T3933470258"] = "Diesen Anhang entfernen."
+
-- There is no social event
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGAGENDA::T1222800281"] = "Es gibt keine gesellschaftliche Veranstaltung."
@@ -4440,6 +4593,60 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T4030229154"] = "Ihre Ein
-- Cancel
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T900713019"] = "Abbrechen"
+-- Failed to store the API key in the operating system. The message was: {0}. Please try again.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1122745046"] = "Der API-Schlüssel konnte nicht im Betriebssystem gespeichert werden. Die Meldung lautete: '{0}'. Bitte versuchen Sie es erneut."
+
+-- API Key
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1324664716"] = "API-Schlüssel"
+
+-- Create account
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1356621346"] = "Konto erstellen"
+
+-- Currently, we cannot query the transcription models for the selected provider and/or host. Therefore, please enter the model name manually.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1381635232"] = "Derzeit können wir die Modelle für Transkriptionen für den ausgewählten Anbieter und/oder Host nicht abfragen. Bitte geben Sie daher den Modellnamen manuell ein."
+
+-- Hostname
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1727440780"] = "Hostname"
+
+-- Load
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1756340745"] = "Laden"
+
+-- Update
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1847791252"] = "Aktualisieren"
+
+-- Failed to load the API key from the operating system. The message was: {0}. You might ignore this message and provide the API key again.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1870831108"] = "Der API-Schlüssel konnte nicht aus dem Betriebssystem geladen werden. Die Meldung lautete: '{0}'. Sie können diese Meldung ignorieren und den API-Schlüssel erneut eingeben."
+
+-- Model
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T2189814010"] = "Modell"
+
+-- (Optional) API Key
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T2331453405"] = "(Optional) API-Schlüssel"
+
+-- Add
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T2646845972"] = "Hinzufügen"
+
+-- No models loaded or available.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T2810182573"] = "Keine Modelle geladen oder verfügbar."
+
+-- Instance Name
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T2842060373"] = "Instanzname"
+
+-- Please enter a transcription model name.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T3703662664"] = "Bitte geben Sie den Namen eines Transkriptionsmodells ein."
+
+-- Model selection
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T416738168"] = "Modellauswahl"
+
+-- Host
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T808120719"] = "Host"
+
+-- Provider
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T900237532"] = "Anbieter"
+
+-- Cancel
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T900713019"] = "Abbrechen"
+
-- Install now
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::UPDATEDIALOG::T2366359512"] = "Jetzt installieren"
@@ -4458,9 +4665,6 @@ UI_TEXT_CONTENT["AISTUDIO::LAYOUT::MAINLAYOUT::T1258653480"] = "Einstellungen"
-- Home
UI_TEXT_CONTENT["AISTUDIO::LAYOUT::MAINLAYOUT::T1391791790"] = "Startseite"
--- About
-UI_TEXT_CONTENT["AISTUDIO::LAYOUT::MAINLAYOUT::T1491113694"] = "Über AI Studio"
-
-- Are you sure you want to leave the chat page? All unsaved changes will be lost.
UI_TEXT_CONTENT["AISTUDIO::LAYOUT::MAINLAYOUT::T1563130494"] = "Sind Sie sicher, dass Sie die Chat-Seite verlassen möchten? Alle nicht gespeicherten Änderungen gehen verloren."
@@ -4491,243 +4695,12 @@ UI_TEXT_CONTENT["AISTUDIO::LAYOUT::MAINLAYOUT::T2979224202"] = "Schreiben"
-- Show details
UI_TEXT_CONTENT["AISTUDIO::LAYOUT::MAINLAYOUT::T3692372066"] = "Details anzeigen"
+-- Information
+UI_TEXT_CONTENT["AISTUDIO::LAYOUT::MAINLAYOUT::T4256323669"] = "Information"
+
-- Chat
UI_TEXT_CONTENT["AISTUDIO::LAYOUT::MAINLAYOUT::T578410699"] = "Chat"
--- Startup log file
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1019424746"] = "Startprotokolldatei"
-
--- About MindWork AI Studio
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1020427799"] = "Über MindWork AI Studio"
-
--- Browse AI Studio's source code on GitHub — we welcome your contributions.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1107156991"] = "Sehen Sie sich den Quellcode von AI Studio auf GitHub an – wir freuen uns über ihre Beiträge."
-
--- This is a private AI Studio installation. It runs without an enterprise configuration.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1209549230"] = "Dies ist eine private AI Studio-Installation. Sie läuft ohne Unternehmenskonfiguration."
-
--- AI Studio runs with an enterprise configuration and a configuration server. The configuration plugin is not yet available.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1282228996"] = "AI Studio läuft mit einer Unternehmenskonfiguration und einem Konfigurationsserver. Das Konfigurations-Plugin ist noch nicht verfügbar."
-
--- This library is used to read PDF files. This is necessary, e.g., for using PDFs as a data source for a chat.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1388816916"] = "Diese Bibliothek wird verwendet, um PDF-Dateien zu lesen. Das ist zum Beispiel notwendig, um PDFs als Datenquelle für einen Chat zu nutzen."
-
--- This library is used to extend the MudBlazor library. It provides additional components that are not part of the MudBlazor library.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1421513382"] = "Diese Bibliothek wird verwendet, um die MudBlazor-Bibliothek zu erweitern. Sie stellt zusätzliche Komponenten bereit, die nicht Teil der MudBlazor-Bibliothek sind."
-
--- We use Lua as the language for plugins. Lua-CSharp lets Lua scripts communicate with AI Studio and vice versa. Thank you, Yusuke Nakada, for this great library.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T162898512"] = "Wir verwenden Lua als Sprache für Plugins. Lua-CSharp ermöglicht die Kommunikation zwischen Lua-Skripten und AI Studio in beide Richtungen. Vielen Dank an Yusuke Nakada für diese großartige Bibliothek."
-
--- Building on .NET, ASP.NET Core, and Blazor, MudBlazor is used as a library for designing and developing the user interface. It is a great project that significantly accelerates the development of advanced user interfaces with Blazor.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1629800076"] = "Basierend auf .NET, ASP.NET Core und Blazor wird MudBlazor als Bibliothek für das Design und die Entwicklung der Benutzeroberfläche verwendet. Es ist ein großartiges Projekt, das die Entwicklung fortschrittlicher Benutzeroberflächen mit Blazor erheblich beschleunigt."
-
--- AI Studio creates a log file at startup, in which events during startup are recorded. After startup, another log file is created that records all events that occur during the use of the app. This includes any errors that may occur. Depending on when an error occurs (at startup or during use), the contents of these log files can be helpful for troubleshooting. Sensitive information such as passwords is not included in the log files.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1630237140"] = "AI Studio erstellt beim Start eine Protokolldatei, in der Ereignisse während des Starts aufgezeichnet werden. Nach dem Start wird eine weitere Protokolldatei erstellt, die alle Ereignisse während der Nutzung der App dokumentiert. Dazu gehören auch eventuell auftretende Fehler. Je nachdem, wann ein Fehler auftritt (beim Start oder während der Nutzung), können die Inhalte dieser Protokolldateien bei der Fehlerbehebung hilfreich sein. Sensible Informationen wie Passwörter werden nicht in den Protokolldateien gespeichert."
-
--- This library is used to display the differences between two texts. This is necessary, e.g., for the grammar and spelling assistant.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1772678682"] = "Diese Bibliothek wird verwendet, um die Unterschiede zwischen zwei Texten anzuzeigen. Das ist zum Beispiel für den Grammatik- und Rechtschreibassistenten notwendig."
-
--- By clicking on the respective path, the path is copied to the clipboard. You might open these files with a text editor to view their contents.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1806897624"] = "Wenn Sie auf den jeweiligen Pfad klicken, wird dieser in die Zwischenablage kopiert. Sie können diese Dateien mit einem Texteditor öffnen, um ihren Inhalt anzusehen."
-
--- Pandoc Installation
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T185447014"] = "Pandoc-Installation"
-
--- Copies the configuration plugin ID to the clipboard
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1859295819"] = "Kopiert die Konfigurations-Plugin-ID in die Zwischenablage"
-
--- Check for updates
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1890416390"] = "Nach Updates suchen"
-
--- Vision
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1892426825"] = "Vision"
-
--- In order to use any LLM, each user must store their so-called API key for each LLM provider. This key must be kept secure, similar to a password. The safest way to do this is offered by operating systems like macOS, Windows, and Linux: They have mechanisms to store such data, if available, on special security hardware. Since this is currently not possible in .NET, we use this Rust library.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1915240766"] = "Um ein beliebiges LLM nutzen zu können, muss jeder User seinen sogenannten API-Schlüssel für jeden LLM-Anbieter speichern. Dieser Schlüssel muss sicher aufbewahrt werden – ähnlich wie ein Passwort. Die sicherste Methode hierfür bieten Betriebssysteme wie macOS, Windows und Linux: Sie verfügen über Mechanismen, solche Daten – sofern vorhanden – auf spezieller Sicherheits-Hardware zu speichern. Da dies derzeit in .NET nicht möglich ist, verwenden wir diese Rust-Bibliothek."
-
--- This library is used to convert HTML to Markdown. This is necessary, e.g., when you provide a URL as input for an assistant.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1924365263"] = "Diese Bibliothek wird verwendet, um HTML in Markdown umzuwandeln. Das ist zum Beispiel notwendig, wenn Sie eine URL als Eingabe für einen Assistenten angeben."
-
--- We use Rocket to implement the runtime API. This is necessary because the runtime must be able to communicate with the user interface (IPC). Rocket is a great framework for implementing web APIs in Rust.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1943216839"] = "Wir verwenden Rocket zur Implementierung der Runtime-API. Dies ist notwendig, da die Runtime mit der Benutzeroberfläche (IPC) kommunizieren muss. Rocket ist ein ausgezeichnetes Framework zur Umsetzung von Web-APIs in Rust."
-
--- Copies the server URL to the clipboard
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2037899437"] = "Kopiert die Server-URL in die Zwischenablage"
-
--- This library is used to determine the file type of a file. This is necessary, e.g., when we want to stream a file.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2173617769"] = "Diese Bibliothek wird verwendet, um den Dateityp einer Datei zu bestimmen. Das ist zum Beispiel notwendig, wenn wir eine Datei streamen möchten."
-
--- For the secure communication between the user interface and the runtime, we need to create certificates. This Rust library is great for this purpose.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2174764529"] = "Für die sichere Kommunikation zwischen der Benutzeroberfläche und der Laufzeit müssen wir Zertifikate erstellen. Diese Rust-Bibliothek eignet sich hervorragend dafür."
-
--- OK
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2246359087"] = "OK"
-
--- Configuration server:
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2272122662"] = "Konfigurationsserver:"
-
--- We must generate random numbers, e.g., for securing the interprocess communication between the user interface and the runtime. The rand library is great for this purpose.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2273492381"] = "Wir müssen Zufallszahlen erzeugen, z. B. um die Kommunikation zwischen der Benutzeroberfläche und der Laufzeitumgebung abzusichern. Die rand-Bibliothek eignet sich dafür hervorragend."
-
--- AI Studio runs with an enterprise configuration using a configuration plugin, without central configuration management.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2280402765"] = "AI Studio läuft mit einer Unternehmenskonfiguration über ein Konfigurations-Plugin, ohne zentrale Konfigurationsverwaltung."
-
--- Configuration plugin ID:
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2301484629"] = "Konfigurations-Plugin-ID:"
-
--- The C# language is used for the implementation of the user interface and the backend. To implement the user interface with C#, the Blazor technology from ASP.NET Core is used. All these technologies are integrated into the .NET SDK.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2329884315"] = "Die Programmiersprache C# wird für die Umsetzung der Benutzeroberfläche und des Backends verwendet. Für die Entwicklung der Benutzeroberfläche mit C# kommt die Blazor-Technologie aus ASP.NET Core zum Einsatz. Alle diese Technologien sind im .NET SDK integriert."
-
--- Used PDFium version
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2368247719"] = "Verwendete PDFium-Version"
-
--- installation provided by the system
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2371107659"] = "Installation vom System bereitgestellt"
-
--- Installed Pandoc version: Pandoc is not installed or not available.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2374031539"] = "Installierte Pandoc-Version: Pandoc ist nicht installiert oder nicht verfügbar."
-
--- This library is used to determine the language of the operating system. This is necessary to set the language of the user interface.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2557014401"] = "Diese Bibliothek wird verwendet, um die Sprache des Betriebssystems zu erkennen. Dies ist notwendig, um die Sprache der Benutzeroberfläche einzustellen."
-
--- Used Open Source Projects
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2557066213"] = "Verwendete Open-Source-Projekte"
-
--- Build time
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T260228112"] = "Build-Zeit"
-
--- To be able to use the responses of the LLM in other apps, we often use the clipboard of the respective operating system. Unfortunately, in .NET there is no solution that works with all operating systems. Therefore, I have opted for this library in Rust. This way, data transfer to other apps works on every system.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2644379659"] = "Um die Antworten des LLM in anderen Apps nutzen zu können, verwenden wir häufig die Zwischenablage des jeweiligen Betriebssystems. Leider gibt es in .NET keine Lösung, die auf allen Betriebssystemen funktioniert. Deshalb habe ich mich für diese Bibliothek in Rust entschieden. So funktioniert die Datenübertragung zu anderen Apps auf jedem System."
-
--- Usage log file
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2689995864"] = "Nutzungsprotokolldatei"
-
--- Logbook
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2706940196"] = "Protokolldateien"
-
--- This component is used to render Markdown text. This is important because the LLM often responds with Markdown-formatted text, allowing us to present it in a way that is easier to read.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2726131107"] = "Diese Komponente wird verwendet, um Markdown-Text darzustellen. Das ist wichtig, weil das LLM häufig mit im Markdown-Format formatiertem Text antwortet. Dadurch können wir die Antworten besser lesbar anzeigen."
-
--- Determine Pandoc version, please wait...
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2765814390"] = "Pandoc-Version wird ermittelt, bitte warten …"
-
--- Code in the Rust language can be specified as synchronous or asynchronous. Unlike .NET and the C# language, Rust cannot execute asynchronous code by itself. Rust requires support in the form of an executor for this. Tokio is one such executor.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2777988282"] = "Code in der Programmiersprache Rust kann als synchron oder asynchron spezifiziert werden. Im Gegensatz zu .NET und der Sprache C# kann Rust asynchronen Code jedoch nicht von selbst ausführen. Dafür benötigt Rust Unterstützung in Form eines Executors. Tokio ist ein solcher Executor."
-
--- Show Details
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T27924674"] = "Details anzeigen"
-
--- View our project roadmap and help shape AI Studio's future development.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2829971158"] = "Sehen Sie sich unsere Roadmap an und helfen Sie mit, die zukünftige Entwicklung von AI Studio mitzugestalten."
-
--- Used .NET runtime
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2840227993"] = "Verwendete .NET-Laufzeit"
-
--- Explanation
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2840582448"] = "Erklärung"
-
--- The .NET backend cannot be started as a desktop app. Therefore, I use a second backend in Rust, which I call runtime. With Rust as the runtime, Tauri can be used to realize a typical desktop app. Thanks to Rust, this app can be offered for Windows, macOS, and Linux desktops. Rust is a great language for developing safe and high-performance software.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2868174483"] = "Das .NET-Backend kann nicht als Desktop-App gestartet werden. Deshalb verwende ich ein zweites Backend in Rust, das ich „Runtime“ nenne. Mit Rust als Runtime kann Tauri genutzt werden, um eine typische Desktop-App zu realisieren. Dank Rust kann diese App für Windows-, macOS- und Linux-Desktops angeboten werden. Rust ist eine großartige Sprache für die Entwicklung sicherer und leistungsstarker Software."
-
--- Changelog
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3017574265"] = "Änderungsprotokoll"
-
--- Enterprise configuration ID:
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3092349641"] = "Unternehmenskonfigurations-ID:"
-
--- Connect AI Studio to your organization's data with our External Retrieval Interface (ERI).
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T313276297"] = "Verbinden Sie AI Studio mit den Daten ihrer Organisation über unsere Schnittstelle für externe Datenabfrage (ERI)."
-
--- Have feature ideas? Submit suggestions for future AI Studio enhancements.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3178730036"] = "Haben Sie Ideen für neue Funktionen? Senden Sie uns Vorschläge für zukünftige Verbesserungen von AI Studio."
-
--- Hide Details
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3183837919"] = "Details ausblenden"
-
--- Update Pandoc
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3249965383"] = "Pandoc aktualisieren"
-
--- Discover MindWork AI's mission and vision on our official homepage.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3294830584"] = "Entdecken Sie die Mission und Vision von MindWork AI auf unserer offiziellen Homepage."
-
--- User-language provided by the OS
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3334355246"] = "Vom Betriebssystem bereitgestellte Sprache"
-
--- The following list shows the versions of the MindWork AI Studio, the used compilers, build time, etc.:
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3405978777"] = "Die folgende Liste zeigt die Versionen von MindWork AI Studio und des verwendeten Compilers, den Build-Zeitpunkt und weitere Informationen:"
-
--- Used Rust compiler
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3440211747"] = "Verwendeter Rust-Compiler"
-
--- Tauri is used to host the Blazor user interface. It is a great project that allows the creation of desktop applications using web technologies. I love Tauri!
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3494984593"] = "Tauri wird verwendet, um die Blazor-Benutzeroberfläche bereitzustellen. Es ist ein großartiges Projekt, das die Erstellung von Desktop-Anwendungen mit Webtechnologien ermöglicht. Ich liebe Tauri!"
-
--- Motivation
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3563271893"] = "Motivation"
-
--- This library is used to read Excel and OpenDocument spreadsheet files. This is necessary, e.g., for using spreadsheets as a data source for a chat.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3722989559"] = "Diese Bibliothek wird verwendet, um Excel- und OpenDocument-Tabellendateien zu lesen. Dies ist zum Beispiel notwendig, wenn Tabellen als Datenquelle für einen Chat verwendet werden sollen."
-
--- AI Studio runs with an enterprise configuration and a configuration server. The configuration plugin is active.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3741877842"] = "AI Studio läuft mit einer Unternehmenskonfiguration und einem Konfigurationsserver. Das Konfigurations-Plugin ist aktiv."
-
--- this version does not met the requirements
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3813932670"] = "diese Version erfüllt die Anforderungen nicht"
-
--- This library is used to access the Windows registry. We use this for Windows enterprise environments to read the desired configuration.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3874337003"] = "Diese Bibliothek wird verwendet, um auf die Windows-Registry zuzugreifen. Wir nutzen sie in Windows-Unternehmensumgebungen, um die gewünschte Konfiguration auszulesen."
-
--- Now we have multiple systems, some developed in .NET and others in Rust. The data format JSON is responsible for translating data between both worlds (called data serialization and deserialization). Serde takes on this task in the Rust world. The counterpart in the .NET world is an integral part of .NET and is located in System.Text.Json.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3908558992"] = "Jetzt haben wir mehrere Systeme, einige entwickelt in .NET und andere in Rust. Das Datenformat JSON ist dafür zuständig, Daten zwischen beiden Welten zu übersetzen (dies nennt man Serialisierung und Deserialisierung von Daten). In der Rust-Welt übernimmt Serde diese Aufgabe. Das Pendant in der .NET-Welt ist ein fester Bestandteil von .NET und findet sich in System.Text.Json."
-
--- Installed Pandoc version
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3983971016"] = "Installierte Pandoc-Version"
-
--- Check Pandoc Installation
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3986423270"] = "Pandoc-Installation prüfen"
-
--- Versions
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T4010195468"] = "Versionen"
-
--- This library is used to create asynchronous streams in Rust. It allows us to work with streams of data that can be produced asynchronously, making it easier to handle events or data that arrive over time. We use this, e.g., to stream arbitrary data from the file system to the embedding system.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T4079152443"] = "Diese Bibliothek wird verwendet, um asynchrone Datenströme in Rust zu erstellen. Sie ermöglicht es uns, mit Datenströmen zu arbeiten, die asynchron bereitgestellt werden, wodurch sich Ereignisse oder Daten, die nach und nach eintreffen, leichter verarbeiten lassen. Wir nutzen dies zum Beispiel, um beliebige Daten aus dem Dateisystem an das Einbettungssystem zu übertragen."
-
--- Community & Code
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T4158546761"] = "Community & Code"
-
--- We use the HtmlAgilityPack to extract content from the web. This is necessary, e.g., when you provide a URL as input for an assistant.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T4184485147"] = "Wir verwenden das HtmlAgilityPack, um Inhalte aus dem Internet zu extrahieren. Das ist zum Beispiel notwendig, wenn Sie eine URL als Eingabe für einen Assistenten angeben."
-
--- When transferring sensitive data between Rust runtime and .NET app, we encrypt the data. We use some libraries from the Rust Crypto project for this purpose: cipher, aes, cbc, pbkdf2, hmac, and sha2. We are thankful for the great work of the Rust Crypto project.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T4229014037"] = "Beim Übertragen sensibler Daten zwischen der Rust-Laufzeitumgebung und der .NET-Anwendung verschlüsseln wir die Daten. Dafür verwenden wir einige Bibliotheken aus dem Rust Crypto-Projekt: cipher, aes, cbc, pbkdf2, hmac und sha2. Wir sind dankbar für die großartige Arbeit des Rust Crypto-Projekts."
-
--- This is a library providing the foundations for asynchronous programming in Rust. It includes key trait definitions like Stream, as well as utilities like join!, select!, and various futures combinator methods which enable expressive asynchronous control flow.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T566998575"] = "Dies ist eine Bibliothek, die die Grundlagen für asynchrones Programmieren in Rust bereitstellt. Sie enthält zentrale Trait-Definitionen wie Stream sowie Hilfsfunktionen wie join!, select! und verschiedene Methoden zur Kombination von Futures, die einen ausdrucksstarken asynchronen Kontrollfluss ermöglichen."
-
--- Used .NET SDK
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T585329785"] = "Verwendetes .NET SDK"
-
--- Did you find a bug or are you experiencing issues? Report your concern here.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T639371534"] = "Haben Sie einen Fehler gefunden oder Probleme festgestellt? Melden Sie Ihr Anliegen hier."
-
--- This Rust library is used to output the app's messages to the terminal. This is helpful during development and troubleshooting. This feature is initially invisible; when the app is started via the terminal, the messages become visible.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T64689067"] = "Diese Rust-Bibliothek wird verwendet, um die Nachrichten der App im Terminal auszugeben. Das ist während der Entwicklung und Fehlersuche hilfreich. Diese Funktion ist zunächst unsichtbar; werden App über das Terminal gestartet, werden die Nachrichten sichtbar."
-
--- Copies the config ID to the clipboard
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T788846912"] = "Kopiert die Konfigurations-ID in die Zwischenablage"
-
--- installed by AI Studio
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T833849470"] = "installiert von AI Studio"
-
--- We use this library to be able to read PowerPoint files. This allows us to insert content from slides into prompts and take PowerPoint files into account in RAG processes. We thank Nils Kruthoff for his work on this Rust crate.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T855925638"] = "Wir verwenden diese Bibliothek, um PowerPoint-Dateien lesen zu können. So ist es möglich, Inhalte aus Folien in Prompts einzufügen und PowerPoint-Dateien in RAG-Prozessen zu berücksichtigen. Wir danken Nils Kruthoff für seine Arbeit an diesem Rust-Crate."
-
--- For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T870640199"] = "Für einige Datenübertragungen müssen wir die Daten in Base64 kodieren. Diese Rust-Bibliothek eignet sich dafür hervorragend."
-
--- Install Pandoc
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T986578435"] = "Pandoc installieren"
-
-- Get coding and debugging support from an LLM.
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T1243850917"] = "Erhalten Sie Unterstützung beim Programmieren und Debuggen durch ein KI-Modell."
@@ -4896,9 +4869,6 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T1702902297"] = "Einführung"
-- Vision
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T1892426825"] = "Vision"
--- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (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"
@@ -4923,6 +4893,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3341379752"] = "Kosteneffizient"
-- Flexibility
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3723223888"] = "Flexibilitä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), OpenRouter, 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::T3892227145"] = "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), OpenRouter, 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."
+
-- Privacy
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3959064551"] = "Datenschutz"
@@ -4944,6 +4917,240 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T873851215"] = "Das zeichnet MindWork AI
-- The app is free to use, both for personal and commercial purposes.
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T91074375"] = "Die App ist sowohl für private als auch für kommerzielle Zwecke kostenlos nutzbar."
+-- Startup log file
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1019424746"] = "Startprotokolldatei"
+
+-- Browse AI Studio's source code on GitHub — we welcome your contributions.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1107156991"] = "Sehen Sie sich den Quellcode von AI Studio auf GitHub an – wir freuen uns über ihre Beiträge."
+
+-- This is a private AI Studio installation. It runs without an enterprise configuration.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1209549230"] = "Dies ist eine private AI Studio-Installation. Sie läuft ohne Unternehmenskonfiguration."
+
+-- AI Studio runs with an enterprise configuration and a configuration server. The configuration plugin is not yet available.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1282228996"] = "AI Studio läuft mit einer Unternehmenskonfiguration und einem Konfigurationsserver. Das Konfigurations-Plugin ist noch nicht verfügbar."
+
+-- This library is used to read PDF files. This is necessary, e.g., for using PDFs as a data source for a chat.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1388816916"] = "Diese Bibliothek wird verwendet, um PDF-Dateien zu lesen. Das ist zum Beispiel notwendig, um PDFs als Datenquelle für einen Chat zu nutzen."
+
+-- This library is used to extend the MudBlazor library. It provides additional components that are not part of the MudBlazor library.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1421513382"] = "Diese Bibliothek wird verwendet, um die MudBlazor-Bibliothek zu erweitern. Sie stellt zusätzliche Komponenten bereit, die nicht Teil der MudBlazor-Bibliothek sind."
+
+-- We use Lua as the language for plugins. Lua-CSharp lets Lua scripts communicate with AI Studio and vice versa. Thank you, Yusuke Nakada, for this great library.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T162898512"] = "Wir verwenden Lua als Sprache für Plugins. Lua-CSharp ermöglicht die Kommunikation zwischen Lua-Skripten und AI Studio in beide Richtungen. Vielen Dank an Yusuke Nakada für diese großartige Bibliothek."
+
+-- Building on .NET, ASP.NET Core, and Blazor, MudBlazor is used as a library for designing and developing the user interface. It is a great project that significantly accelerates the development of advanced user interfaces with Blazor.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1629800076"] = "Basierend auf .NET, ASP.NET Core und Blazor wird MudBlazor als Bibliothek für das Design und die Entwicklung der Benutzeroberfläche verwendet. Es ist ein großartiges Projekt, das die Entwicklung fortschrittlicher Benutzeroberflächen mit Blazor erheblich beschleunigt."
+
+-- AI Studio creates a log file at startup, in which events during startup are recorded. After startup, another log file is created that records all events that occur during the use of the app. This includes any errors that may occur. Depending on when an error occurs (at startup or during use), the contents of these log files can be helpful for troubleshooting. Sensitive information such as passwords is not included in the log files.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1630237140"] = "AI Studio erstellt beim Start eine Protokolldatei, in der Ereignisse während des Starts aufgezeichnet werden. Nach dem Start wird eine weitere Protokolldatei erstellt, die alle Ereignisse während der Nutzung der App dokumentiert. Dazu gehören auch eventuell auftretende Fehler. Je nachdem, wann ein Fehler auftritt (beim Start oder während der Nutzung), können die Inhalte dieser Protokolldateien bei der Fehlerbehebung hilfreich sein. Sensible Informationen wie Passwörter werden nicht in den Protokolldateien gespeichert."
+
+-- This library is used to display the differences between two texts. This is necessary, e.g., for the grammar and spelling assistant.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1772678682"] = "Diese Bibliothek wird verwendet, um die Unterschiede zwischen zwei Texten anzuzeigen. Das ist zum Beispiel für den Grammatik- und Rechtschreibassistenten notwendig."
+
+-- By clicking on the respective path, the path is copied to the clipboard. You might open these files with a text editor to view their contents.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1806897624"] = "Wenn Sie auf den jeweiligen Pfad klicken, wird dieser in die Zwischenablage kopiert. Sie können diese Dateien mit einem Texteditor öffnen, um ihren Inhalt anzusehen."
+
+-- Pandoc Installation
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T185447014"] = "Pandoc-Installation"
+
+-- Copies the configuration plugin ID to the clipboard
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1859295819"] = "Kopiert die Konfigurations-Plugin-ID in die Zwischenablage"
+
+-- Check for updates
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1890416390"] = "Nach Updates suchen"
+
+-- Vision
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1892426825"] = "Vision"
+
+-- In order to use any LLM, each user must store their so-called API key for each LLM provider. This key must be kept secure, similar to a password. The safest way to do this is offered by operating systems like macOS, Windows, and Linux: They have mechanisms to store such data, if available, on special security hardware. Since this is currently not possible in .NET, we use this Rust library.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1915240766"] = "Um ein beliebiges LLM nutzen zu können, muss jeder User seinen sogenannten API-Schlüssel für jeden LLM-Anbieter speichern. Dieser Schlüssel muss sicher aufbewahrt werden – ähnlich wie ein Passwort. Die sicherste Methode hierfür bieten Betriebssysteme wie macOS, Windows und Linux: Sie verfügen über Mechanismen, solche Daten – sofern vorhanden – auf spezieller Sicherheits-Hardware zu speichern. Da dies derzeit in .NET nicht möglich ist, verwenden wir diese Rust-Bibliothek."
+
+-- This library is used to convert HTML to Markdown. This is necessary, e.g., when you provide a URL as input for an assistant.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1924365263"] = "Diese Bibliothek wird verwendet, um HTML in Markdown umzuwandeln. Das ist zum Beispiel notwendig, wenn Sie eine URL als Eingabe für einen Assistenten angeben."
+
+-- We use Rocket to implement the runtime API. This is necessary because the runtime must be able to communicate with the user interface (IPC). Rocket is a great framework for implementing web APIs in Rust.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1943216839"] = "Wir verwenden Rocket zur Implementierung der Runtime-API. Dies ist notwendig, da die Runtime mit der Benutzeroberfläche (IPC) kommunizieren muss. Rocket ist ein ausgezeichnetes Framework zur Umsetzung von Web-APIs in Rust."
+
+-- Copies the server URL to the clipboard
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2037899437"] = "Kopiert die Server-URL in die Zwischenablage"
+
+-- This library is used to determine the file type of a file. This is necessary, e.g., when we want to stream a file.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2173617769"] = "Diese Bibliothek wird verwendet, um den Dateityp einer Datei zu bestimmen. Das ist zum Beispiel notwendig, wenn wir eine Datei streamen möchten."
+
+-- For the secure communication between the user interface and the runtime, we need to create certificates. This Rust library is great for this purpose.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2174764529"] = "Für die sichere Kommunikation zwischen der Benutzeroberfläche und der Laufzeit müssen wir Zertifikate erstellen. Diese Rust-Bibliothek eignet sich hervorragend dafür."
+
+-- OK
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2246359087"] = "OK"
+
+-- Configuration server:
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2272122662"] = "Konfigurationsserver:"
+
+-- We must generate random numbers, e.g., for securing the interprocess communication between the user interface and the runtime. The rand library is great for this purpose.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2273492381"] = "Wir müssen Zufallszahlen erzeugen, z. B. um die Kommunikation zwischen der Benutzeroberfläche und der Laufzeitumgebung abzusichern. Die rand-Bibliothek eignet sich dafür hervorragend."
+
+-- AI Studio runs with an enterprise configuration using a configuration plugin, without central configuration management.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2280402765"] = "AI Studio läuft mit einer Unternehmenskonfiguration über ein Konfigurations-Plugin, ohne zentrale Konfigurationsverwaltung."
+
+-- Configuration plugin ID:
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2301484629"] = "Konfigurations-Plugin-ID:"
+
+-- The C# language is used for the implementation of the user interface and the backend. To implement the user interface with C#, the Blazor technology from ASP.NET Core is used. All these technologies are integrated into the .NET SDK.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2329884315"] = "Die Programmiersprache C# wird für die Umsetzung der Benutzeroberfläche und des Backends verwendet. Für die Entwicklung der Benutzeroberfläche mit C# kommt die Blazor-Technologie aus ASP.NET Core zum Einsatz. Alle diese Technologien sind im .NET SDK integriert."
+
+-- Used PDFium version
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2368247719"] = "Verwendete PDFium-Version"
+
+-- installation provided by the system
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2371107659"] = "Installation vom System bereitgestellt"
+
+-- Installed Pandoc version: Pandoc is not installed or not available.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2374031539"] = "Installierte Pandoc-Version: Pandoc ist nicht installiert oder nicht verfügbar."
+
+-- This library is used to determine the language of the operating system. This is necessary to set the language of the user interface.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2557014401"] = "Diese Bibliothek wird verwendet, um die Sprache des Betriebssystems zu erkennen. Dies ist notwendig, um die Sprache der Benutzeroberfläche einzustellen."
+
+-- Used Open Source Projects
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2557066213"] = "Verwendete Open-Source-Projekte"
+
+-- Build time
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T260228112"] = "Build-Zeit"
+
+-- To be able to use the responses of the LLM in other apps, we often use the clipboard of the respective operating system. Unfortunately, in .NET there is no solution that works with all operating systems. Therefore, I have opted for this library in Rust. This way, data transfer to other apps works on every system.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2644379659"] = "Um die Antworten des LLM in anderen Apps nutzen zu können, verwenden wir häufig die Zwischenablage des jeweiligen Betriebssystems. Leider gibt es in .NET keine Lösung, die auf allen Betriebssystemen funktioniert. Deshalb habe ich mich für diese Bibliothek in Rust entschieden. So funktioniert die Datenübertragung zu anderen Apps auf jedem System."
+
+-- Usage log file
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2689995864"] = "Nutzungsprotokolldatei"
+
+-- Logbook
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2706940196"] = "Protokolldateien"
+
+-- This component is used to render Markdown text. This is important because the LLM often responds with Markdown-formatted text, allowing us to present it in a way that is easier to read.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2726131107"] = "Diese Komponente wird verwendet, um Markdown-Text darzustellen. Das ist wichtig, weil das LLM häufig mit im Markdown-Format formatiertem Text antwortet. Dadurch können wir die Antworten besser lesbar anzeigen."
+
+-- Determine Pandoc version, please wait...
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2765814390"] = "Pandoc-Version wird ermittelt, bitte warten …"
+
+-- Code in the Rust language can be specified as synchronous or asynchronous. Unlike .NET and the C# language, Rust cannot execute asynchronous code by itself. Rust requires support in the form of an executor for this. Tokio is one such executor.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2777988282"] = "Code in der Programmiersprache Rust kann als synchron oder asynchron spezifiziert werden. Im Gegensatz zu .NET und der Sprache C# kann Rust asynchronen Code jedoch nicht von selbst ausführen. Dafür benötigt Rust Unterstützung in Form eines Executors. Tokio ist ein solcher Executor."
+
+-- Show Details
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T27924674"] = "Details anzeigen"
+
+-- View our project roadmap and help shape AI Studio's future development.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2829971158"] = "Sehen Sie sich unsere Roadmap an und helfen Sie mit, die zukünftige Entwicklung von AI Studio mitzugestalten."
+
+-- Used .NET runtime
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2840227993"] = "Verwendete .NET-Laufzeit"
+
+-- Explanation
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2840582448"] = "Erklärung"
+
+-- The .NET backend cannot be started as a desktop app. Therefore, I use a second backend in Rust, which I call runtime. With Rust as the runtime, Tauri can be used to realize a typical desktop app. Thanks to Rust, this app can be offered for Windows, macOS, and Linux desktops. Rust is a great language for developing safe and high-performance software.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2868174483"] = "Das .NET-Backend kann nicht als Desktop-App gestartet werden. Deshalb verwende ich ein zweites Backend in Rust, das ich „Runtime“ nenne. Mit Rust als Runtime kann Tauri genutzt werden, um eine typische Desktop-App zu realisieren. Dank Rust kann diese App für Windows-, macOS- und Linux-Desktops angeboten werden. Rust ist eine großartige Sprache für die Entwicklung sicherer und leistungsstarker Software."
+
+-- Changelog
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3017574265"] = "Änderungsprotokoll"
+
+-- Enterprise configuration ID:
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3092349641"] = "Unternehmenskonfigurations-ID:"
+
+-- Connect AI Studio to your organization's data with our External Retrieval Interface (ERI).
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T313276297"] = "Verbinden Sie AI Studio mit den Daten ihrer Organisation über unsere Schnittstelle für externe Datenabfrage (ERI)."
+
+-- Have feature ideas? Submit suggestions for future AI Studio enhancements.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3178730036"] = "Haben Sie Ideen für neue Funktionen? Senden Sie uns Vorschläge für zukünftige Verbesserungen von AI Studio."
+
+-- Hide Details
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3183837919"] = "Details ausblenden"
+
+-- Update Pandoc
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3249965383"] = "Pandoc aktualisieren"
+
+-- Discover MindWork AI's mission and vision on our official homepage.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3294830584"] = "Entdecken Sie die Mission und Vision von MindWork AI auf unserer offiziellen Homepage."
+
+-- User-language provided by the OS
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3334355246"] = "Vom Betriebssystem bereitgestellte Sprache"
+
+-- The following list shows the versions of the MindWork AI Studio, the used compilers, build time, etc.:
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3405978777"] = "Die folgende Liste zeigt die Versionen von MindWork AI Studio und des verwendeten Compilers, den Build-Zeitpunkt und weitere Informationen:"
+
+-- Information about MindWork AI Studio
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3433065373"] = "Informationen über MindWork AI Studio"
+
+-- Used Rust compiler
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3440211747"] = "Verwendeter Rust-Compiler"
+
+-- Tauri is used to host the Blazor user interface. It is a great project that allows the creation of desktop applications using web technologies. I love Tauri!
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3494984593"] = "Tauri wird verwendet, um die Blazor-Benutzeroberfläche bereitzustellen. Es ist ein großartiges Projekt, das die Erstellung von Desktop-Anwendungen mit Webtechnologien ermöglicht. Ich liebe Tauri!"
+
+-- Motivation
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3563271893"] = "Motivation"
+
+-- This library is used to read Excel and OpenDocument spreadsheet files. This is necessary, e.g., for using spreadsheets as a data source for a chat.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3722989559"] = "Diese Bibliothek wird verwendet, um Excel- und OpenDocument-Tabellendateien zu lesen. Dies ist zum Beispiel notwendig, wenn Tabellen als Datenquelle für einen Chat verwendet werden sollen."
+
+-- AI Studio runs with an enterprise configuration and a configuration server. The configuration plugin is active.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3741877842"] = "AI Studio läuft mit einer Unternehmenskonfiguration und einem Konfigurationsserver. Das Konfigurations-Plugin ist aktiv."
+
+-- this version does not met the requirements
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3813932670"] = "diese Version erfüllt die Anforderungen nicht"
+
+-- This library is used to access the Windows registry. We use this for Windows enterprise environments to read the desired configuration.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3874337003"] = "Diese Bibliothek wird verwendet, um auf die Windows-Registry zuzugreifen. Wir nutzen sie in Windows-Unternehmensumgebungen, um die gewünschte Konfiguration auszulesen."
+
+-- Now we have multiple systems, some developed in .NET and others in Rust. The data format JSON is responsible for translating data between both worlds (called data serialization and deserialization). Serde takes on this task in the Rust world. The counterpart in the .NET world is an integral part of .NET and is located in System.Text.Json.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3908558992"] = "Jetzt haben wir mehrere Systeme, einige entwickelt in .NET und andere in Rust. Das Datenformat JSON ist dafür zuständig, Daten zwischen beiden Welten zu übersetzen (dies nennt man Serialisierung und Deserialisierung von Daten). In der Rust-Welt übernimmt Serde diese Aufgabe. Das Pendant in der .NET-Welt ist ein fester Bestandteil von .NET und findet sich in System.Text.Json."
+
+-- Installed Pandoc version
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3983971016"] = "Installierte Pandoc-Version"
+
+-- Check Pandoc Installation
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3986423270"] = "Pandoc-Installation prüfen"
+
+-- Versions
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T4010195468"] = "Versionen"
+
+-- This library is used to create asynchronous streams in Rust. It allows us to work with streams of data that can be produced asynchronously, making it easier to handle events or data that arrive over time. We use this, e.g., to stream arbitrary data from the file system to the embedding system.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T4079152443"] = "Diese Bibliothek wird verwendet, um asynchrone Datenströme in Rust zu erstellen. Sie ermöglicht es uns, mit Datenströmen zu arbeiten, die asynchron bereitgestellt werden, wodurch sich Ereignisse oder Daten, die nach und nach eintreffen, leichter verarbeiten lassen. Wir nutzen dies zum Beispiel, um beliebige Daten aus dem Dateisystem an das Einbettungssystem zu übertragen."
+
+-- Community & Code
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T4158546761"] = "Community & Code"
+
+-- We use the HtmlAgilityPack to extract content from the web. This is necessary, e.g., when you provide a URL as input for an assistant.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T4184485147"] = "Wir verwenden das HtmlAgilityPack, um Inhalte aus dem Internet zu extrahieren. Das ist zum Beispiel notwendig, wenn Sie eine URL als Eingabe für einen Assistenten angeben."
+
+-- When transferring sensitive data between Rust runtime and .NET app, we encrypt the data. We use some libraries from the Rust Crypto project for this purpose: cipher, aes, cbc, pbkdf2, hmac, and sha2. We are thankful for the great work of the Rust Crypto project.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T4229014037"] = "Beim Übertragen sensibler Daten zwischen der Rust-Laufzeitumgebung und der .NET-Anwendung verschlüsseln wir die Daten. Dafür verwenden wir einige Bibliotheken aus dem Rust Crypto-Projekt: cipher, aes, cbc, pbkdf2, hmac und sha2. Wir sind dankbar für die großartige Arbeit des Rust Crypto-Projekts."
+
+-- This is a library providing the foundations for asynchronous programming in Rust. It includes key trait definitions like Stream, as well as utilities like join!, select!, and various futures combinator methods which enable expressive asynchronous control flow.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T566998575"] = "Dies ist eine Bibliothek, die die Grundlagen für asynchrones Programmieren in Rust bereitstellt. Sie enthält zentrale Trait-Definitionen wie Stream sowie Hilfsfunktionen wie join!, select! und verschiedene Methoden zur Kombination von Futures, die einen ausdrucksstarken asynchronen Kontrollfluss ermöglichen."
+
+-- Used .NET SDK
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T585329785"] = "Verwendetes .NET SDK"
+
+-- Did you find a bug or are you experiencing issues? Report your concern here.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T639371534"] = "Haben Sie einen Fehler gefunden oder Probleme festgestellt? Melden Sie Ihr Anliegen hier."
+
+-- This Rust library is used to output the app's messages to the terminal. This is helpful during development and troubleshooting. This feature is initially invisible; when the app is started via the terminal, the messages become visible.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T64689067"] = "Diese Rust-Bibliothek wird verwendet, um die Nachrichten der App im Terminal auszugeben. Das ist während der Entwicklung und Fehlersuche hilfreich. Diese Funktion ist zunächst unsichtbar; werden App über das Terminal gestartet, werden die Nachrichten sichtbar."
+
+-- Copies the config ID to the clipboard
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T788846912"] = "Kopiert die Konfigurations-ID in die Zwischenablage"
+
+-- installed by AI Studio
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T833849470"] = "installiert von AI Studio"
+
+-- We use this library to be able to read PowerPoint files. This allows us to insert content from slides into prompts and take PowerPoint files into account in RAG processes. We thank Nils Kruthoff for his work on this Rust crate.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T855925638"] = "Wir verwenden diese Bibliothek, um PowerPoint-Dateien lesen zu können. So ist es möglich, Inhalte aus Folien in Prompts einzufügen und PowerPoint-Dateien in RAG-Prozessen zu berücksichtigen. Wir danken Nils Kruthoff für seine Arbeit an diesem Rust-Crate."
+
+-- For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T870640199"] = "Für einige Datenübertragungen müssen wir die Daten in Base64 kodieren. Diese Rust-Bibliothek eignet sich dafür hervorragend."
+
+-- Install Pandoc
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T986578435"] = "Pandoc installieren"
+
-- Disable plugin
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1430375822"] = "Plugin deaktivieren"
@@ -5142,6 +5349,9 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T3424652889"] = "Un
-- no model selected
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::MODEL::T2234274832"] = "Kein Modell ausgewählt"
+-- Model as configured by whisper.cpp
+UI_TEXT_CONTENT["AISTUDIO::PROVIDER::SELFHOSTED::PROVIDERSELFHOSTED::T3313940770"] = "Modell wie in whisper.cpp konfiguriert"
+
-- Use no chat template
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CHATTEMPLATE::T4258819635"] = "Keine Chat-Vorlage verwenden"
@@ -5316,6 +5526,9 @@ UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::PREVIEWFEATURESEXTENSIONS::T2708
-- Unknown preview feature
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::PREVIEWFEATURESEXTENSIONS::T2722827307"] = "Unbekannte Vorschau-Funktion"
+-- Transcription: Preview of our speech to text system where you can transcribe recordings and audio files into text
+UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::PREVIEWFEATURESEXTENSIONS::T714355911"] = "Transkription: Vorschau unseres Sprache-zu-Text-Systems, mit dem Sie Aufnahmen und Audiodateien in Text transkribieren können"
+
-- Use no data sources, when sending an assistant result to a chat
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::SENDTOCHATDATASOURCEBEHAVIOREXTENSIONS::T1223925477"] = "Keine Datenquellen vorauswählen, wenn ein Ergebnis von einem Assistenten an einen neuen Chat gesendet wird"
@@ -5880,6 +6093,12 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::RAG::RAGPROCESSES::AISRCSELWITHRETCTXVAL::T377
-- Executable Files
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPEFILTER::T2217313358"] = "Ausführbare Dateien"
+-- All Source Code Files
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPEFILTER::T2460199369"] = "Alle Quellcodedateien"
+
+-- All Audio Files
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPEFILTER::T2575722901"] = "Alle Audiodateien"
+
-- All Video Files
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPEFILTER::T2850789856"] = "Alle Videodateien"
@@ -6009,6 +6228,30 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::DATASOURCEVALIDATION::T878007824"]
-- Please enter the secret necessary for authentication.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::DATASOURCEVALIDATION::T968385876"] = "Bitte geben Sie das für die Authentifizierung benötigte Geheimnis ein."
+-- Unsupported image format
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T1398282880"] = "Nicht unterstütztes Bildformat"
+
+-- File has no extension
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T1555980031"] = "Datei hat keine Erweiterung"
+
+-- Audio files are not supported yet
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T2919730669"] = "Audio-Dateien werden noch nicht unterstützt."
+
+-- Videos are not supported yet
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T2928927510"] = "Videos werden noch nicht unterstützt."
+
+-- Images are not supported yet
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T298062956"] = "Bilder werden derzeit nicht unterstützt."
+
+-- Images are not supported at this place
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T305247150"] = "Bilder werden an dieser Stelle nicht unterstützt."
+
+-- Executables are not allowed
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T4167762413"] = "Ausführbare Dateien sind nicht erlaubt"
+
+-- Images are not supported by the selected provider and model
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T999194030"] = "Bilder werden vom ausgewählten Anbieter und Modell nicht unterstützt."
+
-- The hostname is not a valid HTTP(S) URL.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::PROVIDERVALIDATION::T1013354736"] = "Der Hostname ist keine gültige HTTP(S)-URL."
@@ -6047,5 +6290,3 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T1307384014"] = "Unbenannt
-- Delete Chat
UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T2244038752"] = "Chat löschen"
-
-
diff --git a/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua b/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua
index d6c147ec..6e0ba76c 100644
--- a/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua
+++ b/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua
@@ -1044,21 +1044,36 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T1214535771"] = "Rem
-- Added Content ({0} entries)
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T1258080997"] = "Added Content ({0} entries)"
+-- No Lua code generated yet.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T1365137848"] = "No Lua code generated yet."
+
-- Localized Content ({0} entries of {1})
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T1492071634"] = "Localized Content ({0} entries of {1})"
-- Select the language plugin used for comparision.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T1523568309"] = "Select the language plugin used for comparision."
+-- Successfully updated plugin file.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T1524590750"] = "Successfully updated plugin file."
+
-- Was not able to load the language plugin for comparison ({0}). Please select a valid, loaded & running language plugin.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T1893011391"] = "Was not able to load the language plugin for comparison ({0}). Please select a valid, loaded & running language plugin."
+-- No language plugin selected.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T237325294"] = "No language plugin selected."
+
-- Target language
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T237828418"] = "Target language"
+-- Write Lua code to language plugin file
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T253827221"] = "Write Lua code to language plugin file"
+
-- Language plugin used for comparision
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T263317578"] = "Language plugin used for comparision"
+-- Plugin file not found.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T2938065913"] = "Plugin file not found."
+
-- Localize AI Studio & generate the Lua code
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T3055634395"] = "Localize AI Studio & generate the Lua code"
@@ -1089,6 +1104,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T453060723"] = "The
-- The selected language plugin for comparison uses the IETF tag '{0}' which does not match the selected target language '{1}'. Please select a valid, loaded & running language plugin which matches the target language.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T458999393"] = "The selected language plugin for comparison uses the IETF tag '{0}' which does not match the selected target language '{1}'. Please select a valid, loaded & running language plugin which matches the target language."
+-- Could not find 'UI_TEXT_CONTENT = {}' marker in plugin file.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T628596031"] = "Could not find 'UI_TEXT_CONTENT = {}' marker in plugin file."
+
-- Please provide a custom language.
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T656744944"] = "Please provide a custom language."
@@ -1098,6 +1116,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T851515643"] = "Plea
-- Localization
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T897888480"] = "Localization"
+-- Error writing to plugin file.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::I18N::ASSISTANTI18N::T948564909"] = "Error writing to plugin file."
+
-- Your icon source
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ICONFINDER::ASSISTANTICONFINDER::T1302165948"] = "Your icon source"
@@ -1494,36 +1515,45 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4188329028"] = "No, kee
-- Export Chat to Microsoft Word
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T861873672"] = "Export Chat to Microsoft Word"
+-- The local image file does not exist. Skipping the image.
+UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T255679918"] = "The local image file does not exist. Skipping the image."
+
+-- Failed to download the image from the URL. Skipping the image.
+UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T2996654916"] = "Failed to download the image from the URL. Skipping the image."
+
+-- The local image file is too large (>10 MB). Skipping the image.
+UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T3219823625"] = "The local image file is too large (>10 MB). Skipping the image."
+
+-- The image at the URL is too large (>10 MB). Skipping the image.
+UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T349928509"] = "The image at the URL is too large (>10 MB). Skipping the image."
+
-- Open Settings
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T1172211894"] = "Open Settings"
+-- Click the paperclip to attach files, or click the number to see your attached files.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T1358313858"] = "Click the paperclip to attach files, or click the number to see your attached files."
+
+-- Drop files here to attach them.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T143112277"] = "Drop files here to attach them."
+
+-- Click here to attach files.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T1875575968"] = "Click here to attach files."
+
-- Drag and drop files into the marked area or click here to attach documents:
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T230755331"] = "Drag and drop files into the marked area or click here to attach documents:"
+-- Select files to attach
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T2495931372"] = "Select files to attach"
+
-- Document Preview
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T285154968"] = "Document Preview"
--- Videos are not supported yet
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T2928927510"] = "Videos are not supported yet"
-
--- Images are not supported yet
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T298062956"] = "Images are not supported yet"
-
--- Click to attach files
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T3521845090"] = "Click to attach files"
-
-- Clear file list
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T3759696136"] = "Clear file list"
-- Add file
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T4014053962"] = "Add file"
--- Executables are not allowed
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T4167762413"] = "Executables are not allowed"
-
--- Select a file to attach
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T595772870"] = "Select a file to attach"
-
-- Changelog
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHANGELOG::T3017574265"] = "Changelog"
@@ -1824,21 +1854,12 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PROVIDERSELECTION::T900237532"] = "Provid
-- Failed to load file content
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T1989554334"] = "Failed to load file content"
--- Videos are not supported yet
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T2928927510"] = "Videos are not supported yet"
-
--- Images are not supported yet
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T298062956"] = "Images are not supported yet"
-
-- Use file content as input
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T3499386973"] = "Use file content as input"
-- Select file to read its content
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T354817589"] = "Select file to read its content"
--- Executables are not allowed
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READFILECONTENT::T4167762413"] = "Executables are not allowed"
-
-- The content is cleaned using an LLM agent: the main content is extracted, advertisements and other irrelevant things are attempted to be removed; relative links are attempted to be converted into absolute links so that they can be used.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::READWEBCONTENT::T1164201762"] = "The content is cleaned using an LLM agent: the main content is extracted, advertisements and other irrelevant things are attempted to be removed; relative links are attempted to be converted into absolute links so that they can be used."
@@ -2004,6 +2025,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1599198973"]
-- Would you like to set one of your profiles as the default for the entire app? When you configure a different profile for an assistant, it will always take precedence.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1666052109"] = "Would you like to set one of your profiles as the default for the entire app? When you configure a different profile for an assistant, it will always take precedence."
+-- Select a transcription provider for transcribing your voice. Without a selected provider, dictation and transcription features will be disabled.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1834486728"] = "Select a transcription provider for transcribing your voice. Without a selected provider, dictation and transcription features will be disabled."
+
-- Select the language behavior for the app. The default is to use the system language. You might want to choose a language manually?
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T186780842"] = "Select the language behavior for the app. The default is to use the system language. You might want to choose a language manually?"
@@ -2016,6 +2040,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1898060643"]
-- Select the language for the app.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1907446663"] = "Select the language for the app."
+-- Disable dictation and transcription
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T215381891"] = "Disable dictation and transcription"
+
-- Language behavior
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T2341504363"] = "Language behavior"
@@ -2046,6 +2073,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T4004501229"]
-- 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.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T4067492921"] = "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."
+-- Select a transcription provider
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T4174666315"] = "Select a transcription provider"
+
-- Navigation bar behavior
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T602293588"] = "Navigation bar behavior"
@@ -2088,24 +2118,30 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T24199
-- Name
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T266367750"] = "Name"
+-- Configured Embedding Providers
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T305753126"] = "Configured Embedding Providers"
+
-- This helps AI Studio understand and compare things in a way that's similar to how humans do. When you're working on something, AI Studio can automatically identify related documents and data by comparing their digital fingerprints. For instance, if you're writing about customer service, AI Studio can instantly find other documents in your data that discuss similar topics or experiences, even if they use different words.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3251217940"] = "This helps AI Studio understand and compare things in a way that's similar to how humans do. When you're working on something, AI Studio can automatically identify related documents and data by comparing their digital fingerprints. For instance, if you're writing about customer service, AI Studio can instantly find other documents in your data that discuss similar topics or experiences, even if they use different words."
-- Edit
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3267849393"] = "Edit"
--- Configured Embeddings
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3526613453"] = "Configured Embeddings"
-
-- Actions
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T3865031940"] = "Actions"
+-- This embedding provider is managed by your organization.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T4062656589"] = "This embedding provider is managed by your organization."
+
-- No embeddings configured yet.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T4068015588"] = "No embeddings configured yet."
-- Edit Embedding Provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T4264602229"] = "Edit Embedding Provider"
+-- Configure Embedding Providers
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T488419116"] = "Configure Embedding Providers"
+
-- Delete Embedding Provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T511304264"] = "Delete Embedding Provider"
@@ -2115,9 +2151,6 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T78223
-- Provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T900237532"] = "Provider"
--- Configure Embeddings
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T970042679"] = "Configure Embeddings"
-
-- Show provider's confidence level?
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1052533048"] = "Show provider's confidence level?"
@@ -2136,6 +2169,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T172585
-- Add Provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1806589097"] = "Add Provider"
+-- Configure LLM Providers
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1810190350"] = "Configure LLM Providers"
+
-- Edit LLM Provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T1868766523"] = "Edit LLM Provider"
@@ -2169,8 +2205,8 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T284206
-- No providers configured yet.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T2911731076"] = "No providers configured yet."
--- Configure Providers
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T3027859089"] = "Configure Providers"
+-- Configured LLM Providers
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T3019870540"] = "Configured LLM Providers"
-- as selected by provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T3082210376"] = "as selected by provider"
@@ -2193,9 +2229,6 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T361241
-- No, do not enforce a minimum confidence level
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T3642102079"] = "No, do not enforce a minimum confidence level"
--- Configured Providers
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T3850871263"] = "Configured Providers"
-
-- Actions
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T3865031940"] = "Actions"
@@ -2223,6 +2256,57 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T853225
-- Provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T900237532"] = "Provider"
+-- No transcription provider configured yet.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T1079350363"] = "No transcription provider configured yet."
+
+-- Edit Transcription Provider
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T1317362918"] = "Edit Transcription Provider"
+
+-- Delete
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T1469573738"] = "Delete"
+
+-- Add transcription provider
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T1645238629"] = "Add transcription provider"
+
+-- Add Transcription Provider
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T2066315685"] = "Add Transcription Provider"
+
+-- Model
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T2189814010"] = "Model"
+
+-- Name
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T266367750"] = "Name"
+
+-- Edit
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T3267849393"] = "Edit"
+
+-- Delete Transcription Provider
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T370103955"] = "Delete Transcription Provider"
+
+-- Actions
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T3865031940"] = "Actions"
+
+-- Configure Transcription Providers
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T4073110625"] = "Configure Transcription Providers"
+
+-- Configured Transcription Providers
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T4210863523"] = "Configured Transcription Providers"
+
+-- With the support of transcription models, MindWork AI Studio can convert human speech into text. This is useful, for example, when you need to dictate text. You can choose from dedicated transcription models, but not multimodal LLMs (large language models) that can handle both speech and text. The configuration of multimodal models is done in the 'Configure providers' section.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T584860404"] = "With the support of transcription models, MindWork AI Studio can convert human speech into text. This is useful, for example, when you need to dictate text. You can choose from dedicated transcription models, but not multimodal LLMs (large language models) that can handle both speech and text. The configuration of multimodal models is done in the 'Configure LLM providers' section."
+
+-- This transcription provider is managed by your organization.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T756131076"] = "This transcription provider is managed by your organization."
+
+-- Open Dashboard
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T78223861"] = "Open Dashboard"
+
+-- Are you sure you want to delete the transcription provider '{0}'?
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T789660305"] = "Are you sure you want to delete the transcription provider '{0}'?"
+
+-- Provider
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T900237532"] = "Provider"
+
-- Copy {0} to the clipboard
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TEXTINFOLINE::T2206391442"] = "Copy {0} to the clipboard"
@@ -2304,6 +2388,33 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VISION::T428040679"] = "Content creation"
-- Useful assistants
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VISION::T586430036"] = "Useful assistants"
+-- Failed to create the transcription provider.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VOICERECORDER::T1689988905"] = "Failed to create the transcription provider."
+
+-- Stop recording and start transcription
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VOICERECORDER::T224155287"] = "Stop recording and start transcription"
+
+-- Start recording your voice for a transcription
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VOICERECORDER::T2372624045"] = "Start recording your voice for a transcription"
+
+-- Transcription in progress...
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VOICERECORDER::T2851219233"] = "Transcription in progress..."
+
+-- The configured transcription provider was not found.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VOICERECORDER::T331613105"] = "The configured transcription provider was not found."
+
+-- The configured transcription provider does not meet the minimum confidence level.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VOICERECORDER::T3834149033"] = "The configured transcription provider does not meet the minimum confidence level."
+
+-- An error occurred during transcription.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VOICERECORDER::T588743762"] = "An error occurred during transcription."
+
+-- No transcription provider is configured.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VOICERECORDER::T663630295"] = "No transcription provider is configured."
+
+-- The transcription result is empty.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VOICERECORDER::T974954792"] = "The transcription result is empty."
+
-- Are you sure you want to delete the chat '{0}' in the workspace '{1}'?
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1016188706"] = "Are you sure you want to delete the chat '{0}' in the workspace '{1}'?"
@@ -2424,6 +2535,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2147062613"] = "Profile
-- Add messages of an example conversation (user prompt followed by assistant prompt) to demonstrate the desired interaction pattern. These examples help the AI understand your expectations by showing it the correct format, style, and content of responses before it receives actual user inputs.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2292424657"] = "Add messages of an example conversation (user prompt followed by assistant prompt) to demonstrate the desired interaction pattern. These examples help the AI understand your expectations by showing it the correct format, style, and content of responses before it receives actual user inputs."
+-- File Attachments
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2294745309"] = "File Attachments"
+
-- Role
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2418769465"] = "Role"
@@ -2463,6 +2577,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3016903701"] = "The nam
-- Image content
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3094908719"] = "Image content"
+-- You can attach files that will be automatically included when using this chat template. These files will be added to the first message sent in any chat using this template.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3108503534"] = "You can attach files that will be automatically included when using this chat template. These files will be added to the first message sent in any chat using this template."
+
-- Are you unsure which system prompt to use? You might start with the default system prompt that AI Studio uses for all chats.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3127437308"] = "Are you unsure which system prompt to use? You might start with the default system prompt that AI Studio uses for all chats."
@@ -2955,6 +3072,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTCHECKDIALOG::T1373123357"] = "Markdo
-- Load file
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTCHECKDIALOG::T2129302565"] = "Load file"
+-- Image View
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTCHECKDIALOG::T2199753423"] = "Image View"
+
-- See how we load your file. Review the content before we process it further.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTCHECKDIALOG::T3271853346"] = "See how we load your file. Review the content before we process it further."
@@ -2973,6 +3093,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTCHECKDIALOG::T652739927"] = "This is
-- File Path
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTCHECKDIALOG::T729508546"] = "File Path"
+-- The specified file could not be found. The file have been moved, deleted, renamed, or is otherwise inaccessible.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::DOCUMENTCHECKDIALOG::T973777830"] = "The specified file could not be found. The file have been moved, deleted, renamed, or is otherwise inaccessible."
+
-- Embedding Name
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGMETHODDIALOG::T1427271797"] = "Embedding Name"
@@ -3069,9 +3192,6 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T2189814010"] = "Mo
-- (Optional) API Key
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T2331453405"] = "(Optional) API Key"
--- Currently, we cannot query the embedding models of self-hosted systems. Therefore, enter the model name manually.
-UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T2615586687"] = "Currently, we cannot query the embedding models of self-hosted systems. Therefore, enter the model name manually."
-
-- Add
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T2646845972"] = "Add"
@@ -3081,6 +3201,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T2810182573"] = "No
-- Instance Name
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T2842060373"] = "Instance Name"
+-- Currently, we cannot query the embedding models for the selected provider and/or host. Therefore, please enter the model name manually.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T290547799"] = "Currently, we cannot query the embedding models for the selected provider and/or host. Therefore, please enter the model name manually."
+
-- Model selection
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T416738168"] = "Model selection"
@@ -3291,6 +3414,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3361153305"] = "Show Expert
-- Show available models
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3763891899"] = "Show available models"
+-- Currently, we cannot query the models for the selected provider and/or host. Therefore, please enter the model name manually.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T4116737656"] = "Currently, we cannot query the models for the selected provider and/or host. Therefore, please enter the model name manually."
+
-- Model selection
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T416738168"] = "Model selection"
@@ -3417,6 +3543,33 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::RETRIEVALPROCESSDIALOG::T900713019"] = "Canc
-- Embeddings
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::RETRIEVALPROCESSDIALOG::T951463987"] = "Embeddings"
+-- Here you can see all attached files. Files that can no longer be found (deleted, renamed, or moved) are marked with a warning icon and a strikethrough name. You can remove any attachment using the trash can icon.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T1746160064"] = "Here you can see all attached files. Files that can no longer be found (deleted, renamed, or moved) are marked with a warning icon and a strikethrough name. You can remove any attachment using the trash can icon."
+
+-- There aren't any file attachments right now.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T2111340711"] = "There aren't any file attachments right now."
+
+-- Document Preview
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T285154968"] = "Document Preview"
+
+-- The file was deleted, renamed, or moved.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T3083729256"] = "The file was deleted, renamed, or moved."
+
+-- Your attached file.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T3154198222"] = "Your attached file."
+
+-- Preview what we send to the AI.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T3160778981"] = "Preview what we send to the AI."
+
+-- Close
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T3448155331"] = "Close"
+
+-- Your attached files
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T3909191077"] = "Your attached files"
+
+-- Remove this attachment.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T3933470258"] = "Remove this attachment."
+
-- There is no social event
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGAGENDA::T1222800281"] = "There is no social event"
@@ -4440,6 +4593,60 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T4030229154"] = "Your Inp
-- Cancel
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SINGLEINPUTDIALOG::T900713019"] = "Cancel"
+-- Failed to store the API key in the operating system. The message was: {0}. Please try again.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1122745046"] = "Failed to store the API key in the operating system. The message was: {0}. Please try again."
+
+-- API Key
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1324664716"] = "API Key"
+
+-- Create account
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1356621346"] = "Create account"
+
+-- Currently, we cannot query the transcription models for the selected provider and/or host. Therefore, please enter the model name manually.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1381635232"] = "Currently, we cannot query the transcription models for the selected provider and/or host. Therefore, please enter the model name manually."
+
+-- Hostname
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1727440780"] = "Hostname"
+
+-- Load
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1756340745"] = "Load"
+
+-- Update
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1847791252"] = "Update"
+
+-- Failed to load the API key from the operating system. The message was: {0}. You might ignore this message and provide the API key again.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T1870831108"] = "Failed to load the API key from the operating system. The message was: {0}. You might ignore this message and provide the API key again."
+
+-- Model
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T2189814010"] = "Model"
+
+-- (Optional) API Key
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T2331453405"] = "(Optional) API Key"
+
+-- Add
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T2646845972"] = "Add"
+
+-- No models loaded or available.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T2810182573"] = "No models loaded or available."
+
+-- Instance Name
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T2842060373"] = "Instance Name"
+
+-- Please enter a transcription model name.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T3703662664"] = "Please enter a transcription model name."
+
+-- Model selection
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T416738168"] = "Model selection"
+
+-- Host
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T808120719"] = "Host"
+
+-- Provider
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T900237532"] = "Provider"
+
+-- Cancel
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::TRANSCRIPTIONPROVIDERDIALOG::T900713019"] = "Cancel"
+
-- Install now
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::UPDATEDIALOG::T2366359512"] = "Install now"
@@ -4458,9 +4665,6 @@ UI_TEXT_CONTENT["AISTUDIO::LAYOUT::MAINLAYOUT::T1258653480"] = "Settings"
-- Home
UI_TEXT_CONTENT["AISTUDIO::LAYOUT::MAINLAYOUT::T1391791790"] = "Home"
--- About
-UI_TEXT_CONTENT["AISTUDIO::LAYOUT::MAINLAYOUT::T1491113694"] = "About"
-
-- Are you sure you want to leave the chat page? All unsaved changes will be lost.
UI_TEXT_CONTENT["AISTUDIO::LAYOUT::MAINLAYOUT::T1563130494"] = "Are you sure you want to leave the chat page? All unsaved changes will be lost."
@@ -4491,243 +4695,12 @@ UI_TEXT_CONTENT["AISTUDIO::LAYOUT::MAINLAYOUT::T2979224202"] = "Writing"
-- Show details
UI_TEXT_CONTENT["AISTUDIO::LAYOUT::MAINLAYOUT::T3692372066"] = "Show details"
+-- Information
+UI_TEXT_CONTENT["AISTUDIO::LAYOUT::MAINLAYOUT::T4256323669"] = "Information"
+
-- Chat
UI_TEXT_CONTENT["AISTUDIO::LAYOUT::MAINLAYOUT::T578410699"] = "Chat"
--- Startup log file
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1019424746"] = "Startup log file"
-
--- About MindWork AI Studio
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1020427799"] = "About MindWork AI Studio"
-
--- Browse AI Studio's source code on GitHub — we welcome your contributions.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1107156991"] = "Browse AI Studio's source code on GitHub — we welcome your contributions."
-
--- This is a private AI Studio installation. It runs without an enterprise configuration.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1209549230"] = "This is a private AI Studio installation. It runs without an enterprise configuration."
-
--- AI Studio runs with an enterprise configuration and a configuration server. The configuration plugin is not yet available.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1282228996"] = "AI Studio runs with an enterprise configuration and a configuration server. The configuration plugin is not yet available."
-
--- This library is used to read PDF files. This is necessary, e.g., for using PDFs as a data source for a chat.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1388816916"] = "This library is used to read PDF files. This is necessary, e.g., for using PDFs as a data source for a chat."
-
--- This library is used to extend the MudBlazor library. It provides additional components that are not part of the MudBlazor library.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1421513382"] = "This library is used to extend the MudBlazor library. It provides additional components that are not part of the MudBlazor library."
-
--- We use Lua as the language for plugins. Lua-CSharp lets Lua scripts communicate with AI Studio and vice versa. Thank you, Yusuke Nakada, for this great library.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T162898512"] = "We use Lua as the language for plugins. Lua-CSharp lets Lua scripts communicate with AI Studio and vice versa. Thank you, Yusuke Nakada, for this great library."
-
--- Building on .NET, ASP.NET Core, and Blazor, MudBlazor is used as a library for designing and developing the user interface. It is a great project that significantly accelerates the development of advanced user interfaces with Blazor.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1629800076"] = "Building on .NET, ASP.NET Core, and Blazor, MudBlazor is used as a library for designing and developing the user interface. It is a great project that significantly accelerates the development of advanced user interfaces with Blazor."
-
--- AI Studio creates a log file at startup, in which events during startup are recorded. After startup, another log file is created that records all events that occur during the use of the app. This includes any errors that may occur. Depending on when an error occurs (at startup or during use), the contents of these log files can be helpful for troubleshooting. Sensitive information such as passwords is not included in the log files.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1630237140"] = "AI Studio creates a log file at startup, in which events during startup are recorded. After startup, another log file is created that records all events that occur during the use of the app. This includes any errors that may occur. Depending on when an error occurs (at startup or during use), the contents of these log files can be helpful for troubleshooting. Sensitive information such as passwords is not included in the log files."
-
--- This library is used to display the differences between two texts. This is necessary, e.g., for the grammar and spelling assistant.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1772678682"] = "This library is used to display the differences between two texts. This is necessary, e.g., for the grammar and spelling assistant."
-
--- By clicking on the respective path, the path is copied to the clipboard. You might open these files with a text editor to view their contents.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1806897624"] = "By clicking on the respective path, the path is copied to the clipboard. You might open these files with a text editor to view their contents."
-
--- Pandoc Installation
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T185447014"] = "Pandoc Installation"
-
--- Copies the configuration plugin ID to the clipboard
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1859295819"] = "Copies the configuration plugin ID to the clipboard"
-
--- Check for updates
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1890416390"] = "Check for updates"
-
--- Vision
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1892426825"] = "Vision"
-
--- In order to use any LLM, each user must store their so-called API key for each LLM provider. This key must be kept secure, similar to a password. The safest way to do this is offered by operating systems like macOS, Windows, and Linux: They have mechanisms to store such data, if available, on special security hardware. Since this is currently not possible in .NET, we use this Rust library.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1915240766"] = "In order to use any LLM, each user must store their so-called API key for each LLM provider. This key must be kept secure, similar to a password. The safest way to do this is offered by operating systems like macOS, Windows, and Linux: They have mechanisms to store such data, if available, on special security hardware. Since this is currently not possible in .NET, we use this Rust library."
-
--- This library is used to convert HTML to Markdown. This is necessary, e.g., when you provide a URL as input for an assistant.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1924365263"] = "This library is used to convert HTML to Markdown. This is necessary, e.g., when you provide a URL as input for an assistant."
-
--- We use Rocket to implement the runtime API. This is necessary because the runtime must be able to communicate with the user interface (IPC). Rocket is a great framework for implementing web APIs in Rust.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T1943216839"] = "We use Rocket to implement the runtime API. This is necessary because the runtime must be able to communicate with the user interface (IPC). Rocket is a great framework for implementing web APIs in Rust."
-
--- Copies the server URL to the clipboard
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2037899437"] = "Copies the server URL to the clipboard"
-
--- This library is used to determine the file type of a file. This is necessary, e.g., when we want to stream a file.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2173617769"] = "This library is used to determine the file type of a file. This is necessary, e.g., when we want to stream a file."
-
--- For the secure communication between the user interface and the runtime, we need to create certificates. This Rust library is great for this purpose.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2174764529"] = "For the secure communication between the user interface and the runtime, we need to create certificates. This Rust library is great for this purpose."
-
--- OK
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2246359087"] = "OK"
-
--- Configuration server:
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2272122662"] = "Configuration server:"
-
--- We must generate random numbers, e.g., for securing the interprocess communication between the user interface and the runtime. The rand library is great for this purpose.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2273492381"] = "We must generate random numbers, e.g., for securing the interprocess communication between the user interface and the runtime. The rand library is great for this purpose."
-
--- AI Studio runs with an enterprise configuration using a configuration plugin, without central configuration management.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2280402765"] = "AI Studio runs with an enterprise configuration using a configuration plugin, without central configuration management."
-
--- Configuration plugin ID:
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2301484629"] = "Configuration plugin ID:"
-
--- The C# language is used for the implementation of the user interface and the backend. To implement the user interface with C#, the Blazor technology from ASP.NET Core is used. All these technologies are integrated into the .NET SDK.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2329884315"] = "The C# language is used for the implementation of the user interface and the backend. To implement the user interface with C#, the Blazor technology from ASP.NET Core is used. All these technologies are integrated into the .NET SDK."
-
--- Used PDFium version
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2368247719"] = "Used PDFium version"
-
--- installation provided by the system
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2371107659"] = "installation provided by the system"
-
--- Installed Pandoc version: Pandoc is not installed or not available.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2374031539"] = "Installed Pandoc version: Pandoc is not installed or not available."
-
--- This library is used to determine the language of the operating system. This is necessary to set the language of the user interface.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2557014401"] = "This library is used to determine the language of the operating system. This is necessary to set the language of the user interface."
-
--- Used Open Source Projects
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2557066213"] = "Used Open Source Projects"
-
--- Build time
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T260228112"] = "Build time"
-
--- To be able to use the responses of the LLM in other apps, we often use the clipboard of the respective operating system. Unfortunately, in .NET there is no solution that works with all operating systems. Therefore, I have opted for this library in Rust. This way, data transfer to other apps works on every system.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2644379659"] = "To be able to use the responses of the LLM in other apps, we often use the clipboard of the respective operating system. Unfortunately, in .NET there is no solution that works with all operating systems. Therefore, I have opted for this library in Rust. This way, data transfer to other apps works on every system."
-
--- Usage log file
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2689995864"] = "Usage log file"
-
--- Logbook
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2706940196"] = "Logbook"
-
--- This component is used to render Markdown text. This is important because the LLM often responds with Markdown-formatted text, allowing us to present it in a way that is easier to read.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2726131107"] = "This component is used to render Markdown text. This is important because the LLM often responds with Markdown-formatted text, allowing us to present it in a way that is easier to read."
-
--- Determine Pandoc version, please wait...
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2765814390"] = "Determine Pandoc version, please wait..."
-
--- Code in the Rust language can be specified as synchronous or asynchronous. Unlike .NET and the C# language, Rust cannot execute asynchronous code by itself. Rust requires support in the form of an executor for this. Tokio is one such executor.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2777988282"] = "Code in the Rust language can be specified as synchronous or asynchronous. Unlike .NET and the C# language, Rust cannot execute asynchronous code by itself. Rust requires support in the form of an executor for this. Tokio is one such executor."
-
--- Show Details
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T27924674"] = "Show Details"
-
--- View our project roadmap and help shape AI Studio's future development.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2829971158"] = "View our project roadmap and help shape AI Studio's future development."
-
--- Used .NET runtime
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2840227993"] = "Used .NET runtime"
-
--- Explanation
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2840582448"] = "Explanation"
-
--- The .NET backend cannot be started as a desktop app. Therefore, I use a second backend in Rust, which I call runtime. With Rust as the runtime, Tauri can be used to realize a typical desktop app. Thanks to Rust, this app can be offered for Windows, macOS, and Linux desktops. Rust is a great language for developing safe and high-performance software.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T2868174483"] = "The .NET backend cannot be started as a desktop app. Therefore, I use a second backend in Rust, which I call runtime. With Rust as the runtime, Tauri can be used to realize a typical desktop app. Thanks to Rust, this app can be offered for Windows, macOS, and Linux desktops. Rust is a great language for developing safe and high-performance software."
-
--- Changelog
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3017574265"] = "Changelog"
-
--- Enterprise configuration ID:
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3092349641"] = "Enterprise configuration ID:"
-
--- Connect AI Studio to your organization's data with our External Retrieval Interface (ERI).
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T313276297"] = "Connect AI Studio to your organization's data with our External Retrieval Interface (ERI)."
-
--- Have feature ideas? Submit suggestions for future AI Studio enhancements.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3178730036"] = "Have feature ideas? Submit suggestions for future AI Studio enhancements."
-
--- Hide Details
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3183837919"] = "Hide Details"
-
--- Update Pandoc
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3249965383"] = "Update Pandoc"
-
--- Discover MindWork AI's mission and vision on our official homepage.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3294830584"] = "Discover MindWork AI's mission and vision on our official homepage."
-
--- User-language provided by the OS
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3334355246"] = "User-language provided by the OS"
-
--- The following list shows the versions of the MindWork AI Studio, the used compilers, build time, etc.:
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3405978777"] = "The following list shows the versions of the MindWork AI Studio, the used compilers, build time, etc.:"
-
--- Used Rust compiler
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3440211747"] = "Used Rust compiler"
-
--- Tauri is used to host the Blazor user interface. It is a great project that allows the creation of desktop applications using web technologies. I love Tauri!
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3494984593"] = "Tauri is used to host the Blazor user interface. It is a great project that allows the creation of desktop applications using web technologies. I love Tauri!"
-
--- Motivation
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3563271893"] = "Motivation"
-
--- This library is used to read Excel and OpenDocument spreadsheet files. This is necessary, e.g., for using spreadsheets as a data source for a chat.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3722989559"] = "This library is used to read Excel and OpenDocument spreadsheet files. This is necessary, e.g., for using spreadsheets as a data source for a chat."
-
--- AI Studio runs with an enterprise configuration and a configuration server. The configuration plugin is active.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3741877842"] = "AI Studio runs with an enterprise configuration and a configuration server. The configuration plugin is active."
-
--- this version does not met the requirements
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3813932670"] = "this version does not met the requirements"
-
--- This library is used to access the Windows registry. We use this for Windows enterprise environments to read the desired configuration.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3874337003"] = "This library is used to access the Windows registry. We use this for Windows enterprise environments to read the desired configuration."
-
--- Now we have multiple systems, some developed in .NET and others in Rust. The data format JSON is responsible for translating data between both worlds (called data serialization and deserialization). Serde takes on this task in the Rust world. The counterpart in the .NET world is an integral part of .NET and is located in System.Text.Json.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3908558992"] = "Now we have multiple systems, some developed in .NET and others in Rust. The data format JSON is responsible for translating data between both worlds (called data serialization and deserialization). Serde takes on this task in the Rust world. The counterpart in the .NET world is an integral part of .NET and is located in System.Text.Json."
-
--- Installed Pandoc version
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3983971016"] = "Installed Pandoc version"
-
--- Check Pandoc Installation
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T3986423270"] = "Check Pandoc Installation"
-
--- Versions
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T4010195468"] = "Versions"
-
--- This library is used to create asynchronous streams in Rust. It allows us to work with streams of data that can be produced asynchronously, making it easier to handle events or data that arrive over time. We use this, e.g., to stream arbitrary data from the file system to the embedding system.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T4079152443"] = "This library is used to create asynchronous streams in Rust. It allows us to work with streams of data that can be produced asynchronously, making it easier to handle events or data that arrive over time. We use this, e.g., to stream arbitrary data from the file system to the embedding system."
-
--- Community & Code
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T4158546761"] = "Community & Code"
-
--- We use the HtmlAgilityPack to extract content from the web. This is necessary, e.g., when you provide a URL as input for an assistant.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T4184485147"] = "We use the HtmlAgilityPack to extract content from the web. This is necessary, e.g., when you provide a URL as input for an assistant."
-
--- When transferring sensitive data between Rust runtime and .NET app, we encrypt the data. We use some libraries from the Rust Crypto project for this purpose: cipher, aes, cbc, pbkdf2, hmac, and sha2. We are thankful for the great work of the Rust Crypto project.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T4229014037"] = "When transferring sensitive data between Rust runtime and .NET app, we encrypt the data. We use some libraries from the Rust Crypto project for this purpose: cipher, aes, cbc, pbkdf2, hmac, and sha2. We are thankful for the great work of the Rust Crypto project."
-
--- This is a library providing the foundations for asynchronous programming in Rust. It includes key trait definitions like Stream, as well as utilities like join!, select!, and various futures combinator methods which enable expressive asynchronous control flow.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T566998575"] = "This is a library providing the foundations for asynchronous programming in Rust. It includes key trait definitions like Stream, as well as utilities like join!, select!, and various futures combinator methods which enable expressive asynchronous control flow."
-
--- Used .NET SDK
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T585329785"] = "Used .NET SDK"
-
--- Did you find a bug or are you experiencing issues? Report your concern here.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T639371534"] = "Did you find a bug or are you experiencing issues? Report your concern here."
-
--- This Rust library is used to output the app's messages to the terminal. This is helpful during development and troubleshooting. This feature is initially invisible; when the app is started via the terminal, the messages become visible.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T64689067"] = "This Rust library is used to output the app's messages to the terminal. This is helpful during development and troubleshooting. This feature is initially invisible; when the app is started via the terminal, the messages become visible."
-
--- Copies the config ID to the clipboard
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T788846912"] = "Copies the config ID to the clipboard"
-
--- installed by AI Studio
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T833849470"] = "installed by AI Studio"
-
--- We use this library to be able to read PowerPoint files. This allows us to insert content from slides into prompts and take PowerPoint files into account in RAG processes. We thank Nils Kruthoff for his work on this Rust crate.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T855925638"] = "We use this library to be able to read PowerPoint files. This allows us to insert content from slides into prompts and take PowerPoint files into account in RAG processes. We thank Nils Kruthoff for his work on this Rust crate."
-
--- For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T870640199"] = "For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose."
-
--- Install Pandoc
-UI_TEXT_CONTENT["AISTUDIO::PAGES::ABOUT::T986578435"] = "Install Pandoc"
-
-- Get coding and debugging support from an LLM.
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T1243850917"] = "Get coding and debugging support from an LLM."
@@ -4896,9 +4869,6 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T1702902297"] = "Introduction"
-- Vision
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T1892426825"] = "Vision"
--- You are not tied to any single provider. Instead, you might choose the provider that best suits your needs. Right now, we support OpenAI (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"
@@ -4923,6 +4893,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3341379752"] = "Cost-effective"
-- Flexibility
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3723223888"] = "Flexibility"
+-- 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), OpenRouter, 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::T3892227145"] = "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), OpenRouter, 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."
+
-- Privacy
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T3959064551"] = "Privacy"
@@ -4944,6 +4917,240 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T873851215"] = "Here's what makes MindWo
-- The app is free to use, both for personal and commercial purposes.
UI_TEXT_CONTENT["AISTUDIO::PAGES::HOME::T91074375"] = "The app is free to use, both for personal and commercial purposes."
+-- Startup log file
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1019424746"] = "Startup log file"
+
+-- Browse AI Studio's source code on GitHub — we welcome your contributions.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1107156991"] = "Browse AI Studio's source code on GitHub — we welcome your contributions."
+
+-- This is a private AI Studio installation. It runs without an enterprise configuration.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1209549230"] = "This is a private AI Studio installation. It runs without an enterprise configuration."
+
+-- AI Studio runs with an enterprise configuration and a configuration server. The configuration plugin is not yet available.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1282228996"] = "AI Studio runs with an enterprise configuration and a configuration server. The configuration plugin is not yet available."
+
+-- This library is used to read PDF files. This is necessary, e.g., for using PDFs as a data source for a chat.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1388816916"] = "This library is used to read PDF files. This is necessary, e.g., for using PDFs as a data source for a chat."
+
+-- This library is used to extend the MudBlazor library. It provides additional components that are not part of the MudBlazor library.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1421513382"] = "This library is used to extend the MudBlazor library. It provides additional components that are not part of the MudBlazor library."
+
+-- We use Lua as the language for plugins. Lua-CSharp lets Lua scripts communicate with AI Studio and vice versa. Thank you, Yusuke Nakada, for this great library.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T162898512"] = "We use Lua as the language for plugins. Lua-CSharp lets Lua scripts communicate with AI Studio and vice versa. Thank you, Yusuke Nakada, for this great library."
+
+-- Building on .NET, ASP.NET Core, and Blazor, MudBlazor is used as a library for designing and developing the user interface. It is a great project that significantly accelerates the development of advanced user interfaces with Blazor.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1629800076"] = "Building on .NET, ASP.NET Core, and Blazor, MudBlazor is used as a library for designing and developing the user interface. It is a great project that significantly accelerates the development of advanced user interfaces with Blazor."
+
+-- AI Studio creates a log file at startup, in which events during startup are recorded. After startup, another log file is created that records all events that occur during the use of the app. This includes any errors that may occur. Depending on when an error occurs (at startup or during use), the contents of these log files can be helpful for troubleshooting. Sensitive information such as passwords is not included in the log files.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1630237140"] = "AI Studio creates a log file at startup, in which events during startup are recorded. After startup, another log file is created that records all events that occur during the use of the app. This includes any errors that may occur. Depending on when an error occurs (at startup or during use), the contents of these log files can be helpful for troubleshooting. Sensitive information such as passwords is not included in the log files."
+
+-- This library is used to display the differences between two texts. This is necessary, e.g., for the grammar and spelling assistant.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1772678682"] = "This library is used to display the differences between two texts. This is necessary, e.g., for the grammar and spelling assistant."
+
+-- By clicking on the respective path, the path is copied to the clipboard. You might open these files with a text editor to view their contents.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1806897624"] = "By clicking on the respective path, the path is copied to the clipboard. You might open these files with a text editor to view their contents."
+
+-- Pandoc Installation
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T185447014"] = "Pandoc Installation"
+
+-- Copies the configuration plugin ID to the clipboard
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1859295819"] = "Copies the configuration plugin ID to the clipboard"
+
+-- Check for updates
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1890416390"] = "Check for updates"
+
+-- Vision
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1892426825"] = "Vision"
+
+-- In order to use any LLM, each user must store their so-called API key for each LLM provider. This key must be kept secure, similar to a password. The safest way to do this is offered by operating systems like macOS, Windows, and Linux: They have mechanisms to store such data, if available, on special security hardware. Since this is currently not possible in .NET, we use this Rust library.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1915240766"] = "In order to use any LLM, each user must store their so-called API key for each LLM provider. This key must be kept secure, similar to a password. The safest way to do this is offered by operating systems like macOS, Windows, and Linux: They have mechanisms to store such data, if available, on special security hardware. Since this is currently not possible in .NET, we use this Rust library."
+
+-- This library is used to convert HTML to Markdown. This is necessary, e.g., when you provide a URL as input for an assistant.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1924365263"] = "This library is used to convert HTML to Markdown. This is necessary, e.g., when you provide a URL as input for an assistant."
+
+-- We use Rocket to implement the runtime API. This is necessary because the runtime must be able to communicate with the user interface (IPC). Rocket is a great framework for implementing web APIs in Rust.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1943216839"] = "We use Rocket to implement the runtime API. This is necessary because the runtime must be able to communicate with the user interface (IPC). Rocket is a great framework for implementing web APIs in Rust."
+
+-- Copies the server URL to the clipboard
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2037899437"] = "Copies the server URL to the clipboard"
+
+-- This library is used to determine the file type of a file. This is necessary, e.g., when we want to stream a file.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2173617769"] = "This library is used to determine the file type of a file. This is necessary, e.g., when we want to stream a file."
+
+-- For the secure communication between the user interface and the runtime, we need to create certificates. This Rust library is great for this purpose.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2174764529"] = "For the secure communication between the user interface and the runtime, we need to create certificates. This Rust library is great for this purpose."
+
+-- OK
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2246359087"] = "OK"
+
+-- Configuration server:
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2272122662"] = "Configuration server:"
+
+-- We must generate random numbers, e.g., for securing the interprocess communication between the user interface and the runtime. The rand library is great for this purpose.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2273492381"] = "We must generate random numbers, e.g., for securing the interprocess communication between the user interface and the runtime. The rand library is great for this purpose."
+
+-- AI Studio runs with an enterprise configuration using a configuration plugin, without central configuration management.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2280402765"] = "AI Studio runs with an enterprise configuration using a configuration plugin, without central configuration management."
+
+-- Configuration plugin ID:
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2301484629"] = "Configuration plugin ID:"
+
+-- The C# language is used for the implementation of the user interface and the backend. To implement the user interface with C#, the Blazor technology from ASP.NET Core is used. All these technologies are integrated into the .NET SDK.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2329884315"] = "The C# language is used for the implementation of the user interface and the backend. To implement the user interface with C#, the Blazor technology from ASP.NET Core is used. All these technologies are integrated into the .NET SDK."
+
+-- Used PDFium version
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2368247719"] = "Used PDFium version"
+
+-- installation provided by the system
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2371107659"] = "installation provided by the system"
+
+-- Installed Pandoc version: Pandoc is not installed or not available.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2374031539"] = "Installed Pandoc version: Pandoc is not installed or not available."
+
+-- This library is used to determine the language of the operating system. This is necessary to set the language of the user interface.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2557014401"] = "This library is used to determine the language of the operating system. This is necessary to set the language of the user interface."
+
+-- Used Open Source Projects
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2557066213"] = "Used Open Source Projects"
+
+-- Build time
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T260228112"] = "Build time"
+
+-- To be able to use the responses of the LLM in other apps, we often use the clipboard of the respective operating system. Unfortunately, in .NET there is no solution that works with all operating systems. Therefore, I have opted for this library in Rust. This way, data transfer to other apps works on every system.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2644379659"] = "To be able to use the responses of the LLM in other apps, we often use the clipboard of the respective operating system. Unfortunately, in .NET there is no solution that works with all operating systems. Therefore, I have opted for this library in Rust. This way, data transfer to other apps works on every system."
+
+-- Usage log file
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2689995864"] = "Usage log file"
+
+-- Logbook
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2706940196"] = "Logbook"
+
+-- This component is used to render Markdown text. This is important because the LLM often responds with Markdown-formatted text, allowing us to present it in a way that is easier to read.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2726131107"] = "This component is used to render Markdown text. This is important because the LLM often responds with Markdown-formatted text, allowing us to present it in a way that is easier to read."
+
+-- Determine Pandoc version, please wait...
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2765814390"] = "Determine Pandoc version, please wait..."
+
+-- Code in the Rust language can be specified as synchronous or asynchronous. Unlike .NET and the C# language, Rust cannot execute asynchronous code by itself. Rust requires support in the form of an executor for this. Tokio is one such executor.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2777988282"] = "Code in the Rust language can be specified as synchronous or asynchronous. Unlike .NET and the C# language, Rust cannot execute asynchronous code by itself. Rust requires support in the form of an executor for this. Tokio is one such executor."
+
+-- Show Details
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T27924674"] = "Show Details"
+
+-- View our project roadmap and help shape AI Studio's future development.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2829971158"] = "View our project roadmap and help shape AI Studio's future development."
+
+-- Used .NET runtime
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2840227993"] = "Used .NET runtime"
+
+-- Explanation
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2840582448"] = "Explanation"
+
+-- The .NET backend cannot be started as a desktop app. Therefore, I use a second backend in Rust, which I call runtime. With Rust as the runtime, Tauri can be used to realize a typical desktop app. Thanks to Rust, this app can be offered for Windows, macOS, and Linux desktops. Rust is a great language for developing safe and high-performance software.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2868174483"] = "The .NET backend cannot be started as a desktop app. Therefore, I use a second backend in Rust, which I call runtime. With Rust as the runtime, Tauri can be used to realize a typical desktop app. Thanks to Rust, this app can be offered for Windows, macOS, and Linux desktops. Rust is a great language for developing safe and high-performance software."
+
+-- Changelog
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3017574265"] = "Changelog"
+
+-- Enterprise configuration ID:
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3092349641"] = "Enterprise configuration ID:"
+
+-- Connect AI Studio to your organization's data with our External Retrieval Interface (ERI).
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T313276297"] = "Connect AI Studio to your organization's data with our External Retrieval Interface (ERI)."
+
+-- Have feature ideas? Submit suggestions for future AI Studio enhancements.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3178730036"] = "Have feature ideas? Submit suggestions for future AI Studio enhancements."
+
+-- Hide Details
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3183837919"] = "Hide Details"
+
+-- Update Pandoc
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3249965383"] = "Update Pandoc"
+
+-- Discover MindWork AI's mission and vision on our official homepage.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3294830584"] = "Discover MindWork AI's mission and vision on our official homepage."
+
+-- User-language provided by the OS
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3334355246"] = "User-language provided by the OS"
+
+-- The following list shows the versions of the MindWork AI Studio, the used compilers, build time, etc.:
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3405978777"] = "The following list shows the versions of the MindWork AI Studio, the used compilers, build time, etc.:"
+
+-- Information about MindWork AI Studio
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3433065373"] = "Information about MindWork AI Studio"
+
+-- Used Rust compiler
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3440211747"] = "Used Rust compiler"
+
+-- Tauri is used to host the Blazor user interface. It is a great project that allows the creation of desktop applications using web technologies. I love Tauri!
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3494984593"] = "Tauri is used to host the Blazor user interface. It is a great project that allows the creation of desktop applications using web technologies. I love Tauri!"
+
+-- Motivation
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3563271893"] = "Motivation"
+
+-- This library is used to read Excel and OpenDocument spreadsheet files. This is necessary, e.g., for using spreadsheets as a data source for a chat.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3722989559"] = "This library is used to read Excel and OpenDocument spreadsheet files. This is necessary, e.g., for using spreadsheets as a data source for a chat."
+
+-- AI Studio runs with an enterprise configuration and a configuration server. The configuration plugin is active.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3741877842"] = "AI Studio runs with an enterprise configuration and a configuration server. The configuration plugin is active."
+
+-- this version does not met the requirements
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3813932670"] = "this version does not met the requirements"
+
+-- This library is used to access the Windows registry. We use this for Windows enterprise environments to read the desired configuration.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3874337003"] = "This library is used to access the Windows registry. We use this for Windows enterprise environments to read the desired configuration."
+
+-- Now we have multiple systems, some developed in .NET and others in Rust. The data format JSON is responsible for translating data between both worlds (called data serialization and deserialization). Serde takes on this task in the Rust world. The counterpart in the .NET world is an integral part of .NET and is located in System.Text.Json.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3908558992"] = "Now we have multiple systems, some developed in .NET and others in Rust. The data format JSON is responsible for translating data between both worlds (called data serialization and deserialization). Serde takes on this task in the Rust world. The counterpart in the .NET world is an integral part of .NET and is located in System.Text.Json."
+
+-- Installed Pandoc version
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3983971016"] = "Installed Pandoc version"
+
+-- Check Pandoc Installation
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3986423270"] = "Check Pandoc Installation"
+
+-- Versions
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T4010195468"] = "Versions"
+
+-- This library is used to create asynchronous streams in Rust. It allows us to work with streams of data that can be produced asynchronously, making it easier to handle events or data that arrive over time. We use this, e.g., to stream arbitrary data from the file system to the embedding system.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T4079152443"] = "This library is used to create asynchronous streams in Rust. It allows us to work with streams of data that can be produced asynchronously, making it easier to handle events or data that arrive over time. We use this, e.g., to stream arbitrary data from the file system to the embedding system."
+
+-- Community & Code
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T4158546761"] = "Community & Code"
+
+-- We use the HtmlAgilityPack to extract content from the web. This is necessary, e.g., when you provide a URL as input for an assistant.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T4184485147"] = "We use the HtmlAgilityPack to extract content from the web. This is necessary, e.g., when you provide a URL as input for an assistant."
+
+-- When transferring sensitive data between Rust runtime and .NET app, we encrypt the data. We use some libraries from the Rust Crypto project for this purpose: cipher, aes, cbc, pbkdf2, hmac, and sha2. We are thankful for the great work of the Rust Crypto project.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T4229014037"] = "When transferring sensitive data between Rust runtime and .NET app, we encrypt the data. We use some libraries from the Rust Crypto project for this purpose: cipher, aes, cbc, pbkdf2, hmac, and sha2. We are thankful for the great work of the Rust Crypto project."
+
+-- This is a library providing the foundations for asynchronous programming in Rust. It includes key trait definitions like Stream, as well as utilities like join!, select!, and various futures combinator methods which enable expressive asynchronous control flow.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T566998575"] = "This is a library providing the foundations for asynchronous programming in Rust. It includes key trait definitions like Stream, as well as utilities like join!, select!, and various futures combinator methods which enable expressive asynchronous control flow."
+
+-- Used .NET SDK
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T585329785"] = "Used .NET SDK"
+
+-- Did you find a bug or are you experiencing issues? Report your concern here.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T639371534"] = "Did you find a bug or are you experiencing issues? Report your concern here."
+
+-- This Rust library is used to output the app's messages to the terminal. This is helpful during development and troubleshooting. This feature is initially invisible; when the app is started via the terminal, the messages become visible.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T64689067"] = "This Rust library is used to output the app's messages to the terminal. This is helpful during development and troubleshooting. This feature is initially invisible; when the app is started via the terminal, the messages become visible."
+
+-- Copies the config ID to the clipboard
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T788846912"] = "Copies the config ID to the clipboard"
+
+-- installed by AI Studio
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T833849470"] = "installed by AI Studio"
+
+-- We use this library to be able to read PowerPoint files. This allows us to insert content from slides into prompts and take PowerPoint files into account in RAG processes. We thank Nils Kruthoff for his work on this Rust crate.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T855925638"] = "We use this library to be able to read PowerPoint files. This allows us to insert content from slides into prompts and take PowerPoint files into account in RAG processes. We thank Nils Kruthoff for his work on this Rust crate."
+
+-- For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T870640199"] = "For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose."
+
+-- Install Pandoc
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T986578435"] = "Install Pandoc"
+
-- Disable plugin
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1430375822"] = "Disable plugin"
@@ -5142,6 +5349,9 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T3424652889"] = "Un
-- no model selected
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::MODEL::T2234274832"] = "no model selected"
+-- Model as configured by whisper.cpp
+UI_TEXT_CONTENT["AISTUDIO::PROVIDER::SELFHOSTED::PROVIDERSELFHOSTED::T3313940770"] = "Model as configured by whisper.cpp"
+
-- Use no chat template
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CHATTEMPLATE::T4258819635"] = "Use no chat template"
@@ -5316,6 +5526,9 @@ UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::PREVIEWFEATURESEXTENSIONS::T2708
-- Unknown preview feature
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::PREVIEWFEATURESEXTENSIONS::T2722827307"] = "Unknown preview feature"
+-- Transcription: Preview of our speech to text system where you can transcribe recordings and audio files into text
+UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::PREVIEWFEATURESEXTENSIONS::T714355911"] = "Transcription: Preview of our speech to text system where you can transcribe recordings and audio files into text"
+
-- Use no data sources, when sending an assistant result to a chat
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::SENDTOCHATDATASOURCEBEHAVIOREXTENSIONS::T1223925477"] = "Use no data sources, when sending an assistant result to a chat"
@@ -5880,6 +6093,12 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::RAG::RAGPROCESSES::AISRCSELWITHRETCTXVAL::T377
-- Executable Files
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPEFILTER::T2217313358"] = "Executable Files"
+-- All Source Code Files
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPEFILTER::T2460199369"] = "All Source Code Files"
+
+-- All Audio Files
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPEFILTER::T2575722901"] = "All Audio Files"
+
-- All Video Files
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPEFILTER::T2850789856"] = "All Video Files"
@@ -6009,6 +6228,30 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::DATASOURCEVALIDATION::T878007824"]
-- Please enter the secret necessary for authentication.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::DATASOURCEVALIDATION::T968385876"] = "Please enter the secret necessary for authentication."
+-- Unsupported image format
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T1398282880"] = "Unsupported image format"
+
+-- File has no extension
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T1555980031"] = "File has no extension"
+
+-- Audio files are not supported yet
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T2919730669"] = "Audio files are not supported yet"
+
+-- Videos are not supported yet
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T2928927510"] = "Videos are not supported yet"
+
+-- Images are not supported yet
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T298062956"] = "Images are not supported yet"
+
+-- Images are not supported at this place
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T305247150"] = "Images are not supported at this place"
+
+-- Executables are not allowed
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T4167762413"] = "Executables are not allowed"
+
+-- Images are not supported by the selected provider and model
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T999194030"] = "Images are not supported by the selected provider and model"
+
-- The hostname is not a valid HTTP(S) URL.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::PROVIDERVALIDATION::T1013354736"] = "The hostname is not a valid HTTP(S) URL."
@@ -6047,4 +6290,3 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T1307384014"] = "Unnamed w
-- Delete Chat
UI_TEXT_CONTENT["AISTUDIO::TOOLS::WORKSPACEBEHAVIOUR::T2244038752"] = "Delete Chat"
-
diff --git a/app/MindWork AI Studio/Program.cs b/app/MindWork AI Studio/Program.cs
index 4c495bb3..0dbab2ca 100644
--- a/app/MindWork AI Studio/Program.cs
+++ b/app/MindWork AI Studio/Program.cs
@@ -119,7 +119,6 @@ internal sealed class Program
var databaseClient = new QdrantClientImplementation("Qdrant", qdrantInfo.Path, qdrantInfo.PortHttp, qdrantInfo.PortGrpc, qdrantInfo.Fingerprint, qdrantInfo.ApiToken);
var builder = WebApplication.CreateBuilder();
-
builder.WebHost.ConfigureKestrel(kestrelServerOptions =>
{
kestrelServerOptions.ConfigureEndpointDefaults(listenOptions =>
@@ -223,6 +222,7 @@ internal sealed class Program
var rustLogger = app.Services.GetRequiredService>();
rust.SetLogger(rustLogger);
rust.SetEncryptor(encryption);
+ TerminalLogger.SetRustService(rust);
RUST_SERVICE = rust;
ENCRYPTION = encryption;
@@ -233,6 +233,7 @@ internal sealed class Program
programLogger.LogInformation("Initialize internal file system.");
app.Use(Redirect.HandlerContentAsync);
+ app.Use(FileHandler.HandlerAsync);
#if DEBUG
app.UseStaticFiles();
diff --git a/app/MindWork AI Studio/Provider/AlibabaCloud/ProviderAlibabaCloud.cs b/app/MindWork AI Studio/Provider/AlibabaCloud/ProviderAlibabaCloud.cs
index 78618db2..2c763678 100644
--- a/app/MindWork AI Studio/Provider/AlibabaCloud/ProviderAlibabaCloud.cs
+++ b/app/MindWork AI Studio/Provider/AlibabaCloud/ProviderAlibabaCloud.cs
@@ -9,7 +9,7 @@ using AIStudio.Settings;
namespace AIStudio.Provider.AlibabaCloud;
-public sealed class ProviderAlibabaCloud() : BaseProvider("https://dashscope-intl.aliyuncs.com/compatible-mode/v1/", LOGGER)
+public sealed class ProviderAlibabaCloud() : BaseProvider(LLMProviders.ALIBABA_CLOUD, "https://dashscope-intl.aliyuncs.com/compatible-mode/v1/", LOGGER)
{
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger();
@@ -25,12 +25,12 @@ public sealed class ProviderAlibabaCloud() : BaseProvider("https://dashscope-int
public override async IAsyncEnumerable StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
{
// Get the API key:
- var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
if(!requestedSecret.Success)
yield break;
// Prepare the system prompt:
- var systemPrompt = new Message
+ var systemPrompt = new TextMessage
{
Role = "system",
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
@@ -40,24 +40,7 @@ public sealed class ProviderAlibabaCloud() : BaseProvider("https://dashscope-int
var apiParameters = this.ParseAdditionalApiParameters();
// Build the list of messages:
- var messages = await chatThread.Blocks.BuildMessages(async n => new Message
- {
- Role = n.Role switch
- {
- ChatRole.USER => "user",
- ChatRole.AI => "assistant",
- ChatRole.AGENT => "assistant",
- ChatRole.SYSTEM => "system",
-
- _ => "user",
- },
-
- Content = n.Content switch
- {
- ContentText text => await text.PrepareContentForAI(),
- _ => string.Empty,
- }
- });
+ var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
// Prepare the AlibabaCloud HTTP chat request:
var alibabaCloudChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
@@ -97,6 +80,12 @@ public sealed class ProviderAlibabaCloud() : BaseProvider("https://dashscope-int
yield break;
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
+
+ ///
+ public override Task TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default)
+ {
+ return Task.FromResult(string.Empty);
+ }
///
public override Task> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
@@ -128,7 +117,7 @@ public sealed class ProviderAlibabaCloud() : BaseProvider("https://dashscope-int
new Model("qwen2.5-vl-3b-instruct", "Qwen2.5-VL 3b"),
};
- return this.LoadModels(["q"],token, apiKeyProvisional).ContinueWith(t => t.Result.Concat(additionalModels).OrderBy(x => x.Id).AsEnumerable(), token);
+ return this.LoadModels(["q"], SecretStoreType.LLM_PROVIDER, token, apiKeyProvisional).ContinueWith(t => t.Result.Concat(additionalModels).OrderBy(x => x.Id).AsEnumerable(), token);
}
///
@@ -146,18 +135,27 @@ public sealed class ProviderAlibabaCloud() : BaseProvider("https://dashscope-int
new Model("text-embedding-v3", "text-embedding-v3"),
};
- return this.LoadModels(["text-embedding-"], token, apiKeyProvisional).ContinueWith(t => t.Result.Concat(additionalModels).OrderBy(x => x.Id).AsEnumerable(), token);
+ return this.LoadModels(["text-embedding-"], SecretStoreType.EMBEDDING_PROVIDER, token, apiKeyProvisional).ContinueWith(t => t.Result.Concat(additionalModels).OrderBy(x => x.Id).AsEnumerable(), token);
}
-
-
+
+ #region Overrides of BaseProvider
+
+ ///
+ public override Task> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default)
+ {
+ return Task.FromResult(Enumerable.Empty());
+ }
+
+ #endregion
+
#endregion
- private async Task> LoadModels(string[] prefixes, CancellationToken token, string? apiKeyProvisional = null)
+ private async Task> LoadModels(string[] prefixes, SecretStoreType storeType, CancellationToken token, string? apiKeyProvisional = null)
{
var secretKey = apiKeyProvisional switch
{
not null => apiKeyProvisional,
- _ => await RUST_SERVICE.GetAPIKey(this) switch
+ _ => await RUST_SERVICE.GetAPIKey(this, storeType) switch
{
{ Success: true } result => await result.Secret.Decrypt(ENCRYPTION),
_ => null,
diff --git a/app/MindWork AI Studio/Provider/Anthropic/ChatRequest.cs b/app/MindWork AI Studio/Provider/Anthropic/ChatRequest.cs
index f7103bd7..d6df3990 100644
--- a/app/MindWork AI Studio/Provider/Anthropic/ChatRequest.cs
+++ b/app/MindWork AI Studio/Provider/Anthropic/ChatRequest.cs
@@ -1,5 +1,4 @@
using System.Text.Json.Serialization;
-using AIStudio.Provider.OpenAI;
namespace AIStudio.Provider.Anthropic;
@@ -13,7 +12,7 @@ namespace AIStudio.Provider.Anthropic;
/// The system prompt for the chat completion.
public readonly record struct ChatRequest(
string Model,
- IList Messages,
+ IList Messages,
int MaxTokens,
bool Stream,
string System
diff --git a/app/MindWork AI Studio/Provider/Anthropic/ISubContentImageSource.cs b/app/MindWork AI Studio/Provider/Anthropic/ISubContentImageSource.cs
new file mode 100644
index 00000000..84015bdd
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/Anthropic/ISubContentImageSource.cs
@@ -0,0 +1,9 @@
+namespace AIStudio.Provider.Anthropic;
+
+public interface ISubContentImageSource
+{
+ ///
+ /// The type of the sub-content image.
+ ///
+ public SubContentImageType Type { get; }
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/Anthropic/ProviderAnthropic.cs b/app/MindWork AI Studio/Provider/Anthropic/ProviderAnthropic.cs
index 4ea73e77..539c4427 100644
--- a/app/MindWork AI Studio/Provider/Anthropic/ProviderAnthropic.cs
+++ b/app/MindWork AI Studio/Provider/Anthropic/ProviderAnthropic.cs
@@ -9,7 +9,7 @@ using AIStudio.Settings;
namespace AIStudio.Provider.Anthropic;
-public sealed class ProviderAnthropic() : BaseProvider("https://api.anthropic.com/v1/", LOGGER)
+public sealed class ProviderAnthropic() : BaseProvider(LLMProviders.ANTHROPIC, "https://api.anthropic.com/v1/", LOGGER)
{
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger();
@@ -23,7 +23,7 @@ public sealed class ProviderAnthropic() : BaseProvider("https://api.anthropic.co
public override async IAsyncEnumerable StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
{
// Get the API key:
- var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
if(!requestedSecret.Success)
yield break;
@@ -31,9 +31,11 @@ public sealed class ProviderAnthropic() : BaseProvider("https://api.anthropic.co
var apiParameters = this.ParseAdditionalApiParameters("system");
// Build the list of messages:
- var messages = await chatThread.Blocks.BuildMessages(async n => new Message
- {
- Role = n.Role switch
+ var messages = await chatThread.Blocks.BuildMessagesAsync(
+ this.Provider, chatModel,
+
+ // Anthropic-specific role mapping:
+ role => role switch
{
ChatRole.USER => "user",
ChatRole.AI => "assistant",
@@ -41,13 +43,26 @@ public sealed class ProviderAnthropic() : BaseProvider("https://api.anthropic.co
_ => "user",
},
-
- Content = n.Content switch
+
+ // Anthropic uses the standard text sub-content:
+ text => new SubContentText
{
- ContentText text => await text.PrepareContentForAI(),
- _ => string.Empty,
+ Text = text,
+ },
+
+ // Anthropic-specific image sub-content:
+ async attachment => new SubContentImage
+ {
+ Source = new SubContentBase64Image
+ {
+ Data = await attachment.TryAsBase64(token: token) is (true, var base64Content)
+ ? base64Content
+ : string.Empty,
+
+ MediaType = attachment.DetermineMimeType(),
+ }
}
- });
+ );
// Prepare the Anthropic HTTP chat request:
var chatRequest = JsonSerializer.Serialize(new ChatRequest
@@ -92,6 +107,12 @@ public sealed class ProviderAnthropic() : BaseProvider("https://api.anthropic.co
yield break;
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
+
+ ///
+ public override Task TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default)
+ {
+ return Task.FromResult(string.Empty);
+ }
///
public override Task> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
@@ -106,7 +127,7 @@ public sealed class ProviderAnthropic() : BaseProvider("https://api.anthropic.co
new Model("claude-3-opus-latest", "Claude 3 Opus (Latest)"),
};
- return this.LoadModels(token, apiKeyProvisional).ContinueWith(t => t.Result.Concat(additionalModels).OrderBy(x => x.Id).AsEnumerable(), token);
+ return this.LoadModels(SecretStoreType.LLM_PROVIDER, token, apiKeyProvisional).ContinueWith(t => t.Result.Concat(additionalModels).OrderBy(x => x.Id).AsEnumerable(), token);
}
///
@@ -121,14 +142,20 @@ public sealed class ProviderAnthropic() : BaseProvider("https://api.anthropic.co
return Task.FromResult(Enumerable.Empty());
}
+ ///
+ public override Task> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default)
+ {
+ return Task.FromResult(Enumerable.Empty());
+ }
+
#endregion
- private async Task> LoadModels(CancellationToken token, string? apiKeyProvisional = null)
+ private async Task> LoadModels(SecretStoreType storeType, CancellationToken token, string? apiKeyProvisional = null)
{
var secretKey = apiKeyProvisional switch
{
not null => apiKeyProvisional,
- _ => await RUST_SERVICE.GetAPIKey(this) switch
+ _ => await RUST_SERVICE.GetAPIKey(this, storeType) switch
{
{ Success: true } result => await result.Secret.Decrypt(ENCRYPTION),
_ => null,
diff --git a/app/MindWork AI Studio/Provider/Anthropic/SubContentBase64Image.cs b/app/MindWork AI Studio/Provider/Anthropic/SubContentBase64Image.cs
new file mode 100644
index 00000000..8123c814
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/Anthropic/SubContentBase64Image.cs
@@ -0,0 +1,10 @@
+namespace AIStudio.Provider.Anthropic;
+
+public record SubContentBase64Image : ISubContentImageSource
+{
+ public SubContentImageType Type => SubContentImageType.BASE64;
+
+ public string MediaType { get; init; } = string.Empty;
+
+ public string Data { get; init; } = string.Empty;
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/Anthropic/SubContentImage.cs b/app/MindWork AI Studio/Provider/Anthropic/SubContentImage.cs
new file mode 100644
index 00000000..074f5db0
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/Anthropic/SubContentImage.cs
@@ -0,0 +1,10 @@
+using AIStudio.Provider.OpenAI;
+
+namespace AIStudio.Provider.Anthropic;
+
+public record SubContentImage(SubContentType Type, ISubContentImageSource Source) : ISubContent
+{
+ public SubContentImage() : this(SubContentType.IMAGE, new SubContentImageUrl())
+ {
+ }
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/Anthropic/SubContentImageSourceConverter.cs b/app/MindWork AI Studio/Provider/Anthropic/SubContentImageSourceConverter.cs
new file mode 100644
index 00000000..11c61ad2
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/Anthropic/SubContentImageSourceConverter.cs
@@ -0,0 +1,32 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace AIStudio.Provider.Anthropic;
+
+///
+/// Custom JSON converter for the ISubContentImageSource interface to handle polymorphic serialization.
+///
+///
+/// This converter ensures that when serializing ISubContentImageSource objects, all properties
+/// of the concrete implementation (e.g., SubContentBase64Image, SubContentImageUrl) are serialized,
+/// not just the properties defined in the ISubContentImageSource interface.
+///
+public sealed class SubContentImageSourceConverter : JsonConverter
+{
+ private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger();
+
+ public override ISubContentImageSource? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ // Deserialization is not needed for request objects, as sub-content image sources are only serialized
+ // when sending requests to LLM providers.
+ LOGGER.LogError("Deserializing ISubContentImageSource is not supported. This converter is only used for serializing request messages.");
+ return null;
+ }
+
+ public override void Write(Utf8JsonWriter writer, ISubContentImageSource value, JsonSerializerOptions options)
+ {
+ // Serialize the actual concrete type (e.g., SubContentBase64Image, SubContentImageUrl) instead of just the ISubContentImageSource interface.
+ // This ensures all properties of the concrete type are included in the JSON output.
+ JsonSerializer.Serialize(writer, value, value.GetType(), options);
+ }
+}
diff --git a/app/MindWork AI Studio/Provider/Anthropic/SubContentImageType.cs b/app/MindWork AI Studio/Provider/Anthropic/SubContentImageType.cs
new file mode 100644
index 00000000..e94c2224
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/Anthropic/SubContentImageType.cs
@@ -0,0 +1,7 @@
+namespace AIStudio.Provider.Anthropic;
+
+public enum SubContentImageType
+{
+ URL,
+ BASE64
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/Anthropic/SubContentImageUrl.cs b/app/MindWork AI Studio/Provider/Anthropic/SubContentImageUrl.cs
new file mode 100644
index 00000000..0247a40c
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/Anthropic/SubContentImageUrl.cs
@@ -0,0 +1,8 @@
+namespace AIStudio.Provider.Anthropic;
+
+public record SubContentImageUrl : ISubContentImageSource
+{
+ public SubContentImageType Type => SubContentImageType.URL;
+
+ public string Url { get; init; } = string.Empty;
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/BaseProvider.cs b/app/MindWork AI Studio/Provider/BaseProvider.cs
index 00b4aa26..3e207da5 100644
--- a/app/MindWork AI Studio/Provider/BaseProvider.cs
+++ b/app/MindWork AI Studio/Provider/BaseProvider.cs
@@ -1,13 +1,21 @@
using System.Net;
+using System.Net.Http.Headers;
using System.Runtime.CompilerServices;
using System.Text.Json;
+using System.Text.Json.Serialization;
using AIStudio.Chat;
+using AIStudio.Provider.Anthropic;
using AIStudio.Provider.OpenAI;
+using AIStudio.Provider.SelfHosted;
using AIStudio.Settings;
+using AIStudio.Tools.MIME;
using AIStudio.Tools.PluginSystem;
+using AIStudio.Tools.Rust;
using AIStudio.Tools.Services;
+using Host = AIStudio.Provider.SelfHosted.Host;
+
namespace AIStudio.Provider;
///
@@ -40,18 +48,28 @@ public abstract class BaseProvider : IProvider, ISecretId
protected static readonly JsonSerializerOptions JSON_SERIALIZER_OPTIONS = new()
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
- Converters = { new AnnotationConverter() },
+ Converters =
+ {
+ new JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseLower),
+ new AnnotationConverter(),
+ new MessageBaseConverter(),
+ new SubContentConverter(),
+ new SubContentImageSourceConverter(),
+ new SubContentImageUrlConverter(),
+ },
AllowTrailingCommas = false
};
///
/// Constructor for the base provider.
///
+ /// The provider enum value.
/// The base URL for the provider.
/// The logger to use.
- protected BaseProvider(string url, ILogger logger)
+ protected BaseProvider(LLMProviders provider, string url, ILogger logger)
{
this.logger = logger;
+ this.Provider = provider;
// Set the base URL:
this.httpClient.BaseAddress = new(url);
@@ -59,6 +77,9 @@ public abstract class BaseProvider : IProvider, ISecretId
#region Handling of IProvider, which all providers must implement
+ ///
+ public LLMProviders Provider { get; }
+
///
public abstract string Id { get; }
@@ -74,6 +95,9 @@ public abstract class BaseProvider : IProvider, ISecretId
///
public abstract IAsyncEnumerable StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, CancellationToken token = default);
+ ///
+ public abstract Task TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default);
+
///
public abstract Task> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default);
@@ -82,6 +106,9 @@ public abstract class BaseProvider : IProvider, ISecretId
///
public abstract Task> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default);
+
+ ///
+ public abstract Task> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default);
#endregion
@@ -130,7 +157,7 @@ public abstract class BaseProvider : IProvider, ISecretId
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 {ResposeStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
+ this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
errorMessage = nextResponse.ReasonPhrase;
break;
}
@@ -138,7 +165,7 @@ public abstract class BaseProvider : IProvider, ISecretId
if(nextResponse.StatusCode is HttpStatusCode.BadRequest)
{
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("Tried to communicate with the LLM provider '{0}'. The required message format might be changed. The provider message is: '{1}'"), this.InstanceName, nextResponse.ReasonPhrase)));
- this.logger.LogError("Failed request with status code {ResposeStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
+ this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
errorMessage = nextResponse.ReasonPhrase;
break;
}
@@ -146,7 +173,7 @@ public abstract class BaseProvider : IProvider, ISecretId
if(nextResponse.StatusCode is HttpStatusCode.NotFound)
{
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("Tried to communicate with the LLM provider '{0}'. Something was not found. The provider message is: '{1}'"), this.InstanceName, nextResponse.ReasonPhrase)));
- this.logger.LogError("Failed request with status code {ResposeStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
+ this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
errorMessage = nextResponse.ReasonPhrase;
break;
}
@@ -154,7 +181,7 @@ public abstract class BaseProvider : IProvider, ISecretId
if(nextResponse.StatusCode is HttpStatusCode.Unauthorized)
{
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Key, string.Format(TB("Tried to communicate with the LLM provider '{0}'. The API key might be invalid. The provider message is: '{1}'"), this.InstanceName, nextResponse.ReasonPhrase)));
- this.logger.LogError("Failed request with status code {ResposeStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
+ this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
errorMessage = nextResponse.ReasonPhrase;
break;
}
@@ -162,7 +189,7 @@ public abstract class BaseProvider : IProvider, ISecretId
if(nextResponse.StatusCode is HttpStatusCode.InternalServerError)
{
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("Tried to communicate with the LLM provider '{0}'. The server might be down or having issues. The provider message is: '{1}'"), this.InstanceName, nextResponse.ReasonPhrase)));
- this.logger.LogError("Failed request with status code {ResposeStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
+ this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
errorMessage = nextResponse.ReasonPhrase;
break;
}
@@ -170,7 +197,7 @@ public abstract class BaseProvider : IProvider, ISecretId
if(nextResponse.StatusCode is HttpStatusCode.ServiceUnavailable)
{
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, string.Format(TB("Tried to communicate with the LLM provider '{0}'. The provider is overloaded. The message is: '{1}'"), this.InstanceName, nextResponse.ReasonPhrase)));
- this.logger.LogError("Failed request with status code {ResposeStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
+ this.logger.LogError("Failed request with status code {ResponseStatusCode} (message = '{ResponseReasonPhrase}', error body = '{ErrorBody}').", nextResponse.StatusCode, nextResponse.ReasonPhrase, errorBody);
errorMessage = nextResponse.ReasonPhrase;
break;
}
@@ -518,6 +545,78 @@ public abstract class BaseProvider : IProvider, ISecretId
streamReader.Dispose();
}
+ protected async Task PerformStandardTranscriptionRequest(RequestedSecret requestedSecret, Model transcriptionModel, string audioFilePath, Host host = Host.NONE, CancellationToken token = default)
+ {
+ try
+ {
+ using var form = new MultipartFormDataContent();
+ var mimeType = Builder.FromFilename(audioFilePath);
+
+ await using var fileStream = File.OpenRead(audioFilePath);
+ using var fileContent = new StreamContent(fileStream);
+ fileContent.Headers.ContentType = new MediaTypeHeaderValue(mimeType);
+
+ form.Add(fileContent, "file", Path.GetFileName(audioFilePath));
+ form.Add(new StringContent(transcriptionModel.Id), "model");
+
+ using var request = new HttpRequestMessage(HttpMethod.Post, host.TranscriptionURL());
+ request.Content = form;
+
+ // Handle the authorization header based on the provider:
+ switch (this.Provider)
+ {
+ case LLMProviders.SELF_HOSTED:
+ if(requestedSecret.Success)
+ request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
+
+ break;
+
+ case LLMProviders.FIREWORKS:
+ if(!requestedSecret.Success)
+ {
+ this.logger.LogError("No valid API key available for transcription request.");
+ return string.Empty;
+ }
+
+ request.Headers.Add("Authorization", await requestedSecret.Secret.Decrypt(ENCRYPTION));
+ break;
+
+ default:
+ if(!requestedSecret.Success)
+ {
+ this.logger.LogError("No valid API key available for transcription request.");
+ return string.Empty;
+ }
+
+ request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
+ break;
+ }
+
+ using var response = await this.httpClient.SendAsync(request, token);
+ var responseBody = response.Content.ReadAsStringAsync(token).Result;
+
+ if (!response.IsSuccessStatusCode)
+ {
+ this.logger.LogError("Transcription request failed with status code {ResponseStatusCode} and body: '{ResponseBody}'.", response.StatusCode, responseBody);
+ return string.Empty;
+ }
+
+ var transcriptionResponse = JsonSerializer.Deserialize(responseBody, JSON_SERIALIZER_OPTIONS);
+ if(transcriptionResponse is null)
+ {
+ this.logger.LogError("Was not able to deserialize the transcription response.");
+ return string.Empty;
+ }
+
+ return transcriptionResponse.Text;
+ }
+ catch (Exception e)
+ {
+ this.logger.LogError("Failed to perform transcription request: '{Message}'.", e.Message);
+ return string.Empty;
+ }
+ }
+
///
/// Parse and convert API parameters from a provided JSON string into a dictionary,
/// optionally merging additional parameters and removing specific keys.
diff --git a/app/MindWork AI Studio/Provider/DeepSeek/ProviderDeepSeek.cs b/app/MindWork AI Studio/Provider/DeepSeek/ProviderDeepSeek.cs
index 991d6a2e..f5e1016d 100644
--- a/app/MindWork AI Studio/Provider/DeepSeek/ProviderDeepSeek.cs
+++ b/app/MindWork AI Studio/Provider/DeepSeek/ProviderDeepSeek.cs
@@ -9,7 +9,7 @@ using AIStudio.Settings;
namespace AIStudio.Provider.DeepSeek;
-public sealed class ProviderDeepSeek() : BaseProvider("https://api.deepseek.com/", LOGGER)
+public sealed class ProviderDeepSeek() : BaseProvider(LLMProviders.DEEP_SEEK, "https://api.deepseek.com/", LOGGER)
{
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger();
@@ -25,12 +25,12 @@ public sealed class ProviderDeepSeek() : BaseProvider("https://api.deepseek.com/
public override async IAsyncEnumerable StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
{
// Get the API key:
- var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
if(!requestedSecret.Success)
yield break;
// Prepare the system prompt:
- var systemPrompt = new Message
+ var systemPrompt = new TextMessage
{
Role = "system",
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
@@ -40,24 +40,7 @@ public sealed class ProviderDeepSeek() : BaseProvider("https://api.deepseek.com/
var apiParameters = this.ParseAdditionalApiParameters();
// Build the list of messages:
- var messages = await chatThread.Blocks.BuildMessages(async n => new Message
- {
- Role = n.Role switch
- {
- ChatRole.USER => "user",
- ChatRole.AI => "assistant",
- ChatRole.AGENT => "assistant",
- ChatRole.SYSTEM => "system",
-
- _ => "user",
- },
-
- Content = n.Content switch
- {
- ContentText text => await text.PrepareContentForAI(),
- _ => string.Empty,
- }
- });
+ var messages = await chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel);
// Prepare the DeepSeek HTTP chat request:
var deepSeekChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
@@ -97,11 +80,17 @@ public sealed class ProviderDeepSeek() : BaseProvider("https://api.deepseek.com/
yield break;
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
+
+ ///
+ public override Task TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default)
+ {
+ return Task.FromResult(string.Empty);
+ }
///
public override Task> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
{
- return this.LoadModels(token, apiKeyProvisional);
+ return this.LoadModels(SecretStoreType.LLM_PROVIDER, token, apiKeyProvisional);
}
///
@@ -116,15 +105,20 @@ public sealed class ProviderDeepSeek() : BaseProvider("https://api.deepseek.com/
return Task.FromResult(Enumerable.Empty());
}
+ ///
+ public override Task> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default)
+ {
+ return Task.FromResult(Enumerable.Empty());
+ }
#endregion
- private async Task> LoadModels(CancellationToken token, string? apiKeyProvisional = null)
+ private async Task> LoadModels(SecretStoreType storeType, CancellationToken token, string? apiKeyProvisional = null)
{
var secretKey = apiKeyProvisional switch
{
not null => apiKeyProvisional,
- _ => await RUST_SERVICE.GetAPIKey(this) switch
+ _ => await RUST_SERVICE.GetAPIKey(this, storeType) switch
{
{ Success: true } result => await result.Secret.Decrypt(ENCRYPTION),
_ => null,
diff --git a/app/MindWork AI Studio/Provider/Fireworks/ChatRequest.cs b/app/MindWork AI Studio/Provider/Fireworks/ChatRequest.cs
index 55154ece..54963feb 100644
--- a/app/MindWork AI Studio/Provider/Fireworks/ChatRequest.cs
+++ b/app/MindWork AI Studio/Provider/Fireworks/ChatRequest.cs
@@ -10,7 +10,7 @@ namespace AIStudio.Provider.Fireworks;
/// Whether to stream the chat completion.
public readonly record struct ChatRequest(
string Model,
- IList Messages,
+ IList Messages,
bool Stream
)
{
diff --git a/app/MindWork AI Studio/Provider/Fireworks/Message.cs b/app/MindWork AI Studio/Provider/Fireworks/Message.cs
deleted file mode 100644
index 2b0055bd..00000000
--- a/app/MindWork AI Studio/Provider/Fireworks/Message.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace AIStudio.Provider.Fireworks;
-
-///
-/// Chat message model.
-///
-/// The text content of the message.
-/// The role of the message.
-public readonly record struct Message(string Content, string Role);
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/Fireworks/ProviderFireworks.cs b/app/MindWork AI Studio/Provider/Fireworks/ProviderFireworks.cs
index 20c79188..25fc2611 100644
--- a/app/MindWork AI Studio/Provider/Fireworks/ProviderFireworks.cs
+++ b/app/MindWork AI Studio/Provider/Fireworks/ProviderFireworks.cs
@@ -9,7 +9,7 @@ using AIStudio.Settings;
namespace AIStudio.Provider.Fireworks;
-public class ProviderFireworks() : BaseProvider("https://api.fireworks.ai/inference/v1/", LOGGER)
+public class ProviderFireworks() : BaseProvider(LLMProviders.FIREWORKS, "https://api.fireworks.ai/inference/v1/", LOGGER)
{
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger();
@@ -25,12 +25,12 @@ public class ProviderFireworks() : BaseProvider("https://api.fireworks.ai/infere
public override async IAsyncEnumerable StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
{
// Get the API key:
- var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
if(!requestedSecret.Success)
yield break;
// Prepare the system prompt:
- var systemPrompt = new Message
+ var systemPrompt = new TextMessage
{
Role = "system",
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
@@ -40,24 +40,7 @@ public class ProviderFireworks() : BaseProvider("https://api.fireworks.ai/infere
var apiParameters = this.ParseAdditionalApiParameters();
// Build the list of messages:
- var messages = await chatThread.Blocks.BuildMessages(async n => new Message
- {
- Role = n.Role switch
- {
- ChatRole.USER => "user",
- ChatRole.AI => "assistant",
- ChatRole.AGENT => "assistant",
- ChatRole.SYSTEM => "system",
-
- _ => "user",
- },
-
- Content = n.Content switch
- {
- ContentText text => await text.PrepareContentForAI(),
- _ => string.Empty,
- }
- });
+ var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
// Prepare the Fireworks HTTP chat request:
var fireworksChatRequest = JsonSerializer.Serialize(new ChatRequest
@@ -98,6 +81,13 @@ public class ProviderFireworks() : BaseProvider("https://api.fireworks.ai/infere
yield break;
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
+
+ ///
+ public override async Task TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default)
+ {
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.TRANSCRIPTION_PROVIDER);
+ return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, token: token);
+ }
///
public override Task> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
@@ -117,5 +107,17 @@ public class ProviderFireworks() : BaseProvider("https://api.fireworks.ai/infere
return Task.FromResult(Enumerable.Empty());
}
+ ///
+ public override Task> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default)
+ {
+ // Source: https://docs.fireworks.ai/api-reference/audio-transcriptions#param-model
+ return Task.FromResult>(
+ new List
+ {
+ new("whisper-v3", "Whisper v3"),
+ // new("whisper-v3-turbo", "Whisper v3 Turbo"), // does not work
+ });
+ }
+
#endregion
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/GWDG/ProviderGWDG.cs b/app/MindWork AI Studio/Provider/GWDG/ProviderGWDG.cs
index b1cb291c..28af268f 100644
--- a/app/MindWork AI Studio/Provider/GWDG/ProviderGWDG.cs
+++ b/app/MindWork AI Studio/Provider/GWDG/ProviderGWDG.cs
@@ -9,7 +9,7 @@ using AIStudio.Settings;
namespace AIStudio.Provider.GWDG;
-public sealed class ProviderGWDG() : BaseProvider("https://chat-ai.academiccloud.de/v1/", LOGGER)
+public sealed class ProviderGWDG() : BaseProvider(LLMProviders.GWDG, "https://chat-ai.academiccloud.de/v1/", LOGGER)
{
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger();
@@ -25,12 +25,12 @@ public sealed class ProviderGWDG() : BaseProvider("https://chat-ai.academiccloud
public override async IAsyncEnumerable StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
{
// Get the API key:
- var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
if(!requestedSecret.Success)
yield break;
// Prepare the system prompt:
- var systemPrompt = new Message
+ var systemPrompt = new TextMessage
{
Role = "system",
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
@@ -40,24 +40,7 @@ public sealed class ProviderGWDG() : BaseProvider("https://chat-ai.academiccloud
var apiParameters = this.ParseAdditionalApiParameters();
// Build the list of messages:
- var messages = await chatThread.Blocks.BuildMessages(async n => new Message
- {
- Role = n.Role switch
- {
- ChatRole.USER => "user",
- ChatRole.AI => "assistant",
- ChatRole.AGENT => "assistant",
- ChatRole.SYSTEM => "system",
-
- _ => "user",
- },
-
- Content = n.Content switch
- {
- ContentText text => await text.PrepareContentForAI(),
- _ => string.Empty,
- }
- });
+ var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
// Prepare the GWDG HTTP chat request:
var gwdgChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
@@ -97,11 +80,18 @@ public sealed class ProviderGWDG() : BaseProvider("https://chat-ai.academiccloud
yield break;
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
+
+ ///
+ public override async Task TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default)
+ {
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.TRANSCRIPTION_PROVIDER);
+ return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, token: token);
+ }
///
public override async Task> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
{
- var models = await this.LoadModels(token, apiKeyProvisional);
+ var models = await this.LoadModels(SecretStoreType.LLM_PROVIDER, token, apiKeyProvisional);
return models.Where(model => !model.Id.StartsWith("e5-mistral-7b-instruct", StringComparison.InvariantCultureIgnoreCase));
}
@@ -114,18 +104,29 @@ public sealed class ProviderGWDG() : BaseProvider("https://chat-ai.academiccloud
///
public override async Task> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default)
{
- var models = await this.LoadModels(token, apiKeyProvisional);
+ var models = await this.LoadModels(SecretStoreType.EMBEDDING_PROVIDER, token, apiKeyProvisional);
return models.Where(model => model.Id.StartsWith("e5-", StringComparison.InvariantCultureIgnoreCase));
}
+ ///
+ public override Task> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default)
+ {
+ // Source: https://docs.hpc.gwdg.de/services/saia/index.html#voice-to-text
+ return Task.FromResult>(
+ new List
+ {
+ new("whisper-large-v2", "Whisper v2 Large"),
+ });
+ }
+
#endregion
- private async Task> LoadModels(CancellationToken token, string? apiKeyProvisional = null)
+ private async Task> LoadModels(SecretStoreType storeType, CancellationToken token, string? apiKeyProvisional = null)
{
var secretKey = apiKeyProvisional switch
{
not null => apiKeyProvisional,
- _ => await RUST_SERVICE.GetAPIKey(this) switch
+ _ => await RUST_SERVICE.GetAPIKey(this, storeType) switch
{
{ Success: true } result => await result.Secret.Decrypt(ENCRYPTION),
_ => null,
diff --git a/app/MindWork AI Studio/Provider/Google/ChatRequest.cs b/app/MindWork AI Studio/Provider/Google/ChatRequest.cs
index 4fcd03cc..1a898c3a 100644
--- a/app/MindWork AI Studio/Provider/Google/ChatRequest.cs
+++ b/app/MindWork AI Studio/Provider/Google/ChatRequest.cs
@@ -1,5 +1,4 @@
using System.Text.Json.Serialization;
-using AIStudio.Provider.OpenAI;
namespace AIStudio.Provider.Google;
@@ -11,7 +10,7 @@ namespace AIStudio.Provider.Google;
/// Whether to stream the chat completion.
public readonly record struct ChatRequest(
string Model,
- IList Messages,
+ IList Messages,
bool Stream
)
{
diff --git a/app/MindWork AI Studio/Provider/Google/ProviderGoogle.cs b/app/MindWork AI Studio/Provider/Google/ProviderGoogle.cs
index 7ce3f24e..55490992 100644
--- a/app/MindWork AI Studio/Provider/Google/ProviderGoogle.cs
+++ b/app/MindWork AI Studio/Provider/Google/ProviderGoogle.cs
@@ -9,7 +9,7 @@ using AIStudio.Settings;
namespace AIStudio.Provider.Google;
-public class ProviderGoogle() : BaseProvider("https://generativelanguage.googleapis.com/v1beta/", LOGGER)
+public class ProviderGoogle() : BaseProvider(LLMProviders.GOOGLE, "https://generativelanguage.googleapis.com/v1beta/openai/", LOGGER)
{
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger();
@@ -25,12 +25,12 @@ public class ProviderGoogle() : BaseProvider("https://generativelanguage.googlea
public override async IAsyncEnumerable StreamChatCompletion(Provider.Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
{
// Get the API key:
- var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
if(!requestedSecret.Success)
yield break;
// Prepare the system prompt:
- var systemPrompt = new Message
+ var systemPrompt = new TextMessage
{
Role = "system",
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
@@ -40,24 +40,7 @@ public class ProviderGoogle() : BaseProvider("https://generativelanguage.googlea
var apiParameters = this.ParseAdditionalApiParameters();
// Build the list of messages:
- var messages = await chatThread.Blocks.BuildMessages(async n => new Message
- {
- Role = n.Role switch
- {
- ChatRole.USER => "user",
- ChatRole.AI => "assistant",
- ChatRole.AGENT => "assistant",
- ChatRole.SYSTEM => "system",
-
- _ => "user",
- },
-
- Content = n.Content switch
- {
- ContentText text => await text.PrepareContentForAI(),
- _ => string.Empty,
- }
- });
+ var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
// Prepare the Google HTTP chat request:
var geminiChatRequest = JsonSerializer.Serialize(new ChatRequest
@@ -99,10 +82,16 @@ public class ProviderGoogle() : BaseProvider("https://generativelanguage.googlea
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
+ ///
+ public override Task TranscribeAudioAsync(Provider.Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default)
+ {
+ return Task.FromResult(string.Empty);
+ }
+
///
public override async Task> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
{
- var modelResponse = await this.LoadModels(token, apiKeyProvisional);
+ var modelResponse = await this.LoadModels(SecretStoreType.LLM_PROVIDER, token, apiKeyProvisional);
if(modelResponse == default)
return [];
@@ -119,7 +108,7 @@ public class ProviderGoogle() : BaseProvider("https://generativelanguage.googlea
public override async Task> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default)
{
- var modelResponse = await this.LoadModels(token, apiKeyProvisional);
+ var modelResponse = await this.LoadModels(SecretStoreType.EMBEDDING_PROVIDER, token, apiKeyProvisional);
if(modelResponse == default)
return [];
@@ -129,15 +118,20 @@ public class ProviderGoogle() : BaseProvider("https://generativelanguage.googlea
.Select(n => new Provider.Model(n.Name.Replace("models/", string.Empty), n.DisplayName));
}
+ ///
+ public override Task> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default)
+ {
+ return Task.FromResult(Enumerable.Empty());
+ }
#endregion
- private async Task LoadModels(CancellationToken token, string? apiKeyProvisional = null)
+ private async Task LoadModels(SecretStoreType storeType, CancellationToken token, string? apiKeyProvisional = null)
{
var secretKey = apiKeyProvisional switch
{
not null => apiKeyProvisional,
- _ => await RUST_SERVICE.GetAPIKey(this) switch
+ _ => await RUST_SERVICE.GetAPIKey(this, storeType) switch
{
{ Success: true } result => await result.Secret.Decrypt(ENCRYPTION),
_ => null,
diff --git a/app/MindWork AI Studio/Provider/Groq/ChatRequest.cs b/app/MindWork AI Studio/Provider/Groq/ChatRequest.cs
index e45683fe..2e7668f1 100644
--- a/app/MindWork AI Studio/Provider/Groq/ChatRequest.cs
+++ b/app/MindWork AI Studio/Provider/Groq/ChatRequest.cs
@@ -1,5 +1,4 @@
using System.Text.Json.Serialization;
-using AIStudio.Provider.OpenAI;
namespace AIStudio.Provider.Groq;
@@ -12,7 +11,7 @@ namespace AIStudio.Provider.Groq;
/// The seed for the chat completion.
public readonly record struct ChatRequest(
string Model,
- IList Messages,
+ IList Messages,
bool Stream,
int Seed
)
diff --git a/app/MindWork AI Studio/Provider/Groq/ProviderGroq.cs b/app/MindWork AI Studio/Provider/Groq/ProviderGroq.cs
index 45473d82..e66cec3e 100644
--- a/app/MindWork AI Studio/Provider/Groq/ProviderGroq.cs
+++ b/app/MindWork AI Studio/Provider/Groq/ProviderGroq.cs
@@ -9,7 +9,7 @@ using AIStudio.Settings;
namespace AIStudio.Provider.Groq;
-public class ProviderGroq() : BaseProvider("https://api.groq.com/openai/v1/", LOGGER)
+public class ProviderGroq() : BaseProvider(LLMProviders.GROQ, "https://api.groq.com/openai/v1/", LOGGER)
{
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger();
@@ -25,12 +25,12 @@ public class ProviderGroq() : BaseProvider("https://api.groq.com/openai/v1/", LO
public override async IAsyncEnumerable StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
{
// Get the API key:
- var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
if(!requestedSecret.Success)
yield break;
// Prepare the system prompt:
- var systemPrompt = new Message
+ var systemPrompt = new TextMessage
{
Role = "system",
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
@@ -40,24 +40,7 @@ public class ProviderGroq() : BaseProvider("https://api.groq.com/openai/v1/", LO
var apiParameters = this.ParseAdditionalApiParameters();
// Build the list of messages:
- var messages = await chatThread.Blocks.BuildMessages(async n => new Message
- {
- Role = n.Role switch
- {
- ChatRole.USER => "user",
- ChatRole.AI => "assistant",
- ChatRole.AGENT => "assistant",
- ChatRole.SYSTEM => "system",
-
- _ => "user",
- },
-
- Content = n.Content switch
- {
- ContentText text => await text.PrepareContentForAI(),
- _ => string.Empty,
- }
- });
+ var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
// Prepare the OpenAI HTTP chat request:
var groqChatRequest = JsonSerializer.Serialize(new ChatRequest
@@ -98,17 +81,23 @@ public class ProviderGroq() : BaseProvider("https://api.groq.com/openai/v1/", LO
yield break;
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
+
+ ///
+ public override Task TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default)
+ {
+ return Task.FromResult(string.Empty);
+ }
///
public override Task> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
{
- return this.LoadModels(token, apiKeyProvisional);
+ return this.LoadModels(SecretStoreType.LLM_PROVIDER, token, apiKeyProvisional);
}
///
public override Task> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default)
{
- return Task.FromResult>(Array.Empty());
+ return Task.FromResult>([]);
}
///
@@ -117,14 +106,20 @@ public class ProviderGroq() : BaseProvider("https://api.groq.com/openai/v1/", LO
return Task.FromResult(Enumerable.Empty());
}
+ ///
+ public override Task> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default)
+ {
+ return Task.FromResult(Enumerable.Empty());
+ }
+
#endregion
- private async Task> LoadModels(CancellationToken token, string? apiKeyProvisional = null)
+ private async Task> LoadModels(SecretStoreType storeType, CancellationToken token, string? apiKeyProvisional = null)
{
var secretKey = apiKeyProvisional switch
{
not null => apiKeyProvisional,
- _ => await RUST_SERVICE.GetAPIKey(this) switch
+ _ => await RUST_SERVICE.GetAPIKey(this, storeType) switch
{
{ Success: true } result => await result.Secret.Decrypt(ENCRYPTION),
_ => null,
diff --git a/app/MindWork AI Studio/Provider/Helmholtz/ProviderHelmholtz.cs b/app/MindWork AI Studio/Provider/Helmholtz/ProviderHelmholtz.cs
index 3f7b405b..cf4359ea 100644
--- a/app/MindWork AI Studio/Provider/Helmholtz/ProviderHelmholtz.cs
+++ b/app/MindWork AI Studio/Provider/Helmholtz/ProviderHelmholtz.cs
@@ -9,7 +9,7 @@ using AIStudio.Settings;
namespace AIStudio.Provider.Helmholtz;
-public sealed class ProviderHelmholtz() : BaseProvider("https://api.helmholtz-blablador.fz-juelich.de/v1/", LOGGER)
+public sealed class ProviderHelmholtz() : BaseProvider(LLMProviders.HELMHOLTZ, "https://api.helmholtz-blablador.fz-juelich.de/v1/", LOGGER)
{
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger();
@@ -25,12 +25,12 @@ public sealed class ProviderHelmholtz() : BaseProvider("https://api.helmholtz-bl
public override async IAsyncEnumerable StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
{
// Get the API key:
- var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
if(!requestedSecret.Success)
yield break;
// Prepare the system prompt:
- var systemPrompt = new Message
+ var systemPrompt = new TextMessage
{
Role = "system",
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
@@ -40,24 +40,7 @@ public sealed class ProviderHelmholtz() : BaseProvider("https://api.helmholtz-bl
var apiParameters = this.ParseAdditionalApiParameters();
// Build the list of messages:
- var messages = await chatThread.Blocks.BuildMessages(async n => new Message
- {
- Role = n.Role switch
- {
- ChatRole.USER => "user",
- ChatRole.AI => "assistant",
- ChatRole.AGENT => "assistant",
- ChatRole.SYSTEM => "system",
-
- _ => "user",
- },
-
- Content = n.Content switch
- {
- ContentText text => await text.PrepareContentForAI(),
- _ => string.Empty,
- }
- });
+ var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
// Prepare the Helmholtz HTTP chat request:
var helmholtzChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
@@ -97,11 +80,17 @@ public sealed class ProviderHelmholtz() : BaseProvider("https://api.helmholtz-bl
yield break;
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
+
+ ///
+ public override Task TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default)
+ {
+ return Task.FromResult(string.Empty);
+ }
///
public override async Task> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
{
- var models = await this.LoadModels(token, apiKeyProvisional);
+ var models = await this.LoadModels(SecretStoreType.LLM_PROVIDER, token, apiKeyProvisional);
return models.Where(model => !model.Id.StartsWith("text-", StringComparison.InvariantCultureIgnoreCase) &&
!model.Id.StartsWith("alias-embedding", StringComparison.InvariantCultureIgnoreCase));
}
@@ -115,21 +104,27 @@ public sealed class ProviderHelmholtz() : BaseProvider("https://api.helmholtz-bl
///
public override async Task> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default)
{
- var models = await this.LoadModels(token, apiKeyProvisional);
+ var models = await this.LoadModels(SecretStoreType.EMBEDDING_PROVIDER, token, apiKeyProvisional);
return models.Where(model =>
model.Id.StartsWith("alias-embedding", StringComparison.InvariantCultureIgnoreCase) ||
model.Id.StartsWith("text-", StringComparison.InvariantCultureIgnoreCase) ||
model.Id.Contains("gritlm", StringComparison.InvariantCultureIgnoreCase));
}
+ ///
+ public override Task> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default)
+ {
+ return Task.FromResult(Enumerable.Empty());
+ }
+
#endregion
- private async Task> LoadModels(CancellationToken token, string? apiKeyProvisional = null)
+ private async Task> LoadModels(SecretStoreType storeType, CancellationToken token, string? apiKeyProvisional = null)
{
var secretKey = apiKeyProvisional switch
{
not null => apiKeyProvisional,
- _ => await RUST_SERVICE.GetAPIKey(this) switch
+ _ => await RUST_SERVICE.GetAPIKey(this, storeType) switch
{
{ Success: true } result => await result.Secret.Decrypt(ENCRYPTION),
_ => null,
diff --git a/app/MindWork AI Studio/Provider/HuggingFace/ProviderHuggingFace.cs b/app/MindWork AI Studio/Provider/HuggingFace/ProviderHuggingFace.cs
index 31522b5c..685dea10 100644
--- a/app/MindWork AI Studio/Provider/HuggingFace/ProviderHuggingFace.cs
+++ b/app/MindWork AI Studio/Provider/HuggingFace/ProviderHuggingFace.cs
@@ -13,9 +13,9 @@ public sealed class ProviderHuggingFace : BaseProvider
{
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger();
- public ProviderHuggingFace(HFInferenceProvider hfProvider, Model model) : base($"https://router.huggingface.co/{hfProvider.Endpoints(model)}", LOGGER)
+ public ProviderHuggingFace(HFInferenceProvider hfProvider, Model model) : base(LLMProviders.HUGGINGFACE, $"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 inference provider '{hfProvider}'. Thus we use the base URL 'https://router.huggingface.co/{hfProvider.Endpoints(model)}'.");
}
#region Implementation of IProvider
@@ -30,12 +30,12 @@ public sealed class ProviderHuggingFace : BaseProvider
public override async IAsyncEnumerable StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
{
// Get the API key:
- var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
if(!requestedSecret.Success)
yield break;
// Prepare the system prompt:
- var systemPrompt = new Message
+ var systemPrompt = new TextMessage
{
Role = "system",
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
@@ -45,24 +45,7 @@ public sealed class ProviderHuggingFace : BaseProvider
var apiParameters = this.ParseAdditionalApiParameters();
// Build the list of messages:
- var message = await chatThread.Blocks.BuildMessages(async n => new Message
- {
- Role = n.Role switch
- {
- ChatRole.USER => "user",
- ChatRole.AI => "assistant",
- ChatRole.AGENT => "assistant",
- ChatRole.SYSTEM => "system",
-
- _ => "user",
- },
-
- Content = n.Content switch
- {
- ContentText text => await text.PrepareContentForAI(),
- _ => string.Empty,
- }
- });
+ var message = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
// Prepare the HuggingFace HTTP chat request:
var huggingfaceChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
@@ -102,6 +85,12 @@ public sealed class ProviderHuggingFace : BaseProvider
yield break;
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
+
+ ///
+ public override Task TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default)
+ {
+ return Task.FromResult(string.Empty);
+ }
///
public override Task> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
@@ -121,5 +110,11 @@ public sealed class ProviderHuggingFace : BaseProvider
return Task.FromResult(Enumerable.Empty());
}
+ ///
+ public override Task> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default)
+ {
+ return Task.FromResult(Enumerable.Empty());
+ }
+
#endregion
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/IMessage.cs b/app/MindWork AI Studio/Provider/IMessage.cs
new file mode 100644
index 00000000..2a8c9e2f
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/IMessage.cs
@@ -0,0 +1,15 @@
+namespace AIStudio.Provider;
+
+///
+/// Standard interface for messages exchanged with AI models.
+///
+/// The type of the message content.
+public interface IMessage : IMessageBase
+{
+ ///
+ /// Gets the main content of the message exchanged with the AI model.
+ /// The content encapsulates the core information or data being transmitted,
+ /// and its type can vary based on the specific implementation or use case.
+ ///
+ public T Content { get; init; }
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/IMessageBase.cs b/app/MindWork AI Studio/Provider/IMessageBase.cs
new file mode 100644
index 00000000..8f67cc8b
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/IMessageBase.cs
@@ -0,0 +1,14 @@
+namespace AIStudio.Provider;
+
+///
+/// The none-generic base interface for messages exchanged with AI models.
+///
+public interface IMessageBase
+{
+ ///
+ /// Gets the role of the entity sending or receiving the message.
+ /// This property typically identifies whether the entity is acting
+ /// as a user, assistant, or system in the context of the interaction.
+ ///
+ public string Role { get; init; }
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/IProvider.cs b/app/MindWork AI Studio/Provider/IProvider.cs
index e883fec8..5c390074 100644
--- a/app/MindWork AI Studio/Provider/IProvider.cs
+++ b/app/MindWork AI Studio/Provider/IProvider.cs
@@ -8,6 +8,11 @@ namespace AIStudio.Provider;
///
public interface IProvider
{
+ ///
+ /// The provider type.
+ ///
+ public LLMProviders Provider { get; }
+
///
/// The provider's ID.
///
@@ -45,6 +50,16 @@ public interface IProvider
/// The image completion stream.
public IAsyncEnumerable StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, CancellationToken token = default);
+ ///
+ /// Transcribe an audio file.
+ ///
+ /// The model to use for transcription.
+ /// The audio file path.
+ /// The settings manager instance to use.
+ /// The cancellation token.
+ /// >The transcription result.
+ public Task TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default);
+
///
/// Load all possible text models that can be used with this provider.
///
@@ -69,4 +84,11 @@ public interface IProvider
/// The list of embedding models.
public Task> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default);
+ ///
+ /// Load all possible transcription models that can be used with this provider.
+ ///
+ /// The provisional API key to use. Useful when the user is adding a new provider. When null, the stored API key is used.
+ /// >The cancellation token.
+ /// >The list of transcription models.
+ public Task> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default);
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/LLMProviders.cs b/app/MindWork AI Studio/Provider/LLMProviders.cs
index f23cd876..6a560036 100644
--- a/app/MindWork AI Studio/Provider/LLMProviders.cs
+++ b/app/MindWork AI Studio/Provider/LLMProviders.cs
@@ -15,7 +15,8 @@ public enum LLMProviders
DEEP_SEEK = 11,
ALIBABA_CLOUD = 12,
PERPLEXITY = 14,
-
+ OPEN_ROUTER = 15,
+
FIREWORKS = 5,
GROQ = 6,
HUGGINGFACE = 13,
diff --git a/app/MindWork AI Studio/Provider/LLMProvidersExtensions.cs b/app/MindWork AI Studio/Provider/LLMProvidersExtensions.cs
index 52fe30a1..ea548923 100644
--- a/app/MindWork AI Studio/Provider/LLMProvidersExtensions.cs
+++ b/app/MindWork AI Studio/Provider/LLMProvidersExtensions.cs
@@ -9,6 +9,7 @@ using AIStudio.Provider.Helmholtz;
using AIStudio.Provider.HuggingFace;
using AIStudio.Provider.Mistral;
using AIStudio.Provider.OpenAI;
+using AIStudio.Provider.OpenRouter;
using AIStudio.Provider.Perplexity;
using AIStudio.Provider.SelfHosted;
using AIStudio.Provider.X;
@@ -42,7 +43,8 @@ public static class LLMProvidersExtensions
LLMProviders.DEEP_SEEK => "DeepSeek",
LLMProviders.ALIBABA_CLOUD => "Alibaba Cloud",
LLMProviders.PERPLEXITY => "Perplexity",
-
+ LLMProviders.OPEN_ROUTER => "OpenRouter",
+
LLMProviders.GROQ => "Groq",
LLMProviders.FIREWORKS => "Fireworks.ai",
LLMProviders.HUGGINGFACE => "Hugging Face",
@@ -92,7 +94,9 @@ public static class LLMProvidersExtensions
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.OPEN_ROUTER => Confidence.USA_HUB.WithRegion("America, U.S.").WithSources("https://openrouter.ai/privacy", "https://openrouter.ai/terms").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
+
LLMProviders.SELF_HOSTED => Confidence.SELF_HOSTED.WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
LLMProviders.HELMHOLTZ => Confidence.GDPR_NO_TRAINING.WithRegion("Europe, Germany").WithSources("https://helmholtz.cloud/services/?serviceID=d7d5c597-a2f6-4bd1-b71e-4d6499d98570").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
@@ -106,7 +110,7 @@ public static class LLMProvidersExtensions
///
/// The provider to check.
/// True if the provider supports embeddings; otherwise, false.
- public static bool ProvideEmbeddings(this LLMProviders llmProvider) => llmProvider switch
+ public static bool ProvideEmbeddingAPI(this LLMProviders llmProvider) => llmProvider switch
{
//
// Providers that support embeddings:
@@ -128,7 +132,45 @@ public static class LLMProvidersExtensions
LLMProviders.DEEP_SEEK => false,
LLMProviders.HUGGINGFACE => false,
LLMProviders.PERPLEXITY => false,
+ LLMProviders.OPEN_ROUTER => true,
+
+ //
+ // Self-hosted providers are treated as a special case anyway.
+ //
+ LLMProviders.SELF_HOSTED => true,
+ _ => false,
+ };
+
+ public static bool ProvideTranscriptionAPI(this LLMProviders llmProvider) => llmProvider switch
+ {
+ //
+ // Providers that support transcription:
+ //
+ LLMProviders.OPEN_AI => true,
+ LLMProviders.MISTRAL => true,
+ LLMProviders.FIREWORKS => true,
+ LLMProviders.GWDG => true,
+
+ //
+ // Providers that support transcription but provide no OpenAI-compatible API yet:
+ //
+ LLMProviders.ALIBABA_CLOUD => false,
+ LLMProviders.GOOGLE => false,
+
+ //
+ // Providers that do not support transcription:
+ //
+ LLMProviders.OPEN_ROUTER => false,
+ LLMProviders.GROQ => false,
+ LLMProviders.ANTHROPIC => false,
+ LLMProviders.X => false,
+ LLMProviders.DEEP_SEEK => false,
+ LLMProviders.HUGGINGFACE => false,
+ LLMProviders.PERPLEXITY => false,
+
+ LLMProviders.HELMHOLTZ => false,
+
//
// Self-hosted providers are treated as a special case anyway.
//
@@ -157,6 +199,16 @@ public static class LLMProvidersExtensions
return embeddingProviderSettings.UsedLLMProvider.CreateProvider(embeddingProviderSettings.Name, embeddingProviderSettings.Host, embeddingProviderSettings.Hostname, embeddingProviderSettings.Model, HFInferenceProvider.NONE);
}
+ ///
+ /// Creates a new provider instance based on the speech provider value.
+ ///
+ /// The speech provider settings.
+ /// The provider instance.
+ public static IProvider CreateProvider(this TranscriptionProvider transcriptionProviderSettings)
+ {
+ return transcriptionProviderSettings.UsedLLMProvider.CreateProvider(transcriptionProviderSettings.Name, transcriptionProviderSettings.Host, transcriptionProviderSettings.Hostname, transcriptionProviderSettings.Model, HFInferenceProvider.NONE);
+ }
+
private static IProvider CreateProvider(this LLMProviders provider, string instanceName, Host host, string hostname, Model model, HFInferenceProvider inferenceProvider, string expertProviderApiParameter = "")
{
try
@@ -171,7 +223,8 @@ public static class LLMProvidersExtensions
LLMProviders.DEEP_SEEK => new ProviderDeepSeek { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
LLMProviders.ALIBABA_CLOUD => new ProviderAlibabaCloud { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
LLMProviders.PERPLEXITY => new ProviderPerplexity { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
-
+ LLMProviders.OPEN_ROUTER => new ProviderOpenRouter { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
+
LLMProviders.GROQ => new ProviderGroq { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
LLMProviders.FIREWORKS => new ProviderFireworks { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
LLMProviders.HUGGINGFACE => new ProviderHuggingFace(inferenceProvider, model) { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter },
@@ -201,7 +254,8 @@ public static class LLMProvidersExtensions
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.OPEN_ROUTER => "https://openrouter.ai/keys",
+
LLMProviders.GROQ => "https://console.groq.com/",
LLMProviders.FIREWORKS => "https://fireworks.ai/login",
LLMProviders.HUGGINGFACE => "https://huggingface.co/login",
@@ -224,8 +278,9 @@ public static class LLMProvidersExtensions
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.OPEN_ROUTER => "https://openrouter.ai/activity",
LLMProviders.HUGGINGFACE => "https://huggingface.co/settings/billing",
-
+
_ => string.Empty,
};
@@ -241,8 +296,9 @@ public static class LLMProvidersExtensions
LLMProviders.DEEP_SEEK => true,
LLMProviders.ALIBABA_CLOUD => true,
LLMProviders.PERPLEXITY => true,
+ LLMProviders.OPEN_ROUTER => true,
LLMProviders.HUGGINGFACE => true,
-
+
_ => false,
};
@@ -265,6 +321,11 @@ public static class LLMProvidersExtensions
LLMProviders.SELF_HOSTED => host is not Host.LM_STUDIO,
_ => false,
};
+
+ public static bool IsTranscriptionModelProvidedManually(this LLMProviders provider, Host host) => provider switch
+ {
+ _ => false,
+ };
public static bool IsHostNeeded(this LLMProviders provider) => provider switch
{
@@ -288,7 +349,8 @@ public static class LLMProvidersExtensions
LLMProviders.DEEP_SEEK => true,
LLMProviders.ALIBABA_CLOUD => true,
LLMProviders.PERPLEXITY => true,
-
+ LLMProviders.OPEN_ROUTER => true,
+
LLMProviders.GROQ => true,
LLMProviders.FIREWORKS => true,
LLMProviders.HELMHOLTZ => true,
@@ -310,7 +372,8 @@ public static class LLMProvidersExtensions
LLMProviders.DEEP_SEEK => true,
LLMProviders.ALIBABA_CLOUD => true,
LLMProviders.PERPLEXITY => true,
-
+ LLMProviders.OPEN_ROUTER => true,
+
LLMProviders.GROQ => true,
LLMProviders.FIREWORKS => true,
LLMProviders.HELMHOLTZ => true,
@@ -327,13 +390,14 @@ public static class LLMProvidersExtensions
switch (host)
{
case Host.NONE:
- case Host.LLAMACPP:
+ case Host.LLAMA_CPP:
default:
return false;
case Host.OLLAMA:
case Host.LM_STUDIO:
case Host.VLLM:
+ case Host.WHISPER_CPP:
return true;
}
}
diff --git a/app/MindWork AI Studio/Provider/MessageBaseConverter.cs b/app/MindWork AI Studio/Provider/MessageBaseConverter.cs
new file mode 100644
index 00000000..7707736e
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/MessageBaseConverter.cs
@@ -0,0 +1,32 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace AIStudio.Provider;
+
+///
+/// Custom JSON converter for the IMessageBase interface to handle polymorphic serialization.
+///
+///
+/// This converter ensures that when serializing IMessageBase objects, all properties
+/// of the concrete implementation (e.g., TextMessage) are serialized, not just the
+/// properties defined in the IMessageBase interface.
+///
+public sealed class MessageBaseConverter : JsonConverter
+{
+ private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger();
+
+ public override IMessageBase? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ // Deserialization is not needed for request objects, as messages are only serialized
+ // when sending requests to LLM providers.
+ LOGGER.LogError("Deserializing IMessageBase is not supported. This converter is only used for serializing request messages.");
+ return null;
+ }
+
+ public override void Write(Utf8JsonWriter writer, IMessageBase value, JsonSerializerOptions options)
+ {
+ // Serialize the actual concrete type (e.g., TextMessage) instead of just the IMessageBase interface.
+ // This ensures all properties of the concrete type are included in the JSON output.
+ JsonSerializer.Serialize(writer, value, value.GetType(), options);
+ }
+}
diff --git a/app/MindWork AI Studio/Provider/Mistral/ChatRequest.cs b/app/MindWork AI Studio/Provider/Mistral/ChatRequest.cs
index b12dd15d..01a45a89 100644
--- a/app/MindWork AI Studio/Provider/Mistral/ChatRequest.cs
+++ b/app/MindWork AI Studio/Provider/Mistral/ChatRequest.cs
@@ -12,7 +12,7 @@ namespace AIStudio.Provider.Mistral;
/// Whether to inject a safety prompt before all conversations.
public readonly record struct ChatRequest(
string Model,
- IList Messages,
+ IList Messages,
bool Stream,
int RandomSeed,
bool SafePrompt = false
diff --git a/app/MindWork AI Studio/Provider/Mistral/ProviderMistral.cs b/app/MindWork AI Studio/Provider/Mistral/ProviderMistral.cs
index bd999b92..0755f349 100644
--- a/app/MindWork AI Studio/Provider/Mistral/ProviderMistral.cs
+++ b/app/MindWork AI Studio/Provider/Mistral/ProviderMistral.cs
@@ -9,7 +9,7 @@ using AIStudio.Settings;
namespace AIStudio.Provider.Mistral;
-public sealed class ProviderMistral() : BaseProvider("https://api.mistral.ai/v1/", LOGGER)
+public sealed class ProviderMistral() : BaseProvider(LLMProviders.MISTRAL, "https://api.mistral.ai/v1/", LOGGER)
{
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger();
@@ -23,12 +23,12 @@ public sealed class ProviderMistral() : BaseProvider("https://api.mistral.ai/v1/
public override async IAsyncEnumerable StreamChatCompletion(Provider.Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
{
// Get the API key:
- var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
if(!requestedSecret.Success)
yield break;
// Prepare the system prompt:
- var systemPrompt = new RegularMessage
+ var systemPrompt = new TextMessage
{
Role = "system",
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
@@ -38,24 +38,7 @@ public sealed class ProviderMistral() : BaseProvider("https://api.mistral.ai/v1/
var apiParameters = this.ParseAdditionalApiParameters();
// Build the list of messages:
- var messages = await chatThread.Blocks.BuildMessages(async n => new RegularMessage
- {
- Role = n.Role switch
- {
- ChatRole.USER => "user",
- ChatRole.AI => "assistant",
- ChatRole.AGENT => "assistant",
- ChatRole.SYSTEM => "system",
-
- _ => "user",
- },
-
- Content = n.Content switch
- {
- ContentText text => await text.PrepareContentForAI(),
- _ => string.Empty,
- }
- });
+ var messages = await chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel);
// Prepare the Mistral HTTP chat request:
var mistralChatRequest = JsonSerializer.Serialize(new ChatRequest
@@ -98,11 +81,18 @@ public sealed class ProviderMistral() : BaseProvider("https://api.mistral.ai/v1/
yield break;
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
+
+ ///
+ public override async Task TranscribeAudioAsync(Provider.Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default)
+ {
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.TRANSCRIPTION_PROVIDER);
+ return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, token: token);
+ }
///
public override async Task> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
{
- var modelResponse = await this.LoadModelList(apiKeyProvisional, token);
+ var modelResponse = await this.LoadModelList(SecretStoreType.LLM_PROVIDER, apiKeyProvisional, token);
if(modelResponse == default)
return [];
@@ -116,7 +106,7 @@ public sealed class ProviderMistral() : BaseProvider("https://api.mistral.ai/v1/
///
public override async Task> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default)
{
- var modelResponse = await this.LoadModelList(apiKeyProvisional, token);
+ var modelResponse = await this.LoadModelList(SecretStoreType.EMBEDDING_PROVIDER, apiKeyProvisional, token);
if(modelResponse == default)
return [];
@@ -130,14 +120,25 @@ public sealed class ProviderMistral() : BaseProvider("https://api.mistral.ai/v1/
return Task.FromResult(Enumerable.Empty());
}
+ ///
+ public override Task> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default)
+ {
+ // Source: https://docs.mistral.ai/capabilities/audio_transcription
+ return Task.FromResult>(
+ new List
+ {
+ new("voxtral-mini-latest", "Voxtral Mini Latest"),
+ });
+ }
+
#endregion
- private async Task LoadModelList(string? apiKeyProvisional, CancellationToken token)
+ private async Task LoadModelList(SecretStoreType storeType, string? apiKeyProvisional, CancellationToken token)
{
var secretKey = apiKeyProvisional switch
{
not null => apiKeyProvisional,
- _ => await RUST_SERVICE.GetAPIKey(this) switch
+ _ => await RUST_SERVICE.GetAPIKey(this, storeType) switch
{
{ Success: true } result => await result.Secret.Decrypt(ENCRYPTION),
_ => null,
diff --git a/app/MindWork AI Studio/Provider/Mistral/RegularMessage.cs b/app/MindWork AI Studio/Provider/Mistral/RegularMessage.cs
deleted file mode 100644
index df5bdcd3..00000000
--- a/app/MindWork AI Studio/Provider/Mistral/RegularMessage.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace AIStudio.Provider.Mistral;
-
-///
-/// Regulat chat message model.
-///
-/// The text content of the message.
-/// The role of the message.
-public readonly record struct RegularMessage(string Content, string Role);
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/NoProvider.cs b/app/MindWork AI Studio/Provider/NoProvider.cs
index b87820ca..a650ac34 100644
--- a/app/MindWork AI Studio/Provider/NoProvider.cs
+++ b/app/MindWork AI Studio/Provider/NoProvider.cs
@@ -9,6 +9,8 @@ public class NoProvider : IProvider
{
#region Implementation of IProvider
+ public LLMProviders Provider => LLMProviders.NONE;
+
public string Id => "none";
public string InstanceName { get; set; } = "None";
@@ -21,6 +23,8 @@ public class NoProvider : IProvider
public Task> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default) => Task.FromResult>([]);
public Task> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default) => Task.FromResult>([]);
+
+ public Task> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default) => Task.FromResult>([]);
public async IAsyncEnumerable StreamChatCompletion(Model chatModel, ChatThread chatChatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
{
@@ -34,6 +38,8 @@ public class NoProvider : IProvider
yield break;
}
+ public Task TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default) => Task.FromResult(string.Empty);
+
public IReadOnlyCollection GetModelCapabilities(Model model) => [ Capability.NONE ];
#endregion
diff --git a/app/MindWork AI Studio/Provider/OpenAI/ChatCompletionAPIRequest.cs b/app/MindWork AI Studio/Provider/OpenAI/ChatCompletionAPIRequest.cs
index 51805910..bd9c08e7 100644
--- a/app/MindWork AI Studio/Provider/OpenAI/ChatCompletionAPIRequest.cs
+++ b/app/MindWork AI Studio/Provider/OpenAI/ChatCompletionAPIRequest.cs
@@ -10,7 +10,7 @@ namespace AIStudio.Provider.OpenAI;
/// Whether to stream the chat completion.
public record ChatCompletionAPIRequest(
string Model,
- IList Messages,
+ IList Messages,
bool Stream
)
{
diff --git a/app/MindWork AI Studio/Provider/OpenAI/ISubContent.cs b/app/MindWork AI Studio/Provider/OpenAI/ISubContent.cs
new file mode 100644
index 00000000..165d07b1
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/OpenAI/ISubContent.cs
@@ -0,0 +1,12 @@
+namespace AIStudio.Provider.OpenAI;
+
+///
+/// Contract for sub-content in multimodal messages.
+///
+public interface ISubContent
+{
+ ///
+ /// The type of the sub-content.
+ ///
+ public SubContentType Type { get; init; }
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/OpenAI/ISubContentImageUrl.cs b/app/MindWork AI Studio/Provider/OpenAI/ISubContentImageUrl.cs
new file mode 100644
index 00000000..b2f9b51d
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/OpenAI/ISubContentImageUrl.cs
@@ -0,0 +1,19 @@
+namespace AIStudio.Provider.OpenAI;
+
+///
+/// Contract for nested image URL sub-content.
+///
+///
+/// Some providers use a nested object format for image URLs:
+///
+/// { "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,..." } }
+///
+/// This interface represents the inner object with the "url" property.
+///
+public interface ISubContentImageUrl
+{
+ ///
+ /// The URL or base64-encoded data URI of the image.
+ ///
+ public string Url { get; init; }
+}
diff --git a/app/MindWork AI Studio/Provider/OpenAI/Message.cs b/app/MindWork AI Studio/Provider/OpenAI/Message.cs
deleted file mode 100644
index 508645b0..00000000
--- a/app/MindWork AI Studio/Provider/OpenAI/Message.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace AIStudio.Provider.OpenAI;
-
-///
-/// Chat message model.
-///
-/// The text content of the message.
-/// The role of the message.
-public readonly record struct Message(string Content, string Role);
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/OpenAI/MultimodalMessage.cs b/app/MindWork AI Studio/Provider/OpenAI/MultimodalMessage.cs
new file mode 100644
index 00000000..8b7ff8e0
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/OpenAI/MultimodalMessage.cs
@@ -0,0 +1,13 @@
+namespace AIStudio.Provider.OpenAI;
+
+///
+/// A multimodal chat message model that can contain various types of content.
+///
+/// The list of sub-contents in the message.
+/// The role of the message.
+public record MultimodalMessage(List Content, string Role) : IMessage>
+{
+ public MultimodalMessage() : this([], string.Empty)
+ {
+ }
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs b/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs
index 89da7b7d..c4a213db 100644
--- a/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs
+++ b/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs
@@ -11,7 +11,7 @@ namespace AIStudio.Provider.OpenAI;
///
/// The OpenAI provider.
///
-public sealed class ProviderOpenAI() : BaseProvider("https://api.openai.com/v1/", LOGGER)
+public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, "https://api.openai.com/v1/", LOGGER)
{
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger();
@@ -27,7 +27,7 @@ public sealed class ProviderOpenAI() : BaseProvider("https://api.openai.com/v1/"
public override async IAsyncEnumerable StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
{
// Get the API key:
- var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
if(!requestedSecret.Success)
yield break;
@@ -59,7 +59,7 @@ public sealed class ProviderOpenAI() : BaseProvider("https://api.openai.com/v1/"
};
// Read the model capabilities:
- var modelCapabilities = ProviderExtensions.GetModelCapabilitiesOpenAI(chatModel);
+ var modelCapabilities = this.Provider.GetModelCapabilities(chatModel);
// Check if we are using the Responses API or the Chat Completion API:
var usingResponsesAPI = modelCapabilities.Contains(Capability.RESPONSES_API);
@@ -70,7 +70,7 @@ public sealed class ProviderOpenAI() : BaseProvider("https://api.openai.com/v1/"
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
+ var systemPrompt = new TextMessage
{
Role = systemPromptRole,
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
@@ -90,9 +90,11 @@ public sealed class ProviderOpenAI() : BaseProvider("https://api.openai.com/v1/"
var apiParameters = this.ParseAdditionalApiParameters("input", "store", "tools");
// Build the list of messages:
- var messages = await chatThread.Blocks.BuildMessages(async n => new Message
- {
- Role = n.Role switch
+ var messages = await chatThread.Blocks.BuildMessagesAsync(
+ this.Provider, chatModel,
+
+ // OpenAI-specific role mapping:
+ role => role switch
{
ChatRole.USER => "user",
ChatRole.AI => "assistant",
@@ -102,12 +104,46 @@ public sealed class ProviderOpenAI() : BaseProvider("https://api.openai.com/v1/"
_ => "user",
},
- Content = n.Content switch
+ // OpenAI's text sub-content depends on the model, whether we are using
+ // the Responses API or the Chat Completion API:
+ text => usingResponsesAPI switch
{
- ContentText text => await text.PrepareContentForAI(),
- _ => string.Empty,
- }
- });
+ // Responses API uses INPUT_TEXT:
+ true => new SubContentInputText
+ {
+ Text = text,
+ },
+
+ // Chat Completion API uses TEXT:
+ false => new SubContentText
+ {
+ Text = text,
+ },
+ },
+
+ // OpenAI's image sub-content depends on the model as well,
+ // whether we are using the Responses API or the Chat Completion API:
+ async attachment => usingResponsesAPI switch
+ {
+ // Responses API uses INPUT_IMAGE:
+ true => new SubContentInputImage
+ {
+ ImageUrl = await attachment.TryAsBase64(token: token) is (true, var base64Content)
+ ? $"data:{attachment.DetermineMimeType()};base64,{base64Content}"
+ : string.Empty,
+ },
+
+ // Chat Completion API uses IMAGE_URL:
+ false => new SubContentImageUrlNested
+ {
+ ImageUrl = new SubContentImageUrlData
+ {
+ Url = await attachment.TryAsBase64(token: token) is (true, var base64Content)
+ ? $"data:{attachment.DetermineMimeType()};base64,{base64Content}"
+ : string.Empty,
+ },
+ }
+ });
//
// Create the request: either for the Responses API or the Chat Completion API
@@ -119,9 +155,7 @@ public sealed class ProviderOpenAI() : BaseProvider("https://api.openai.com/v1/"
{
Model = chatModel.Id,
- // Build the messages:
- // - First of all the system prompt
- // - Then none-empty user and AI messages
+ // All messages go into the messages field:
Messages = [systemPrompt, ..messages],
// Right now, we only support streaming completions:
@@ -134,27 +168,8 @@ public sealed class ProviderOpenAI() : BaseProvider("https://api.openai.com/v1/"
{
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()],
+ // All messages go into the input field:
+ Input = [systemPrompt, ..messages],
// Right now, we only support streaming completions:
Stream = true,
@@ -170,7 +185,7 @@ public sealed class ProviderOpenAI() : BaseProvider("https://api.openai.com/v1/"
}, JSON_SERIALIZER_OPTIONS),
};
-
+
async Task RequestBuilder()
{
// Build the HTTP post request:
@@ -202,11 +217,18 @@ public sealed class ProviderOpenAI() : BaseProvider("https://api.openai.com/v1/"
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
+
+ ///
+ public override async Task TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default)
+ {
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.TRANSCRIPTION_PROVIDER);
+ return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, token: token);
+ }
///
public override async Task> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
{
- var models = await this.LoadModels(["chatgpt-", "gpt-", "o1-", "o3-", "o4-"], token, apiKeyProvisional);
+ var models = await this.LoadModels(SecretStoreType.LLM_PROVIDER, ["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) &&
@@ -217,23 +239,31 @@ public sealed class ProviderOpenAI() : BaseProvider("https://api.openai.com/v1/"
///
public override Task> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default)
{
- return this.LoadModels(["dall-e-", "gpt-image"], token, apiKeyProvisional);
+ return this.LoadModels(SecretStoreType.IMAGE_PROVIDER, ["dall-e-", "gpt-image"], token, apiKeyProvisional);
}
///
public override Task> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default)
{
- return this.LoadModels(["text-embedding-"], token, apiKeyProvisional);
+ return this.LoadModels(SecretStoreType.EMBEDDING_PROVIDER, ["text-embedding-"], token, apiKeyProvisional);
+ }
+
+ ///
+ public override async Task> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default)
+ {
+ var models = await this.LoadModels(SecretStoreType.TRANSCRIPTION_PROVIDER, ["whisper-", "gpt-"], token, apiKeyProvisional);
+ return models.Where(model => model.Id.StartsWith("whisper-", StringComparison.InvariantCultureIgnoreCase) ||
+ model.Id.Contains("-transcribe", StringComparison.InvariantCultureIgnoreCase));
}
#endregion
- private async Task> LoadModels(string[] prefixes, CancellationToken token, string? apiKeyProvisional = null)
+ private async Task> LoadModels(SecretStoreType storeType, string[] prefixes, CancellationToken token, string? apiKeyProvisional = null)
{
var secretKey = apiKeyProvisional switch
{
not null => apiKeyProvisional,
- _ => await RUST_SERVICE.GetAPIKey(this) switch
+ _ => await RUST_SERVICE.GetAPIKey(this, storeType) switch
{
{ Success: true } result => await result.Secret.Decrypt(ENCRYPTION),
_ => null,
diff --git a/app/MindWork AI Studio/Provider/OpenAI/ResponsesAPIRequest.cs b/app/MindWork AI Studio/Provider/OpenAI/ResponsesAPIRequest.cs
index e3c9541b..deb315d6 100644
--- a/app/MindWork AI Studio/Provider/OpenAI/ResponsesAPIRequest.cs
+++ b/app/MindWork AI Studio/Provider/OpenAI/ResponsesAPIRequest.cs
@@ -12,7 +12,7 @@ namespace AIStudio.Provider.OpenAI;
/// The tools to use for the request.
public record ResponsesAPIRequest(
string Model,
- IList Input,
+ IList Input,
bool Stream,
bool Store,
IList Tools)
diff --git a/app/MindWork AI Studio/Provider/OpenAI/SubContentImageUrl.cs b/app/MindWork AI Studio/Provider/OpenAI/SubContentImageUrl.cs
new file mode 100644
index 00000000..fe190fb8
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/OpenAI/SubContentImageUrl.cs
@@ -0,0 +1,11 @@
+namespace AIStudio.Provider.OpenAI;
+
+///
+/// Image sub-content for multimodal messages.
+///
+public record SubContentImageUrl(SubContentType Type, string ImageUrl) : ISubContent
+{
+ public SubContentImageUrl() : this(SubContentType.IMAGE_URL, string.Empty)
+ {
+ }
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/OpenAI/SubContentImageUrlData.cs b/app/MindWork AI Studio/Provider/OpenAI/SubContentImageUrlData.cs
new file mode 100644
index 00000000..af0ffc9e
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/OpenAI/SubContentImageUrlData.cs
@@ -0,0 +1,17 @@
+namespace AIStudio.Provider.OpenAI;
+
+///
+/// Represents the inner object of a nested image URL sub-content.
+///
+///
+/// This record is used when the provider expects the format:
+///
+/// { "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,..." } }
+///
+/// This class represents the inner { "url": "..." } part.
+///
+public record SubContentImageUrlData : ISubContentImageUrl
+{
+ ///
+ public string Url { get; init; } = string.Empty;
+}
diff --git a/app/MindWork AI Studio/Provider/OpenAI/SubContentImageUrlNested.cs b/app/MindWork AI Studio/Provider/OpenAI/SubContentImageUrlNested.cs
new file mode 100644
index 00000000..297a73fe
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/OpenAI/SubContentImageUrlNested.cs
@@ -0,0 +1,18 @@
+namespace AIStudio.Provider.OpenAI;
+
+///
+/// Image sub-content for multimodal messages using nested URL format.
+///
+///
+/// This record is used when the provider expects the format:
+///
+/// { "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,..." } }
+///
+/// Used by LM Studio, VLLM, and other OpenAI-compatible providers.
+///
+public record SubContentImageUrlNested(SubContentType Type, ISubContentImageUrl ImageUrl) : ISubContent
+{
+ public SubContentImageUrlNested() : this(SubContentType.IMAGE_URL, new SubContentImageUrlData())
+ {
+ }
+}
diff --git a/app/MindWork AI Studio/Provider/OpenAI/SubContentInputImage.cs b/app/MindWork AI Studio/Provider/OpenAI/SubContentInputImage.cs
new file mode 100644
index 00000000..21144952
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/OpenAI/SubContentInputImage.cs
@@ -0,0 +1,14 @@
+namespace AIStudio.Provider.OpenAI;
+
+///
+/// Image input sub-content for multimodal messages.
+///
+///
+/// Right now, this is used only by OpenAI in its responses API.
+///
+public record SubContentInputImage(SubContentType Type, string ImageUrl) : ISubContent
+{
+ public SubContentInputImage() : this(SubContentType.INPUT_IMAGE, string.Empty)
+ {
+ }
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/OpenAI/SubContentInputText.cs b/app/MindWork AI Studio/Provider/OpenAI/SubContentInputText.cs
new file mode 100644
index 00000000..30fb51d8
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/OpenAI/SubContentInputText.cs
@@ -0,0 +1,14 @@
+namespace AIStudio.Provider.OpenAI;
+
+///
+/// Text input sub-content for multimodal messages.
+///
+///
+/// Right now, this is used only by OpenAI in its responses API.
+///
+public record SubContentInputText(SubContentType Type, string Text) : ISubContent
+{
+ public SubContentInputText() : this(SubContentType.INPUT_TEXT, string.Empty)
+ {
+ }
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/OpenAI/SubContentText.cs b/app/MindWork AI Studio/Provider/OpenAI/SubContentText.cs
new file mode 100644
index 00000000..f94dd710
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/OpenAI/SubContentText.cs
@@ -0,0 +1,11 @@
+namespace AIStudio.Provider.OpenAI;
+
+///
+/// Text sub-content for multimodal messages.
+///
+public record SubContentText(SubContentType Type, string Text) : ISubContent
+{
+ public SubContentText() : this(SubContentType.TEXT, string.Empty)
+ {
+ }
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/OpenAI/TextMessage.cs b/app/MindWork AI Studio/Provider/OpenAI/TextMessage.cs
new file mode 100644
index 00000000..0e75f87f
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/OpenAI/TextMessage.cs
@@ -0,0 +1,13 @@
+namespace AIStudio.Provider.OpenAI;
+
+///
+/// Standard text-based chat message model.
+///
+/// The text content of the message.
+/// The role of the message.
+public record TextMessage(string Content, string Role) : IMessage
+{
+ public TextMessage() : this(string.Empty, string.Empty)
+ {
+ }
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/OpenRouter/OpenRouterModel.cs b/app/MindWork AI Studio/Provider/OpenRouter/OpenRouterModel.cs
new file mode 100644
index 00000000..7cd47a59
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/OpenRouter/OpenRouterModel.cs
@@ -0,0 +1,8 @@
+namespace AIStudio.Provider.OpenRouter;
+
+///
+/// A data model for an OpenRouter model from the API.
+///
+/// The model's ID.
+/// The model's human-readable display name.
+public readonly record struct OpenRouterModel(string Id, string? Name);
diff --git a/app/MindWork AI Studio/Provider/OpenRouter/OpenRouterModelsResponse.cs b/app/MindWork AI Studio/Provider/OpenRouter/OpenRouterModelsResponse.cs
new file mode 100644
index 00000000..0680c4e6
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/OpenRouter/OpenRouterModelsResponse.cs
@@ -0,0 +1,7 @@
+namespace AIStudio.Provider.OpenRouter;
+
+///
+/// A data model for the response from the OpenRouter models endpoint.
+///
+/// The list of models.
+public readonly record struct OpenRouterModelsResponse(IList Data);
diff --git a/app/MindWork AI Studio/Provider/OpenRouter/ProviderOpenRouter.cs b/app/MindWork AI Studio/Provider/OpenRouter/ProviderOpenRouter.cs
new file mode 100644
index 00000000..e627ee4c
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/OpenRouter/ProviderOpenRouter.cs
@@ -0,0 +1,197 @@
+using System.Net.Http.Headers;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Text.Json;
+
+using AIStudio.Chat;
+using AIStudio.Provider.OpenAI;
+using AIStudio.Settings;
+
+namespace AIStudio.Provider.OpenRouter;
+
+public sealed class ProviderOpenRouter() : BaseProvider(LLMProviders.OPEN_ROUTER, "https://openrouter.ai/api/v1/", LOGGER)
+{
+ private const string PROJECT_WEBSITE = "https://github.com/MindWorkAI/AI-Studio";
+ private const string PROJECT_NAME = "MindWork AI Studio";
+
+ private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger();
+
+ #region Implementation of IProvider
+
+ ///
+ public override string Id => LLMProviders.OPEN_ROUTER.ToName();
+
+ ///
+ public override string InstanceName { get; set; } = "OpenRouter";
+
+ ///
+ public override async IAsyncEnumerable StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
+ {
+ // Get the API key:
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
+ if(!requestedSecret.Success)
+ yield break;
+
+ // Prepare the system prompt:
+ var systemPrompt = new TextMessage
+ {
+ Role = "system",
+ Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
+ };
+
+ // Parse the API parameters:
+ var apiParameters = this.ParseAdditionalApiParameters();
+
+ // Build the list of messages:
+ var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
+
+ // Prepare the OpenRouter HTTP chat request:
+ var openRouterChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
+ {
+ Model = chatModel.Id,
+
+ // Build the messages:
+ // - First of all the system prompt
+ // - Then none-empty user and AI messages
+ Messages = [systemPrompt, ..messages],
+
+ // Right now, we only support streaming completions:
+ Stream = true,
+ AdditionalApiParameters = apiParameters
+ }, JSON_SERIALIZER_OPTIONS);
+
+ async Task RequestBuilder()
+ {
+ // Build the HTTP post request:
+ var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
+
+ // Set the authorization header:
+ request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
+
+ // Set custom headers for project identification:
+ request.Headers.Add("HTTP-Referer", PROJECT_WEBSITE);
+ request.Headers.Add("X-Title", PROJECT_NAME);
+
+ // Set the content:
+ request.Content = new StringContent(openRouterChatRequest, Encoding.UTF8, "application/json");
+ return request;
+ }
+
+ await foreach (var content in this.StreamChatCompletionInternal("OpenRouter", RequestBuilder, token))
+ yield return content;
+ }
+
+ #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
+ ///
+ public override async IAsyncEnumerable 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
+
+ ///
+ public override Task TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default)
+ {
+ return Task.FromResult(string.Empty);
+ }
+
+ ///
+ public override Task> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
+ {
+ return this.LoadModels(SecretStoreType.LLM_PROVIDER, token, apiKeyProvisional);
+ }
+
+ ///
+ public override Task> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default)
+ {
+ return Task.FromResult(Enumerable.Empty());
+ }
+
+ ///
+ public override Task> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default)
+ {
+ return this.LoadEmbeddingModels(token, apiKeyProvisional);
+ }
+
+ ///
+ public override Task> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default)
+ {
+ return Task.FromResult(Enumerable.Empty());
+ }
+
+ #endregion
+
+ private async Task> LoadModels(SecretStoreType storeType, CancellationToken token, string? apiKeyProvisional = null)
+ {
+ var secretKey = apiKeyProvisional switch
+ {
+ not null => apiKeyProvisional,
+ _ => await RUST_SERVICE.GetAPIKey(this, storeType) switch
+ {
+ { Success: true } result => await result.Secret.Decrypt(ENCRYPTION),
+ _ => null,
+ }
+ };
+
+ if (secretKey is null)
+ return [];
+
+ using var request = new HttpRequestMessage(HttpMethod.Get, "models");
+ request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secretKey);
+
+ // Set custom headers for project identification:
+ request.Headers.Add("HTTP-Referer", PROJECT_WEBSITE);
+ request.Headers.Add("X-Title", PROJECT_NAME);
+
+ using var response = await this.httpClient.SendAsync(request, token);
+ if(!response.IsSuccessStatusCode)
+ return [];
+
+ var modelResponse = await response.Content.ReadFromJsonAsync(token);
+
+ // Filter out non-text models (image, audio, embedding models) and convert to Model
+ return modelResponse.Data
+ .Where(n =>
+ !n.Id.Contains("whisper", StringComparison.OrdinalIgnoreCase) &&
+ !n.Id.Contains("dall-e", StringComparison.OrdinalIgnoreCase) &&
+ !n.Id.Contains("tts", StringComparison.OrdinalIgnoreCase) &&
+ !n.Id.Contains("embedding", StringComparison.OrdinalIgnoreCase) &&
+ !n.Id.Contains("moderation", StringComparison.OrdinalIgnoreCase) &&
+ !n.Id.Contains("stable-diffusion", StringComparison.OrdinalIgnoreCase) &&
+ !n.Id.Contains("flux", StringComparison.OrdinalIgnoreCase) &&
+ !n.Id.Contains("midjourney", StringComparison.OrdinalIgnoreCase))
+ .Select(n => new Model(n.Id, n.Name));
+ }
+
+ private async Task> LoadEmbeddingModels(CancellationToken token, string? apiKeyProvisional = null)
+ {
+ var secretKey = apiKeyProvisional switch
+ {
+ not null => apiKeyProvisional,
+ _ => await RUST_SERVICE.GetAPIKey(this, SecretStoreType.EMBEDDING_PROVIDER) switch
+ {
+ { Success: true } result => await result.Secret.Decrypt(ENCRYPTION),
+ _ => null,
+ }
+ };
+
+ if (secretKey is null)
+ return [];
+
+ using var request = new HttpRequestMessage(HttpMethod.Get, "embeddings/models");
+ request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secretKey);
+
+ // Set custom headers for project identification:
+ request.Headers.Add("HTTP-Referer", PROJECT_WEBSITE);
+ request.Headers.Add("X-Title", PROJECT_NAME);
+
+ using var response = await this.httpClient.SendAsync(request, token);
+ if(!response.IsSuccessStatusCode)
+ return [];
+
+ var modelResponse = await response.Content.ReadFromJsonAsync(token);
+
+ // Convert all embedding models to Model
+ return modelResponse.Data.Select(n => new Model(n.Id, n.Name));
+ }
+}
diff --git a/app/MindWork AI Studio/Provider/Perplexity/ProviderPerplexity.cs b/app/MindWork AI Studio/Provider/Perplexity/ProviderPerplexity.cs
index 3687ad7b..6ecfc69f 100644
--- a/app/MindWork AI Studio/Provider/Perplexity/ProviderPerplexity.cs
+++ b/app/MindWork AI Studio/Provider/Perplexity/ProviderPerplexity.cs
@@ -9,7 +9,7 @@ using AIStudio.Settings;
namespace AIStudio.Provider.Perplexity;
-public sealed class ProviderPerplexity() : BaseProvider("https://api.perplexity.ai/", LOGGER)
+public sealed class ProviderPerplexity() : BaseProvider(LLMProviders.PERPLEXITY, "https://api.perplexity.ai/", LOGGER)
{
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger();
@@ -34,12 +34,12 @@ public sealed class ProviderPerplexity() : BaseProvider("https://api.perplexity.
public override async IAsyncEnumerable StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
{
// Get the API key:
- var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
if(!requestedSecret.Success)
yield break;
// Prepare the system prompt:
- var systemPrompt = new Message
+ var systemPrompt = new TextMessage
{
Role = "system",
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
@@ -49,24 +49,7 @@ public sealed class ProviderPerplexity() : BaseProvider("https://api.perplexity.
var apiParameters = this.ParseAdditionalApiParameters();
// Build the list of messages:
- var messages = await chatThread.Blocks.BuildMessages(async n => new Message()
- {
- Role = n.Role switch
- {
- ChatRole.USER => "user",
- ChatRole.AI => "assistant",
- ChatRole.AGENT => "assistant",
- ChatRole.SYSTEM => "system",
-
- _ => "user",
- },
-
- Content = n.Content switch
- {
- ContentText text => await text.PrepareContentForAI(),
- _ => string.Empty,
- }
- });
+ var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
// Prepare the Perplexity HTTP chat request:
var perplexityChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
@@ -105,6 +88,12 @@ public sealed class ProviderPerplexity() : BaseProvider("https://api.perplexity.
yield break;
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
+
+ ///
+ public override Task TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default)
+ {
+ return Task.FromResult(string.Empty);
+ }
///
public override Task> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
@@ -124,6 +113,12 @@ public sealed class ProviderPerplexity() : BaseProvider("https://api.perplexity.
return Task.FromResult(Enumerable.Empty());
}
+ ///
+ public override Task> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default)
+ {
+ return Task.FromResult(Enumerable.Empty());
+ }
+
#endregion
private Task> LoadModels() => Task.FromResult>(KNOWN_MODELS);
diff --git a/app/MindWork AI Studio/Provider/SelfHosted/ChatRequest.cs b/app/MindWork AI Studio/Provider/SelfHosted/ChatRequest.cs
index 4791692c..e1da56bd 100644
--- a/app/MindWork AI Studio/Provider/SelfHosted/ChatRequest.cs
+++ b/app/MindWork AI Studio/Provider/SelfHosted/ChatRequest.cs
@@ -10,7 +10,7 @@ namespace AIStudio.Provider.SelfHosted;
/// Whether to stream the chat completion.
public readonly record struct ChatRequest(
string Model,
- IList Messages,
+ IList Messages,
bool Stream
)
{
diff --git a/app/MindWork AI Studio/Provider/SelfHosted/Host.cs b/app/MindWork AI Studio/Provider/SelfHosted/Host.cs
index d922ccd5..d0dde806 100644
--- a/app/MindWork AI Studio/Provider/SelfHosted/Host.cs
+++ b/app/MindWork AI Studio/Provider/SelfHosted/Host.cs
@@ -5,7 +5,8 @@ public enum Host
NONE,
LM_STUDIO,
- LLAMACPP,
+ LLAMA_CPP,
+ WHISPER_CPP,
OLLAMA,
VLLM,
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/SelfHosted/HostExtensions.cs b/app/MindWork AI Studio/Provider/SelfHosted/HostExtensions.cs
index 3478d9e5..6c475273 100644
--- a/app/MindWork AI Studio/Provider/SelfHosted/HostExtensions.cs
+++ b/app/MindWork AI Studio/Provider/SelfHosted/HostExtensions.cs
@@ -7,7 +7,8 @@ public static class HostExtensions
Host.NONE => "None",
Host.LM_STUDIO => "LM Studio",
- Host.LLAMACPP => "llama.cpp",
+ Host.LLAMA_CPP => "llama.cpp",
+ Host.WHISPER_CPP => "whisper.cpp",
Host.OLLAMA => "ollama",
Host.VLLM => "vLLM",
@@ -24,7 +25,28 @@ public static class HostExtensions
_ => "chat/completions",
};
- public static bool AreEmbeddingsSupported(this Host host)
+ public static string TranscriptionURL(this Host host) => host switch
+ {
+ _ => "audio/transcriptions",
+ };
+
+ public static bool IsChatSupported(this Host host)
+ {
+ switch (host)
+ {
+ case Host.WHISPER_CPP:
+ return false;
+
+ default:
+ case Host.OLLAMA:
+ case Host.VLLM:
+ case Host.LM_STUDIO:
+ case Host.LLAMA_CPP:
+ return true;
+ }
+ }
+
+ public static bool IsEmbeddingSupported(this Host host)
{
switch (host)
{
@@ -34,7 +56,23 @@ public static class HostExtensions
return true;
default:
- case Host.LLAMACPP:
+ case Host.LLAMA_CPP:
+ return false;
+ }
+ }
+
+ public static bool IsTranscriptionSupported(this Host host)
+ {
+ switch (host)
+ {
+ case Host.OLLAMA:
+ case Host.VLLM:
+ case Host.WHISPER_CPP:
+ return true;
+
+ default:
+ case Host.LM_STUDIO:
+ case Host.LLAMA_CPP:
return false;
}
}
diff --git a/app/MindWork AI Studio/Provider/SelfHosted/Message.cs b/app/MindWork AI Studio/Provider/SelfHosted/Message.cs
deleted file mode 100644
index e4ecc70a..00000000
--- a/app/MindWork AI Studio/Provider/SelfHosted/Message.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace AIStudio.Provider.SelfHosted;
-
-///
-/// Chat message model.
-///
-/// The text content of the message.
-/// The role of the message.
-public readonly record struct Message(string Content, string Role);
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/SelfHosted/ProviderSelfHosted.cs b/app/MindWork AI Studio/Provider/SelfHosted/ProviderSelfHosted.cs
index 4389099e..d63ec0d1 100644
--- a/app/MindWork AI Studio/Provider/SelfHosted/ProviderSelfHosted.cs
+++ b/app/MindWork AI Studio/Provider/SelfHosted/ProviderSelfHosted.cs
@@ -6,12 +6,15 @@ using System.Text.Json;
using AIStudio.Chat;
using AIStudio.Provider.OpenAI;
using AIStudio.Settings;
+using AIStudio.Tools.PluginSystem;
namespace AIStudio.Provider.SelfHosted;
-public sealed class ProviderSelfHosted(Host host, string hostname) : BaseProvider($"{hostname}{host.BaseURL()}", LOGGER)
+public sealed class ProviderSelfHosted(Host host, string hostname) : BaseProvider(LLMProviders.SELF_HOSTED, $"{hostname}{host.BaseURL()}", LOGGER)
{
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger();
+
+ private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ProviderSelfHosted).Namespace, nameof(ProviderSelfHosted));
#region Implementation of IProvider
@@ -23,10 +26,10 @@ public sealed class ProviderSelfHosted(Host host, string hostname) : BaseProvide
public override async IAsyncEnumerable StreamChatCompletion(Provider.Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
{
// Get the API key:
- var requestedSecret = await RUST_SERVICE.GetAPIKey(this, isTrying: true);
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER, isTrying: true);
// Prepare the system prompt:
- var systemPrompt = new Message
+ var systemPrompt = new TextMessage
{
Role = "system",
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
@@ -34,26 +37,15 @@ public sealed class ProviderSelfHosted(Host host, string hostname) : BaseProvide
// Parse the API parameters:
var apiParameters = this.ParseAdditionalApiParameters();
-
- // Build the list of messages:
- var messages = await chatThread.Blocks.BuildMessages(async n => new Message
+
+ // Build the list of messages. The image format depends on the host:
+ // - Ollama uses the direct image URL format: { "type": "image_url", "image_url": "data:..." }
+ // - LM Studio, vLLM, and llama.cpp use the nested image URL format: { "type": "image_url", "image_url": { "url": "data:..." } }
+ var messages = host switch
{
- Role = n.Role switch
- {
- ChatRole.USER => "user",
- ChatRole.AI => "assistant",
- ChatRole.AGENT => "assistant",
- ChatRole.SYSTEM => "system",
-
- _ => "user",
- },
-
- Content = n.Content switch
- {
- ContentText text => await text.PrepareContentForAI(),
- _ => string.Empty,
- }
- });
+ Host.OLLAMA => await chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel),
+ _ => await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel),
+ };
// Prepare the OpenAI HTTP chat request:
var providerChatRequest = JsonSerializer.Serialize(new ChatRequest
@@ -96,13 +88,20 @@ public sealed class ProviderSelfHosted(Host host, string hostname) : BaseProvide
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
+ ///
+ public override async Task TranscribeAudioAsync(Provider.Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default)
+ {
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.TRANSCRIPTION_PROVIDER, isTrying: true);
+ return await this.PerformStandardTranscriptionRequest(requestedSecret, transcriptionModel, audioFilePath, host, token);
+ }
+
public override async Task> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
{
try
{
switch (host)
{
- case Host.LLAMACPP:
+ case Host.LLAMA_CPP:
// Right now, llama.cpp only supports one model.
// There is no API to list the model(s).
return [ new Provider.Model("as configured by llama.cpp", null) ];
@@ -110,7 +109,7 @@ public sealed class ProviderSelfHosted(Host host, string hostname) : BaseProvide
case Host.LM_STUDIO:
case Host.OLLAMA:
case Host.VLLM:
- return await this.LoadModels(["embed"], [], token, apiKeyProvisional);
+ return await this.LoadModels( SecretStoreType.LLM_PROVIDER, ["embed"], [], token, apiKeyProvisional);
}
return [];
@@ -137,7 +136,7 @@ public sealed class ProviderSelfHosted(Host host, string hostname) : BaseProvide
case Host.LM_STUDIO:
case Host.OLLAMA:
case Host.VLLM:
- return await this.LoadModels([], ["embed"], token, apiKeyProvisional);
+ return await this.LoadModels( SecretStoreType.EMBEDDING_PROVIDER, [], ["embed"], token, apiKeyProvisional);
}
return [];
@@ -149,14 +148,43 @@ public sealed class ProviderSelfHosted(Host host, string hostname) : BaseProvide
}
}
+ ///
+ public override Task> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default)
+ {
+ try
+ {
+ switch (host)
+ {
+ case Host.WHISPER_CPP:
+ return Task.FromResult>(
+ new List
+ {
+ new("loaded-model", TB("Model as configured by whisper.cpp")),
+ });
+
+ case Host.OLLAMA:
+ case Host.VLLM:
+ return this.LoadModels(SecretStoreType.TRANSCRIPTION_PROVIDER, [], [], token, apiKeyProvisional);
+
+ default:
+ return Task.FromResult(Enumerable.Empty());
+ }
+ }
+ catch (Exception e)
+ {
+ LOGGER.LogError(e, "Failed to load transcription models from self-hosted provider.");
+ return Task.FromResult(Enumerable.Empty());
+ }
+ }
+
#endregion
- private async Task> LoadModels(string[] ignorePhrases, string[] filterPhrases, CancellationToken token, string? apiKeyProvisional = null)
+ private async Task> LoadModels(SecretStoreType storeType, string[] ignorePhrases, string[] filterPhrases, CancellationToken token, string? apiKeyProvisional = null)
{
var secretKey = apiKeyProvisional switch
{
not null => apiKeyProvisional,
- _ => await RUST_SERVICE.GetAPIKey(this, isTrying: true) switch
+ _ => await RUST_SERVICE.GetAPIKey(this, storeType, isTrying: true) switch
{
{ Success: true } result => await result.Secret.Decrypt(ENCRYPTION),
_ => null,
diff --git a/app/MindWork AI Studio/Provider/SubContentConverter.cs b/app/MindWork AI Studio/Provider/SubContentConverter.cs
new file mode 100644
index 00000000..cd257a3d
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/SubContentConverter.cs
@@ -0,0 +1,34 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+using AIStudio.Provider.OpenAI;
+
+namespace AIStudio.Provider;
+
+///
+/// Custom JSON converter for the ISubContent interface to handle polymorphic serialization.
+///
+///
+/// This converter ensures that when serializing ISubContent objects, all properties
+/// of the concrete implementation (e.g., SubContentText, SubContentImageUrl) are serialized,
+/// not just the properties defined in the ISubContent interface.
+///
+public sealed class SubContentConverter : JsonConverter
+{
+ private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger();
+
+ public override ISubContent? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ // Deserialization is not needed for request objects, as sub-content is only serialized
+ // when sending requests to LLM providers.
+ LOGGER.LogError("Deserializing ISubContent is not supported. This converter is only used for serializing request messages.");
+ return null;
+ }
+
+ public override void Write(Utf8JsonWriter writer, ISubContent value, JsonSerializerOptions options)
+ {
+ // Serialize the actual concrete type (e.g., SubContentText, SubContentImageUrl) instead of just the ISubContent interface.
+ // This ensures all properties of the concrete type are included in the JSON output.
+ JsonSerializer.Serialize(writer, value, value.GetType(), options);
+ }
+}
diff --git a/app/MindWork AI Studio/Provider/SubContentImageUrlConverter.cs b/app/MindWork AI Studio/Provider/SubContentImageUrlConverter.cs
new file mode 100644
index 00000000..d6df6878
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/SubContentImageUrlConverter.cs
@@ -0,0 +1,34 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+using AIStudio.Provider.OpenAI;
+
+namespace AIStudio.Provider;
+
+///
+/// Custom JSON converter for the ISubContentImageUrl interface to handle polymorphic serialization.
+///
+///
+/// This converter ensures that when serializing ISubContentImageUrl objects, all properties
+/// of the concrete implementation (e.g., SubContentImageUrlData) are serialized,
+/// not just the properties defined in the ISubContentImageUrl interface.
+///
+public sealed class SubContentImageUrlConverter : JsonConverter
+{
+ private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger();
+
+ public override ISubContentImageUrl? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ // Deserialization is not needed for request objects, as sub-content image URLs are only serialized
+ // when sending requests to LLM providers.
+ LOGGER.LogError("Deserializing ISubContentImageUrl is not supported. This converter is only used for serializing request messages.");
+ return null;
+ }
+
+ public override void Write(Utf8JsonWriter writer, ISubContentImageUrl value, JsonSerializerOptions options)
+ {
+ // Serialize the actual concrete type (e.g., SubContentImageUrlData) instead of just the ISubContentImageUrl interface.
+ // This ensures all properties of the concrete type are included in the JSON output.
+ JsonSerializer.Serialize(writer, value, value.GetType(), options);
+ }
+}
diff --git a/app/MindWork AI Studio/Provider/SubContentType.cs b/app/MindWork AI Studio/Provider/SubContentType.cs
new file mode 100644
index 00000000..cca2d802
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/SubContentType.cs
@@ -0,0 +1,39 @@
+namespace AIStudio.Provider;
+
+///
+/// Sub content types for OpenAI-compatible API interactions when using multimodal messages.
+///
+public enum SubContentType
+{
+ ///
+ /// Default type for user prompts in multimodal messages. This type is supported across all providers.
+ ///
+ TEXT,
+
+ ///
+ /// Right now only supported by OpenAI and it's responses API. Even other providers that support multimodal messages
+ /// and the responses API do not support this type. They use TEXT instead.
+ ///
+ INPUT_TEXT,
+
+ ///
+ /// Right now only supported by OpenAI and it's responses API. Even other providers that support multimodal messages
+ /// and the responses API do not support this type. They use IMAGE_URL instead.
+ ///
+ INPUT_IMAGE,
+
+ ///
+ /// Default type for images in multimodal messages. This type is supported across all providers.
+ ///
+ IMAGE_URL,
+
+ ///
+ /// The image type is used exclusively by Anthropic's messages API.
+ ///
+ IMAGE,
+
+ ///
+ /// Right now only supported by OpenAI (responses & chat completion API), Google (chat completions API), and Mistral (chat completions API).
+ ///
+ INPUT_AUDIO,
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Provider/TranscriptionResponse.cs b/app/MindWork AI Studio/Provider/TranscriptionResponse.cs
new file mode 100644
index 00000000..7ba1f587
--- /dev/null
+++ b/app/MindWork AI Studio/Provider/TranscriptionResponse.cs
@@ -0,0 +1,3 @@
+namespace AIStudio.Provider;
+
+public sealed record TranscriptionResponse(string Text);
diff --git a/app/MindWork AI Studio/Provider/X/ProviderX.cs b/app/MindWork AI Studio/Provider/X/ProviderX.cs
index e8a0b2e7..82d92c2a 100644
--- a/app/MindWork AI Studio/Provider/X/ProviderX.cs
+++ b/app/MindWork AI Studio/Provider/X/ProviderX.cs
@@ -9,7 +9,7 @@ using AIStudio.Settings;
namespace AIStudio.Provider.X;
-public sealed class ProviderX() : BaseProvider("https://api.x.ai/v1/", LOGGER)
+public sealed class ProviderX() : BaseProvider(LLMProviders.X, "https://api.x.ai/v1/", LOGGER)
{
private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger();
@@ -25,12 +25,12 @@ public sealed class ProviderX() : BaseProvider("https://api.x.ai/v1/", LOGGER)
public override async IAsyncEnumerable StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
{
// Get the API key:
- var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
+ var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
if(!requestedSecret.Success)
yield break;
// Prepare the system prompt:
- var systemPrompt = new Message
+ var systemPrompt = new TextMessage
{
Role = "system",
Content = chatThread.PrepareSystemPrompt(settingsManager, chatThread),
@@ -40,24 +40,7 @@ public sealed class ProviderX() : BaseProvider("https://api.x.ai/v1/", LOGGER)
var apiParameters = this.ParseAdditionalApiParameters();
// Build the list of messages:
- var messages = await chatThread.Blocks.BuildMessages(async n => new Message()
- {
- Role = n.Role switch
- {
- ChatRole.USER => "user",
- ChatRole.AI => "assistant",
- ChatRole.AGENT => "assistant",
- ChatRole.SYSTEM => "system",
-
- _ => "user",
- },
-
- Content = n.Content switch
- {
- ContentText text => await text.PrepareContentForAI(),
- _ => string.Empty,
- }
- });
+ var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
// Prepare the xAI HTTP chat request:
var xChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
@@ -98,11 +81,17 @@ public sealed class ProviderX() : BaseProvider("https://api.x.ai/v1/", LOGGER)
yield break;
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
-
+
+ ///
+ public override Task TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default)
+ {
+ return Task.FromResult(string.Empty);
+ }
+
///
public override async Task> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
{
- var models = await this.LoadModels(["grok-"], token, apiKeyProvisional);
+ var models = await this.LoadModels(SecretStoreType.LLM_PROVIDER, ["grok-"], token, apiKeyProvisional);
return models.Where(n => !n.Id.Contains("-image", StringComparison.OrdinalIgnoreCase));
}
@@ -118,14 +107,20 @@ public sealed class ProviderX() : BaseProvider("https://api.x.ai/v1/", LOGGER)
return Task.FromResult>([]);
}
+ ///
+ public override Task> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default)
+ {
+ return Task.FromResult(Enumerable.Empty());
+ }
+
#endregion
- private async Task> LoadModels(string[] prefixes, CancellationToken token, string? apiKeyProvisional = null)
+ private async Task> LoadModels(SecretStoreType storeType, string[] prefixes, CancellationToken token, string? apiKeyProvisional = null)
{
var secretKey = apiKeyProvisional switch
{
not null => apiKeyProvisional,
- _ => await RUST_SERVICE.GetAPIKey(this) switch
+ _ => await RUST_SERVICE.GetAPIKey(this, storeType) switch
{
{ Success: true } result => await result.Secret.Decrypt(ENCRYPTION),
_ => null,
diff --git a/app/MindWork AI Studio/Settings/ChatTemplate.cs b/app/MindWork AI Studio/Settings/ChatTemplate.cs
index 38f3623d..5f3c597a 100644
--- a/app/MindWork AI Studio/Settings/ChatTemplate.cs
+++ b/app/MindWork AI Studio/Settings/ChatTemplate.cs
@@ -12,11 +12,12 @@ public record ChatTemplate(
string SystemPrompt,
string PredefinedUserPrompt,
List ExampleConversation,
+ List FileAttachments,
bool AllowProfileUsage,
bool IsEnterpriseConfiguration = false,
Guid EnterpriseConfigurationPluginId = default) : ConfigurationBaseObject
{
- public ChatTemplate() : this(0, Guid.Empty.ToString(), string.Empty, string.Empty, string.Empty, [], false)
+ public ChatTemplate() : this(0, Guid.Empty.ToString(), string.Empty, string.Empty, string.Empty, [], [], false)
{
}
@@ -26,12 +27,13 @@ public record ChatTemplate(
public static readonly ChatTemplate NO_CHAT_TEMPLATE = new()
{
- Name = TB("Use no chat template"),
+ Name = TB("Use no chat template"), // Cannot be localized due to being a static readonly field
SystemPrompt = string.Empty,
PredefinedUserPrompt = string.Empty,
Id = Guid.Empty.ToString(),
Num = uint.MaxValue,
ExampleConversation = [],
+ FileAttachments = [],
AllowProfileUsage = true,
EnterpriseConfigurationPluginId = Guid.Empty,
IsEnterpriseConfiguration = false,
@@ -43,9 +45,26 @@ public record ChatTemplate(
/// Returns a string that represents the profile in a human-readable format.
///