diff --git a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua index c94b4b7a..96d8c23c 100644 --- a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua +++ b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua @@ -1684,12 +1684,24 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CHATROLEEXTENSIONS::T601166687"] = "AI" -- Edit Message UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1183581066"] = "Edit Message" +-- Result +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1347088452"] = "Result" + -- Do you really want to remove this message? UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1347427447"] = "Do you really want to remove this message?" -- Yes, remove the AI response and edit it UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1350385882"] = "Yes, remove the AI response and edit it" +-- Failed +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1434043348"] = "Failed" + +-- Tool Calls ({0}) +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1493057571"] = "Tool Calls ({0})" + +-- Executed +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1564757972"] = "Executed" + -- Yes, regenerate it UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T1603883875"] = "Yes, regenerate it" @@ -1708,6 +1720,9 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T2093355991"] = "Removes -- Regenerate Message UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T2308444540"] = "Regenerate Message" +-- Arguments +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T2738624831"] = "Arguments" + -- Number of attachments UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3018847255"] = "Number of attachments" @@ -1717,9 +1732,15 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3175548294"] = "Cannot -- Edit UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3267849393"] = "Edit" +-- Unknown +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3424652889"] = "Unknown" + -- Regenerate UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3587744975"] = "Regenerate" +-- Blocked +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3816336467"] = "Blocked" + -- Do you really want to regenerate this message? UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T3878878761"] = "Do you really want to regenerate this message?" @@ -1732,6 +1753,9 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4188329028"] = "No, kee -- Export Chat to Microsoft Word UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T861873672"] = "Export Chat to Microsoft Word" +-- No arguments +UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T931993614"] = "No arguments" + -- The local image file does not exist. Skipping the image. UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T255679918"] = "The local image file does not exist. Skipping the image." @@ -2590,6 +2614,27 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T900237 -- Export configuration UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T975426229"] = "Export configuration" +-- 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." + +-- Configured +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T2463440925"] = "Configured" + +-- Tools +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T2499909372"] = "Tools" + +-- Tool +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3517012711"] = "Tool" + +-- Actions +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T3865031940"] = "Actions" + +-- State +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T502047894"] = "State" + +-- Configuration required +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTOOLS::T979592518"] = "Configuration required" + -- No transcription provider configured yet. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T1079350363"] = "No transcription provider configured yet." @@ -2659,6 +2704,48 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::THIRDPARTYCOMPONENT::T1392042694"] = "Ope -- License: UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::THIRDPARTYCOMPONENT::T1908172666"] = "License:" +-- Tool selection is hidden +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLDEFAULTSCONFIGURATION::T2096103917"] = "Tool selection is hidden" + +-- 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?" + +-- 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." + +-- 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." + +-- Required settings are missing. Configure this tool before enabling it. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3119156561"] = "Required settings are missing. Configure this tool before enabling it." + +-- The selected provider or model does not support tool calling. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3364063757"] = "The selected provider or model does not support tool calling." + +-- Close +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3448155331"] = "Close" + +-- No tools are available in this context. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T3904490680"] = "No tools are available in this context." + +-- Tool Selection +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T749664565"] = "Tool Selection" + +-- Select tools +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::TOOLSELECTION::T998515990"] = "Select tools" + -- You'll interact with the AI systems using your voice. To achieve this, we want to integrate voice input (speech-to-text) and output (text-to-speech). However, later on, it should also have a natural conversation flow, i.e., seamless conversation. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::VISION::T1015366320"] = "You'll interact with the AI systems using your voice. To achieve this, we want to integrate voice input (speech-to-text) and output (text-to-speech). However, later on, it should also have a natural conversation flow, i.e., seamless conversation." @@ -4696,6 +4783,12 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T13933 -- Preselect aspects for the LLM to focus on when generating slides, such as bullet points or specific topics to emphasize. UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T1528169602"] = "Preselect aspects for the LLM to focus on when generating slides, such as bullet points or specific topics to emphasize." +-- Slide Planner Assistant options are preselected +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T1549358578"] = "Slide Planner Assistant options are preselected" + +-- No Slide Planner Assistant options are preselected +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T1694374279"] = "No Slide Planner Assistant options are preselected" + -- Choose whether the assistant should use the app default profile, no profile, or a specific profile. UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T1766361623"] = "Choose whether the assistant should use the app default profile, no profile, or a specific profile." @@ -4705,9 +4798,6 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T20146 -- Which audience organizational level should be preselected? UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T216511105"] = "Which audience organizational level should be preselected?" --- Preselect Slide Planner Assistant options? -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T227645894"] = "Preselect Slide Planner Assistant options?" - -- Preselect a profile UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T2322771068"] = "Preselect a profile" @@ -4724,26 +4814,23 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T25714 UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T2645589441"] = "Preselect the audience age group" -- Assistant: Slide Planner Assistant Options -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T3215549988"] = "Assistant: Slide Planner Assistant Options" +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T3226042276"] = "Assistant: Slide Planner Assistant Options" -- Which audience expertise should be preselected? UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T3228597992"] = "Which audience expertise should be preselected?" +-- Preselect Slide Planner Assistant options? +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T339924858"] = "Preselect Slide Planner Assistant options?" + -- Close UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T3448155331"] = "Close" -- Preselect important aspects UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T3705987833"] = "Preselect important aspects" --- No Slide Planner Assistant options are preselected -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T4214398691"] = "No Slide Planner Assistant options are preselected" - -- Preselect the audience profile UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T861397972"] = "Preselect the audience profile" --- Slide Planner Assistant options are preselected -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T93124146"] = "Slide Planner Assistant options are preselected" - -- Which audience age group should be preselected? UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGSLIDEBUILDER::T956845877"] = "Which audience age group should be preselected?" @@ -5002,6 +5089,18 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGWRITINGEMAILS::T3547 -- Preselect e-mail options? UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGWRITINGEMAILS::T3832719342"] = "Preselect e-mail options?" +-- Save +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T1294818664"] = "Save" + +-- Tool Settings +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T3730473128"] = "Tool Settings" + +-- The selected tool could not be loaded. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T3907843187"] = "The selected tool could not be loaded." + +-- Cancel +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::TOOLSETTINGSDIALOG::T900713019"] = "Cancel" + -- Save UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SHORTCUTDIALOG::T1294818664"] = "Save" diff --git a/app/MindWork AI Studio/Chat/ContentBlockComponent.razor b/app/MindWork AI Studio/Chat/ContentBlockComponent.razor index 5ad88ce2..36d82948 100644 --- a/app/MindWork AI Studio/Chat/ContentBlockComponent.razor +++ b/app/MindWork AI Studio/Chat/ContentBlockComponent.razor @@ -16,6 +16,18 @@ + @if (this.Role is ChatRole.AI && this.Content is ContentText toolContent && toolContent.ToolInvocations.Count > 0) + { + + + + @foreach (var toolIcon in this.GetDistinctToolIcons()) + { + + } + + + } @if (this.Content.FileAttachments.Count > 0) { @@ -123,50 +135,50 @@ } - @if (this.Role is ChatRole.AI && textContent.ToolInvocations.Count > 0) + @if (this.Role is ChatRole.AI && textContent.ToolInvocations.Count > 0 && this.showToolTrace) { @string.Format(T("Tool Calls ({0})"), textContent.ToolInvocations.Count) - - @foreach (var invocation in textContent.ToolInvocations.OrderBy(x => x.Order)) - { - - - - @this.GetTraceStatusText(invocation) - - + @foreach (var invocation in textContent.ToolInvocations.OrderBy(x => x.Order)) + { + + + + @($"{invocation.Order}. {invocation.ToolName}") + + @this.GetTraceStatusText(invocation) + + - @if (!string.IsNullOrWhiteSpace(invocation.StatusMessage)) - { - @invocation.StatusMessage - } + @if (!string.IsNullOrWhiteSpace(invocation.StatusMessage)) + { + @invocation.StatusMessage + } - @T("Arguments") - @if (invocation.Arguments.Count == 0) - { - @T("No arguments") - } - else - { - - @foreach (var argument in invocation.Arguments) - { - - @argument.Key: @argument.Value - - } - - } + @T("Arguments") + @if (invocation.Arguments.Count == 0) + { + @T("No arguments") + } + else + { + + @foreach (var argument in invocation.Arguments) + { + + @argument.Key: @argument.Value + + } + + } - @T("Result") - - @invocation.Result - - - } - + @T("Result") + + @invocation.Result + + + } } } } diff --git a/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs b/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs index 92a3cdcb..9e71cf83 100644 --- a/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs +++ b/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs @@ -104,6 +104,7 @@ public partial class ContentBlockComponent : MSGComponentBase, IAsyncDisposable private string lastMathRenderSignature = string.Empty; private bool hasActiveMathContainer; private bool isDisposed; + private bool showToolTrace; #region Overrides of ComponentBase @@ -203,6 +204,7 @@ public partial class ContentBlockComponent : MSGComponentBase, IAsyncDisposable hash.Add(text.ToolInvocations.Count); hash.Add(text.ToolRuntimeStatus.IsRunning); hash.Add(text.ToolRuntimeStatus.Message); + hash.Add(this.showToolTrace); foreach (var invocation in text.ToolInvocations) { hash.Add(invocation.Order); @@ -250,6 +252,28 @@ public partial class ContentBlockComponent : MSGComponentBase, IAsyncDisposable _ => this.T("Unknown"), }; + private IReadOnlyList GetToolInvocations() => this.Content is ContentText textContent + ? textContent.ToolInvocations.OrderBy(x => x.Order).ToList() + : []; + + private IReadOnlyList GetDistinctToolIcons() => this.GetToolInvocations() + .Select(x => x.ToolIcon) + .Distinct(StringComparer.Ordinal) + .ToList(); + + private string GetToolTraceTooltip() + { + var invocations = this.GetToolInvocations(); + return invocations.Count switch + { + 0 => this.T("No tool calls"), + 1 => string.Format(this.T("Show tool call for {0}"), invocations[0].ToolName), + _ => string.Format(this.T("Show {0} tool calls"), invocations.Count), + }; + } + + private void ToggleToolTrace() => this.showToolTrace = !this.showToolTrace; + private MudMarkdownStyling MarkdownStyling => new() { CodeBlock = { Theme = this.CodeColorPalette }, diff --git a/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor b/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor index 202c478b..e47f9e79 100644 --- a/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor +++ b/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor @@ -8,19 +8,33 @@ - @T("Tool") + @T("Icon") + @T("Name") + @T("Description") @T("State") - @T("Actions") + @T("Settings") - - - @context.Definition.DisplayName - + - @(context.ConfigurationState.IsConfigured ? T("Configured") : T("Configuration required")) + @context.Implementation.GetDisplayName() + + + @context.Implementation.GetDescription() + + + @if (context.ConfigurationState.IsConfigured) + { + + } + else + { + + + + } diff --git a/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor.cs b/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor.cs index b399c78e..56f75474 100644 --- a/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor.cs +++ b/app/MindWork AI Studio/Components/Settings/SettingsPanelTools.razor.cs @@ -31,4 +31,19 @@ public partial class SettingsPanelTools : SettingsPanelBase this.items = await this.ToolRegistry.GetCatalogAsync(this.ToolRegistry.GetAllDefinitions()); this.StateHasChanged(); } + + private string GetConfigurationTooltip(ToolCatalogItem item) => item.ConfigurationState.MissingRequiredFields.Count switch + { + 0 => this.T("This tool still needs to be configured."), + _ => string.Format(this.T("Missing required settings: {0}"), string.Join(", ", item.ConfigurationState.MissingRequiredFields.Select(fieldName => this.GetFieldDisplayName(item, fieldName)))) + }; + + private string GetFieldDisplayName(ToolCatalogItem item, string fieldName) + { + var fieldDefinition = item.Definition.SettingsSchema.Properties.GetValueOrDefault(fieldName); + if (fieldDefinition is null) + return fieldName; + + return item.Implementation.GetSettingsFieldLabel(fieldName, fieldDefinition); + } } diff --git a/app/MindWork AI Studio/Components/ToolDefaultsConfiguration.razor.cs b/app/MindWork AI Studio/Components/ToolDefaultsConfiguration.razor.cs index ea3d68da..03d40ca7 100644 --- a/app/MindWork AI Studio/Components/ToolDefaultsConfiguration.razor.cs +++ b/app/MindWork AI Studio/Components/ToolDefaultsConfiguration.razor.cs @@ -25,13 +25,12 @@ public partial class ToolDefaultsConfiguration : MSGComponentBase ? this.T("Choose which tools should be preselected for new chats.") : this.T("Choose which tools should be preselected for new runs of this assistant."); - protected override void OnInitialized() + protected override async Task OnInitializedAsync() { - this.availableTools = this.ToolRegistry - .GetDefinitionsForComponent(this.Component) - .Select(x => new ConfigurationSelectData(x.DisplayName, x.Id)) + this.availableTools = (await this.ToolRegistry.GetCatalogAsync(this.Component)) + .Select(x => new ConfigurationSelectData(x.Implementation.GetDisplayName(), x.Definition.Id)) .ToList(); - base.OnInitialized(); + await base.OnInitializedAsync(); } private HashSet GetSelectedValues() => this.SettingsManager.GetDefaultToolIds(this.Component); diff --git a/app/MindWork AI Studio/Components/ToolSelection.razor b/app/MindWork AI Studio/Components/ToolSelection.razor index c5123d78..fb637856 100644 --- a/app/MindWork AI Studio/Components/ToolSelection.razor +++ b/app/MindWork AI Studio/Components/ToolSelection.razor @@ -42,12 +42,13 @@ - - - @item.Definition.DisplayName + + + @item.Implementation.GetDisplayName() + @item.Implementation.GetDescription() @if (!isConfigured) { @T("Required settings are missing. Configure this tool before enabling it.") @@ -57,6 +58,7 @@ } + @T("Close") diff --git a/app/MindWork AI Studio/Dialogs/Settings/ToolSettingsDialog.razor b/app/MindWork AI Studio/Dialogs/Settings/ToolSettingsDialog.razor index 9f8b9d0b..b41ee616 100644 --- a/app/MindWork AI Studio/Dialogs/Settings/ToolSettingsDialog.razor +++ b/app/MindWork AI Studio/Dialogs/Settings/ToolSettingsDialog.razor @@ -4,8 +4,8 @@ - - @(this.toolDefinition?.DisplayName ?? T("Tool Settings")) + + @(this.implementation?.GetDisplayName() ?? T("Tool Settings")) @@ -15,13 +15,18 @@ } else { + + @this.implementation?.GetDescription() + + + @foreach (var property in this.toolDefinition.SettingsSchema.Properties) { var fieldName = property.Key; var field = property.Value; if (field.EnumValues.Count > 0) { - + @foreach (var option in field.EnumValues) { @option @@ -30,9 +35,10 @@ } else { - + } } + } diff --git a/app/MindWork AI Studio/Dialogs/Settings/ToolSettingsDialog.razor.cs b/app/MindWork AI Studio/Dialogs/Settings/ToolSettingsDialog.razor.cs index cf4c9ded..e4cf432c 100644 --- a/app/MindWork AI Studio/Dialogs/Settings/ToolSettingsDialog.razor.cs +++ b/app/MindWork AI Studio/Dialogs/Settings/ToolSettingsDialog.razor.cs @@ -16,6 +16,7 @@ public partial class ToolSettingsDialog : SettingsDialogBase private ToolSettingsService ToolSettingsService { get; init; } = null!; private ToolDefinition? toolDefinition; + private IToolImplementation? implementation; private Dictionary values = new(StringComparer.Ordinal); protected override async Task OnInitializedAsync() @@ -23,11 +24,20 @@ public partial class ToolSettingsDialog : SettingsDialogBase await base.OnInitializedAsync(); this.toolDefinition = this.ToolRegistry.GetDefinition(this.ToolId); if (this.toolDefinition is not null) + { + this.implementation = this.ToolRegistry.GetImplementation(this.toolDefinition.ImplementationKey); this.values = await this.ToolSettingsService.GetSettingsAsync(this.toolDefinition); + } } private string GetValue(string fieldName) => this.values.GetValueOrDefault(fieldName, string.Empty); + private string GetFieldLabel(string fieldName, ToolSettingsFieldDefinition fieldDefinition) => + this.implementation?.GetSettingsFieldLabel(fieldName, fieldDefinition) ?? fieldDefinition.Title; + + private string GetFieldDescription(string fieldName, ToolSettingsFieldDefinition fieldDefinition) => + this.implementation?.GetSettingsFieldDescription(fieldName, fieldDefinition) ?? fieldDefinition.Description; + private void UpdateValue(string fieldName, string? value) => this.values[fieldName] = value ?? string.Empty; private async Task Save() diff --git a/app/MindWork AI Studio/Provider/BaseProvider.cs b/app/MindWork AI Studio/Provider/BaseProvider.cs index 5f3ba7a0..2488db63 100644 --- a/app/MindWork AI Studio/Provider/BaseProvider.cs +++ b/app/MindWork AI Studio/Provider/BaseProvider.cs @@ -668,7 +668,7 @@ public abstract class BaseProvider : IProvider, ISecretId { IsRunning = true, ToolNames = responseMessage.ToolCalls - .Select(x => runnableTools.FirstOrDefault(tool => tool.Definition.Function.Name.Equals(x.Function.Name, StringComparison.Ordinal)).Definition?.DisplayName ?? x.Function.Name) + .Select(x => runnableTools.FirstOrDefault(tool => tool.Definition.Function.Name.Equals(x.Function.Name, StringComparison.Ordinal)).Implementation?.GetDisplayName() ?? x.Function.Name) .ToList(), }; await currentAssistantContent.StreamingEvent(); diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/GetCurrentWeatherTool.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/GetCurrentWeatherTool.cs index 3098bf52..88666ff1 100644 --- a/app/MindWork AI Studio/Tools/ToolCallingSystem/GetCurrentWeatherTool.cs +++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/GetCurrentWeatherTool.cs @@ -1,13 +1,33 @@ using System.Text.Json; +using AIStudio.Tools.PluginSystem; + namespace AIStudio.Tools.ToolCallingSystem; public sealed class GetCurrentWeatherTool : IToolImplementation { public string ImplementationKey => "get_current_weather"; + public string Icon => Icons.Material.Filled.Cloud; + public IReadOnlySet SensitiveTraceArgumentNames => new HashSet(StringComparer.Ordinal); + public string GetDisplayName() => I18N.I.T("Current Weather", typeof(GetCurrentWeatherTool).Namespace, nameof(GetCurrentWeatherTool)); + + public string GetDescription() => I18N.I.T("Use this demo tool to retrieve the current weather for a given city and state. It is primarily meant to demonstrate tool calling and tool settings in AI Studio.", typeof(GetCurrentWeatherTool).Namespace, nameof(GetCurrentWeatherTool)); + + public string GetSettingsFieldLabel(string fieldName, ToolSettingsFieldDefinition fieldDefinition) => fieldName switch + { + "demoLabel" => I18N.I.T("Demo Label", typeof(GetCurrentWeatherTool).Namespace, nameof(GetCurrentWeatherTool)), + _ => I18N.I.T(fieldDefinition.Title, typeof(GetCurrentWeatherTool).Namespace, nameof(GetCurrentWeatherTool)), + }; + + public string GetSettingsFieldDescription(string fieldName, ToolSettingsFieldDefinition fieldDefinition) => fieldName switch + { + "demoLabel" => I18N.I.T("Required demo setting for validating tool settings in tests. It does not affect the weather result.", typeof(GetCurrentWeatherTool).Namespace, nameof(GetCurrentWeatherTool)), + _ => I18N.I.T(fieldDefinition.Description, typeof(GetCurrentWeatherTool).Namespace, nameof(GetCurrentWeatherTool)), + }; + public Task ExecuteAsync(JsonElement arguments, ToolExecutionContext context, CancellationToken token = default) { var city = arguments.TryGetProperty("city", out var cityValue) ? cityValue.GetString() ?? string.Empty : string.Empty; diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/IToolImplementation.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/IToolImplementation.cs index 1c727ec3..a425a6f5 100644 --- a/app/MindWork AI Studio/Tools/ToolCallingSystem/IToolImplementation.cs +++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/IToolImplementation.cs @@ -1,14 +1,30 @@ using System.Text.Json; +using AIStudio.Tools.PluginSystem; + namespace AIStudio.Tools.ToolCallingSystem; public interface IToolImplementation { public string ImplementationKey { get; } + public string Icon => Icons.Material.Filled.Build; + public IReadOnlySet SensitiveTraceArgumentNames { get; } + public string GetDisplayName() => this.T("Tool"); + + public string GetDescription() => this.T("Tool description"); + + public string GetSettingsFieldLabel(string fieldName, ToolSettingsFieldDefinition fieldDefinition) => + this.T(fieldDefinition.Title); + + public string GetSettingsFieldDescription(string fieldName, ToolSettingsFieldDefinition fieldDefinition) => + this.T(fieldDefinition.Description); + public Task ExecuteAsync(JsonElement arguments, ToolExecutionContext context, CancellationToken token = default); public string FormatTraceResult(string rawResult) => rawResult; + + private string T(string fallbackEN) => I18N.I.T(fallbackEN, this.GetType().Namespace, this.GetType().Name); } diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolExecutionModels.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolExecutionModels.cs index abebc10a..ebd61b1f 100644 --- a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolExecutionModels.cs +++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolExecutionModels.cs @@ -85,6 +85,8 @@ public sealed class ToolCatalogItem { public required ToolDefinition Definition { get; init; } + public required IToolImplementation Implementation { get; init; } + public required ToolConfigurationState ConfigurationState { get; init; } } diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolExecutor.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolExecutor.cs index dbe9f9dc..4c8dd2f0 100644 --- a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolExecutor.cs +++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolExecutor.cs @@ -46,8 +46,8 @@ public sealed class ToolExecutor(ToolSettingsService toolSettingsService) { Order = order, ToolId = definition.Id, - ToolName = definition.DisplayName, - ToolIcon = definition.Icon, + ToolName = implementation.GetDisplayName(), + ToolIcon = implementation.Icon, ToolCallId = toolCallId, Status = ToolInvocationTraceStatus.SUCCESS, WasExecuted = true, @@ -72,8 +72,8 @@ public sealed class ToolExecutor(ToolSettingsService toolSettingsService) { Order = order, ToolId = definition.Id, - ToolName = definition.DisplayName, - ToolIcon = definition.Icon, + ToolName = implementation.GetDisplayName(), + ToolIcon = implementation.Icon, ToolCallId = toolCallId, Status = ToolInvocationTraceStatus.ERROR, StatusMessage = error, diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolRegistry.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolRegistry.cs index 7b4ba943..1b99ea66 100644 --- a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolRegistry.cs +++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolRegistry.cs @@ -69,12 +69,12 @@ public sealed class ToolRegistry var isChat = component is AIStudio.Tools.Components.CHAT; return this.definitionsById.Values .Where(x => isChat ? x.VisibleIn.Chat : x.VisibleIn.Assistants) - .OrderBy(x => x.DisplayName, StringComparer.OrdinalIgnoreCase) + .OrderBy(x => this.implementationsByKey.GetValueOrDefault(x.ImplementationKey)?.GetDisplayName(), StringComparer.OrdinalIgnoreCase) .ToList(); } public IReadOnlyList GetAllDefinitions() => this.definitionsById.Values - .OrderBy(x => x.DisplayName, StringComparer.OrdinalIgnoreCase) + .OrderBy(x => this.implementationsByKey.GetValueOrDefault(x.ImplementationKey)?.GetDisplayName(), StringComparer.OrdinalIgnoreCase) .ToList(); public ToolDefinition? GetDefinition(string toolId) => this.definitionsById.GetValueOrDefault(toolId); @@ -93,9 +93,13 @@ public sealed class ToolRegistry var items = new List(definitionList.Count); foreach (var definition in definitionList) { + if (!this.implementationsByKey.TryGetValue(definition.ImplementationKey, out var implementation)) + continue; + items.Add(new ToolCatalogItem { Definition = definition, + Implementation = implementation, ConfigurationState = await this.toolSettingsService.GetConfigurationStateAsync(definition), }); }