Fixed some bugs.

This commit is contained in:
Peer Schütt 2026-06-11 16:25:26 +02:00
parent 8fd7e18113
commit 2ad591f117
21 changed files with 675 additions and 189 deletions

View File

@ -194,6 +194,10 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
private async Task Start() private async Task Start()
{ {
await this.RefreshProviderSelectionFromConfigurationAsync();
if (this.ProviderSettings == Settings.Provider.NONE)
return;
using (this.CancellationTokenSource = new()) using (this.CancellationTokenSource = new())
{ {
await this.SubmitAction(); await this.SubmitAction();
@ -275,11 +279,18 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
return chatId; return chatId;
} }
private Task RefreshProviderSelectionFromConfigurationAsync()
{
this.ProviderSettings = this.SettingsManager.GetPreselectedProvider(this.Component, this.ProviderSettings.Id);
return Task.CompletedTask;
}
protected virtual void ResetProviderAndProfileSelection() protected virtual void ResetProviderAndProfileSelection()
{ {
this.ProviderSettings = this.SettingsManager.GetPreselectedProvider(this.Component); this.ProviderSettings = this.SettingsManager.GetPreselectedProvider(this.Component);
this.CurrentProfile = this.SettingsManager.GetPreselectedProfile(this.Component); this.CurrentProfile = this.SettingsManager.GetPreselectedProfile(this.Component);
this.CurrentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(this.Component); this.CurrentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(this.Component);
this.selectedToolIds = this.SettingsManager.GetDefaultToolIds(this.Component);
} }
protected Task SelectedToolIdsChanged(HashSet<string> updatedToolIds) protected Task SelectedToolIdsChanged(HashSet<string> updatedToolIds)
@ -336,7 +347,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
this.ChatThread.SelectedProvider = this.ProviderSettings.Id; this.ChatThread.SelectedProvider = this.ProviderSettings.Id;
this.ChatThread.RuntimeComponent = this.Component; this.ChatThread.RuntimeComponent = this.Component;
this.ChatThread.RuntimeSelectedToolIds = this.SettingsManager.IsToolSelectionVisible(this.Component) this.ChatThread.RuntimeSelectedToolIds = this.SettingsManager.IsToolSelectionVisible(this.Component)
? ToolSelectionRules.NormalizeSelection(this.selectedToolIds) ? this.SettingsManager.FilterToolIdsForProvider(this.ProviderSettings, this.selectedToolIds)
: []; : [];
} }

View File

@ -3031,9 +3031,6 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T1725856265
-- Icon -- Icon
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T1759955728"] = "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. -- This tool still needs to be configured.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T1958939818"] = "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 -- Minimum provider confidence
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3461070436"] = "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 -- Tool Settings
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3730473128"] = "Tool Settings" UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3730473128"] = "Tool Settings"
-- State -- Status
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T502047894"] = "State" UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T6222351"] = "Status"
-- No transcription provider configured yet. -- No transcription provider configured yet.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T1079350363"] = "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. -- 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." 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. -- Enabling this tool also enables Read Web Page.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3023833839"] = "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. -- 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." 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 -- Close
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3448155331"] = "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. -- No tools are available in this context.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3904490680"] = "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. -- 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." 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. -- 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." 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 -- Read Web Page
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T3612587998"] = "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. -- 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." 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."

View File

@ -78,7 +78,6 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
private Guid loadedParameterWorkspaceId = Guid.Empty; private Guid loadedParameterWorkspaceId = Guid.Empty;
private Guid foregroundChatId = Guid.Empty; private Guid foregroundChatId = Guid.Empty;
private int workspaceHeaderSyncVersion; private int workspaceHeaderSyncVersion;
private CancellationTokenSource? cancellationTokenSource;
// Unfortunately, we need the input field reference to blur the focus away. Without // Unfortunately, we need the input field reference to blur the focus away. Without
// this, we cannot clear the input field. // this, we cannot clear the input field.
@ -104,7 +103,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
// Apply the filters for the message bus: // 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: // Configure the spellchecking for the user input:
this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES); this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES);
@ -577,6 +576,8 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
private async Task SendMessage(bool reuseLastUserPrompt = false) private async Task SendMessage(bool reuseLastUserPrompt = false)
{ {
await this.RefreshProviderSelectionFromConfigurationAsync();
if (!this.IsProviderSelected) if (!this.IsProviderSelected)
return; 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}'."); 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.StateHasChanged();
this.ChatThread!.RuntimeComponent = Tools.Components.CHAT; this.ChatThread!.RuntimeComponent = Tools.Components.CHAT;
this.ChatThread.RuntimeSelectedToolIds = ToolSelectionRules.NormalizeSelection(this.selectedToolIds); this.ChatThread.RuntimeSelectedToolIds = this.SettingsManager.FilterToolIdsForProvider(this.Provider, this.selectedToolIds);
await this.AIJobService.TryStartChatGenerationAsync(new ChatGenerationRequest
// 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(); await this.SyncForegroundChatAsync();
this.StateHasChanged(); this.StateHasChanged();
} }
@ -801,7 +785,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
// //
this.hasUnsavedChanges = false; this.hasUnsavedChanges = false;
this.ComposerState.Clear(); 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: // Reset the LLM provider considering the user's settings:
@ -949,6 +933,19 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
this.StateHasChanged(); 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() private async Task ResetState()
{ {
this.hasUnsavedChanges = false; this.hasUnsavedChanges = false;
@ -1091,6 +1088,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
break; break;
case Event.CONFIGURATION_CHANGED: case Event.CONFIGURATION_CHANGED:
await this.RefreshProviderSelectionFromConfigurationAsync();
await this.InvokeAsync(this.StateHasChanged); await this.InvokeAsync(this.StateHasChanged);
break; break;
} }

View File

@ -186,5 +186,6 @@ public partial class SettingsPanelProviders : SettingsPanelProviderBase
{ {
this.SettingsManager.ConfigurationData.LLMProviders.CustomConfidenceScheme[llmProvider] = level; this.SettingsManager.ConfigurationData.LLMProviders.CustomConfidenceScheme[llmProvider] = level;
await this.SettingsManager.StoreSettings(); await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
} }
} }

View File

@ -4,7 +4,7 @@
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Build" HeaderText="@T("Tool Settings")"> <ExpansionPanel HeaderIcon="@Icons.Material.Filled.Build" HeaderText="@T("Tool Settings")">
<MudText Typo="Typo.body1" Class="mb-4"> <MudText Typo="Typo.body1" Class="mb-4">
@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.")
</MudText> </MudText>
<MudTable Items="@this.items" Hover="@true" Dense="@true"> <MudTable Items="@this.items" Hover="@true" Dense="@true">
@ -13,7 +13,7 @@
<MudTh>@T("Name")</MudTh> <MudTh>@T("Name")</MudTh>
<MudTh>@T("Description")</MudTh> <MudTh>@T("Description")</MudTh>
<MudTh>@T("Minimum provider confidence")</MudTh> <MudTh>@T("Minimum provider confidence")</MudTh>
<MudTh>@T("State")</MudTh> <MudTh>@T("Status")</MudTh>
<MudTh>@T("Settings")</MudTh> <MudTh>@T("Settings")</MudTh>
</HeaderContent> </HeaderContent>
<RowTemplate> <RowTemplate>

View File

@ -18,6 +18,9 @@
</CardHeaderContent> </CardHeaderContent>
</MudCardHeader> </MudCardHeader>
<MudCardContent Style="min-width: 28em; max-height: 60vh; max-width: 48vw; overflow: auto;"> <MudCardContent Style="min-width: 28em; max-height: 60vh; max-width: 48vw; overflow: auto;">
<MudText Typo="Typo.body1" Class="mb-3">
@T("Tools allow the LLM to perform targeted additional actions such as web searches or reading web pages.")
</MudText>
@if (!this.SupportsTools) @if (!this.SupportsTools)
{ {
<MudText Typo="Typo.body1">@this.UnsupportedToolsMessage</MudText> <MudText Typo="Typo.body1">@this.UnsupportedToolsMessage</MudText>

View File

@ -1,7 +1,6 @@
using AIStudio.Dialogs.Settings; using AIStudio.Dialogs.Settings;
using AIStudio.Provider; using AIStudio.Provider;
using AIStudio.Settings; using AIStudio.Settings;
using AIStudio.Tools;
using AIStudio.Tools.ToolCallingSystem; using AIStudio.Tools.ToolCallingSystem;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
@ -49,22 +48,15 @@ public partial class ToolSelection : MSGComponentBase
await base.OnInitializedAsync(); await base.OnInitializedAsync();
} }
private bool SupportsTools => private ToolCallingAvailability ToolCallingAvailability => this.LLMProvider.GetToolCallingAvailability();
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 bool IsAnthropicProvider => this.LLMProvider != AIStudio.Settings.Provider.NONE && private bool SupportsTools => this.ToolCallingAvailability.IsAvailable;
this.LLMProvider.UsedLLMProvider is LLMProviders.ANTHROPIC;
private string ToolButtonTooltip => this.SupportsTools private string ToolButtonTooltip => this.SupportsTools
? this.T("Select tools") ? this.T("Select tools")
: this.UnsupportedToolsMessage; : this.UnsupportedToolsMessage;
private string UnsupportedToolsMessage => this.IsAnthropicProvider private string UnsupportedToolsMessage => this.ToolCallingAvailability.Message;
? this.T("Tool calling for this provider is not implemented yet.")
: this.T("The selected model does not support tool calling.");
private ConfidenceLevel ProviderConfidence => this.LLMProvider == AIStudio.Settings.Provider.NONE private ConfidenceLevel ProviderConfidence => this.LLMProvider == AIStudio.Settings.Provider.NONE
? ConfidenceLevel.NONE ? ConfidenceLevel.NONE

View File

@ -1927,7 +1927,7 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1350385882"] = "Ja, ent
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1434043348"] = "Fehlgeschlagen" UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1434043348"] = "Fehlgeschlagen"
-- Tool Calls ({0}) -- Tool Calls ({0})
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1493057571"] = "Tool-Aufrufe" UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1493057571"] = "Werkzeugaufrufe"
-- Executed -- Executed
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1564757972"] = "Ausgeführt" 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" UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1848978959"] = "Anzahl der Quellen"
-- Show {0} tool calls -- 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} -- 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. -- 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." 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 -- Export Chat to Microsoft Word
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T861873672"] = "Chat in Microsoft Word exportieren" 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. -- 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." 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 -- Icon
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T1759955728"] = "Symbol" 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. -- This tool still needs to be configured.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T1958939818"] = "Dieses Werkzeug muss noch konfiguriert werden." 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 -- Minimum provider confidence
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3461070436"] = "Minimale Anbieterzuverlässigkeit" 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 -- Tool Settings
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3730473128"] = "Werkzeugeinstellungen" UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3730473128"] = "Werkzeugeinstellungen"
-- State -- Status
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T502047894"] = "Status" UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T6222351"] = "Status"
-- No transcription provider configured yet. -- No transcription provider configured yet.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T1079350363"] = "Es ist bisher kein Anbieter für Transkriptionen konfiguriert." 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." UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLDEFAULTSCONFIGURATION::T3729156356"] = "Sie haben {0} Werkzeuge ausgewählt."
-- No tools selected. -- 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 -- Default tools for chat
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLDEFAULTSCONFIGURATION::T907403808"] = "Standardwerkzeuge für den 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. -- 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." 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. -- Enabling this tool also enables Read Web Page.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3023833839"] = "Das Aktivieren dieses Werkzeugs aktiviert auch „Webseite lesen“." 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. -- 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." UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3119156561"] = "Erforderliche Einstellungen fehlen. Konfigurieren Sie dieses Werkzeug, 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."
-- Close -- Close
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3448155331"] = "Schließen" 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" UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T749664565"] = "Werkzeugauswahl"
-- Select tools -- 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. -- 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." 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. -- The selected tool could not be loaded.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T3907843187"] = "Das ausgewählte Werkzeug konnte nicht geladen werden." 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 -- Cancel
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T900713019"] = "Abbrechen" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T900713019"] = "Abbrechen"
@ -6399,12 +6405,12 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2989678330"] = "Kopiert den Fing
-- Changelog -- Changelog
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3017574265"] = "Änderungsprotokoll" 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. -- 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." 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: -- Enterprise configuration ID:
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3092349641"] = "Unternehmenskonfigurations-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}“." 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. -- 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}' -- 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}'" 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 -- no model selected
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::MODEL::T2234274832"] = "Kein Modell ausgewählt" 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. -- 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." 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. -- 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." 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. -- 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." 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 -- Tool description
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::ITOOLIMPLEMENTATION::T4056470505"] = "Werkzeugbeschreibung" UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::ITOOLIMPLEMENTATION::T4056470505"] = "Werkzeugbeschreibung"
-- Optional global truncation limit for extracted Markdown returned to the model. -- Load a single web page and extract its main HTML content.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T2066580916"] = "Optionales globales Kürzungslimit für extrahiertes Markdown, das an das Modell zurückgegeben wird." 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. -- 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." 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 -- Maximum Content Characters
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T2801581200"] = "Maximale Inhaltszeichen" 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 -- Read Web Page
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::READWEBPAGETOOL::T3612587998"] = "Webseite lesen" 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. -- 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." 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 -- Maximum Results
UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T1273024715"] = "Maximale Anzahl an Ergebnissen" 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." 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. -- 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. -- 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." 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. -- 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." 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://. -- 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." UI_TEXT_CONTENT["AISTUDIO::TOOLS::TOOLCALLINGSYSTEM::TOOLCALLINGIMPLEMENTATIONS::SEARXNGWEBSEARCHTOOL::T944878454"] = "Die konfigurierte SearXNG-URL muss mit http:// oder https:// beginnen."

View File

@ -1914,21 +1914,42 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CHATROLEEXTENSIONS::T601166687"] = "AI"
-- Edit Message -- Edit Message
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1183581066"] = "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? -- Do you really want to remove this message?
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1347427447"] = "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 -- Yes, remove the AI response and edit it
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1350385882"] = "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 -- Yes, regenerate it
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1603883875"] = "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 -- Yes, remove it
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1820166585"] = "Yes, remove it" UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1820166585"] = "Yes, remove it"
-- Number of sources -- Number of sources
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1848978959"] = "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. -- 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." 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 -- Regenerate Message
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T2308444540"] = "Regenerate Message" UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T2308444540"] = "Regenerate Message"
-- Arguments
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T2738624831"] = "Arguments"
-- Number of attachments -- Number of attachments
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3018847255"] = "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 -- Edit
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3267849393"] = "Edit" UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3267849393"] = "Edit"
-- Unknown
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3424652889"] = "Unknown"
-- Regenerate -- Regenerate
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3587744975"] = "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? -- Do you really want to regenerate this message?
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3878878761"] = "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 -- No, keep it
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4188329028"] = "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 -- Export Chat to Microsoft Word
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T861873672"] = "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. -- 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." 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 -- Select a minimum confidence level
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CONFIGURATIONMINCONFIDENCESELECTION::T2579793544"] = "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 -- Preselected provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CONFIGURATIONPROVIDERSELECTION::T1469984996"] = "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 -- Export configuration
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T975426229"] = "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. -- No transcription provider configured yet.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T1079350363"] = "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: -- License:
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::THIRDPARTYCOMPONENT::T1908172666"] = "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. -- 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." 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? -- Preselect e-mail options?
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGWRITINGEMAILS::T3832719342"] = "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 -- Save
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SHORTCUTDIALOG::T1294818664"] = "Save" UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SHORTCUTDIALOG::T1294818664"] = "Save"
@ -6270,11 +6405,12 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2989678330"] = "Copies the root
-- Changelog -- Changelog
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3017574265"] = "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. -- 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." 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: -- Enterprise configuration ID:
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3092349641"] = "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}'. -- 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}'." 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}' -- 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}'" 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. -- 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." 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. -- 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." 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 -- Sources provided by the AI
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SOURCEEXTENSIONS::T4261248356"] = "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 -- Pandoc Installation
UI_TEXT_CONTENT["AISTUDIO::TOOLS::USERFILE::T185447014"] = "Pandoc Installation" UI_TEXT_CONTENT["AISTUDIO::TOOLS::USERFILE::T185447014"] = "Pandoc Installation"

View File

@ -997,10 +997,33 @@ public abstract class BaseProvider : IProvider, ISecretId
var currentAssistantContent = chatThread.Blocks.LastOrDefault(x => x.Role is ChatRole.AI)?.Content as ContentText; var currentAssistantContent = chatThread.Blocks.LastOrDefault(x => x.Role is ChatRole.AI)?.Content as ContentText;
currentAssistantContent?.ToolInvocations.Clear(); currentAssistantContent?.ToolInvocations.Clear();
async Task ResetToolRuntimeStatusAsync()
{
if (currentAssistantContent is null)
return;
currentAssistantContent.ToolRuntimeStatus = new();
await currentAssistantContent.StreamingEvent();
}
async Task ShowToolRuntimeStatusAsync(IEnumerable<string> toolNames)
{
if (currentAssistantContent is null)
return;
currentAssistantContent.ToolRuntimeStatus = new ToolRuntimeStatus
{
IsRunning = true,
ToolNames = toolNames.ToList(),
};
await currentAssistantContent.StreamingEvent();
}
TextMessage systemPrompt; TextMessage systemPrompt;
if (toolRegistry is not null && toolExecutor is not null) if (toolRegistry is not null && toolExecutor is not null)
{ {
var runnableTools = await toolRegistry.GetRunnableToolsAsync( var runnableTools = await toolRegistry.GetRunnableToolsAsync(
this.CreateSettingsProvider(chatModel),
chatThread.RuntimeComponent, chatThread.RuntimeComponent,
chatThread.RuntimeSelectedToolIds, chatThread.RuntimeSelectedToolIds,
this.Provider.GetModelCapabilities(chatModel), this.Provider.GetModelCapabilities(chatModel),
@ -1031,14 +1054,13 @@ public abstract class BaseProvider : IProvider, ISecretId
var responseMessage = response?.Choices.FirstOrDefault()?.Message; var responseMessage = response?.Choices.FirstOrDefault()?.Message;
if (responseMessage is null) if (responseMessage is null)
{ {
currentAssistantContent!.ToolRuntimeStatus = new(); await ResetToolRuntimeStatusAsync();
await currentAssistantContent.StreamingEvent();
yield break; yield break;
} }
if (responseMessage.ToolCalls.Count == 0) if (responseMessage.ToolCalls.Count == 0)
{ {
currentAssistantContent!.ToolRuntimeStatus = new(); await ResetToolRuntimeStatusAsync();
if (!string.IsNullOrWhiteSpace(responseMessage.Content)) if (!string.IsNullOrWhiteSpace(responseMessage.Content))
yield return new ContentStreamChunk(responseMessage.Content, []); yield return new ContentStreamChunk(responseMessage.Content, []);
else if (toolCallCount > 0) else if (toolCallCount > 0)
@ -1047,14 +1069,8 @@ public abstract class BaseProvider : IProvider, ISecretId
yield break; yield break;
} }
currentAssistantContent!.ToolRuntimeStatus = new ToolRuntimeStatus await ShowToolRuntimeStatusAsync(responseMessage.ToolCalls
{ .Select(x => runnableTools.FirstOrDefault(tool => tool.Definition.Function.Name.Equals(x.Function.Name, StringComparison.Ordinal)).Implementation?.GetDisplayName() ?? x.Function.Name));
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();
internalMessages.Add(new AssistantToolCallMessage internalMessages.Add(new AssistantToolCallMessage
{ {
@ -1068,7 +1084,7 @@ public abstract class BaseProvider : IProvider, ISecretId
if (toolCallCount > ToolSelectionRules.MAX_TOOL_CALLS) if (toolCallCount > ToolSelectionRules.MAX_TOOL_CALLS)
{ {
var limitMessage = ToolSelectionRules.GetMaxToolCallsLimitMessage(); var limitMessage = ToolSelectionRules.GetMaxToolCallsLimitMessage();
currentAssistantContent.ToolInvocations.Add(new ToolInvocationTrace currentAssistantContent?.ToolInvocations.Add(new ToolInvocationTrace
{ {
Order = toolCallCount, Order = toolCallCount,
ToolId = toolCall.Function.Name, ToolId = toolCall.Function.Name,
@ -1078,8 +1094,7 @@ public abstract class BaseProvider : IProvider, ISecretId
StatusMessage = limitMessage, StatusMessage = limitMessage,
Result = limitMessage, Result = limitMessage,
}); });
currentAssistantContent.ToolRuntimeStatus = new(); await ResetToolRuntimeStatusAsync();
await currentAssistantContent.StreamingEvent();
yield return new ContentStreamChunk(limitMessage, []); yield return new ContentStreamChunk(limitMessage, []);
yield break; yield break;
} }
@ -1093,7 +1108,7 @@ public abstract class BaseProvider : IProvider, ISecretId
toolCallCount, toolCallCount,
token); token);
currentAssistantContent.ToolInvocations.Add(trace); currentAssistantContent?.ToolInvocations.Add(trace);
internalMessages.Add(new ToolResultMessage internalMessages.Add(new ToolResultMessage
{ {
Content = toolContent, Content = toolContent,
@ -1102,6 +1117,7 @@ public abstract class BaseProvider : IProvider, ISecretId
}); });
} }
if (currentAssistantContent is not null)
await currentAssistantContent.StreamingEvent(); await currentAssistantContent.StreamingEvent();
} }
} }
@ -1140,6 +1156,13 @@ public abstract class BaseProvider : IProvider, ISecretId
yield return content; yield return content;
} }
private AIStudio.Settings.Provider CreateSettingsProvider(Model chatModel) => new()
{
UsedLLMProvider = this.Provider,
Model = chatModel,
InstanceName = this.InstanceName,
};
private async Task<ChatCompletionResponse?> ExecuteChatCompletionRequest( private async Task<ChatCompletionResponse?> ExecuteChatCompletionRequest(
ChatCompletionAPIRequest requestDto, ChatCompletionAPIRequest requestDto,
string requestPath, string requestPath,

View File

@ -176,6 +176,12 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, new Ur
IReadOnlyList<(ToolDefinition Definition, IToolImplementation Implementation)> runnableTools = toolRegistry is null IReadOnlyList<(ToolDefinition Definition, IToolImplementation Implementation)> runnableTools = toolRegistry is null
? [] ? []
: await toolRegistry.GetRunnableToolsAsync( : await toolRegistry.GetRunnableToolsAsync(
new AIStudio.Settings.Provider
{
UsedLLMProvider = this.Provider,
Model = chatModel,
InstanceName = this.InstanceName,
},
chatThread.RuntimeComponent, chatThread.RuntimeComponent,
chatThread.RuntimeSelectedToolIds, chatThread.RuntimeSelectedToolIds,
modelCapabilities, modelCapabilities,
@ -334,23 +340,14 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, new Ur
var response = await this.ExecuteResponsesRequest(requestDto, requestedSecret, token); var response = await this.ExecuteResponsesRequest(requestDto, requestedSecret, token);
if (response is null) if (response is null)
{ {
if (currentAssistantContent is not null) await ResetToolRuntimeStatusAsync(currentAssistantContent);
{
currentAssistantContent.ToolRuntimeStatus = new();
await currentAssistantContent.StreamingEvent();
}
yield break; yield break;
} }
var functionCalls = response.GetFunctionCalls(); var functionCalls = response.GetFunctionCalls();
if (functionCalls.Count == 0) if (functionCalls.Count == 0)
{ {
if (currentAssistantContent is not null) await ResetToolRuntimeStatusAsync(currentAssistantContent);
{
currentAssistantContent.ToolRuntimeStatus = new();
await currentAssistantContent.StreamingEvent();
}
var textOutput = response.GetTextOutput(); var textOutput = response.GetTextOutput();
if (!string.IsNullOrWhiteSpace(textOutput)) if (!string.IsNullOrWhiteSpace(textOutput))
@ -361,17 +358,8 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, new Ur
yield break; yield break;
} }
if (currentAssistantContent is not null) await ShowToolRuntimeStatusAsync(currentAssistantContent, functionCalls
{ .Select(x => runnableTools.FirstOrDefault(tool => tool.Definition.Function.Name.Equals(x.Name, StringComparison.Ordinal)).Implementation?.GetDisplayName() ?? x.Name));
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();
}
foreach (var functionCallItem in response.GetRawFunctionCallItems()) foreach (var functionCallItem in response.GetRawFunctionCallItems())
internalItems.Add(functionCallItem); internalItems.Add(functionCallItem);
@ -393,12 +381,7 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, new Ur
Result = limitMessage, Result = limitMessage,
}); });
if (currentAssistantContent is not null) await ResetToolRuntimeStatusAsync(currentAssistantContent);
{
currentAssistantContent.ToolRuntimeStatus = new();
await currentAssistantContent.StreamingEvent();
}
yield return new ContentStreamChunk(limitMessage, []); yield return new ContentStreamChunk(limitMessage, []);
yield break; 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<string> toolNames)
{
if (currentAssistantContent is null)
return;
currentAssistantContent.ToolRuntimeStatus = new ToolRuntimeStatus
{
IsRunning = true,
ToolNames = toolNames.ToList(),
};
await currentAssistantContent.StreamingEvent();
}
private async Task<ResponsesResponse?> ExecuteResponsesRequest(ResponsesAPIRequest requestDto, RequestedSecret requestedSecret, CancellationToken token) private async Task<ResponsesResponse?> ExecuteResponsesRequest(ResponsesAPIRequest requestDto, RequestedSecret requestedSecret, CancellationToken token)
{ {
using var request = new HttpRequestMessage(HttpMethod.Post, "responses"); using var request = new HttpRequestMessage(HttpMethod.Post, "responses");

View File

@ -766,7 +766,7 @@ public static partial class ManagedConfiguration
return false; return false;
var successful = 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: // 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) && if (settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredLuaList) &&
@ -803,7 +803,9 @@ public static partial class ManagedConfiguration
if(dryRun) if(dryRun)
return successful; 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);
} }
/// <summary> /// <summary>
@ -925,6 +927,67 @@ public static partial class ManagedConfiguration
return successful; return successful;
} }
private static bool HandleParsedDictionaryValue<TClass>(
Guid configPluginId,
bool dryRun,
bool successful,
ConfigMeta<TClass, IDictionary<string, string>> configMeta,
IDictionary<string, string> 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<TClass, TValue>( private static ManagedConfigurationMode ReadManagedConfigurationMode<TClass, TValue>(
Expression<Func<TClass, TValue>> propertyExpression, Expression<Func<TClass, TValue>> propertyExpression,
LuaTable settings) LuaTable settings)
@ -950,4 +1013,12 @@ public static partial class ManagedConfiguration
_ => value.ToString() ?? string.Empty, _ => value.ToString() ?? string.Empty,
}; };
private static Dictionary<string, string> CloneStringDictionary(IDictionary<string, string> values) => new(values, StringComparer.Ordinal);
private static string SerializeManagedStringDictionaryValue(IDictionary<string, string> values) => string.Join(
"\n",
values
.OrderBy(pair => pair.Key, StringComparer.Ordinal)
.Select(pair => $"{pair.Key}={pair.Value}"));
} }

View File

@ -387,17 +387,17 @@ public static partial class ManagedConfiguration
if (!TryGet(configSelection, propertyExpression, out var configMeta)) if (!TryGet(configSelection, propertyExpression, out var configMeta))
return false; return false;
if (configMeta.LockedByConfigPluginId == Guid.Empty || !configMeta.IsLocked) if (configMeta.LockedByConfigPluginId != Guid.Empty && configMeta.IsLocked)
return false; {
var plugin = availablePlugins.FirstOrDefault(x => x.Id == configMeta.LockedByConfigPluginId); var plugin = availablePlugins.FirstOrDefault(x => x.Id == configMeta.LockedByConfigPluginId);
if (plugin is null) if (plugin is null)
{ {
configMeta.ResetLockedConfiguration(); configMeta.ResetLockedConfiguration();
return true; return true;
} }
}
return false; return CleanupEditableDefaultState(configMeta, SettingName(propertyExpression), [..availablePlugins]);
} }
private static string Path<TClass, TValue>(Expression<Func<Data, TClass>> configSelection, Expression<Func<TClass, TValue>> propertyExpression) private static string Path<TClass, TValue>(Expression<Func<Data, TClass>> configSelection, Expression<Func<TClass, TValue>> propertyExpression)

View File

@ -18,6 +18,8 @@ namespace AIStudio.Settings;
/// </summary> /// </summary>
public sealed class SettingsManager public sealed class SettingsManager
{ {
public readonly record struct ToolMinimumProviderConfidenceResolution(ConfidenceLevel ConfidenceLevel, string Source);
private const string SETTINGS_FILENAME = "settings.json"; private const string SETTINGS_FILENAME = "settings.json";
private static readonly JsonSerializerOptions JSON_OPTIONS = new() private static readonly JsonSerializerOptions JSON_OPTIONS = new()
@ -392,6 +394,46 @@ public sealed class SettingsManager
return []; return [];
} }
public HashSet<string> FilterToolIdsForProvider(AIStudio.Settings.Provider provider, IEnumerable<string> 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 public bool IsToolSelectionVisible(AIStudio.Tools.Components component) => component switch
{ {
AIStudio.Tools.Components.CHAT => true, AIStudio.Tools.Components.CHAT => true,
@ -410,18 +452,31 @@ public sealed class SettingsManager
this.ConfigurationData.Tools.VisibleToolSelectionComponents.Remove(key); 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<ConfidenceLevel>(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) && if (this.ConfigurationData.Tools.MinimumProviderConfidenceByToolId.TryGetValue(toolId, out var configuredLevel) &&
Enum.TryParse<ConfidenceLevel>(configuredLevel, true, out var confidenceLevel) && Enum.TryParse<ConfidenceLevel>(configuredLevel, true, out var confidenceLevel) &&
confidenceLevel is not ConfidenceLevel.UNKNOWN) 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) public void SetMinimumProviderConfidenceForTool(string toolId, ConfidenceLevel confidenceLevel)
{ {
var defaultLevel = ToolSelectionRules.GetDefaultMinimumProviderConfidence(toolId); var defaultLevel = ToolSelectionRules.GetDefaultMinimumProviderConfidence(toolId);

View File

@ -46,6 +46,16 @@ public static class ExternalHttpClientTimeout
return httpClient; 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 ExternalHttpCustomRootCertificateState CustomRootCertificateState => GetCustomRootCertificateCache().State;
public static string GetTimeoutDescription() public static string GetTimeoutDescription()
@ -336,6 +346,23 @@ public static class ExternalHttpClientTimeout
SslPolicyErrors sslPolicyErrors, SslPolicyErrors sslPolicyErrors,
CustomRootCertificateCache customRootCertificateCache, CustomRootCertificateCache customRootCertificateCache,
ExternalHttpTrustPolicy trustPolicy) 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) if (sslPolicyErrors is SslPolicyErrors.None)
return true; return true;
@ -343,7 +370,6 @@ public static class ExternalHttpClientTimeout
if (sslPolicyErrors is not SslPolicyErrors.RemoteCertificateChainErrors || certificate is null) if (sslPolicyErrors is not SslPolicyErrors.RemoteCertificateChainErrors || certificate is null)
return false; return false;
var host = ReadRequestHost(request);
if (trustPolicy is ExternalHttpTrustPolicy.SYSTEM_TRUST_ONLY) 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."); 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); var isValid = customChain.Build(serverCertificate);
if (isValid) if (isValid)
LogCustomRootCertificateAccepted(request); LogCustomRootCertificateAccepted(host);
return isValid; return isValid;
} }
@ -436,10 +462,11 @@ public static class ExternalHttpClientTimeout
private static void LogCustomRootCertificateAccepted(HttpRequestMessage request) private static void LogCustomRootCertificateAccepted(HttpRequestMessage request)
{ {
var host = ReadRequestHost(request); LogCustomRootCertificateAccepted(ReadRequestHost(request));
LOGGER.Value.LogWarning($"Accepted an external HTTPS certificate for '{host}' using configured custom root certificates.");
} }
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) private static string ReadRequestHost(HttpRequestMessage request)
{ {
var host = request.RequestUri?.IdnHost; var host = request.RequestUri?.IdnHost;

View File

@ -50,34 +50,22 @@ public sealed class HTMLParser
int timeoutSeconds = 30, int timeoutSeconds = 30,
Func<Uri, CancellationToken, Task<IReadOnlyList<IPAddress>>>? resolveUrlAddressesAsync = null, Func<Uri, CancellationToken, Task<IReadOnlyList<IPAddress>>>? resolveUrlAddressesAsync = null,
int maxResponseBytes = DEFAULT_MAX_RESPONSE_BYTES, 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); using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(token);
timeoutCts.CancelAfter(TimeSpan.FromSeconds(timeoutSeconds)); timeoutCts.CancelAfter(TimeSpan.FromSeconds(timeoutSeconds));
var cookieContainer = new CookieContainer();
var currentUrl = url; var currentUrl = url;
for (var redirectCount = 0; redirectCount <= MAX_REDIRECTS; redirectCount++) for (var redirectCount = 0; redirectCount <= MAX_REDIRECTS; redirectCount++)
{ {
ValidateHttpOrHttpsUrl(currentUrl); 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 request = CreateRequest(currentUrl);
using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, timeoutCts.Token); 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}'."); throw new HttpRequestException($"The server returned more than {MAX_REDIRECTS} redirects for '{url}'.");
} }
private static SocketsHttpHandler CreateHandler(
Uri url,
Func<Uri, CancellationToken, Task<IReadOnlyList<IPAddress>>>? 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) private static CredentialCache CreateDefaultCredentialCache(Uri url)
{ {
var credentialCache = new CredentialCache(); var credentialCache = new CredentialCache();

View File

@ -56,7 +56,7 @@ public sealed class ReadWebPageTool(HTMLParser htmlParser, ILogger<ReadWebPageTo
public string GetSettingsFieldDescription(string fieldName, ToolSettingsFieldDefinition fieldDefinition) => fieldName switch public string GetSettingsFieldDescription(string fieldName, ToolSettingsFieldDefinition fieldDefinition) => fieldName switch
{ {
"timeoutSeconds" => TB("Optional HTTP timeout for loading a web page in seconds."), "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."), 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), _ => TB(fieldDefinition.Description),
}; };

View File

@ -2,6 +2,7 @@ using System.Net;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Nodes; using System.Text.Json.Nodes;
using AIStudio.Tools;
using AIStudio.Tools.PluginSystem; using AIStudio.Tools.PluginSystem;
namespace AIStudio.Tools.ToolCallingSystem.ToolCallingImplementations; namespace AIStudio.Tools.ToolCallingSystem.ToolCallingImplementations;
@ -167,16 +168,14 @@ public sealed class SearXNGWebSearchTool : IToolImplementation
if (!string.IsNullOrWhiteSpace(safeSearch)) if (!string.IsNullOrWhiteSpace(safeSearch))
queryParameters.Add(new KeyValuePair<string, string>("safesearch", safeSearch)); queryParameters.Add(new KeyValuePair<string, string>("safesearch", safeSearch));
using var httpClient = new HttpClient using var httpClient = ExternalHttpClientTimeout.CreateHttpClient(searchUri, ExternalHttpTrustPolicy.ALLOW_CUSTOM_ROOTS_WHEN_HOST_WHITELISTED);
{ httpClient.Timeout = Timeout.InfiniteTimeSpan;
Timeout = Timeout.InfiniteTimeSpan,
};
using var request = new HttpRequestMessage(HttpMethod.Get, BuildRequestUri(searchUri, queryParameters)); using var request = new HttpRequestMessage(HttpMethod.Get, BuildRequestUri(searchUri, queryParameters));
using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(token); using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(token);
timeoutCts.CancelAfter(TimeSpan.FromSeconds(timeoutSeconds)); timeoutCts.CancelAfter(TimeSpan.FromSeconds(timeoutSeconds));
using var response = await SendAsync(httpClient, request, timeoutCts.Token, timeoutSeconds, token); 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) if (!response.IsSuccessStatusCode)
{ {
var responseDetails = string.IsNullOrWhiteSpace(responseBody) ? string.Empty : $" Response body: {responseBody[..Math.Min(responseBody.Length, 400)]}"; var responseDetails = string.IsNullOrWhiteSpace(responseBody) ? string.Empty : $" Response body: {responseBody[..Math.Min(responseBody.Length, 400)]}";

View File

@ -113,6 +113,7 @@ public sealed class ToolRegistry
} }
public async Task<IReadOnlyList<(ToolDefinition Definition, IToolImplementation Implementation)>> GetRunnableToolsAsync( public async Task<IReadOnlyList<(ToolDefinition Definition, IToolImplementation Implementation)>> GetRunnableToolsAsync(
AIStudio.Settings.Provider provider,
AIStudio.Tools.Components component, AIStudio.Tools.Components component,
IEnumerable<string> selectedToolIds, IEnumerable<string> selectedToolIds,
IReadOnlyCollection<Capability> modelCapabilities, IReadOnlyCollection<Capability> modelCapabilities,
@ -120,31 +121,61 @@ public sealed class ToolRegistry
bool isToolSelectionVisible) bool isToolSelectionVisible)
{ {
if (!isToolSelectionVisible) if (!isToolSelectionVisible)
{
this.logger.LogInformation("Tool calling is skipped for component '{Component}' because tool selection is not visible.", component);
return []; 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) || if (!modelCapabilities.Contains(Capability.FUNCTION_CALLING) ||
(!modelCapabilities.Contains(Capability.CHAT_COMPLETION_API) && !modelCapabilities.Contains(Capability.RESPONSES_API))) (!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 []; return [];
}
var selectedToolIdSet = ToolSelectionRules.NormalizeSelection(selectedToolIds); 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 definitions = this.GetDefinitionsForComponent(component).Where(x => selectedToolIdSet.Contains(x.Id)).ToList();
var result = new List<(ToolDefinition, IToolImplementation)>(definitions.Count); var result = new List<(ToolDefinition, IToolImplementation)>(definitions.Count);
foreach (var definition in definitions) foreach (var definition in definitions)
{ {
if (!this.implementationsByKey.TryGetValue(definition.ImplementationKey, out var implementation)) if (!this.implementationsByKey.TryGetValue(definition.ImplementationKey, out var implementation))
{
this.logger.LogInformation("Skipping tool '{ToolId}' because no implementation is registered.", definition.Id);
continue; continue;
}
var configurationState = await this.toolSettingsService.GetConfigurationStateAsync(definition, implementation); var configurationState = await this.toolSettingsService.GetConfigurationStateAsync(definition, implementation);
if (!configurationState.IsConfigured) if (!configurationState.IsConfigured)
{
this.logger.LogInformation("Skipping tool '{ToolId}' because it is not configured.", definition.Id);
continue; 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)) 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; continue;
}
result.Add((definition, implementation)); 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; return result;
} }
} }

View File

@ -47,7 +47,7 @@ public static class ToolSelectionRules
return $""" return $"""
Tool usage policy: Tool usage policy:
{policyLines} {string.Join(Environment.NewLine + Environment.NewLine, policyLines)}
"""; """;
} }