From 2ad591f117917b8d445cfbb1bd909ca26b21b050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peer=20Sch=C3=BCtt?= <20603780+peerschuett@users.noreply.github.com> Date: Thu, 11 Jun 2026 16:25:26 +0200 Subject: [PATCH] Fixed some bugs. --- .../Assistants/AssistantBase.razor.cs | 13 +- .../Assistants/I18N/allTexts.lua | 25 +- .../Components/ChatComponent.razor.cs | 58 ++-- .../Settings/SettingsPanelProviders.razor.cs | 1 + .../Settings/SettingsPanelTools.razor | 4 +- .../Components/ToolSelection.razor | 3 + .../Components/ToolSelection.razor.cs | 14 +- .../plugin.lua | 66 +++-- .../plugin.lua | 266 +++++++++++++++++- .../Provider/BaseProvider.cs | 55 ++-- .../Provider/OpenAI/ProviderOpenAI.cs | 61 ++-- .../Settings/ManagedConfiguration.Parsing.cs | 77 ++++- .../Settings/ManagedConfiguration.cs | 18 +- .../Settings/SettingsManager.cs | 61 +++- .../Tools/ExternalHttpClientTimeout.cs | 37 ++- app/MindWork AI Studio/Tools/HTMLParser.cs | 57 ++-- .../ReadWebPageTool.cs | 2 +- .../SearXNGWebSearchTool.cs | 9 +- .../Tools/ToolCallingSystem/ToolExecutor.cs | 2 +- .../Tools/ToolCallingSystem/ToolRegistry.cs | 33 ++- .../ToolCallingSystem/ToolSelectionRules.cs | 2 +- 21 files changed, 675 insertions(+), 189 deletions(-) diff --git a/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs b/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs index baed2afc..4fe926db 100644 --- a/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs +++ b/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs @@ -194,6 +194,10 @@ public abstract partial class AssistantBase : AssistantLowerBase wher private async Task Start() { + await this.RefreshProviderSelectionFromConfigurationAsync(); + if (this.ProviderSettings == Settings.Provider.NONE) + return; + using (this.CancellationTokenSource = new()) { await this.SubmitAction(); @@ -275,11 +279,18 @@ public abstract partial class AssistantBase : AssistantLowerBase wher return chatId; } + private Task RefreshProviderSelectionFromConfigurationAsync() + { + this.ProviderSettings = this.SettingsManager.GetPreselectedProvider(this.Component, this.ProviderSettings.Id); + return Task.CompletedTask; + } + protected virtual void ResetProviderAndProfileSelection() { this.ProviderSettings = this.SettingsManager.GetPreselectedProvider(this.Component); this.CurrentProfile = this.SettingsManager.GetPreselectedProfile(this.Component); this.CurrentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(this.Component); + this.selectedToolIds = this.SettingsManager.GetDefaultToolIds(this.Component); } protected Task SelectedToolIdsChanged(HashSet updatedToolIds) @@ -336,7 +347,7 @@ public abstract partial class AssistantBase : AssistantLowerBase wher this.ChatThread.SelectedProvider = this.ProviderSettings.Id; this.ChatThread.RuntimeComponent = this.Component; this.ChatThread.RuntimeSelectedToolIds = this.SettingsManager.IsToolSelectionVisible(this.Component) - ? ToolSelectionRules.NormalizeSelection(this.selectedToolIds) + ? this.SettingsManager.FilterToolIdsForProvider(this.ProviderSettings, this.selectedToolIds) : []; } diff --git a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua index 42845fe1..202f179a 100644 --- a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua +++ b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua @@ -3031,9 +3031,6 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T1725856265 -- Icon UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T1759955728"] = "Icon" --- Configure global settings for each tool. Tool defaults for chat and assistants are configured in the corresponding feature settings. -UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T176751696"] = "Configure global settings for each tool. Tool defaults for chat and assistants are configured in the corresponding feature settings." - -- This tool still needs to be configured. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T1958939818"] = "This tool still needs to be configured." @@ -3049,11 +3046,14 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T2828607242 -- Minimum provider confidence UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3461070436"] = "Minimum provider confidence" +-- Configure global settings for each tool. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3728248397"] = "Configure global settings for each tool." + -- Tool Settings UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3730473128"] = "Tool Settings" --- State -UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T502047894"] = "State" +-- Status +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T6222351"] = "Status" -- No transcription provider configured yet. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T1079350363"] = "No transcription provider configured yet." @@ -3160,21 +3160,18 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T1351725609"] = "This tool -- Tool changes are locked while a response is running. Your current selection is shown below and applies again from the next message once the run is finished. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T1688023907"] = "Tool changes are locked while a response is running. Your current selection is shown below and applies again from the next message once the run is finished." +-- Tools allow the LLM to perform targeted additional actions such as web searches or reading web pages. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T1944689297"] = "Tools allow the LLM to perform targeted additional actions such as web searches or reading web pages." + -- Enabling this tool also enables Read Web Page. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3023833839"] = "Enabling this tool also enables Read Web Page." -- Required settings are missing. Configure this tool before enabling it. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3119156561"] = "Required settings are missing. Configure this tool before enabling it." --- The selected provider or model does not support tool calling. -UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3364063757"] = "The selected provider or model does not support tool calling." - -- Close UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3448155331"] = "Close" --- Tool calling for this provider is not implemented yet. -UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3776963202"] = "Tool calling for this provider is not implemented yet." - -- No tools are available in this context. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3904490680"] = "No tools are available in this context." @@ -8008,9 +8005,6 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::ITOOLIMPLEMENTATION::T40564 -- Load a single web page and extract its main HTML content. UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T204256540"] = "Load a single web page and extract its main HTML content." --- Optional global truncation limit for extracted Markdown returned to the model. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T2066580916"] = "Optional global truncation limit for extracted Markdown returned to the model." - -- Allowed private hosts must be host names only, without scheme or path. UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T2196457612"] = "Allowed private hosts must be host names only, without scheme or path." @@ -8032,6 +8026,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS: -- Read Web Page UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T3612587998"] = "Read Web Page" +-- Optional global truncation limit for extracted characters returned to the model. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T364016543"] = "Optional global truncation limit for extracted characters returned to the model." + -- The web page was not loaded because private or VPN web pages require a High-confidence provider. UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T3856267430"] = "The web page was not loaded because private or VPN web pages require a High-confidence provider." diff --git a/app/MindWork AI Studio/Components/ChatComponent.razor.cs b/app/MindWork AI Studio/Components/ChatComponent.razor.cs index 62edb472..b5a7ad6f 100644 --- a/app/MindWork AI Studio/Components/ChatComponent.razor.cs +++ b/app/MindWork AI Studio/Components/ChatComponent.razor.cs @@ -78,7 +78,6 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable private Guid loadedParameterWorkspaceId = Guid.Empty; private Guid foregroundChatId = Guid.Empty; private int workspaceHeaderSyncVersion; - private CancellationTokenSource? cancellationTokenSource; // Unfortunately, we need the input field reference to blur the focus away. Without // this, we cannot clear the input field. @@ -104,7 +103,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable protected override async Task OnInitializedAsync() { // Apply the filters for the message bus: - this.ApplyFilters([], [ Event.HAS_CHAT_UNSAVED_CHANGES, Event.RESET_CHAT_STATE, Event.CHAT_STREAMING_DONE, Event.AI_JOB_CHANGED, Event.AI_JOB_FINISHED, Event.CHAT_GENERATION_CHANGED ]); + this.ApplyFilters([], [ Event.HAS_CHAT_UNSAVED_CHANGES, Event.RESET_CHAT_STATE, Event.CHAT_STREAMING_DONE, Event.AI_JOB_CHANGED, Event.AI_JOB_FINISHED, Event.CHAT_GENERATION_CHANGED, Event.CONFIGURATION_CHANGED ]); // Configure the spellchecking for the user input: this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES); @@ -577,6 +576,8 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable private async Task SendMessage(bool reuseLastUserPrompt = false) { + await this.RefreshProviderSelectionFromConfigurationAsync(); + if (!this.IsProviderSelected) return; @@ -695,34 +696,17 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable } this.Logger.LogDebug($"Start processing user input using provider '{this.Provider.InstanceName}' with model '{this.Provider.Model}'."); - // TODO: await this.AIJobService.TryStartChatGenerationAsync(new ChatGenerationRequest - //{ - // ChatThread = this.ChatThread!, - // AIText = aiText, - // LastUserPrompt = lastUserPrompt, - // ProviderSettings = this.Provider, - // IsForeground = true, - //}); - using (this.cancellationTokenSource = new CancellationTokenSource()) + this.StateHasChanged(); + this.ChatThread!.RuntimeComponent = Tools.Components.CHAT; + this.ChatThread.RuntimeSelectedToolIds = this.SettingsManager.FilterToolIdsForProvider(this.Provider, this.selectedToolIds); + await this.AIJobService.TryStartChatGenerationAsync(new ChatGenerationRequest { - this.StateHasChanged(); - this.ChatThread!.RuntimeComponent = Tools.Components.CHAT; - this.ChatThread.RuntimeSelectedToolIds = ToolSelectionRules.NormalizeSelection(this.selectedToolIds); - - // Use the selected provider to get the AI response. - // By awaiting this line, we wait for the entire - // content to be streamed. - this.ChatThread = await aiText.CreateFromProviderAsync(this.Provider.CreateProvider(), this.Provider.Model, lastUserPrompt, this.ChatThread, this.cancellationTokenSource.Token); - } - - this.cancellationTokenSource = null; - - // Save the chat: - if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY) - { - await this.SaveThread(); - } - + ChatThread = this.ChatThread, + AIText = aiText, + LastUserPrompt = lastUserPrompt, + ProviderSettings = this.Provider, + IsForeground = true, + }); await this.SyncForegroundChatAsync(); this.StateHasChanged(); } @@ -801,7 +785,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable // this.hasUnsavedChanges = false; this.ComposerState.Clear(); - this.selectedToolIds = this.SettingsManager.GetDefaultToolIds(Tools.Components.CHAT); + this.selectedToolIds = ToolSelectionRules.NormalizeSelection(this.SettingsManager.GetDefaultToolIds(Tools.Components.CHAT)); // // Reset the LLM provider considering the user's settings: @@ -948,6 +932,19 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable this.StateHasChanged(); } + + private async Task RefreshProviderSelectionFromConfigurationAsync() + { + var updatedProvider = this.SettingsManager.GetPreselectedProvider(Tools.Components.CHAT, this.Provider.Id); + var providerChanged = updatedProvider != this.Provider; + if (providerChanged) + this.Provider = updatedProvider; + + if (!providerChanged) + return; + + await this.ProviderChanged.InvokeAsync(this.Provider); + } private async Task ResetState() { @@ -1091,6 +1088,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable break; case Event.CONFIGURATION_CHANGED: + await this.RefreshProviderSelectionFromConfigurationAsync(); await this.InvokeAsync(this.StateHasChanged); break; } diff --git a/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor.cs b/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor.cs index 500a4c2d..e8a30f89 100644 --- a/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor.cs +++ b/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor.cs @@ -186,5 +186,6 @@ public partial class SettingsPanelProviders : SettingsPanelProviderBase { this.SettingsManager.ConfigurationData.LLMProviders.CustomConfidenceScheme[llmProvider] = level; await this.SettingsManager.StoreSettings(); + await this.MessageBus.SendMessage(this, Event.CONFIGURATION_CHANGED); } } diff --git a/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor b/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor index 238b0133..a7791799 100644 --- a/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor +++ b/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor @@ -4,7 +4,7 @@ - @T("Configure global settings for each tool. Tool defaults for chat and assistants are configured in the corresponding feature settings.") + @T("Configure global settings for each tool.") @@ -13,7 +13,7 @@ @T("Name") @T("Description") @T("Minimum provider confidence") - @T("State") + @T("Status") @T("Settings") diff --git a/app/MindWork AI Studio/Components/ToolSelection.razor b/app/MindWork AI Studio/Components/ToolSelection.razor index ef547703..557468ab 100644 --- a/app/MindWork AI Studio/Components/ToolSelection.razor +++ b/app/MindWork AI Studio/Components/ToolSelection.razor @@ -18,6 +18,9 @@ + + @T("Tools allow the LLM to perform targeted additional actions such as web searches or reading web pages.") + @if (!this.SupportsTools) { @this.UnsupportedToolsMessage diff --git a/app/MindWork AI Studio/Components/ToolSelection.razor.cs b/app/MindWork AI Studio/Components/ToolSelection.razor.cs index 27f4ced5..f65eeb5d 100644 --- a/app/MindWork AI Studio/Components/ToolSelection.razor.cs +++ b/app/MindWork AI Studio/Components/ToolSelection.razor.cs @@ -1,7 +1,6 @@ using AIStudio.Dialogs.Settings; using AIStudio.Provider; using AIStudio.Settings; -using AIStudio.Tools; using AIStudio.Tools.ToolCallingSystem; using Microsoft.AspNetCore.Components; @@ -49,22 +48,15 @@ public partial class ToolSelection : MSGComponentBase await base.OnInitializedAsync(); } - private bool SupportsTools => - this.LLMProvider != AIStudio.Settings.Provider.NONE && - (this.LLMProvider.GetModelCapabilities().Contains(Capability.CHAT_COMPLETION_API) || - this.LLMProvider.GetModelCapabilities().Contains(Capability.RESPONSES_API)) && - this.LLMProvider.GetModelCapabilities().Contains(Capability.FUNCTION_CALLING); + private ToolCallingAvailability ToolCallingAvailability => this.LLMProvider.GetToolCallingAvailability(); - private bool IsAnthropicProvider => this.LLMProvider != AIStudio.Settings.Provider.NONE && - this.LLMProvider.UsedLLMProvider is LLMProviders.ANTHROPIC; + private bool SupportsTools => this.ToolCallingAvailability.IsAvailable; private string ToolButtonTooltip => this.SupportsTools ? this.T("Select tools") : this.UnsupportedToolsMessage; - private string UnsupportedToolsMessage => this.IsAnthropicProvider - ? this.T("Tool calling for this provider is not implemented yet.") - : this.T("The selected model does not support tool calling."); + private string UnsupportedToolsMessage => this.ToolCallingAvailability.Message; private ConfidenceLevel ProviderConfidence => this.LLMProvider == AIStudio.Settings.Provider.NONE ? ConfidenceLevel.NONE 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 f10e818f..25294a08 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 @@ -1927,7 +1927,7 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1350385882"] = "Ja, ent UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1434043348"] = "Fehlgeschlagen" -- Tool Calls ({0}) -UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1493057571"] = "Tool-Aufrufe" +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1493057571"] = "Werkzeugaufrufe" -- Executed UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1564757972"] = "Ausgeführt" @@ -1945,10 +1945,10 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1820166585"] = "Ja, ent UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1848978959"] = "Anzahl der Quellen" -- Show {0} tool calls -UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1981771421"] = "{0} Toolaufrufe anzeigen" +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1981771421"] = "{0} Werkzeugaufrufe anzeigen" -- Show tool call for {0} -UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T2004842583"] = "Tool-Aufruf für {0}" +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T2004842583"] = "Werkzeugaufruf für {0}" -- Do you really want to edit this message? In order to edit this message, the AI response will be deleted. UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T2018431076"] = "Möchten Sie diese Nachricht wirklich bearbeiten? Um die Nachricht zu bearbeiten, wird die Antwort der KI gelöscht." @@ -1995,6 +1995,9 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4224149521"] = "Verstan -- Export Chat to Microsoft Word UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T861873672"] = "Chat in Microsoft Word exportieren" +-- No arguments +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T931993614"] = "Keine Argumente" + -- The selected model '{0}' is no longer available from '{1}' (provider={2}). Please adapt your provider settings. UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTTEXT::T3267850764"] = "Das ausgewählte Modell '{0}' ist bei '{1}' (Anbieter={2}) nicht mehr verfügbar. Bitte passen Sie Ihre Anbietereinstellungen an." @@ -3030,9 +3033,6 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T1725856265 -- Icon UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T1759955728"] = "Symbol" --- Configure global settings for each tool. Tool defaults for chat and assistants are configured in the corresponding feature settings. -UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T176751696"] = "Globale Einstellungen für jedes Tool konfigurieren. Standardwerte für Tools für Chats und Assistenten werden in den entsprechenden Funktionseinstellungen konfiguriert." - -- This tool still needs to be configured. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T1958939818"] = "Dieses Werkzeug muss noch konfiguriert werden." @@ -3048,11 +3048,14 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T2828607242 -- Minimum provider confidence UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3461070436"] = "Minimale Anbieterzuverlässigkeit" +-- Configure global settings for each tool. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3728248397"] = "Konfiguriere globale Einstellungen für jedes Werkzeug." + -- Tool Settings UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3730473128"] = "Werkzeugeinstellungen" --- State -UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T502047894"] = "Status" +-- Status +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T6222351"] = "Status" -- No transcription provider configured yet. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T1079350363"] = "Es ist bisher kein Anbieter für Transkriptionen konfiguriert." @@ -3145,7 +3148,7 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLDEFAULTSCONFIGURATION::T3494508870"] UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLDEFAULTSCONFIGURATION::T3729156356"] = "Sie haben {0} Werkzeuge ausgewählt." -- No tools selected. -UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLDEFAULTSCONFIGURATION::T3934845540"] = "Keine Tools ausgewählt." +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLDEFAULTSCONFIGURATION::T3934845540"] = "Keine Werkzeuge ausgewählt." -- Default tools for chat UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLDEFAULTSCONFIGURATION::T907403808"] = "Standardwerkzeuge für den Chat" @@ -3159,14 +3162,14 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T1351725609"] = "Dieses We -- Tool changes are locked while a response is running. Your current selection is shown below and applies again from the next message once the run is finished. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T1688023907"] = "Werkzeugänderungen sind gesperrt, während eine Antwort ausgeführt wird. Ihre aktuelle Auswahl wird unten angezeigt und gilt nach Abschluss der Ausführung ab der nächsten Nachricht wieder." +-- Tools allow the LLM to perform targeted additional actions such as web searches or reading web pages. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T1944689297"] = "Werkzeuge ermöglichen es dem LLM, gezielte zusätzliche Aktionen auszuführen, wie z. B. Websuchen oder das Lesen von Webseiten." + -- Enabling this tool also enables Read Web Page. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3023833839"] = "Das Aktivieren dieses Werkzeugs aktiviert auch „Webseite lesen“." -- Required settings are missing. Configure this tool before enabling it. -UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3119156561"] = "Erforderliche Einstellungen fehlen. Konfigurieren Sie dieses Tool, bevor Sie es aktivieren." - --- The selected provider or model does not support tool calling. -UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3364063757"] = "Der ausgewählte Anbieter oder das ausgewählte Modell unterstützt keine Tool-Aufrufe." +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3119156561"] = "Erforderliche Einstellungen fehlen. Konfigurieren Sie dieses Werkzeug, bevor Sie es aktivieren." -- Close UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3448155331"] = "Schließen" @@ -3181,7 +3184,7 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T4097602620"] = "Dieses We UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T749664565"] = "Werkzeugauswahl" -- Select tools -UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T998515990"] = "Tools auswählen" +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T998515990"] = "Werkzeuge auswählen" -- You'll interact with the AI systems using your voice. To achieve this, we want to integrate voice input (speech-to-text) and output (text-to-speech). However, later on, it should also have a natural conversation flow, i.e., seamless conversation. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VISION::T1015366320"] = "Sie werden mit den KI-Systemen über ihre Stimme interagieren. Dafür möchten wir Spracheingabe (Sprache-zu-Text) und Sprachausgabe (Text-zu-Sprache) integrieren. Später soll außerdem ein natürlicher Gesprächsfluss möglich sein, also eine nahtlose Unterhaltung." @@ -5793,6 +5796,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T3730473128"] -- The selected tool could not be loaded. UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T3907843187"] = "Das ausgewählte Werkzeug konnte nicht geladen werden." +-- {0} Default: {1} +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T403490413"] = "{0} Standard: {1}" + -- Cancel UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T900713019"] = "Abbrechen" @@ -6399,12 +6405,12 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2989678330"] = "Kopiert den Fing -- Changelog UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3017574265"] = "Änderungsprotokoll" --- Vector store -UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3046399223"] = "Vektordatenbank" - -- External HTTPS custom root certificates are configured but not active. UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3021325354"] = "Externe benutzerdefinierte Stammzertifikate sind konfiguriert, aber nicht aktiv." +-- Vector store +UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3046399223"] = "Vektordatenbank" + -- Enterprise configuration ID: UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3092349641"] = "Unternehmenskonfigurations-ID:" @@ -6757,7 +6763,7 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T3014737766"] = "Wir haben ve UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T3049689432"] = "Wir haben versucht, mit dem LLM-Anbieter „{0}“ (Typ={1}) zu kommunizieren. Selbst nach {2} erneuten Versuchen gab es weiterhin Probleme mit der Anfrage. Die Meldung des Anbieters lautet: „{3}“." -- The tool calling request failed with status code {0}. See the logs for details. -UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T3117779001"] = "Die Tool-Aufrufanfrage ist mit dem Statuscode {0} fehlgeschlagen. Details finden Sie in den Logs." +UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T3117779001"] = "Die Werkzeuganfrage ist mit dem Statuscode {0} fehlgeschlagen. Details finden Sie in den Logs." -- Tried to communicate with the LLM provider '{0}'. There were some problems with the request. The provider message is: '{1}' UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T3573577433"] = "Es wurde versucht, mit dem LLM-Anbieter '{0}' zu kommunizieren. Dabei sind Probleme bei der Anfrage aufgetreten. Die Meldung des Anbieters lautet: '{1}'" @@ -6828,9 +6834,6 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T3424652889"] = "Un -- no model selected UI_TEXT_CONTENT["AISTUDIO::PROVIDER::MODEL::T2234274832"] = "Kein Modell ausgewählt" --- The tool calling request failed with status code {0}. See the logs for details. -UI_TEXT_CONTENT["AISTUDIO::PROVIDER::OPENAI::PROVIDEROPENAI::T3117779001"] = "Die Anfrage zum Aufruf des Tools ist mit dem Statuscode {0} fehlgeschlagen. Details findest du in den Protokollen." - -- We could not load models from '{0}'. The account or API key does not have the required permissions. UI_TEXT_CONTENT["AISTUDIO::PROVIDER::MODELLOADFAILUREREASONEXTENSIONS::T1143085203"] = "Wir konnten keine Modelle von '{0}' laden. Das Konto oder der API-Schlüssel verfügt nicht über die erforderlichen Berechtigungen." @@ -6852,6 +6855,9 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::MODELLOADFAILUREREASONEXTENSIONS::T37333904 -- We could not load models from '{0}' due to an unknown error. UI_TEXT_CONTENT["AISTUDIO::PROVIDER::MODELLOADFAILUREREASONEXTENSIONS::T3907712809"] = "Wir konnten die Modelle aus '{0}' aufgrund eines unbekannten Fehlers nicht laden." +-- The tool calling request failed with status code {0}. See the logs for details. +UI_TEXT_CONTENT["AISTUDIO::PROVIDER::OPENAI::PROVIDEROPENAI::T3117779001"] = "Die Anfrage zum Aufruf des Werkzeugs ist mit dem Statuscode {0} fehlgeschlagen. Details findest du in den Protokollen." + -- It looks like you do not have any API credits left with OpenAI. Please add credits to your account and try again. UI_TEXT_CONTENT["AISTUDIO::PROVIDER::OPENAI::PROVIDEROPENAI::T757371511"] = "Anscheinend haben Sie bei OpenAI kein API-Guthaben mehr. Bitte fügen Sie Ihrem Konto Guthaben hinzu und versuchen Sie es erneut." @@ -7998,15 +8004,12 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::ITOOLIMPLEMENTATION::T35170 -- Tool description UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::ITOOLIMPLEMENTATION::T4056470505"] = "Werkzeugbeschreibung" --- Optional global truncation limit for extracted Markdown returned to the model. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T2066580916"] = "Optionales globales Kürzungslimit für extrahiertes Markdown, das an das Modell zurückgegeben wird." +-- Load a single web page and extract its main HTML content. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T204256540"] = "Eine einzelne Webseite laden und deren Haupt-HTML-Inhalt extrahieren." -- Allowed private hosts must be host names only, without scheme or path. UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T2196457612"] = "Zulässige private Hosts dürfen nur Hostnamen enthalten, ohne Schema oder Pfad." --- Optional host allowlist for private or VPN web pages. Separate host patterns with commas, such as example.de, *.example.de. Allowed private hosts require a High-confidence provider. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T237631450"] = "Optionale Host-Zulassungsliste für private oder VPN-Webseiten. Trennen Sie Host-Muster mit Kommas, zum Beispiel example.de, *.example.de. Zugelassene private Hosts erfordern einen Anbieter mit hoher Vertrauensstufe." - -- Maximum Content Characters UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T2801581200"] = "Maximale Inhaltszeichen" @@ -8025,9 +8028,15 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS: -- Read Web Page UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T3612587998"] = "Webseite lesen" +-- Optional global truncation limit for extracted characters returned to the model. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T364016543"] = "Optionale globale Begrenzung für extrahierte Zeichen, die an das Modell zurückgegeben werden." + -- The web page was not loaded because private or VPN web pages require a High-confidence provider. UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T3856267430"] = "Die Webseite wurde nicht geladen, da private Webseiten oder Webseiten über ein VPN einen Anbieter mit hoher Vertrauensstufe erfordern." +-- Optional host allowlist for private or VPN web pages. For security reasons, private or VPN web pages aren't allowed to be read by default. Separate host patterns with commas, such as example.de, *.example.de. Allowed private hosts require a high-confidence provider. For allowed internal hosts, AI Studio also tries the operating system's default sign-in automatically when the server responds with integrated authentication. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T854695329"] = "Optionale Host-Allowlist für private oder VPN-Webseiten. Aus Sicherheitsgründen ist der Zugriff auf private oder VPN-Webseiten standardmäßig nicht erlaubt. Trennen Sie Host-Muster durch Kommas, z. B. example.de, *.example.de. Für erlaubte private Hosts ist ein Anbieter mit hohem Vertrauensniveau erforderlich. Bei erlaubten internen Hosts versucht AI Studio automatisch die Standardanmeldung des Betriebssystems, wenn der Server mit integrierter Authentifizierung antwortet." + -- Maximum Results UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T1273024715"] = "Maximale Anzahl an Ergebnissen" @@ -8074,7 +8083,7 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS: UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T3967748757"] = "Optionale SafeSearch-Richtlinie, die bei entsprechender Konfiguration an SearXNG gesendet wird." -- Default categories and default engines cannot both be set for the web search tool. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T4009446158"] = "Standardkategorien und Standard-Engines können für das Websuch-Tool nicht gleichzeitig festgelegt werden." +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T4009446158"] = "Standardkategorien und Standard-Engines können für die Websuche nicht gleichzeitig festgelegt werden." -- Optional comma-separated default engines. Do not set this together with default categories. UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T4108908537"] = "Optionale, durch Kommas getrennte Standard-Engines. Nicht zusammen mit Standardkategorien festlegen." @@ -8082,6 +8091,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS: -- The setting '{0}' must be a positive integer. UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T4199432074"] = "Die Einstellung „{0}“ muss eine positive ganze Zahl sein." +-- Search the web with a configured SearXNG instance and return candidate URLs for the model. Use Read Web Page on relevant result URLs before answering factual or detailed web questions. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T764865565"] = "Im Internet mit einer konfigurierten SearXNG-Instanz suchen und Kandidaten-URLs für das Modell zurückgeben. Verwende „Webseite lesen“ auf relevanten Ergebnis-URLs, bevor du faktische oder detaillierte Webfragen beantwortest." + -- The configured SearXNG URL must start with http:// or https://. UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T944878454"] = "Die konfigurierte SearXNG-URL muss mit http:// oder https:// beginnen." 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 70eec49d..eafbf37a 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 @@ -1914,21 +1914,42 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CHATROLEEXTENSIONS::T601166687"] = "AI" -- Edit Message UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1183581066"] = "Edit Message" +-- Result +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1347088452"] = "Result" + -- Do you really want to remove this message? UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1347427447"] = "Do you really want to remove this message?" -- Yes, remove the AI response and edit it UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1350385882"] = "Yes, remove the AI response and edit it" +-- Failed +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1434043348"] = "Failed" + +-- Tool Calls ({0}) +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1493057571"] = "Tool Calls ({0})" + +-- Executed +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1564757972"] = "Executed" + -- Yes, regenerate it UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1603883875"] = "Yes, regenerate it" +-- No result +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1684269223"] = "No result" + -- Yes, remove it UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1820166585"] = "Yes, remove it" -- Number of sources UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1848978959"] = "Number of sources" +-- Show {0} tool calls +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1981771421"] = "Show {0} tool calls" + +-- Show tool call for {0} +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T2004842583"] = "Show tool call for {0}" + -- Do you really want to edit this message? In order to edit this message, the AI response will be deleted. UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T2018431076"] = "Do you really want to edit this message? In order to edit this message, the AI response will be deleted." @@ -1938,6 +1959,9 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T2093355991"] = "Removes -- Regenerate Message UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T2308444540"] = "Regenerate Message" +-- Arguments +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T2738624831"] = "Arguments" + -- Number of attachments UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3018847255"] = "Number of attachments" @@ -1947,9 +1971,15 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3175548294"] = "Cannot -- Edit UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3267849393"] = "Edit" +-- Unknown +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3424652889"] = "Unknown" + -- Regenerate UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3587744975"] = "Regenerate" +-- Blocked +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3816336467"] = "Blocked" + -- Do you really want to regenerate this message? UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3878878761"] = "Do you really want to regenerate this message?" @@ -1959,9 +1989,15 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4070211974"] = "Remove -- No, keep it UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4188329028"] = "No, keep it" +-- No tool calls +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4224149521"] = "No tool calls" + -- Export Chat to Microsoft Word UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T861873672"] = "Export Chat to Microsoft Word" +-- No arguments +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T931993614"] = "No arguments" + -- The selected model '{0}' is no longer available from '{1}' (provider={2}). Please adapt your provider settings. UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTTEXT::T3267850764"] = "The selected model '{0}' is no longer available from '{1}' (provider={2}). Please adapt your provider settings." @@ -2178,15 +2214,6 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CONFIGURATIONMINCONFIDENCESELECTION::T252 -- Select a minimum confidence level UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CONFIGURATIONMINCONFIDENCESELECTION::T2579793544"] = "Select a minimum confidence level" --- You have selected 1 preview feature. -UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CONFIGURATIONMULTISELECT::T1384241824"] = "You have selected 1 preview feature." - --- No preview features selected. -UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CONFIGURATIONMULTISELECT::T2809641588"] = "No preview features selected." - --- You have selected {0} preview features. -UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CONFIGURATIONMULTISELECT::T3513450626"] = "You have selected {0} preview features." - -- Preselected provider UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CONFIGURATIONPROVIDERSELECTION::T1469984996"] = "Preselected provider" @@ -2997,6 +3024,39 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T900237 -- Export configuration UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T975426229"] = "Export configuration" +-- Settings +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T1258653480"] = "Settings" + +-- Description +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T1725856265"] = "Description" + +-- Icon +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T1759955728"] = "Icon" + +-- This tool still needs to be configured. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T1958939818"] = "This tool still needs to be configured." + +-- Missing required settings: {0} +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T2588115579"] = "Missing required settings: {0}" + +-- Name +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T266367750"] = "Name" + +-- No minimum confidence level chosen +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T2828607242"] = "No minimum confidence level chosen" + +-- Minimum provider confidence +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3461070436"] = "Minimum provider confidence" + +-- Configure global settings for each tool. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3728248397"] = "Configure global settings for each tool." + +-- Tool Settings +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3730473128"] = "Tool Settings" + +-- Status +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T6222351"] = "Status" + -- No transcription provider configured yet. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T1079350363"] = "No transcription provider configured yet." @@ -3066,6 +3126,66 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::THIRDPARTYCOMPONENT::T1392042694"] = "Ope -- License: UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::THIRDPARTYCOMPONENT::T1908172666"] = "License:" +-- Tool selection is hidden +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLDEFAULTSCONFIGURATION::T2096103917"] = "Tool selection is hidden" + +-- You have selected 1 tool. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLDEFAULTSCONFIGURATION::T2493128368"] = "You have selected 1 tool." + +-- Choose which tools should be preselected for new runs of this assistant. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLDEFAULTSCONFIGURATION::T2696618758"] = "Choose which tools should be preselected for new runs of this assistant." + +-- Default tools for this assistant +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLDEFAULTSCONFIGURATION::T3253667950"] = "Default tools for this assistant" + +-- Tool selection is visible +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLDEFAULTSCONFIGURATION::T3384582069"] = "Tool selection is visible" + +-- Show tool selection in this assistant? +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLDEFAULTSCONFIGURATION::T3494508870"] = "Show tool selection in this assistant?" + +-- You have selected {0} tools. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLDEFAULTSCONFIGURATION::T3729156356"] = "You have selected {0} tools." + +-- No tools selected. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLDEFAULTSCONFIGURATION::T3934845540"] = "No tools selected." + +-- Default tools for chat +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLDEFAULTSCONFIGURATION::T907403808"] = "Default tools for chat" + +-- Choose which tools should be preselected for new chats. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLDEFAULTSCONFIGURATION::T948842182"] = "Choose which tools should be preselected for new chats." + +-- This tool is currently required because Web Search is enabled. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T1351725609"] = "This tool is currently required because Web Search is enabled." + +-- Tool changes are locked while a response is running. Your current selection is shown below and applies again from the next message once the run is finished. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T1688023907"] = "Tool changes are locked while a response is running. Your current selection is shown below and applies again from the next message once the run is finished." + +-- Tools allow the LLM to perform targeted additional actions such as web searches or reading web pages. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T1944689297"] = "Tools allow the LLM to perform targeted additional actions such as web searches or reading web pages." + +-- Enabling this tool also enables Read Web Page. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3023833839"] = "Enabling this tool also enables Read Web Page." + +-- Required settings are missing. Configure this tool before enabling it. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3119156561"] = "Required settings are missing. Configure this tool before enabling it." + +-- Close +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3448155331"] = "Close" + +-- No tools are available in this context. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3904490680"] = "No tools are available in this context." + +-- This tool requires provider confidence {0}. The selected provider has {1}. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T4097602620"] = "This tool requires provider confidence {0}. The selected provider has {1}." + +-- Tool Selection +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T749664565"] = "Tool Selection" + +-- Select tools +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T998515990"] = "Select tools" + -- You'll interact with the AI systems using your voice. To achieve this, we want to integrate voice input (speech-to-text) and output (text-to-speech). However, later on, it should also have a natural conversation flow, i.e., seamless conversation. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VISION::T1015366320"] = "You'll interact with the AI systems using your voice. To achieve this, we want to integrate voice input (speech-to-text) and output (text-to-speech). However, later on, it should also have a natural conversation flow, i.e., seamless conversation." @@ -5667,6 +5787,21 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGWRITINGEMAILS::T3547 -- Preselect e-mail options? UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGWRITINGEMAILS::T3832719342"] = "Preselect e-mail options?" +-- Save +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T1294818664"] = "Save" + +-- Tool Settings +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T3730473128"] = "Tool Settings" + +-- The selected tool could not be loaded. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T3907843187"] = "The selected tool could not be loaded." + +-- {0} Default: {1} +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T403490413"] = "{0} Default: {1}" + +-- Cancel +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T900713019"] = "Cancel" + -- Save UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SHORTCUTDIALOG::T1294818664"] = "Save" @@ -6270,11 +6405,12 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2989678330"] = "Copies the root -- Changelog UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3017574265"] = "Changelog" --- Vector store -UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3046399223"] = "Vector store" -- External HTTPS custom root certificates are configured but not active. UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3021325354"] = "External HTTPS custom root certificates are configured but not active." +-- Vector store +UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3046399223"] = "Vector store" + -- Enterprise configuration ID: UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3092349641"] = "Enterprise configuration ID:" @@ -6626,6 +6762,9 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T3014737766"] = "We tried to -- We tried to communicate with the LLM provider '{0}' (type={1}). Even after {2} retries, there were some problems with the request. The provider message is: '{3}'. UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T3049689432"] = "We tried to communicate with the LLM provider '{0}' (type={1}). Even after {2} retries, there were some problems with the request. The provider message is: '{3}'." +-- The tool calling request failed with status code {0}. See the logs for details. +UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T3117779001"] = "The tool calling request failed with status code {0}. See the logs for details." + -- Tried to communicate with the LLM provider '{0}'. There were some problems with the request. The provider message is: '{1}' UI_TEXT_CONTENT["AISTUDIO::PROVIDER::BASEPROVIDER::T3573577433"] = "Tried to communicate with the LLM provider '{0}'. There were some problems with the request. The provider message is: '{1}'" @@ -6716,6 +6855,9 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::MODELLOADFAILUREREASONEXTENSIONS::T37333904 -- We could not load models from '{0}' due to an unknown error. UI_TEXT_CONTENT["AISTUDIO::PROVIDER::MODELLOADFAILUREREASONEXTENSIONS::T3907712809"] = "We could not load models from '{0}' due to an unknown error." +-- The tool calling request failed with status code {0}. See the logs for details. +UI_TEXT_CONTENT["AISTUDIO::PROVIDER::OPENAI::PROVIDEROPENAI::T3117779001"] = "The tool calling request failed with status code {0}. See the logs for details." + -- It looks like you do not have any API credits left with OpenAI. Please add credits to your account and try again. UI_TEXT_CONTENT["AISTUDIO::PROVIDER::OPENAI::PROVIDEROPENAI::T757371511"] = "It looks like you do not have any API credits left with OpenAI. Please add credits to your account and try again." @@ -7856,6 +7998,108 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SOURCEEXTENSIONS::T4174900468"] = "Sources pro -- Sources provided by the AI UI_TEXT_CONTENT["AISTUDIO::TOOLS::SOURCEEXTENSIONS::T4261248356"] = "Sources provided by the AI" +-- Tool +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::ITOOLIMPLEMENTATION::T3517012711"] = "Tool" + +-- Tool description +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::ITOOLIMPLEMENTATION::T4056470505"] = "Tool description" + +-- Load a single web page and extract its main HTML content. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T204256540"] = "Load a single web page and extract its main HTML content." + +-- Allowed private hosts must be host names only, without scheme or path. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T2196457612"] = "Allowed private hosts must be host names only, without scheme or path." + +-- Maximum Content Characters +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T2801581200"] = "Maximum Content Characters" + +-- Optional HTTP timeout for loading a web page in seconds. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T2941521561"] = "Optional HTTP timeout for loading a web page in seconds." + +-- Allowed private host '{0}' is not valid. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T3089707139"] = "Allowed private host '{0}' is not valid." + +-- Allowed Private Hosts +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T3415515539"] = "Allowed Private Hosts" + +-- Timeout Seconds +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T3567699845"] = "Timeout Seconds" + +-- Read Web Page +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T3612587998"] = "Read Web Page" + +-- Optional global truncation limit for extracted characters returned to the model. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T364016543"] = "Optional global truncation limit for extracted characters returned to the model." + +-- The web page was not loaded because private or VPN web pages require a High-confidence provider. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T3856267430"] = "The web page was not loaded because private or VPN web pages require a High-confidence provider." + +-- Optional host allowlist for private or VPN web pages. For security reasons, private or VPN web pages aren't allowed to be read by default. Separate host patterns with commas, such as example.de, *.example.de. Allowed private hosts require a high-confidence provider. For allowed internal hosts, AI Studio also tries the operating system's default sign-in automatically when the server responds with integrated authentication. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T854695329"] = "Optional host allowlist for private or VPN web pages. For security reasons, private or VPN web pages aren't allowed to be read by default. Separate host patterns with commas, such as example.de, *.example.de. Allowed private hosts require a high-confidence provider. For allowed internal hosts, AI Studio also tries the operating system's default sign-in automatically when the server responds with integrated authentication." + +-- Maximum Results +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T1273024715"] = "Maximum Results" + +-- Optional comma-separated default categories. Do not set this together with default engines. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T1342681591"] = "Optional comma-separated default categories. Do not set this together with default engines." + +-- Default Safe Search +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T1343180281"] = "Default Safe Search" + +-- Base URL of the SearXNG instance. You can enter either the instance root URL or the /search endpoint. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T1739312423"] = "Base URL of the SearXNG instance. You can enter either the instance root URL or the /search endpoint." + +-- A SearXNG URL is required. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T1746583720"] = "A SearXNG URL is required." + +-- Default Engines +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T1865580137"] = "Default Engines" + +-- Optional fallback language code when the model does not provide a language. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T1868101906"] = "Optional fallback language code when the model does not provide a language." + +-- Default Categories +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T2053347010"] = "Default Categories" + +-- Default Language +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T2526826120"] = "Default Language" + +-- The configured SearXNG URL is not a valid absolute URL. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T3038368943"] = "The configured SearXNG URL is not a valid absolute URL." + +-- Optional HTTP timeout for the search request in seconds. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T3078115445"] = "Optional HTTP timeout for the search request in seconds." + +-- Timeout Seconds +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T3567699845"] = "Timeout Seconds" + +-- Optional default maximum number of results returned to the model when the model does not provide a limit. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T3603838271"] = "Optional default maximum number of results returned to the model when the model does not provide a limit." + +-- Web Search +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T3815068443"] = "Web Search" + +-- Optional safe search policy sent to SearXNG when configured. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T3967748757"] = "Optional safe search policy sent to SearXNG when configured." + +-- Default categories and default engines cannot both be set for the web search tool. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T4009446158"] = "Default categories and default engines cannot both be set for the web search tool." + +-- Optional comma-separated default engines. Do not set this together with default categories. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T4108908537"] = "Optional comma-separated default engines. Do not set this together with default categories." + +-- The setting '{0}' must be a positive integer. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T4199432074"] = "The setting '{0}' must be a positive integer." + +-- Search the web with a configured SearXNG instance and return candidate URLs for the model. Use Read Web Page on relevant result URLs before answering factual or detailed web questions. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T764865565"] = "Search the web with a configured SearXNG instance and return candidate URLs for the model. Use Read Web Page on relevant result URLs before answering factual or detailed web questions." + +-- The configured SearXNG URL must start with http:// or https://. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T944878454"] = "The configured SearXNG URL must start with http:// or https://." + +-- SearXNG URL +UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T993547568"] = "SearXNG URL" + -- Pandoc Installation UI_TEXT_CONTENT["AISTUDIO::TOOLS::USERFILE::T185447014"] = "Pandoc Installation" diff --git a/app/MindWork AI Studio/Provider/BaseProvider.cs b/app/MindWork AI Studio/Provider/BaseProvider.cs index 124b4b10..919da8fe 100644 --- a/app/MindWork AI Studio/Provider/BaseProvider.cs +++ b/app/MindWork AI Studio/Provider/BaseProvider.cs @@ -997,10 +997,33 @@ public abstract class BaseProvider : IProvider, ISecretId var currentAssistantContent = chatThread.Blocks.LastOrDefault(x => x.Role is ChatRole.AI)?.Content as ContentText; currentAssistantContent?.ToolInvocations.Clear(); + async Task ResetToolRuntimeStatusAsync() + { + if (currentAssistantContent is null) + return; + + currentAssistantContent.ToolRuntimeStatus = new(); + await currentAssistantContent.StreamingEvent(); + } + + async Task ShowToolRuntimeStatusAsync(IEnumerable toolNames) + { + if (currentAssistantContent is null) + return; + + currentAssistantContent.ToolRuntimeStatus = new ToolRuntimeStatus + { + IsRunning = true, + ToolNames = toolNames.ToList(), + }; + await currentAssistantContent.StreamingEvent(); + } + TextMessage systemPrompt; if (toolRegistry is not null && toolExecutor is not null) { var runnableTools = await toolRegistry.GetRunnableToolsAsync( + this.CreateSettingsProvider(chatModel), chatThread.RuntimeComponent, chatThread.RuntimeSelectedToolIds, this.Provider.GetModelCapabilities(chatModel), @@ -1031,14 +1054,13 @@ public abstract class BaseProvider : IProvider, ISecretId var responseMessage = response?.Choices.FirstOrDefault()?.Message; if (responseMessage is null) { - currentAssistantContent!.ToolRuntimeStatus = new(); - await currentAssistantContent.StreamingEvent(); + await ResetToolRuntimeStatusAsync(); yield break; } if (responseMessage.ToolCalls.Count == 0) { - currentAssistantContent!.ToolRuntimeStatus = new(); + await ResetToolRuntimeStatusAsync(); if (!string.IsNullOrWhiteSpace(responseMessage.Content)) yield return new ContentStreamChunk(responseMessage.Content, []); else if (toolCallCount > 0) @@ -1047,14 +1069,8 @@ public abstract class BaseProvider : IProvider, ISecretId yield break; } - currentAssistantContent!.ToolRuntimeStatus = new ToolRuntimeStatus - { - IsRunning = true, - ToolNames = responseMessage.ToolCalls - .Select(x => runnableTools.FirstOrDefault(tool => tool.Definition.Function.Name.Equals(x.Function.Name, StringComparison.Ordinal)).Implementation?.GetDisplayName() ?? x.Function.Name) - .ToList(), - }; - await currentAssistantContent.StreamingEvent(); + await ShowToolRuntimeStatusAsync(responseMessage.ToolCalls + .Select(x => runnableTools.FirstOrDefault(tool => tool.Definition.Function.Name.Equals(x.Function.Name, StringComparison.Ordinal)).Implementation?.GetDisplayName() ?? x.Function.Name)); internalMessages.Add(new AssistantToolCallMessage { @@ -1068,7 +1084,7 @@ public abstract class BaseProvider : IProvider, ISecretId if (toolCallCount > ToolSelectionRules.MAX_TOOL_CALLS) { var limitMessage = ToolSelectionRules.GetMaxToolCallsLimitMessage(); - currentAssistantContent.ToolInvocations.Add(new ToolInvocationTrace + currentAssistantContent?.ToolInvocations.Add(new ToolInvocationTrace { Order = toolCallCount, ToolId = toolCall.Function.Name, @@ -1078,8 +1094,7 @@ public abstract class BaseProvider : IProvider, ISecretId StatusMessage = limitMessage, Result = limitMessage, }); - currentAssistantContent.ToolRuntimeStatus = new(); - await currentAssistantContent.StreamingEvent(); + await ResetToolRuntimeStatusAsync(); yield return new ContentStreamChunk(limitMessage, []); yield break; } @@ -1093,7 +1108,7 @@ public abstract class BaseProvider : IProvider, ISecretId toolCallCount, token); - currentAssistantContent.ToolInvocations.Add(trace); + currentAssistantContent?.ToolInvocations.Add(trace); internalMessages.Add(new ToolResultMessage { Content = toolContent, @@ -1102,7 +1117,8 @@ public abstract class BaseProvider : IProvider, ISecretId }); } - await currentAssistantContent.StreamingEvent(); + if (currentAssistantContent is not null) + await currentAssistantContent.StreamingEvent(); } } @@ -1140,6 +1156,13 @@ public abstract class BaseProvider : IProvider, ISecretId yield return content; } + private AIStudio.Settings.Provider CreateSettingsProvider(Model chatModel) => new() + { + UsedLLMProvider = this.Provider, + Model = chatModel, + InstanceName = this.InstanceName, + }; + private async Task ExecuteChatCompletionRequest( ChatCompletionAPIRequest requestDto, string requestPath, diff --git a/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs b/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs index 8c7624be..11d6fcd6 100644 --- a/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs +++ b/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs @@ -176,6 +176,12 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, new Ur IReadOnlyList<(ToolDefinition Definition, IToolImplementation Implementation)> runnableTools = toolRegistry is null ? [] : await toolRegistry.GetRunnableToolsAsync( + new AIStudio.Settings.Provider + { + UsedLLMProvider = this.Provider, + Model = chatModel, + InstanceName = this.InstanceName, + }, chatThread.RuntimeComponent, chatThread.RuntimeSelectedToolIds, modelCapabilities, @@ -334,23 +340,14 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, new Ur var response = await this.ExecuteResponsesRequest(requestDto, requestedSecret, token); if (response is null) { - if (currentAssistantContent is not null) - { - currentAssistantContent.ToolRuntimeStatus = new(); - await currentAssistantContent.StreamingEvent(); - } - + await ResetToolRuntimeStatusAsync(currentAssistantContent); yield break; } var functionCalls = response.GetFunctionCalls(); if (functionCalls.Count == 0) { - if (currentAssistantContent is not null) - { - currentAssistantContent.ToolRuntimeStatus = new(); - await currentAssistantContent.StreamingEvent(); - } + await ResetToolRuntimeStatusAsync(currentAssistantContent); var textOutput = response.GetTextOutput(); if (!string.IsNullOrWhiteSpace(textOutput)) @@ -361,17 +358,8 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, new Ur yield break; } - if (currentAssistantContent is not null) - { - currentAssistantContent.ToolRuntimeStatus = new ToolRuntimeStatus - { - IsRunning = true, - ToolNames = functionCalls - .Select(x => runnableTools.FirstOrDefault(tool => tool.Definition.Function.Name.Equals(x.Name, StringComparison.Ordinal)).Implementation?.GetDisplayName() ?? x.Name) - .ToList(), - }; - await currentAssistantContent.StreamingEvent(); - } + await ShowToolRuntimeStatusAsync(currentAssistantContent, functionCalls + .Select(x => runnableTools.FirstOrDefault(tool => tool.Definition.Function.Name.Equals(x.Name, StringComparison.Ordinal)).Implementation?.GetDisplayName() ?? x.Name)); foreach (var functionCallItem in response.GetRawFunctionCallItems()) internalItems.Add(functionCallItem); @@ -393,12 +381,7 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, new Ur Result = limitMessage, }); - if (currentAssistantContent is not null) - { - currentAssistantContent.ToolRuntimeStatus = new(); - await currentAssistantContent.StreamingEvent(); - } - + await ResetToolRuntimeStatusAsync(currentAssistantContent); yield return new ContentStreamChunk(limitMessage, []); yield break; } @@ -425,6 +408,28 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, new Ur } } + private static async Task ResetToolRuntimeStatusAsync(ContentText? currentAssistantContent) + { + if (currentAssistantContent is null) + return; + + currentAssistantContent.ToolRuntimeStatus = new(); + await currentAssistantContent.StreamingEvent(); + } + + private static async Task ShowToolRuntimeStatusAsync(ContentText? currentAssistantContent, IEnumerable toolNames) + { + if (currentAssistantContent is null) + return; + + currentAssistantContent.ToolRuntimeStatus = new ToolRuntimeStatus + { + IsRunning = true, + ToolNames = toolNames.ToList(), + }; + await currentAssistantContent.StreamingEvent(); + } + private async Task ExecuteResponsesRequest(ResponsesAPIRequest requestDto, RequestedSecret requestedSecret, CancellationToken token) { using var request = new HttpRequestMessage(HttpMethod.Post, "responses"); diff --git a/app/MindWork AI Studio/Settings/ManagedConfiguration.Parsing.cs b/app/MindWork AI Studio/Settings/ManagedConfiguration.Parsing.cs index 4b453d27..5189161e 100644 --- a/app/MindWork AI Studio/Settings/ManagedConfiguration.Parsing.cs +++ b/app/MindWork AI Studio/Settings/ManagedConfiguration.Parsing.cs @@ -766,7 +766,7 @@ public static partial class ManagedConfiguration return false; var successful = false; - var configuredValue = configMeta.Default; + var configuredValue = CloneStringDictionary(configMeta.Default); // Step 1 -- try to read the Lua value (we expect a table) out of the Lua table: if (settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredLuaList) && @@ -803,7 +803,9 @@ public static partial class ManagedConfiguration if(dryRun) return successful; - return HandleParsedValue(configPluginId, dryRun, successful, configMeta, configuredValue); + var settingName = SettingName(propertyExpression); + var managedMode = ReadManagedConfigurationMode(propertyExpression, settings); + return HandleParsedDictionaryValue(configPluginId, dryRun, successful, configMeta, configuredValue, managedMode, settingName); } /// @@ -925,6 +927,67 @@ public static partial class ManagedConfiguration return successful; } + private static bool HandleParsedDictionaryValue( + Guid configPluginId, + bool dryRun, + bool successful, + ConfigMeta> configMeta, + IDictionary configuredValue, + ManagedConfigurationMode managedMode, + string settingName) + { + if (dryRun) + return successful; + + switch (successful) + { + case true when managedMode is ManagedConfigurationMode.LOCKED: + ClearEditableDefaultState(settingName); + configMeta.ClearEditableDefaultConfiguration(); + configMeta.SetValue(CloneStringDictionary(configuredValue)); + configMeta.LockConfiguration(configPluginId); + break; + + case true when managedMode is ManagedConfigurationMode.EDITABLE_DEFAULT: + var currentValueSerialized = SerializeManagedStringDictionaryValue(configMeta.GetValue()); + var configuredValueSerialized = SerializeManagedStringDictionaryValue(configuredValue); + + string lastAppliedValue; + if (!TryGetEditableDefaultState(settingName, out var editableDefaultState)) + { + configMeta.SetValue(CloneStringDictionary(configuredValue)); + lastAppliedValue = configuredValueSerialized; + } + else + { + lastAppliedValue = editableDefaultState.LastAppliedValue; + if (string.Equals(currentValueSerialized, lastAppliedValue, StringComparison.Ordinal)) + { + configMeta.SetValue(CloneStringDictionary(configuredValue)); + lastAppliedValue = configuredValueSerialized; + } + } + + SetEditableDefaultState(settingName, configPluginId, lastAppliedValue); + configMeta.UnlockConfiguration(); + configMeta.SetEditableDefaultConfiguration(configPluginId); + break; + + case false when configMeta.IsLocked && configMeta.LockedByConfigPluginId == configPluginId: + configMeta.ResetLockedConfiguration(); + break; + + case false when configMeta.ManagedMode is ManagedConfigurationMode.EDITABLE_DEFAULT + && TryGetEditableDefaultState(settingName, out var editableDefaultStateToRemove) + && editableDefaultStateToRemove.ConfigPluginId == configPluginId: + configMeta.ClearEditableDefaultConfiguration(); + ClearEditableDefaultState(settingName); + break; + } + + return successful; + } + private static ManagedConfigurationMode ReadManagedConfigurationMode( Expression> propertyExpression, LuaTable settings) @@ -950,4 +1013,12 @@ public static partial class ManagedConfiguration _ => value.ToString() ?? string.Empty, }; -} \ No newline at end of file + + private static Dictionary CloneStringDictionary(IDictionary values) => new(values, StringComparer.Ordinal); + + private static string SerializeManagedStringDictionaryValue(IDictionary values) => string.Join( + "\n", + values + .OrderBy(pair => pair.Key, StringComparer.Ordinal) + .Select(pair => $"{pair.Key}={pair.Value}")); +} diff --git a/app/MindWork AI Studio/Settings/ManagedConfiguration.cs b/app/MindWork AI Studio/Settings/ManagedConfiguration.cs index 0e62f2c6..89dc6ded 100644 --- a/app/MindWork AI Studio/Settings/ManagedConfiguration.cs +++ b/app/MindWork AI Studio/Settings/ManagedConfiguration.cs @@ -387,17 +387,17 @@ public static partial class ManagedConfiguration if (!TryGet(configSelection, propertyExpression, out var configMeta)) return false; - if (configMeta.LockedByConfigPluginId == Guid.Empty || !configMeta.IsLocked) - return false; - - var plugin = availablePlugins.FirstOrDefault(x => x.Id == configMeta.LockedByConfigPluginId); - if (plugin is null) + if (configMeta.LockedByConfigPluginId != Guid.Empty && configMeta.IsLocked) { - configMeta.ResetLockedConfiguration(); - return true; + var plugin = availablePlugins.FirstOrDefault(x => x.Id == configMeta.LockedByConfigPluginId); + if (plugin is null) + { + configMeta.ResetLockedConfiguration(); + return true; + } } - return false; + return CleanupEditableDefaultState(configMeta, SettingName(propertyExpression), [..availablePlugins]); } private static string Path(Expression> configSelection, Expression> propertyExpression) @@ -453,4 +453,4 @@ public static partial class ManagedConfiguration configMeta.ClearEditableDefaultConfiguration(); return ClearEditableDefaultState(settingName); } -} \ No newline at end of file +} diff --git a/app/MindWork AI Studio/Settings/SettingsManager.cs b/app/MindWork AI Studio/Settings/SettingsManager.cs index 44b0838f..74b408ee 100644 --- a/app/MindWork AI Studio/Settings/SettingsManager.cs +++ b/app/MindWork AI Studio/Settings/SettingsManager.cs @@ -18,6 +18,8 @@ namespace AIStudio.Settings; /// public sealed class SettingsManager { + public readonly record struct ToolMinimumProviderConfidenceResolution(ConfidenceLevel ConfidenceLevel, string Source); + private const string SETTINGS_FILENAME = "settings.json"; private static readonly JsonSerializerOptions JSON_OPTIONS = new() @@ -392,6 +394,46 @@ public sealed class SettingsManager return []; } + public HashSet FilterToolIdsForProvider(AIStudio.Settings.Provider provider, IEnumerable selectedToolIds) + { + var toolCallingAvailability = provider.GetToolCallingAvailability(); + if (!toolCallingAvailability.IsAvailable) + return []; + + var modelCapabilities = provider.GetModelCapabilities(); + var supportsRequiredApis = + modelCapabilities.Contains(Capability.CHAT_COMPLETION_API) || + modelCapabilities.Contains(Capability.RESPONSES_API); + if (!supportsRequiredApis || !modelCapabilities.Contains(Capability.FUNCTION_CALLING)) + return []; + + var providerConfidence = provider.UsedLLMProvider.GetConfidence(this).Level; + var filtered = ToolSelectionRules.NormalizeSelection(selectedToolIds); + + var changed = true; + while (changed) + { + changed = false; + foreach (var toolId in filtered.ToList()) + { + var minimumToolConfidence = this.GetMinimumProviderConfidenceForTool(toolId); + if (ToolSelectionRules.IsProviderConfidenceAllowed(providerConfidence, minimumToolConfidence)) + continue; + + filtered.Remove(toolId); + changed = true; + } + + if (filtered.Contains(ToolSelectionRules.WEB_SEARCH_TOOL_ID) && !filtered.Contains(ToolSelectionRules.READ_WEB_PAGE_TOOL_ID)) + { + filtered.Remove(ToolSelectionRules.WEB_SEARCH_TOOL_ID); + changed = true; + } + } + + return filtered; + } + public bool IsToolSelectionVisible(AIStudio.Tools.Components component) => component switch { AIStudio.Tools.Components.CHAT => true, @@ -410,18 +452,31 @@ public sealed class SettingsManager this.ConfigurationData.Tools.VisibleToolSelectionComponents.Remove(key); } - public ConfidenceLevel GetMinimumProviderConfidenceForTool(string toolId) + public ToolMinimumProviderConfidenceResolution GetMinimumProviderConfidenceResolutionForTool(string toolId) { + if (ManagedConfiguration.TryGet(x => x.Tools, x => x.MinimumProviderConfidenceByToolId, out var configMeta) && configMeta.IsLocked) + { + var managedValues = configMeta.GetValue(); + if (managedValues.TryGetValue(toolId, out var configuredManagedLevel) && + Enum.TryParse(configuredManagedLevel, true, out var managedConfidenceLevel) && + managedConfidenceLevel is not ConfidenceLevel.UNKNOWN) + { + return new(managedConfidenceLevel, "managed config"); + } + } + if (this.ConfigurationData.Tools.MinimumProviderConfidenceByToolId.TryGetValue(toolId, out var configuredLevel) && Enum.TryParse(configuredLevel, true, out var confidenceLevel) && confidenceLevel is not ConfidenceLevel.UNKNOWN) { - return confidenceLevel; + return new(confidenceLevel, "stored override"); } - return ToolSelectionRules.GetDefaultMinimumProviderConfidence(toolId); + return new(ToolSelectionRules.GetDefaultMinimumProviderConfidence(toolId), "default fallback"); } + public ConfidenceLevel GetMinimumProviderConfidenceForTool(string toolId) => this.GetMinimumProviderConfidenceResolutionForTool(toolId).ConfidenceLevel; + public void SetMinimumProviderConfidenceForTool(string toolId, ConfidenceLevel confidenceLevel) { var defaultLevel = ToolSelectionRules.GetDefaultMinimumProviderConfidence(toolId); diff --git a/app/MindWork AI Studio/Tools/ExternalHttpClientTimeout.cs b/app/MindWork AI Studio/Tools/ExternalHttpClientTimeout.cs index 3d465737..d8cf7f55 100644 --- a/app/MindWork AI Studio/Tools/ExternalHttpClientTimeout.cs +++ b/app/MindWork AI Studio/Tools/ExternalHttpClientTimeout.cs @@ -46,6 +46,16 @@ public static class ExternalHttpClientTimeout return httpClient; } + public static void ConfigureSocketsHttpHandler(SocketsHttpHandler handler, string host, ExternalHttpTrustPolicy trustPolicy) + { + var customRootCertificateCache = GetCustomRootCertificateCache(); + if (!customRootCertificateCache.State.IsUsable) + return; + + handler.SslOptions.RemoteCertificateValidationCallback = (_, certificate, chain, sslPolicyErrors) => + ValidateServerCertificateWithCustomRootCertificates(host, certificate, chain, sslPolicyErrors, customRootCertificateCache, trustPolicy); + } + public static ExternalHttpCustomRootCertificateState CustomRootCertificateState => GetCustomRootCertificateCache().State; public static string GetTimeoutDescription() @@ -336,6 +346,23 @@ public static class ExternalHttpClientTimeout SslPolicyErrors sslPolicyErrors, CustomRootCertificateCache customRootCertificateCache, ExternalHttpTrustPolicy trustPolicy) + { + return ValidateServerCertificateWithCustomRootCertificates( + ReadRequestHost(request), + certificate, + originalChain, + sslPolicyErrors, + customRootCertificateCache, + trustPolicy); + } + + private static bool ValidateServerCertificateWithCustomRootCertificates( + string host, + X509Certificate? certificate, + X509Chain? originalChain, + SslPolicyErrors sslPolicyErrors, + CustomRootCertificateCache customRootCertificateCache, + ExternalHttpTrustPolicy trustPolicy) { if (sslPolicyErrors is SslPolicyErrors.None) return true; @@ -343,7 +370,6 @@ public static class ExternalHttpClientTimeout if (sslPolicyErrors is not SslPolicyErrors.RemoteCertificateChainErrors || certificate is null) return false; - var host = ReadRequestHost(request); if (trustPolicy is ExternalHttpTrustPolicy.SYSTEM_TRUST_ONLY) { LOGGER.Value.LogError($"Rejected external HTTPS certificate for '{HostForLog(host)}' because this request requires system trust only. Configured custom root certificates are not allowed for this request."); @@ -378,7 +404,7 @@ public static class ExternalHttpClientTimeout var isValid = customChain.Build(serverCertificate); if (isValid) - LogCustomRootCertificateAccepted(request); + LogCustomRootCertificateAccepted(host); return isValid; } @@ -436,10 +462,11 @@ public static class ExternalHttpClientTimeout private static void LogCustomRootCertificateAccepted(HttpRequestMessage request) { - var host = ReadRequestHost(request); - LOGGER.Value.LogWarning($"Accepted an external HTTPS certificate for '{host}' using configured custom root certificates."); + LogCustomRootCertificateAccepted(ReadRequestHost(request)); } + private static void LogCustomRootCertificateAccepted(string host) => LOGGER.Value.LogWarning($"Accepted an external HTTPS certificate for '{host}' using configured custom root certificates."); + private static string ReadRequestHost(HttpRequestMessage request) { var host = request.RequestUri?.IdnHost; @@ -465,4 +492,4 @@ public static class ExternalHttpClientTimeout string CacheKey, X509Certificate2Collection Certificates, ExternalHttpCustomRootCertificateState State); -} \ No newline at end of file +} diff --git a/app/MindWork AI Studio/Tools/HTMLParser.cs b/app/MindWork AI Studio/Tools/HTMLParser.cs index d7b144de..09d1a160 100644 --- a/app/MindWork AI Studio/Tools/HTMLParser.cs +++ b/app/MindWork AI Studio/Tools/HTMLParser.cs @@ -50,34 +50,22 @@ public sealed class HTMLParser int timeoutSeconds = 30, Func>>? resolveUrlAddressesAsync = null, int maxResponseBytes = DEFAULT_MAX_RESPONSE_BYTES, - ExternalWebAuthenticationMode authenticationMode = ExternalWebAuthenticationMode.NONE) + ExternalWebAuthenticationMode authenticationMode = ExternalWebAuthenticationMode.NONE, + ExternalHttpTrustPolicy trustPolicy = ExternalHttpTrustPolicy.ALLOW_CUSTOM_ROOTS_WHEN_HOST_WHITELISTED) { - using var handler = new SocketsHttpHandler - { - AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate | DecompressionMethods.Brotli, - AllowAutoRedirect = false, - }; - if (authenticationMode is ExternalWebAuthenticationMode.OS_DEFAULT_CREDENTIALS) - handler.Credentials = CreateDefaultCredentialCache(url); - - if (resolveUrlAddressesAsync is not null) - { - // The callback binds the request to a vetted target IP; a proxy would change the endpoint being connected to. - handler.UseProxy = false; - handler.ConnectCallback = async (context, connectionToken) => await ConnectToResolvedAddressAsync(context, resolveUrlAddressesAsync, connectionToken); - } - - using var httpClient = new HttpClient(handler) - { - Timeout = Timeout.InfiniteTimeSpan, - }; using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(token); timeoutCts.CancelAfter(TimeSpan.FromSeconds(timeoutSeconds)); + var cookieContainer = new CookieContainer(); var currentUrl = url; for (var redirectCount = 0; redirectCount <= MAX_REDIRECTS; redirectCount++) { ValidateHttpOrHttpsUrl(currentUrl); + using var handler = CreateHandler(currentUrl, resolveUrlAddressesAsync, authenticationMode, trustPolicy, cookieContainer); + using var httpClient = new HttpClient(handler) + { + Timeout = Timeout.InfiniteTimeSpan, + }; using var request = CreateRequest(currentUrl); using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, timeoutCts.Token); @@ -116,6 +104,35 @@ public sealed class HTMLParser throw new HttpRequestException($"The server returned more than {MAX_REDIRECTS} redirects for '{url}'."); } + private static SocketsHttpHandler CreateHandler( + Uri url, + Func>>? resolveUrlAddressesAsync, + ExternalWebAuthenticationMode authenticationMode, + ExternalHttpTrustPolicy trustPolicy, + CookieContainer cookieContainer) + { + var handler = new SocketsHttpHandler + { + AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate | DecompressionMethods.Brotli, + AllowAutoRedirect = false, + UseCookies = true, + CookieContainer = cookieContainer, + }; + ExternalHttpClientTimeout.ConfigureSocketsHttpHandler(handler, url.Host, trustPolicy); + + if (authenticationMode is ExternalWebAuthenticationMode.OS_DEFAULT_CREDENTIALS) + handler.Credentials = CreateDefaultCredentialCache(url); + + if (resolveUrlAddressesAsync is not null) + { + // The callback binds the request to a vetted target IP; a proxy would change the endpoint being connected to. + handler.UseProxy = false; + handler.ConnectCallback = async (context, connectionToken) => await ConnectToResolvedAddressAsync(context, resolveUrlAddressesAsync, connectionToken); + } + + return handler; + } + private static CredentialCache CreateDefaultCredentialCache(Uri url) { var credentialCache = new CredentialCache(); diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolCallingImplementations/ReadWebPageTool.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolCallingImplementations/ReadWebPageTool.cs index 590fecee..d0671167 100644 --- a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolCallingImplementations/ReadWebPageTool.cs +++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolCallingImplementations/ReadWebPageTool.cs @@ -56,7 +56,7 @@ public sealed class ReadWebPageTool(HTMLParser htmlParser, ILogger fieldName switch { "timeoutSeconds" => TB("Optional HTTP timeout for loading a web page in seconds."), - "maxContentCharacters" => TB("Optional global truncation limit for extracted Markdown returned to the model."), + "maxContentCharacters" => TB("Optional global truncation limit for extracted characters returned to the model."), ALLOWED_PRIVATE_HOSTS_SETTING => TB("Optional host allowlist for private or VPN web pages. For security reasons, private or VPN web pages aren't allowed to be read by default. Separate host patterns with commas, such as example.de, *.example.de. Allowed private hosts require a high-confidence provider. For allowed internal hosts, AI Studio also tries the operating system's default sign-in automatically when the server responds with integrated authentication."), _ => TB(fieldDefinition.Description), }; diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolCallingImplementations/SearXNGWebSearchTool.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolCallingImplementations/SearXNGWebSearchTool.cs index 6e0d8954..e75f5c30 100644 --- a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolCallingImplementations/SearXNGWebSearchTool.cs +++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolCallingImplementations/SearXNGWebSearchTool.cs @@ -2,6 +2,7 @@ using System.Net; using System.Text; using System.Text.Json; using System.Text.Json.Nodes; +using AIStudio.Tools; using AIStudio.Tools.PluginSystem; namespace AIStudio.Tools.ToolCallingSystem.ToolCallingImplementations; @@ -167,16 +168,14 @@ public sealed class SearXNGWebSearchTool : IToolImplementation if (!string.IsNullOrWhiteSpace(safeSearch)) queryParameters.Add(new KeyValuePair("safesearch", safeSearch)); - using var httpClient = new HttpClient - { - Timeout = Timeout.InfiniteTimeSpan, - }; + using var httpClient = ExternalHttpClientTimeout.CreateHttpClient(searchUri, ExternalHttpTrustPolicy.ALLOW_CUSTOM_ROOTS_WHEN_HOST_WHITELISTED); + httpClient.Timeout = Timeout.InfiniteTimeSpan; using var request = new HttpRequestMessage(HttpMethod.Get, BuildRequestUri(searchUri, queryParameters)); using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(token); timeoutCts.CancelAfter(TimeSpan.FromSeconds(timeoutSeconds)); using var response = await SendAsync(httpClient, request, timeoutCts.Token, timeoutSeconds, token); - var responseBody = await ReadContentAsStringWithLimitAsync(response.Content, MAX_RESPONSE_BYTES, token); + var responseBody = await ReadContentAsStringWithLimitAsync(response.Content, MAX_RESPONSE_BYTES, timeoutCts.Token); if (!response.IsSuccessStatusCode) { var responseDetails = string.IsNullOrWhiteSpace(responseBody) ? string.Empty : $" Response body: {responseBody[..Math.Min(responseBody.Length, 400)]}"; diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolExecutor.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolExecutor.cs index 285dc9d7..dec39fc9 100644 --- a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolExecutor.cs +++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolExecutor.cs @@ -23,7 +23,7 @@ public sealed class ToolExecutor(ToolSettingsService toolSettingsService, ILogge try { using var document = JsonDocument.Parse(string.IsNullOrWhiteSpace(argumentsJson) ? "{}" : argumentsJson); - formattedArguments = FormatArguments(document.RootElement, runnableTool.Implementation?.SensitiveTraceArgumentNames ?? EmptySensitiveTraceArgumentNames.INSTANCE); + formattedArguments = FormatArguments(document.RootElement, runnableTool.Implementation?.SensitiveTraceArgumentNames ?? EmptySensitiveTraceArgumentNames.INSTANCE); } catch { diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolRegistry.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolRegistry.cs index 7c77ed1e..daa6c06d 100644 --- a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolRegistry.cs +++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolRegistry.cs @@ -113,6 +113,7 @@ public sealed class ToolRegistry } public async Task> GetRunnableToolsAsync( + AIStudio.Settings.Provider provider, AIStudio.Tools.Components component, IEnumerable selectedToolIds, IReadOnlyCollection modelCapabilities, @@ -120,31 +121,61 @@ public sealed class ToolRegistry bool isToolSelectionVisible) { if (!isToolSelectionVisible) + { + this.logger.LogInformation("Tool calling is skipped for component '{Component}' because tool selection is not visible.", component); return []; + } + + var toolCallingAvailability = provider.GetToolCallingAvailability(); + if (!toolCallingAvailability.IsAvailable) + { + this.logger.LogInformation("Tool calling is unavailable for provider '{Provider}' with model '{ModelId}': {Reason}", provider.InstanceName, provider.Model.Id, toolCallingAvailability.Message); + return []; + } if (!modelCapabilities.Contains(Capability.FUNCTION_CALLING) || (!modelCapabilities.Contains(Capability.CHAT_COMPLETION_API) && !modelCapabilities.Contains(Capability.RESPONSES_API))) + { + this.logger.LogInformation("Tool calling is unavailable for provider '{Provider}' with model '{ModelId}' because the model lacks the required API or function-calling capability.", provider.InstanceName, provider.Model.Id); return []; + } var selectedToolIdSet = ToolSelectionRules.NormalizeSelection(selectedToolIds); + this.logger.LogInformation("Resolving runnable tools for provider '{Provider}' with model '{ModelId}'. Selected tool IDs: [{ToolIds}].", provider.InstanceName, provider.Model.Id, string.Join(", ", selectedToolIdSet.OrderBy(x => x, StringComparer.Ordinal))); + var definitions = this.GetDefinitionsForComponent(component).Where(x => selectedToolIdSet.Contains(x.Id)).ToList(); var result = new List<(ToolDefinition, IToolImplementation)>(definitions.Count); foreach (var definition in definitions) { if (!this.implementationsByKey.TryGetValue(definition.ImplementationKey, out var implementation)) + { + this.logger.LogInformation("Skipping tool '{ToolId}' because no implementation is registered.", definition.Id); continue; + } var configurationState = await this.toolSettingsService.GetConfigurationStateAsync(definition, implementation); if (!configurationState.IsConfigured) + { + this.logger.LogInformation("Skipping tool '{ToolId}' because it is not configured.", definition.Id); continue; + } + + var resolution = this.settingsManager.GetMinimumProviderConfidenceResolutionForTool(definition.Id); + var minimumToolConfidence = resolution.ConfidenceLevel; + this.logger.LogInformation("Tool '{ToolId}' uses minimum provider confidence '{ConfidenceLevel}' from {Source}.", definition.Id, minimumToolConfidence, resolution.Source); - var minimumToolConfidence = this.settingsManager.GetMinimumProviderConfidenceForTool(definition.Id); if (!ToolSelectionRules.IsProviderConfidenceAllowed(providerConfidence, minimumToolConfidence)) + { + this.logger.LogInformation("Skipping tool '{ToolId}' because provider confidence '{ProviderConfidence}' is below the required minimum '{MinimumConfidence}'.", definition.Id, providerConfidence, minimumToolConfidence); continue; + } result.Add((definition, implementation)); } + foreach (var selectedToolId in selectedToolIdSet.Where(selectedToolId => definitions.All(definition => !definition.Id.Equals(selectedToolId, StringComparison.Ordinal)))) + this.logger.LogInformation("Skipping tool '{ToolId}' because it is not selected in this component or not available in this context.", selectedToolId); + return result; } } diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolSelectionRules.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolSelectionRules.cs index a7771ec8..32770392 100644 --- a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolSelectionRules.cs +++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolSelectionRules.cs @@ -47,7 +47,7 @@ public static class ToolSelectionRules return $""" Tool usage policy: - {policyLines} + {string.Join(Environment.NewLine + Environment.NewLine, policyLines)} """; }