mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-05-21 10:32:16 +00:00
Erste Version des Tool Callings von Codex
This commit is contained in:
parent
f024de8322
commit
447fe9d712
@ -151,6 +151,11 @@
|
|||||||
<ProfileSelection MarginLeft="" @bind-CurrentProfile="@this.currentProfile"/>
|
<ProfileSelection MarginLeft="" @bind-CurrentProfile="@this.currentProfile"/>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if (this.SettingsManager.IsToolSelectionVisible(this.Component))
|
||||||
|
{
|
||||||
|
<ToolSelection Component="@this.Component" LLMProvider="@this.providerSettings" SelectedToolIds="@this.selectedToolIds" SelectedToolIdsChanged="@this.SelectedToolIdsChanged" Disabled="@this.isProcessing" />
|
||||||
|
}
|
||||||
|
|
||||||
<MudSpacer />
|
<MudSpacer />
|
||||||
<HalluzinationReminder ContainerClass="my-0 ml-2"/>
|
<HalluzinationReminder ContainerClass="my-0 ml-2"/>
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
|||||||
@ -93,6 +93,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
|||||||
protected ChatThread? chatThread;
|
protected ChatThread? chatThread;
|
||||||
protected IContent? lastUserPrompt;
|
protected IContent? lastUserPrompt;
|
||||||
protected CancellationTokenSource? cancellationTokenSource;
|
protected CancellationTokenSource? cancellationTokenSource;
|
||||||
|
protected HashSet<string> selectedToolIds = [];
|
||||||
|
|
||||||
private readonly Timer formChangeTimer = new(TimeSpan.FromSeconds(1.6));
|
private readonly Timer formChangeTimer = new(TimeSpan.FromSeconds(1.6));
|
||||||
|
|
||||||
@ -124,6 +125,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
|||||||
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 override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
@ -223,6 +225,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
|||||||
ChatId = Guid.NewGuid(),
|
ChatId = Guid.NewGuid(),
|
||||||
Name = string.Format(this.TB("Assistant - {0}"), this.Title),
|
Name = string.Format(this.TB("Assistant - {0}"), this.Title),
|
||||||
Blocks = [],
|
Blocks = [],
|
||||||
|
RuntimeComponent = this.Component,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,6 +242,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
|||||||
ChatId = chatId,
|
ChatId = chatId,
|
||||||
Name = name,
|
Name = name,
|
||||||
Blocks = [],
|
Blocks = [],
|
||||||
|
RuntimeComponent = this.Component,
|
||||||
};
|
};
|
||||||
|
|
||||||
return chatId;
|
return chatId;
|
||||||
@ -251,6 +255,12 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
|||||||
this.currentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(this.Component);
|
this.currentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(this.Component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Task SelectedToolIdsChanged(HashSet<string> updatedToolIds)
|
||||||
|
{
|
||||||
|
this.selectedToolIds = updatedToolIds;
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
protected DateTimeOffset AddUserRequest(string request, bool hideContentFromUser = false, params List<FileAttachment> attachments)
|
protected DateTimeOffset AddUserRequest(string request, bool hideContentFromUser = false, params List<FileAttachment> attachments)
|
||||||
{
|
{
|
||||||
var time = DateTimeOffset.Now;
|
var time = DateTimeOffset.Now;
|
||||||
@ -297,6 +307,10 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
|||||||
{
|
{
|
||||||
this.chatThread.Blocks.Add(this.resultingContentBlock);
|
this.chatThread.Blocks.Add(this.resultingContentBlock);
|
||||||
this.chatThread.SelectedProvider = this.providerSettings.Id;
|
this.chatThread.SelectedProvider = this.providerSettings.Id;
|
||||||
|
this.chatThread.RuntimeComponent = this.Component;
|
||||||
|
this.chatThread.RuntimeSelectedToolIds = this.SettingsManager.IsToolSelectionVisible(this.Component)
|
||||||
|
? [..this.selectedToolIds]
|
||||||
|
: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isProcessing = true;
|
this.isProcessing = true;
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
using AIStudio.Components;
|
using AIStudio.Components;
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
using AIStudio.Settings.DataModel;
|
using AIStudio.Settings.DataModel;
|
||||||
|
using AIStudio.Tools;
|
||||||
using AIStudio.Tools.ERIClient.DataModel;
|
using AIStudio.Tools.ERIClient.DataModel;
|
||||||
|
|
||||||
namespace AIStudio.Chat;
|
namespace AIStudio.Chat;
|
||||||
@ -80,6 +82,12 @@ public sealed record ChatThread
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public List<ContentBlock> Blocks { get; init; } = [];
|
public List<ContentBlock> Blocks { get; init; } = [];
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public AIStudio.Tools.Components RuntimeComponent { get; set; } = AIStudio.Tools.Components.CHAT;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public HashSet<string> RuntimeSelectedToolIds { get; set; } = [];
|
||||||
|
|
||||||
private bool allowProfile = true;
|
private bool allowProfile = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -115,6 +115,59 @@
|
|||||||
<MudMarkdown Value="@textContent.Sources.ToMarkdown()" Props="Markdown.DefaultConfig" Styling="@this.MarkdownStyling" MarkdownPipeline="Markdown.SAFE_MARKDOWN_PIPELINE" />
|
<MudMarkdown Value="@textContent.Sources.ToMarkdown()" Props="Markdown.DefaultConfig" Styling="@this.MarkdownStyling" MarkdownPipeline="Markdown.SAFE_MARKDOWN_PIPELINE" />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if (this.Role is ChatRole.AI && !string.IsNullOrWhiteSpace(textContent.ToolRuntimeStatus.Message))
|
||||||
|
{
|
||||||
|
<MudAlert Dense="@true" Severity="Severity.Info" Variant="Variant.Outlined" Class="mt-4">
|
||||||
|
@textContent.ToolRuntimeStatus.Message
|
||||||
|
</MudAlert>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (this.Role is ChatRole.AI && textContent.ToolInvocations.Count > 0)
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.subtitle2" Class="mt-4 mb-2">
|
||||||
|
@string.Format(T("Tool Calls ({0})"), textContent.ToolInvocations.Count)
|
||||||
|
</MudText>
|
||||||
|
<MudExpansionPanels MultiExpansion="@true" Class="mt-4">
|
||||||
|
@foreach (var invocation in textContent.ToolInvocations.OrderBy(x => x.Order))
|
||||||
|
{
|
||||||
|
<ExpansionPanel HeaderIcon="@invocation.ToolIcon" HeaderText="@($"{invocation.Order}. {invocation.ToolName}")">
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2" Class="mb-3">
|
||||||
|
<MudChip T="string" Color="@ContentBlockComponent.GetTraceColor(invocation.Status)" Size="Size.Small" Variant="Variant.Outlined">
|
||||||
|
@this.GetTraceStatusText(invocation)
|
||||||
|
</MudChip>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
@if (!string.IsNullOrWhiteSpace(invocation.StatusMessage))
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.body2" Color="Color.Warning" Class="mb-3">@invocation.StatusMessage</MudText>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudText Typo="Typo.subtitle2">@T("Arguments")</MudText>
|
||||||
|
@if (invocation.Arguments.Count == 0)
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.body2" Class="mb-3">@T("No arguments")</MudText>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudList T="string" Dense="@true" Class="mb-3">
|
||||||
|
@foreach (var argument in invocation.Arguments)
|
||||||
|
{
|
||||||
|
<MudListItem T="string">
|
||||||
|
<MudText Typo="Typo.body2"><strong>@argument.Key:</strong> @argument.Value</MudText>
|
||||||
|
</MudListItem>
|
||||||
|
}
|
||||||
|
</MudList>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudText Typo="Typo.subtitle2">@T("Result")</MudText>
|
||||||
|
<MudPaper Class="pa-3 mt-2">
|
||||||
|
<MudText Typo="Typo.body2" Style="white-space: pre-wrap;">@invocation.Result</MudText>
|
||||||
|
</MudPaper>
|
||||||
|
</ExpansionPanel>
|
||||||
|
}
|
||||||
|
</MudExpansionPanels>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
using AIStudio.Components;
|
using AIStudio.Components;
|
||||||
using AIStudio.Dialogs;
|
using AIStudio.Dialogs;
|
||||||
using AIStudio.Tools.Services;
|
using AIStudio.Tools.Services;
|
||||||
|
using AIStudio.Tools.ToolCallingSystem;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
namespace AIStudio.Chat;
|
namespace AIStudio.Chat;
|
||||||
@ -199,6 +200,23 @@ public partial class ContentBlockComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
hash.Add(textValue.Length);
|
hash.Add(textValue.Length);
|
||||||
hash.Add(textValue.GetHashCode(StringComparison.Ordinal));
|
hash.Add(textValue.GetHashCode(StringComparison.Ordinal));
|
||||||
hash.Add(text.Sources.Count);
|
hash.Add(text.Sources.Count);
|
||||||
|
hash.Add(text.ToolInvocations.Count);
|
||||||
|
hash.Add(text.ToolRuntimeStatus.IsRunning);
|
||||||
|
hash.Add(text.ToolRuntimeStatus.Message);
|
||||||
|
foreach (var invocation in text.ToolInvocations)
|
||||||
|
{
|
||||||
|
hash.Add(invocation.Order);
|
||||||
|
hash.Add(invocation.ToolId);
|
||||||
|
hash.Add(invocation.Status);
|
||||||
|
hash.Add(invocation.StatusMessage);
|
||||||
|
hash.Add(invocation.Result);
|
||||||
|
hash.Add(invocation.Arguments.Count);
|
||||||
|
foreach (var argument in invocation.Arguments)
|
||||||
|
{
|
||||||
|
hash.Add(argument.Key);
|
||||||
|
hash.Add(argument.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ContentImage image:
|
case ContentImage image:
|
||||||
@ -216,6 +234,22 @@ public partial class ContentBlockComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
|
|
||||||
private CodeBlockTheme CodeColorPalette => this.SettingsManager.IsDarkMode ? CodeBlockTheme.Dark : CodeBlockTheme.Default;
|
private CodeBlockTheme CodeColorPalette => this.SettingsManager.IsDarkMode ? CodeBlockTheme.Dark : CodeBlockTheme.Default;
|
||||||
|
|
||||||
|
private static Color GetTraceColor(ToolInvocationTraceStatus status) => status switch
|
||||||
|
{
|
||||||
|
ToolInvocationTraceStatus.SUCCESS => Color.Success,
|
||||||
|
ToolInvocationTraceStatus.ERROR => Color.Error,
|
||||||
|
ToolInvocationTraceStatus.BLOCKED => Color.Warning,
|
||||||
|
_ => Color.Default,
|
||||||
|
};
|
||||||
|
|
||||||
|
private string GetTraceStatusText(ToolInvocationTrace trace) => trace.Status switch
|
||||||
|
{
|
||||||
|
ToolInvocationTraceStatus.SUCCESS => this.T("Executed"),
|
||||||
|
ToolInvocationTraceStatus.ERROR => this.T("Failed"),
|
||||||
|
ToolInvocationTraceStatus.BLOCKED => this.T("Blocked"),
|
||||||
|
_ => this.T("Unknown"),
|
||||||
|
};
|
||||||
|
|
||||||
private MudMarkdownStyling MarkdownStyling => new()
|
private MudMarkdownStyling MarkdownStyling => new()
|
||||||
{
|
{
|
||||||
CodeBlock = { Theme = this.CodeColorPalette },
|
CodeBlock = { Theme = this.CodeColorPalette },
|
||||||
|
|||||||
@ -4,6 +4,7 @@ using System.Text.Json.Serialization;
|
|||||||
using AIStudio.Provider;
|
using AIStudio.Provider;
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
using AIStudio.Tools.RAG.RAGProcesses;
|
using AIStudio.Tools.RAG.RAGProcesses;
|
||||||
|
using AIStudio.Tools.ToolCallingSystem;
|
||||||
|
|
||||||
namespace AIStudio.Chat;
|
namespace AIStudio.Chat;
|
||||||
|
|
||||||
@ -44,6 +45,11 @@ public sealed class ContentText : IContent
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public List<FileAttachment> FileAttachments { get; set; } = [];
|
public List<FileAttachment> FileAttachments { get; set; } = [];
|
||||||
|
|
||||||
|
public List<ToolInvocationTrace> ToolInvocations { get; set; } = [];
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public ToolRuntimeStatus ToolRuntimeStatus { get; set; } = new();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastUserPrompt, ChatThread? chatThread, CancellationToken token = default)
|
public async Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastUserPrompt, ChatThread? chatThread, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
@ -145,6 +151,19 @@ public sealed class ContentText : IContent
|
|||||||
IsStreaming = this.IsStreaming,
|
IsStreaming = this.IsStreaming,
|
||||||
Sources = [..this.Sources],
|
Sources = [..this.Sources],
|
||||||
FileAttachments = [..this.FileAttachments],
|
FileAttachments = [..this.FileAttachments],
|
||||||
|
ToolInvocations = [..this.ToolInvocations.Select(x => new ToolInvocationTrace
|
||||||
|
{
|
||||||
|
Order = x.Order,
|
||||||
|
ToolId = x.ToolId,
|
||||||
|
ToolName = x.ToolName,
|
||||||
|
ToolIcon = x.ToolIcon,
|
||||||
|
ToolCallId = x.ToolCallId,
|
||||||
|
Status = x.Status,
|
||||||
|
WasExecuted = x.WasExecuted,
|
||||||
|
StatusMessage = x.StatusMessage,
|
||||||
|
Arguments = new Dictionary<string, string>(x.Arguments, StringComparer.Ordinal),
|
||||||
|
Result = x.Result,
|
||||||
|
})],
|
||||||
};
|
};
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -124,6 +124,8 @@
|
|||||||
|
|
||||||
<ProfileSelection MarginLeft="" CurrentProfile="@this.currentProfile" CurrentProfileChanged="@this.ProfileWasChanged" Disabled="@(!this.currentChatTemplate.AllowProfileUsage)" DisabledText="@T("Profile usage is disabled according to your chat template settings.")"/>
|
<ProfileSelection MarginLeft="" CurrentProfile="@this.currentProfile" CurrentProfileChanged="@this.ProfileWasChanged" Disabled="@(!this.currentChatTemplate.AllowProfileUsage)" DisabledText="@T("Profile usage is disabled according to your chat template settings.")"/>
|
||||||
|
|
||||||
|
<ToolSelection Component="Components.CHAT" LLMProvider="@this.Provider" SelectedToolIds="@this.selectedToolIds" SelectedToolIdsChanged="@this.SelectedToolIdsChanged" Disabled="@this.isStreaming" />
|
||||||
|
|
||||||
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
|
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
|
||||||
{
|
{
|
||||||
<DataSourceSelection @ref="@this.dataSourceSelectionComponent" PopoverTriggerMode="PopoverTriggerMode.BUTTON" LLMProvider="@this.Provider" DataSourceOptions="@this.GetCurrentDataSourceOptions()" DataSourceOptionsChanged="@(async options => await this.SetCurrentDataSourceOptions(options))" DataSourcesAISelected="@this.GetAgentSelectedDataSources()"/>
|
<DataSourceSelection @ref="@this.dataSourceSelectionComponent" PopoverTriggerMode="PopoverTriggerMode.BUTTON" LLMProvider="@this.Provider" DataSourceOptions="@this.GetCurrentDataSourceOptions()" DataSourceOptionsChanged="@(async options => await this.SetCurrentDataSourceOptions(options))" DataSourcesAISelected="@this.GetAgentSelectedDataSources()"/>
|
||||||
|
|||||||
@ -64,6 +64,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
private bool mustLoadChat;
|
private bool mustLoadChat;
|
||||||
private LoadChat loadChat;
|
private LoadChat loadChat;
|
||||||
private bool autoSaveEnabled;
|
private bool autoSaveEnabled;
|
||||||
|
private HashSet<string> selectedToolIds = [];
|
||||||
private string currentWorkspaceName = string.Empty;
|
private string currentWorkspaceName = string.Empty;
|
||||||
private Guid currentWorkspaceId = Guid.Empty;
|
private Guid currentWorkspaceId = Guid.Empty;
|
||||||
private Guid currentChatThreadId = Guid.Empty;
|
private Guid currentChatThreadId = Guid.Empty;
|
||||||
@ -91,6 +92,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
// Get the preselected chat template:
|
// Get the preselected chat template:
|
||||||
this.currentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(Tools.Components.CHAT);
|
this.currentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(Tools.Components.CHAT);
|
||||||
this.userInput = this.currentChatTemplate.PredefinedUserPrompt;
|
this.userInput = this.currentChatTemplate.PredefinedUserPrompt;
|
||||||
|
this.selectedToolIds = this.SettingsManager.GetDefaultToolIds(Tools.Components.CHAT);
|
||||||
|
|
||||||
// Apply template's file attachments, if any:
|
// Apply template's file attachments, if any:
|
||||||
foreach (var attachment in this.currentChatTemplate.FileAttachments)
|
foreach (var attachment in this.currentChatTemplate.FileAttachments)
|
||||||
@ -607,6 +609,8 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
using (this.cancellationTokenSource = new())
|
using (this.cancellationTokenSource = new())
|
||||||
{
|
{
|
||||||
this.StateHasChanged();
|
this.StateHasChanged();
|
||||||
|
this.ChatThread!.RuntimeComponent = Tools.Components.CHAT;
|
||||||
|
this.ChatThread.RuntimeSelectedToolIds = [..this.selectedToolIds];
|
||||||
|
|
||||||
// Use the selected provider to get the AI response.
|
// Use the selected provider to get the AI response.
|
||||||
// By awaiting this line, we wait for the entire
|
// By awaiting this line, we wait for the entire
|
||||||
@ -637,6 +641,12 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
await this.cancellationTokenSource.CancelAsync();
|
await this.cancellationTokenSource.CancelAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Task SelectedToolIdsChanged(HashSet<string> updatedToolIds)
|
||||||
|
{
|
||||||
|
this.selectedToolIds = updatedToolIds;
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SaveThread()
|
private async Task SaveThread()
|
||||||
{
|
{
|
||||||
if(this.ChatThread is null)
|
if(this.ChatThread is null)
|
||||||
@ -700,6 +710,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
|||||||
this.isStreaming = false;
|
this.isStreaming = false;
|
||||||
this.hasUnsavedChanges = false;
|
this.hasUnsavedChanges = false;
|
||||||
this.userInput = string.Empty;
|
this.userInput = string.Empty;
|
||||||
|
this.selectedToolIds = this.SettingsManager.GetDefaultToolIds(Tools.Components.CHAT);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Reset the LLM provider considering the user's settings:
|
// Reset the LLM provider considering the user's settings:
|
||||||
|
|||||||
@ -0,0 +1,30 @@
|
|||||||
|
@using AIStudio.Tools.ToolCallingSystem
|
||||||
|
@inherits SettingsPanelBase
|
||||||
|
|
||||||
|
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Build" HeaderText="@T("Tools")">
|
||||||
|
<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.")
|
||||||
|
</MudText>
|
||||||
|
|
||||||
|
<MudTable Items="@this.items" Hover="@true" Dense="@true">
|
||||||
|
<HeaderContent>
|
||||||
|
<MudTh>@T("Tool")</MudTh>
|
||||||
|
<MudTh>@T("State")</MudTh>
|
||||||
|
<MudTh>@T("Actions")</MudTh>
|
||||||
|
</HeaderContent>
|
||||||
|
<RowTemplate>
|
||||||
|
<MudTd>
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
|
||||||
|
<MudIcon Icon="@context.Definition.Icon" />
|
||||||
|
<MudText Typo="Typo.body1">@context.Definition.DisplayName</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudTd>
|
||||||
|
<MudTd>
|
||||||
|
@(context.ConfigurationState.IsConfigured ? T("Configured") : T("Configuration required"))
|
||||||
|
</MudTd>
|
||||||
|
<MudTd>
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Settings" OnClick="@(async () => await this.OpenSettings(context.Definition.Id))" />
|
||||||
|
</MudTd>
|
||||||
|
</RowTemplate>
|
||||||
|
</MudTable>
|
||||||
|
</ExpansionPanel>
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
using AIStudio.Dialogs.Settings;
|
||||||
|
using AIStudio.Tools;
|
||||||
|
using AIStudio.Tools.ToolCallingSystem;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace AIStudio.Components.Settings;
|
||||||
|
|
||||||
|
public partial class SettingsPanelTools : SettingsPanelBase
|
||||||
|
{
|
||||||
|
[Inject]
|
||||||
|
private ToolRegistry ToolRegistry { get; init; } = null!;
|
||||||
|
|
||||||
|
private IReadOnlyList<ToolCatalogItem> items = [];
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
await base.OnInitializedAsync();
|
||||||
|
this.items = await this.ToolRegistry.GetCatalogAsync(this.ToolRegistry.GetAllDefinitions());
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenSettings(string toolId)
|
||||||
|
{
|
||||||
|
var parameters = new DialogParameters<ToolSettingsDialog>
|
||||||
|
{
|
||||||
|
{ x => x.ToolId, toolId },
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialog = await this.DialogService.ShowAsync<ToolSettingsDialog>(null, parameters, Dialogs.DialogOptions.FULLSCREEN);
|
||||||
|
await dialog.Result;
|
||||||
|
this.items = await this.ToolRegistry.GetCatalogAsync(this.ToolRegistry.GetAllDefinitions());
|
||||||
|
this.StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
@using AIStudio.Tools
|
||||||
|
@using AIStudio.Tools.ToolCallingSystem
|
||||||
|
@inherits MSGComponentBase
|
||||||
|
|
||||||
|
@if (this.availableTools.Count > 0)
|
||||||
|
{
|
||||||
|
@if (this.Component is not Components.CHAT && this.IncludeVisibilityToggle)
|
||||||
|
{
|
||||||
|
<ConfigurationOption OptionDescription="@T("Show tool selection in this assistant?")" LabelOn="@T("Tool selection is visible")" LabelOff="@T("Tool selection is hidden")" State="@(() => this.SettingsManager.IsToolSelectionVisible(this.Component))" StateUpdate="@(value => this.SettingsManager.SetToolSelectionVisibility(this.Component, value))" />
|
||||||
|
}
|
||||||
|
<ConfigurationMultiSelect TData="string" OptionDescription="@this.OptionTitle" SelectedValues="@this.GetSelectedValues" Data="@this.availableTools" SelectionUpdate="@this.UpdateSelection" OptionHelp="@this.OptionHelp" />
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
using AIStudio.Settings;
|
||||||
|
using AIStudio.Tools;
|
||||||
|
using AIStudio.Tools.ToolCallingSystem;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
|
public partial class ToolDefaultsConfiguration : MSGComponentBase
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public AIStudio.Tools.Components Component { get; set; } = AIStudio.Tools.Components.CHAT;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool IncludeVisibilityToggle { get; set; } = true;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private ToolRegistry ToolRegistry { get; init; } = null!;
|
||||||
|
|
||||||
|
private List<ConfigurationSelectData<string>> availableTools = [];
|
||||||
|
|
||||||
|
private string OptionTitle => this.Component is AIStudio.Tools.Components.CHAT ? this.T("Default tools for chat") : this.T("Default tools for this assistant");
|
||||||
|
|
||||||
|
private string OptionHelp => this.Component is AIStudio.Tools.Components.CHAT
|
||||||
|
? 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()
|
||||||
|
{
|
||||||
|
this.availableTools = this.ToolRegistry
|
||||||
|
.GetDefinitionsForComponent(this.Component)
|
||||||
|
.Select(x => new ConfigurationSelectData<string>(x.DisplayName, x.Id))
|
||||||
|
.ToList();
|
||||||
|
base.OnInitialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashSet<string> GetSelectedValues() => this.SettingsManager.GetDefaultToolIds(this.Component);
|
||||||
|
|
||||||
|
private void UpdateSelection(HashSet<string> values) => this.SettingsManager.ConfigurationData.Tools.DefaultToolIdsByComponent[this.Component.ToString()] = [..values];
|
||||||
|
}
|
||||||
64
app/MindWork AI Studio/Components/ToolSelection.razor
Normal file
64
app/MindWork AI Studio/Components/ToolSelection.razor
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
@using AIStudio.Settings
|
||||||
|
@using AIStudio.Tools.ToolCallingSystem
|
||||||
|
@inherits MSGComponentBase
|
||||||
|
|
||||||
|
<div class="d-flex">
|
||||||
|
<MudTooltip Text="@T("Select tools")" Placement="Placement.Top">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Build" Class="@this.PopoverButtonClasses" OnClick="@this.ToggleSelection"/>
|
||||||
|
</MudTooltip>
|
||||||
|
|
||||||
|
<MudPopover Open="@this.showSelection" AnchorOrigin="Origin.TopLeft" TransformOrigin="Origin.BottomLeft" DropShadow="@true" Class="border-solid border-4 rounded-lg">
|
||||||
|
<MudCard>
|
||||||
|
<MudCardHeader>
|
||||||
|
<CardHeaderContent>
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center">
|
||||||
|
<MudText Typo="Typo.h5">@T("Tool Selection")</MudText>
|
||||||
|
<MudSpacer />
|
||||||
|
</MudStack>
|
||||||
|
</CardHeaderContent>
|
||||||
|
</MudCardHeader>
|
||||||
|
<MudCardContent Style="min-width: 28em; max-height: 60vh; max-width: 48vw; overflow: auto;">
|
||||||
|
@if (!this.SupportsTools)
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.body1">@T("The selected provider or model does not support tool calling.")</MudText>
|
||||||
|
}
|
||||||
|
else if (this.Disabled)
|
||||||
|
{
|
||||||
|
<MudAlert Dense="@true" Severity="Severity.Info" Variant="Variant.Outlined" Class="mb-3">
|
||||||
|
@T("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.")
|
||||||
|
</MudAlert>
|
||||||
|
}
|
||||||
|
else if (this.catalog.Count == 0)
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.body1">@T("No tools are available in this context.")</MudText>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (this.SupportsTools && this.catalog.Count > 0)
|
||||||
|
{
|
||||||
|
@foreach (var item in this.catalog)
|
||||||
|
{
|
||||||
|
var isSelected = this.SelectedToolIds.Contains(item.Definition.Id);
|
||||||
|
var isConfigured = item.ConfigurationState.IsConfigured;
|
||||||
|
<MudPaper Class="pa-2 mb-2 border rounded-lg">
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween">
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
|
||||||
|
<MudSwitch T="bool" Value="@isSelected" ValueChanged="@(value => this.ChangeSelection(item.Definition.Id, value))" Disabled="@(!isConfigured || this.Disabled || !this.SupportsTools)" />
|
||||||
|
<MudIcon Icon="@item.Definition.Icon" />
|
||||||
|
<MudText Typo="Typo.body1">@item.Definition.DisplayName</MudText>
|
||||||
|
</MudStack>
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Settings" OnClick="@(async () => await this.OpenSettings(item.Definition.Id))" />
|
||||||
|
</MudStack>
|
||||||
|
@if (!isConfigured)
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.caption" Color="Color.Warning">@T("Required settings are missing. Configure this tool before enabling it.")</MudText>
|
||||||
|
}
|
||||||
|
</MudPaper>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</MudCardContent>
|
||||||
|
<MudCardActions>
|
||||||
|
<MudButton Variant="Variant.Filled" OnClick="@this.Hide">@T("Close")</MudButton>
|
||||||
|
</MudCardActions>
|
||||||
|
</MudCard>
|
||||||
|
</MudPopover>
|
||||||
|
</div>
|
||||||
78
app/MindWork AI Studio/Components/ToolSelection.razor.cs
Normal file
78
app/MindWork AI Studio/Components/ToolSelection.razor.cs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
using AIStudio.Dialogs.Settings;
|
||||||
|
using AIStudio.Provider;
|
||||||
|
using AIStudio.Settings;
|
||||||
|
using AIStudio.Tools;
|
||||||
|
using AIStudio.Tools.ToolCallingSystem;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace AIStudio.Components;
|
||||||
|
|
||||||
|
public partial class ToolSelection : MSGComponentBase
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public AIStudio.Tools.Components Component { get; set; } = AIStudio.Tools.Components.CHAT;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public required AIStudio.Settings.Provider LLMProvider { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public HashSet<string> SelectedToolIds { get; set; } = [];
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback<HashSet<string>> SelectedToolIdsChanged { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool Disabled { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string PopoverButtonClasses { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private ToolRegistry ToolRegistry { get; init; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private IDialogService DialogService { get; init; } = null!;
|
||||||
|
|
||||||
|
private bool showSelection;
|
||||||
|
private IReadOnlyList<ToolCatalogItem> catalog = [];
|
||||||
|
|
||||||
|
private bool SupportsTools =>
|
||||||
|
this.LLMProvider != AIStudio.Settings.Provider.NONE &&
|
||||||
|
this.LLMProvider.GetModelCapabilities().Contains(Capability.CHAT_COMPLETION_API) &&
|
||||||
|
this.LLMProvider.GetModelCapabilities().Contains(Capability.FUNCTION_CALLING);
|
||||||
|
|
||||||
|
private async Task ToggleSelection()
|
||||||
|
{
|
||||||
|
this.showSelection = !this.showSelection;
|
||||||
|
if (this.showSelection)
|
||||||
|
this.catalog = await this.ToolRegistry.GetCatalogAsync(this.Component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Hide() => this.showSelection = false;
|
||||||
|
|
||||||
|
private async Task ChangeSelection(string toolId, bool isSelected)
|
||||||
|
{
|
||||||
|
var updated = new HashSet<string>(this.SelectedToolIds, StringComparer.Ordinal);
|
||||||
|
if (isSelected)
|
||||||
|
updated.Add(toolId);
|
||||||
|
else
|
||||||
|
updated.Remove(toolId);
|
||||||
|
|
||||||
|
this.SelectedToolIds = updated;
|
||||||
|
await this.SelectedToolIdsChanged.InvokeAsync(updated);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenSettings(string toolId)
|
||||||
|
{
|
||||||
|
var parameters = new DialogParameters<ToolSettingsDialog>
|
||||||
|
{
|
||||||
|
{ x => x.ToolId, toolId },
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialog = await this.DialogService.ShowAsync<ToolSettingsDialog>(null, parameters, Dialogs.DialogOptions.FULLSCREEN);
|
||||||
|
await dialog.Result;
|
||||||
|
this.catalog = await this.ToolRegistry.GetCatalogAsync(this.Component);
|
||||||
|
this.StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -36,6 +36,7 @@
|
|||||||
<ConfigurationProviderSelection Component="Components.AGENDA_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.AGENDA_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Agenda.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectedProvider = selectedValue)"/>
|
||||||
<ConfigurationSelect OptionDescription="@T("Preselect a profile")" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" SelectedValue="@(() => ProfilePreselection.FromStoredValue(this.SettingsManager.ConfigurationData.Agenda.PreselectedProfile))" Data="@ConfigurationSelectDataFactory.GetComponentProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectedProfile = selectedValue)" OptionHelp="@T("Choose whether the assistant should use the app default profile, no profile, or a specific profile.")"/>
|
<ConfigurationSelect OptionDescription="@T("Preselect a profile")" Disabled="@(() => !this.SettingsManager.ConfigurationData.Agenda.PreselectOptions)" SelectedValue="@(() => ProfilePreselection.FromStoredValue(this.SettingsManager.ConfigurationData.Agenda.PreselectedProfile))" Data="@ConfigurationSelectDataFactory.GetComponentProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Agenda.PreselectedProfile = selectedValue)" OptionHelp="@T("Choose whether the assistant should use the app default profile, no profile, or a specific profile.")"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
<ToolDefaultsConfiguration Component="Components.AGENDA_ASSISTANT" />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
||||||
|
|||||||
@ -32,6 +32,7 @@
|
|||||||
<ConfigurationProviderSelection Component="Components.BIAS_DAY_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.BIAS_DAY_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.BiasOfTheDay.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</MudField>
|
</MudField>
|
||||||
|
<ToolDefaultsConfiguration Component="Components.BIAS_DAY_ASSISTANT" />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
||||||
|
|||||||
@ -22,6 +22,8 @@
|
|||||||
<ConfigurationSelect OptionDescription="@T("Preselect one of your chat templates?")" Disabled="@(() => !this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectedChatTemplate)" Data="@ConfigurationSelectDataFactory.GetChatTemplatesData(this.SettingsManager.ConfigurationData.ChatTemplates)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.PreselectedChatTemplate = selectedValue)" OptionHelp="@T("Would you like to set one of your chat templates as the default for chats?")"/>
|
<ConfigurationSelect OptionDescription="@T("Preselect one of your chat templates?")" Disabled="@(() => !this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectedChatTemplate)" Data="@ConfigurationSelectDataFactory.GetChatTemplatesData(this.SettingsManager.ConfigurationData.ChatTemplates)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.PreselectedChatTemplate = selectedValue)" OptionHelp="@T("Would you like to set one of your chat templates as the default for chats?")"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
|
||||||
|
<ToolDefaultsConfiguration Component="Components.CHAT" IncludeVisibilityToggle="@false" />
|
||||||
|
|
||||||
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
|
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
|
||||||
{
|
{
|
||||||
<DataSourceSelection SelectionMode="DataSourceSelectionMode.CONFIGURATION_MODE" AutoSaveAppSettings="@true" @bind-DataSourceOptions="@this.SettingsManager.ConfigurationData.Chat.PreselectedDataSourceOptions" ConfigurationHeaderMessage="@T("You can set default data sources and options for new chats. You can change these settings later for each individual chat.")"/>
|
<DataSourceSelection SelectionMode="DataSourceSelectionMode.CONFIGURATION_MODE" AutoSaveAppSettings="@true" @bind-DataSourceOptions="@this.SettingsManager.ConfigurationData.Chat.PreselectedDataSourceOptions" ConfigurationHeaderMessage="@T("You can set default data sources and options for new chats. You can change these settings later for each individual chat.")"/>
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
<ConfigurationProviderSelection Component="Components.CODING_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Coding.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.CODING_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Coding.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Coding.PreselectedProvider = selectedValue)"/>
|
||||||
<ConfigurationSelect OptionDescription="@T("Preselect a profile")" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" SelectedValue="@(() => ProfilePreselection.FromStoredValue(this.SettingsManager.ConfigurationData.Coding.PreselectedProfile))" Data="@ConfigurationSelectDataFactory.GetComponentProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Coding.PreselectedProfile = selectedValue)" OptionHelp="@T("Choose whether the assistant should use the app default profile, no profile, or a specific profile.")"/>
|
<ConfigurationSelect OptionDescription="@T("Preselect a profile")" Disabled="@(() => !this.SettingsManager.ConfigurationData.Coding.PreselectOptions)" SelectedValue="@(() => ProfilePreselection.FromStoredValue(this.SettingsManager.ConfigurationData.Coding.PreselectedProfile))" Data="@ConfigurationSelectDataFactory.GetComponentProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Coding.PreselectedProfile = selectedValue)" OptionHelp="@T("Choose whether the assistant should use the app default profile, no profile, or a specific profile.")"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
<ToolDefaultsConfiguration Component="Components.CODING_ASSISTANT" />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">Close</MudButton>
|
<MudButton OnClick="@this.Close" Variant="Variant.Filled">Close</MudButton>
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.GrammarSpelling.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.GrammarSpelling.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.GRAMMAR_SPELLING_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.GRAMMAR_SPELLING_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.GrammarSpelling.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
<ToolDefaultsConfiguration Component="Components.GRAMMAR_SPELLING_ASSISTANT" />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
<ConfigurationSelect OptionDescription="@T("Language plugin used for comparision")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.I18N.PreselectedLanguagePluginId)" Data="@ConfigurationSelectDataFactory.GetLanguagesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.I18N.PreselectedLanguagePluginId = selectedValue)" OptionHelp="@T("Select the language plugin used for comparision.")"/>
|
<ConfigurationSelect OptionDescription="@T("Language plugin used for comparision")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.I18N.PreselectedLanguagePluginId)" Data="@ConfigurationSelectDataFactory.GetLanguagesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.I18N.PreselectedLanguagePluginId = selectedValue)" OptionHelp="@T("Select the language plugin used for comparision.")"/>
|
||||||
<ConfigurationProviderSelection Component="Components.I18N_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.I18N.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.I18N.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.I18N.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.I18N_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.I18N.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.I18N.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.I18N.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
<ToolDefaultsConfiguration Component="Components.I18N_ASSISTANT" />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.IconFinder.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.IconFinder.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.IconFinder.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.IconFinder.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.ICON_FINDER_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.IconFinder.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.IconFinder.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.ICON_FINDER_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.IconFinder.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.IconFinder.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.IconFinder.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
<ToolDefaultsConfiguration Component="Components.ICON_FINDER_ASSISTANT" />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.JobPostings.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.JobPostings.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.JobPostings.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.JobPostings.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.JOB_POSTING_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.JobPostings.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.JobPostings.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.JOB_POSTING_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.JobPostings.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.JobPostings.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.JobPostings.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
<ToolDefaultsConfiguration Component="Components.JOB_POSTING_ASSISTANT" />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
<ConfigurationProviderSelection Component="Components.LEGAL_CHECK_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.LEGAL_CHECK_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProvider = selectedValue)"/>
|
||||||
<ConfigurationSelect OptionDescription="@T("Preselect a profile")" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)" SelectedValue="@(() => ProfilePreselection.FromStoredValue(this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProfile))" Data="@ConfigurationSelectDataFactory.GetComponentProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProfile = selectedValue)" OptionHelp="@T("Choose whether the assistant should use the app default profile, no profile, or a specific profile.")"/>
|
<ConfigurationSelect OptionDescription="@T("Preselect a profile")" Disabled="@(() => !this.SettingsManager.ConfigurationData.LegalCheck.PreselectOptions)" SelectedValue="@(() => ProfilePreselection.FromStoredValue(this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProfile))" Data="@ConfigurationSelectDataFactory.GetComponentProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.LegalCheck.PreselectedProfile = selectedValue)" OptionHelp="@T("Choose whether the assistant should use the app default profile, no profile, or a specific profile.")"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
<ToolDefaultsConfiguration Component="Components.LEGAL_CHECK_ASSISTANT" />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.MyTasks.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.MyTasks.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.MyTasks.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.MyTasks.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.MY_TASKS_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.MY_TASKS_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.MyTasks.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.MyTasks.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
<ToolDefaultsConfiguration Component="Components.MY_TASKS_ASSISTANT" />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.REWRITE_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.REWRITE_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.RewriteImprove.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.RewriteImprove.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
<ToolDefaultsConfiguration Component="Components.REWRITE_ASSISTANT" />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
<ConfigurationProviderSelection Component="Components.SLIDE_BUILDER_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.SlideBuilder.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.SlideBuilder.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.SlideBuilder.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.SLIDE_BUILDER_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.SlideBuilder.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.SlideBuilder.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.SlideBuilder.PreselectedProvider = selectedValue)"/>
|
||||||
<ConfigurationSelect OptionDescription="@T("Preselect a profile")" Disabled="@(() => !this.SettingsManager.ConfigurationData.SlideBuilder.PreselectOptions)" SelectedValue="@(() => ProfilePreselection.FromStoredValue(this.SettingsManager.ConfigurationData.SlideBuilder.PreselectedProfile))" Data="@ConfigurationSelectDataFactory.GetComponentProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.SlideBuilder.PreselectedProfile = selectedValue)" OptionHelp="@T("Choose whether the assistant should use the app default profile, no profile, or a specific profile.")"/>
|
<ConfigurationSelect OptionDescription="@T("Preselect a profile")" Disabled="@(() => !this.SettingsManager.ConfigurationData.SlideBuilder.PreselectOptions)" SelectedValue="@(() => ProfilePreselection.FromStoredValue(this.SettingsManager.ConfigurationData.SlideBuilder.PreselectedProfile))" Data="@ConfigurationSelectDataFactory.GetComponentProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.SlideBuilder.PreselectedProfile = selectedValue)" OptionHelp="@T("Choose whether the assistant should use the app default profile, no profile, or a specific profile.")"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
<ToolDefaultsConfiguration Component="Components.SLIDE_BUILDER_ASSISTANT" />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.Synonyms.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Synonyms.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Synonyms.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.Synonyms.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Synonyms.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Synonyms.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.SYNONYMS_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Synonyms.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Synonyms.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Synonyms.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.SYNONYMS_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Synonyms.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Synonyms.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Synonyms.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
<ToolDefaultsConfiguration Component="Components.SYNONYMS_ASSISTANT" />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.TextSummarizer.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.TextSummarizer.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.TextSummarizer.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.TextSummarizer.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.TEXT_SUMMARIZER_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.TEXT_SUMMARIZER_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.TextSummarizer.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.TextSummarizer.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
<ToolDefaultsConfiguration Component="Components.TEXT_SUMMARIZER_ASSISTANT" />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
||||||
|
|||||||
@ -23,6 +23,7 @@
|
|||||||
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.Translation.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Translation.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Translation.MinimumProviderConfidence = selectedValue)"/>
|
<ConfigurationMinConfidenceSelection Disabled="@(() => !this.SettingsManager.ConfigurationData.Translation.PreselectOptions)" RestrictToGlobalMinimumConfidence="@true" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Translation.MinimumProviderConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Translation.MinimumProviderConfidence = selectedValue)"/>
|
||||||
<ConfigurationProviderSelection Component="Components.TRANSLATION_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Translation.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Translation.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Translation.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.TRANSLATION_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Translation.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Translation.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Translation.PreselectedProvider = selectedValue)"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
<ToolDefaultsConfiguration Component="Components.TRANSLATION_ASSISTANT" />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
||||||
|
|||||||
@ -23,6 +23,7 @@
|
|||||||
<ConfigurationProviderSelection Component="Components.EMAIL_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.PreselectedProvider = selectedValue)"/>
|
<ConfigurationProviderSelection Component="Components.EMAIL_ASSISTANT" Data="@this.availableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.EMail.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.PreselectedProvider = selectedValue)"/>
|
||||||
<ConfigurationSelect OptionDescription="@T("Preselect a profile")" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" SelectedValue="@(() => ProfilePreselection.FromStoredValue(this.SettingsManager.ConfigurationData.EMail.PreselectedProfile))" Data="@ConfigurationSelectDataFactory.GetComponentProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.PreselectedProfile = selectedValue)" OptionHelp="@T("Choose whether the assistant should use the app default profile, no profile, or a specific profile.")"/>
|
<ConfigurationSelect OptionDescription="@T("Preselect a profile")" Disabled="@(() => !this.SettingsManager.ConfigurationData.EMail.PreselectOptions)" SelectedValue="@(() => ProfilePreselection.FromStoredValue(this.SettingsManager.ConfigurationData.EMail.PreselectedProfile))" Data="@ConfigurationSelectDataFactory.GetComponentProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.EMail.PreselectedProfile = selectedValue)" OptionHelp="@T("Choose whether the assistant should use the app default profile, no profile, or a specific profile.")"/>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
|
<ToolDefaultsConfiguration Component="Components.EMAIL_ASSISTANT" />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
||||||
|
|||||||
@ -0,0 +1,46 @@
|
|||||||
|
@using AIStudio.Tools.ToolCallingSystem
|
||||||
|
@inherits SettingsDialogBase
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<TitleContent>
|
||||||
|
<MudText Typo="Typo.h6" Class="d-flex align-center">
|
||||||
|
<MudIcon Icon="@this.toolDefinition?.Icon" Class="mr-2" />
|
||||||
|
@(this.toolDefinition?.DisplayName ?? T("Tool Settings"))
|
||||||
|
</MudText>
|
||||||
|
</TitleContent>
|
||||||
|
<DialogContent>
|
||||||
|
@if (this.toolDefinition is null)
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.body1">@T("The selected tool could not be loaded.")</MudText>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@foreach (var property in this.toolDefinition.SettingsSchema.Properties)
|
||||||
|
{
|
||||||
|
var fieldName = property.Key;
|
||||||
|
var field = property.Value;
|
||||||
|
if (field.EnumValues.Count > 0)
|
||||||
|
{
|
||||||
|
<MudSelect T="string" Label="@field.Title" Value="@this.GetValue(fieldName)" ValueChanged="@(value => this.UpdateValue(fieldName, value))" Variant="Variant.Outlined" Margin="Margin.Dense" Class="mb-3">
|
||||||
|
@foreach (var option in field.EnumValues)
|
||||||
|
{
|
||||||
|
<MudSelectItem T="string" Value="@option">@option</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudTextField T="string" Label="@field.Title" Value="@this.GetValue(fieldName)" ValueChanged="@(value => this.UpdateValue(fieldName, value))" Variant="Variant.Outlined" Margin="Margin.Dense" Class="mb-3" HelperText="@field.Description" InputType="@(field.Secret ? InputType.Password : InputType.Text)" />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<MudButton OnClick="@this.Close" Variant="Variant.Text">
|
||||||
|
@T("Cancel")
|
||||||
|
</MudButton>
|
||||||
|
<MudButton OnClick="@this.Save" Variant="Variant.Filled" Disabled="@(this.toolDefinition is null)">
|
||||||
|
@T("Save")
|
||||||
|
</MudButton>
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
using AIStudio.Tools.ToolCallingSystem;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace AIStudio.Dialogs.Settings;
|
||||||
|
|
||||||
|
public partial class ToolSettingsDialog : SettingsDialogBase
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public string ToolId { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private ToolRegistry ToolRegistry { get; init; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private ToolSettingsService ToolSettingsService { get; init; } = null!;
|
||||||
|
|
||||||
|
private ToolDefinition? toolDefinition;
|
||||||
|
private Dictionary<string, string> values = new(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
await base.OnInitializedAsync();
|
||||||
|
this.toolDefinition = this.ToolRegistry.GetDefinition(this.ToolId);
|
||||||
|
if (this.toolDefinition is not null)
|
||||||
|
this.values = await this.ToolSettingsService.GetSettingsAsync(this.toolDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetValue(string fieldName) => this.values.GetValueOrDefault(fieldName, string.Empty);
|
||||||
|
|
||||||
|
private void UpdateValue(string fieldName, string? value) => this.values[fieldName] = value ?? string.Empty;
|
||||||
|
|
||||||
|
private async Task Save()
|
||||||
|
{
|
||||||
|
if (this.toolDefinition is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await this.ToolSettingsService.SaveSettingsAsync(this.toolDefinition, this.values);
|
||||||
|
this.MudDialog.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -21,6 +21,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
<SettingsPanelApp AvailableLLMProvidersFunc="@(() => this.availableLLMProviders)"/>
|
<SettingsPanelApp AvailableLLMProvidersFunc="@(() => this.availableLLMProviders)"/>
|
||||||
|
<SettingsPanelTools />
|
||||||
|
|
||||||
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
|
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using AIStudio.Agents;
|
using AIStudio.Agents;
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
|
using AIStudio.Tools.ToolCallingSystem;
|
||||||
using AIStudio.Tools.Databases;
|
using AIStudio.Tools.Databases;
|
||||||
using AIStudio.Tools.Databases.Qdrant;
|
using AIStudio.Tools.Databases.Qdrant;
|
||||||
using AIStudio.Tools.PluginSystem;
|
using AIStudio.Tools.PluginSystem;
|
||||||
@ -168,6 +169,10 @@ internal sealed class Program
|
|||||||
builder.Services.AddSingleton(rust);
|
builder.Services.AddSingleton(rust);
|
||||||
builder.Services.AddMudMarkdownClipboardService<MarkdownClipboardService>();
|
builder.Services.AddMudMarkdownClipboardService<MarkdownClipboardService>();
|
||||||
builder.Services.AddSingleton<SettingsManager>();
|
builder.Services.AddSingleton<SettingsManager>();
|
||||||
|
builder.Services.AddSingleton<ToolSettingsService>();
|
||||||
|
builder.Services.AddSingleton<IToolImplementation, GetCurrentWeatherTool>();
|
||||||
|
builder.Services.AddSingleton<ToolRegistry>();
|
||||||
|
builder.Services.AddSingleton<ToolExecutor>();
|
||||||
builder.Services.AddSingleton<ThreadSafeRandom>();
|
builder.Services.AddSingleton<ThreadSafeRandom>();
|
||||||
builder.Services.AddSingleton<VoiceRecordingAvailabilityService>();
|
builder.Services.AddSingleton<VoiceRecordingAvailabilityService>();
|
||||||
builder.Services.AddSingleton<DataSourceService>();
|
builder.Services.AddSingleton<DataSourceService>();
|
||||||
|
|||||||
@ -22,29 +22,22 @@ public sealed class ProviderAlibabaCloud() : BaseProvider(LLMProviders.ALIBABA_C
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
{
|
{
|
||||||
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
||||||
"AlibabaCloud",
|
"AlibabaCloud",
|
||||||
chatModel,
|
chatModel,
|
||||||
chatThread,
|
chatThread,
|
||||||
settingsManager,
|
settingsManager,
|
||||||
async (systemPrompt, apiParameters) =>
|
() => chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel),
|
||||||
{
|
(systemPrompt, messages, apiParameters, stream, tools) =>
|
||||||
// Build the list of messages:
|
Task.FromResult(new ChatCompletionAPIRequest
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
|
||||||
|
|
||||||
return new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
Messages = [systemPrompt, ..messages],
|
||||||
|
Stream = stream,
|
||||||
Stream = true,
|
Tools = tools,
|
||||||
|
ParallelToolCalls = tools is null ? null : true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
token: token))
|
token: token))
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,11 +10,14 @@ using AIStudio.Provider.Anthropic;
|
|||||||
using AIStudio.Provider.OpenAI;
|
using AIStudio.Provider.OpenAI;
|
||||||
using AIStudio.Provider.SelfHosted;
|
using AIStudio.Provider.SelfHosted;
|
||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
|
using AIStudio.Tools.ToolCallingSystem;
|
||||||
using AIStudio.Tools.MIME;
|
using AIStudio.Tools.MIME;
|
||||||
using AIStudio.Tools.PluginSystem;
|
using AIStudio.Tools.PluginSystem;
|
||||||
using AIStudio.Tools.Rust;
|
using AIStudio.Tools.Rust;
|
||||||
using AIStudio.Tools.Services;
|
using AIStudio.Tools.Services;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
using Host = AIStudio.Provider.SelfHosted.Host;
|
using Host = AIStudio.Provider.SelfHosted.Host;
|
||||||
|
|
||||||
namespace AIStudio.Provider;
|
namespace AIStudio.Provider;
|
||||||
@ -572,6 +575,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
/// <param name="chatModel">The selected chat model.</param>
|
/// <param name="chatModel">The selected chat model.</param>
|
||||||
/// <param name="chatThread">The current chat thread.</param>
|
/// <param name="chatThread">The current chat thread.</param>
|
||||||
/// <param name="settingsManager">The settings manager.</param>
|
/// <param name="settingsManager">The settings manager.</param>
|
||||||
|
/// <param name="messagesFactory">Builds the provider-specific base messages.</param>
|
||||||
/// <param name="requestFactory">Builds the provider-specific request body.</param>
|
/// <param name="requestFactory">Builds the provider-specific request body.</param>
|
||||||
/// <param name="storeType">The secret store type.</param>
|
/// <param name="storeType">The secret store type.</param>
|
||||||
/// <param name="isTryingSecret">Whether the API key is optional.</param>
|
/// <param name="isTryingSecret">Whether the API key is optional.</param>
|
||||||
@ -579,16 +583,16 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
/// <param name="requestPath">The request path, relative to the provider base URL.</param>
|
/// <param name="requestPath">The request path, relative to the provider base URL.</param>
|
||||||
/// <param name="headersAction">Optional additional headers to add.</param>
|
/// <param name="headersAction">Optional additional headers to add.</param>
|
||||||
/// <param name="token">The cancellation token.</param>
|
/// <param name="token">The cancellation token.</param>
|
||||||
/// <typeparam name="TRequest">The request DTO type.</typeparam>
|
|
||||||
/// <typeparam name="TDelta">The delta stream line type.</typeparam>
|
/// <typeparam name="TDelta">The delta stream line type.</typeparam>
|
||||||
/// <typeparam name="TAnnotation">The annotation stream line type.</typeparam>
|
/// <typeparam name="TAnnotation">The annotation stream line type.</typeparam>
|
||||||
/// <returns>The streamed content chunks.</returns>
|
/// <returns>The streamed content chunks.</returns>
|
||||||
protected async IAsyncEnumerable<ContentStreamChunk> StreamOpenAICompatibleChatCompletion<TRequest, TDelta, TAnnotation>(
|
protected async IAsyncEnumerable<ContentStreamChunk> StreamOpenAICompatibleChatCompletion<TDelta, TAnnotation>(
|
||||||
string providerName,
|
string providerName,
|
||||||
Model chatModel,
|
Model chatModel,
|
||||||
ChatThread chatThread,
|
ChatThread chatThread,
|
||||||
SettingsManager settingsManager,
|
SettingsManager settingsManager,
|
||||||
Func<TextMessage, IDictionary<string, object>, Task<TRequest>> requestFactory,
|
Func<Task<IList<IMessageBase>>> messagesFactory,
|
||||||
|
Func<TextMessage, IList<IMessageBase>, IDictionary<string, object>, bool, IList<object>?, Task<ChatCompletionAPIRequest>> requestFactory,
|
||||||
SecretStoreType storeType = SecretStoreType.LLM_PROVIDER,
|
SecretStoreType storeType = SecretStoreType.LLM_PROVIDER,
|
||||||
bool isTryingSecret = false,
|
bool isTryingSecret = false,
|
||||||
string systemPromptRole = "system",
|
string systemPromptRole = "system",
|
||||||
@ -613,8 +617,114 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
// Parse the API parameters:
|
// Parse the API parameters:
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
var apiParameters = this.ParseAdditionalApiParameters();
|
||||||
|
|
||||||
|
var baseMessages = await messagesFactory();
|
||||||
|
var toolRegistry = Program.SERVICE_PROVIDER.GetService<ToolRegistry>();
|
||||||
|
var toolExecutor = Program.SERVICE_PROVIDER.GetService<ToolExecutor>();
|
||||||
|
var currentAssistantContent = chatThread.Blocks.LastOrDefault(x => x.Role is ChatRole.AI)?.Content as ContentText;
|
||||||
|
currentAssistantContent?.ToolInvocations.Clear();
|
||||||
|
|
||||||
|
if (toolRegistry is not null && toolExecutor is not null)
|
||||||
|
{
|
||||||
|
var runnableTools = await toolRegistry.GetRunnableToolsAsync(
|
||||||
|
chatThread.RuntimeComponent,
|
||||||
|
chatThread.RuntimeSelectedToolIds,
|
||||||
|
this.Provider.GetModelCapabilities(chatModel),
|
||||||
|
settingsManager.IsToolSelectionVisible(chatThread.RuntimeComponent));
|
||||||
|
|
||||||
|
if (runnableTools.Count > 0)
|
||||||
|
{
|
||||||
|
var providerTools = runnableTools.Select(x => (object)new
|
||||||
|
{
|
||||||
|
type = "function",
|
||||||
|
function = new
|
||||||
|
{
|
||||||
|
name = x.Definition.Function.Name,
|
||||||
|
description = x.Definition.Function.Description,
|
||||||
|
parameters = x.Definition.Function.Parameters,
|
||||||
|
strict = x.Definition.Function.Strict,
|
||||||
|
}
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
var internalMessages = new List<IMessageBase>();
|
||||||
|
var toolCallCount = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var requestDto = await requestFactory(systemPrompt, [..baseMessages, ..internalMessages], apiParameters, false, providerTools);
|
||||||
|
var response = await this.ExecuteChatCompletionRequest(requestDto, requestPath, requestedSecret, headersAction, token);
|
||||||
|
var responseMessage = response?.Choices.FirstOrDefault()?.Message;
|
||||||
|
if (responseMessage is null)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
if (responseMessage.ToolCalls.Count == 0)
|
||||||
|
{
|
||||||
|
currentAssistantContent!.ToolRuntimeStatus = new();
|
||||||
|
if (!string.IsNullOrWhiteSpace(responseMessage.Content))
|
||||||
|
yield return new ContentStreamChunk(responseMessage.Content, []);
|
||||||
|
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAssistantContent!.ToolRuntimeStatus = new ToolRuntimeStatus
|
||||||
|
{
|
||||||
|
IsRunning = true,
|
||||||
|
ToolNames = responseMessage.ToolCalls
|
||||||
|
.Select(x => runnableTools.FirstOrDefault(tool => tool.Definition.Function.Name.Equals(x.Function.Name, StringComparison.Ordinal)).Definition?.DisplayName ?? x.Function.Name)
|
||||||
|
.ToList(),
|
||||||
|
};
|
||||||
|
await currentAssistantContent.StreamingEvent();
|
||||||
|
|
||||||
|
internalMessages.Add(new AssistantToolCallMessage
|
||||||
|
{
|
||||||
|
Content = responseMessage.Content,
|
||||||
|
ToolCalls = responseMessage.ToolCalls,
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach (var toolCall in responseMessage.ToolCalls)
|
||||||
|
{
|
||||||
|
toolCallCount++;
|
||||||
|
if (toolCallCount > 10)
|
||||||
|
{
|
||||||
|
var limitMessage = "Tool calling stopped because the maximum of 10 tool calls was reached.";
|
||||||
|
currentAssistantContent.ToolInvocations.Add(new ToolInvocationTrace
|
||||||
|
{
|
||||||
|
Order = toolCallCount,
|
||||||
|
ToolId = toolCall.Function.Name,
|
||||||
|
ToolName = toolCall.Function.Name,
|
||||||
|
ToolCallId = toolCall.Id,
|
||||||
|
Status = ToolInvocationTraceStatus.BLOCKED,
|
||||||
|
StatusMessage = limitMessage,
|
||||||
|
Result = limitMessage,
|
||||||
|
});
|
||||||
|
currentAssistantContent.ToolRuntimeStatus = new();
|
||||||
|
await currentAssistantContent.StreamingEvent();
|
||||||
|
yield return new ContentStreamChunk(limitMessage, []);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (toolContent, trace) = await toolExecutor.ExecuteAsync(
|
||||||
|
toolCall.Id,
|
||||||
|
toolCall.Function.Name,
|
||||||
|
toolCall.Function.Arguments,
|
||||||
|
runnableTools,
|
||||||
|
toolCallCount,
|
||||||
|
token);
|
||||||
|
|
||||||
|
currentAssistantContent.ToolInvocations.Add(trace);
|
||||||
|
internalMessages.Add(new ToolResultMessage
|
||||||
|
{
|
||||||
|
Content = toolContent,
|
||||||
|
ToolCallId = toolCall.Id,
|
||||||
|
Name = toolCall.Function.Name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await currentAssistantContent.StreamingEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare the provider HTTP chat request:
|
// Prepare the provider HTTP chat request:
|
||||||
var providerChatRequest = JsonSerializer.Serialize(await requestFactory(systemPrompt, apiParameters), JSON_SERIALIZER_OPTIONS);
|
var providerChatRequest = JsonSerializer.Serialize(await requestFactory(systemPrompt, baseMessages, apiParameters, true, null), JSON_SERIALIZER_OPTIONS);
|
||||||
|
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
async Task<HttpRequestMessage> RequestBuilder()
|
||||||
{
|
{
|
||||||
@ -637,6 +747,27 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<ChatCompletionResponse?> ExecuteChatCompletionRequest(
|
||||||
|
ChatCompletionAPIRequest requestDto,
|
||||||
|
string requestPath,
|
||||||
|
RequestedSecret requestedSecret,
|
||||||
|
Action<HttpRequestHeaders>? headersAction,
|
||||||
|
CancellationToken token)
|
||||||
|
{
|
||||||
|
using var request = new HttpRequestMessage(HttpMethod.Post, requestPath);
|
||||||
|
if (requestedSecret.Success)
|
||||||
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
||||||
|
|
||||||
|
headersAction?.Invoke(request.Headers);
|
||||||
|
request.Content = new StringContent(JsonSerializer.Serialize(requestDto, JSON_SERIALIZER_OPTIONS), Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
using var response = await this.httpClient.SendAsync(request, token);
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return await response.Content.ReadFromJsonAsync<ChatCompletionResponse>(JSON_SERIALIZER_OPTIONS, token);
|
||||||
|
}
|
||||||
|
|
||||||
protected async Task<string> PerformStandardTranscriptionRequest(RequestedSecret requestedSecret, Model transcriptionModel, string audioFilePath, Host host = Host.NONE, CancellationToken token = default)
|
protected async Task<string> PerformStandardTranscriptionRequest(RequestedSecret requestedSecret, Model transcriptionModel, string audioFilePath, Host host = Host.NONE, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@ -22,29 +22,22 @@ public sealed class ProviderDeepSeek() : BaseProvider(LLMProviders.DEEP_SEEK, "h
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
{
|
{
|
||||||
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
||||||
"DeepSeek",
|
"DeepSeek",
|
||||||
chatModel,
|
chatModel,
|
||||||
chatThread,
|
chatThread,
|
||||||
settingsManager,
|
settingsManager,
|
||||||
async (systemPrompt, apiParameters) =>
|
() => chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel),
|
||||||
{
|
(systemPrompt, messages, apiParameters, stream, tools) =>
|
||||||
// Build the list of messages:
|
Task.FromResult(new ChatCompletionAPIRequest
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel);
|
|
||||||
|
|
||||||
return new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
Messages = [systemPrompt, ..messages],
|
||||||
|
Stream = stream,
|
||||||
Stream = true,
|
Tools = tools,
|
||||||
|
ParallelToolCalls = tools is null ? null : true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
token: token))
|
token: token))
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,30 +21,22 @@ public class ProviderFireworks() : BaseProvider(LLMProviders.FIREWORKS, "https:/
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
{
|
{
|
||||||
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ResponseStreamLine, ChatCompletionAnnotationStreamLine>(
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ResponseStreamLine, ChatCompletionAnnotationStreamLine>(
|
||||||
"Fireworks",
|
"Fireworks",
|
||||||
chatModel,
|
chatModel,
|
||||||
chatThread,
|
chatThread,
|
||||||
settingsManager,
|
settingsManager,
|
||||||
async (systemPrompt, apiParameters) =>
|
() => chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel),
|
||||||
{
|
(systemPrompt, messages, apiParameters, stream, tools) =>
|
||||||
// Build the list of messages:
|
Task.FromResult(new ChatCompletionAPIRequest
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
|
||||||
|
|
||||||
return new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
Messages = [systemPrompt, ..messages],
|
||||||
|
Stream = stream,
|
||||||
// Right now, we only support streaming completions:
|
Tools = tools,
|
||||||
Stream = true,
|
ParallelToolCalls = tools is null ? null : true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
token: token))
|
token: token))
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,29 +22,22 @@ public sealed class ProviderGWDG() : BaseProvider(LLMProviders.GWDG, "https://ch
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
{
|
{
|
||||||
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>(
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>(
|
||||||
"GWDG",
|
"GWDG",
|
||||||
chatModel,
|
chatModel,
|
||||||
chatThread,
|
chatThread,
|
||||||
settingsManager,
|
settingsManager,
|
||||||
async (systemPrompt, apiParameters) =>
|
() => chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel),
|
||||||
{
|
(systemPrompt, messages, apiParameters, stream, tools) =>
|
||||||
// Build the list of messages:
|
Task.FromResult(new ChatCompletionAPIRequest
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
|
||||||
|
|
||||||
return new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
Messages = [systemPrompt, ..messages],
|
||||||
|
Stream = stream,
|
||||||
Stream = true,
|
Tools = tools,
|
||||||
|
ParallelToolCalls = tools is null ? null : true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
token: token))
|
token: token))
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,30 +24,22 @@ public class ProviderGoogle() : BaseProvider(LLMProviders.GOOGLE, "https://gener
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
{
|
{
|
||||||
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
||||||
"Google",
|
"Google",
|
||||||
chatModel,
|
chatModel,
|
||||||
chatThread,
|
chatThread,
|
||||||
settingsManager,
|
settingsManager,
|
||||||
async (systemPrompt, apiParameters) =>
|
() => chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel),
|
||||||
{
|
(systemPrompt, messages, apiParameters, stream, tools) =>
|
||||||
// Build the list of messages:
|
Task.FromResult(new ChatCompletionAPIRequest
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
|
||||||
|
|
||||||
return new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
Messages = [systemPrompt, ..messages],
|
||||||
|
Stream = stream,
|
||||||
// Right now, we only support streaming completions:
|
Tools = tools,
|
||||||
Stream = true,
|
ParallelToolCalls = tools is null ? null : true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
token: token))
|
token: token))
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,32 +22,26 @@ public class ProviderGroq() : BaseProvider(LLMProviders.GROQ, "https://api.groq.
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
{
|
{
|
||||||
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>(
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>(
|
||||||
"Groq",
|
"Groq",
|
||||||
chatModel,
|
chatModel,
|
||||||
chatThread,
|
chatThread,
|
||||||
settingsManager,
|
settingsManager,
|
||||||
async (systemPrompt, apiParameters) =>
|
() => chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel),
|
||||||
|
(systemPrompt, messages, apiParameters, stream, tools) =>
|
||||||
{
|
{
|
||||||
if (TryPopIntParameter(apiParameters, "seed", out var parsedSeed))
|
if (TryPopIntParameter(apiParameters, "seed", out var parsedSeed))
|
||||||
apiParameters["seed"] = parsedSeed;
|
apiParameters["seed"] = parsedSeed;
|
||||||
|
|
||||||
// Build the list of messages:
|
return Task.FromResult(new ChatCompletionAPIRequest
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
|
||||||
|
|
||||||
return new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
Messages = [systemPrompt, ..messages],
|
||||||
|
Stream = stream,
|
||||||
// Right now, we only support streaming completions:
|
Tools = tools,
|
||||||
Stream = true,
|
ParallelToolCalls = tools is null ? null : true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
token: token))
|
token: token))
|
||||||
yield return content;
|
yield return content;
|
||||||
|
|||||||
@ -22,29 +22,22 @@ public sealed class ProviderHelmholtz() : BaseProvider(LLMProviders.HELMHOLTZ, "
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
{
|
{
|
||||||
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>(
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>(
|
||||||
"Helmholtz",
|
"Helmholtz",
|
||||||
chatModel,
|
chatModel,
|
||||||
chatThread,
|
chatThread,
|
||||||
settingsManager,
|
settingsManager,
|
||||||
async (systemPrompt, apiParameters) =>
|
() => chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel),
|
||||||
{
|
(systemPrompt, messages, apiParameters, stream, tools) =>
|
||||||
// Build the list of messages:
|
Task.FromResult(new ChatCompletionAPIRequest
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
|
||||||
|
|
||||||
return new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
Messages = [systemPrompt, ..messages],
|
||||||
|
Stream = stream,
|
||||||
Stream = true,
|
Tools = tools,
|
||||||
|
ParallelToolCalls = tools is null ? null : true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
token: token))
|
token: token))
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,29 +26,22 @@ public sealed class ProviderHuggingFace : BaseProvider
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
{
|
{
|
||||||
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>(
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>(
|
||||||
"HuggingFace",
|
"HuggingFace",
|
||||||
chatModel,
|
chatModel,
|
||||||
chatThread,
|
chatThread,
|
||||||
settingsManager,
|
settingsManager,
|
||||||
async (systemPrompt, apiParameters) =>
|
() => chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel),
|
||||||
{
|
(systemPrompt, messages, apiParameters, stream, tools) =>
|
||||||
// Build the list of messages:
|
Task.FromResult(new ChatCompletionAPIRequest
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
|
||||||
|
|
||||||
return new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
Messages = [systemPrompt, ..messages],
|
||||||
|
Stream = stream,
|
||||||
Stream = true,
|
Tools = tools,
|
||||||
|
ParallelToolCalls = tools is null ? null : true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
token: token))
|
token: token))
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,12 +20,13 @@ public sealed class ProviderMistral() : BaseProvider(LLMProviders.MISTRAL, "http
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Provider.Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Provider.Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
{
|
{
|
||||||
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
||||||
"Mistral",
|
"Mistral",
|
||||||
chatModel,
|
chatModel,
|
||||||
chatThread,
|
chatThread,
|
||||||
settingsManager,
|
settingsManager,
|
||||||
async (systemPrompt, apiParameters) =>
|
() => chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel),
|
||||||
|
(systemPrompt, messages, apiParameters, stream, tools) =>
|
||||||
{
|
{
|
||||||
if (TryPopBoolParameter(apiParameters, "safe_prompt", out var parsedSafePrompt))
|
if (TryPopBoolParameter(apiParameters, "safe_prompt", out var parsedSafePrompt))
|
||||||
apiParameters["safe_prompt"] = parsedSafePrompt;
|
apiParameters["safe_prompt"] = parsedSafePrompt;
|
||||||
@ -33,22 +34,15 @@ public sealed class ProviderMistral() : BaseProvider(LLMProviders.MISTRAL, "http
|
|||||||
if (TryPopIntParameter(apiParameters, "random_seed", out var parsedRandomSeed))
|
if (TryPopIntParameter(apiParameters, "random_seed", out var parsedRandomSeed))
|
||||||
apiParameters["random_seed"] = parsedRandomSeed;
|
apiParameters["random_seed"] = parsedRandomSeed;
|
||||||
|
|
||||||
// Build the list of messages:
|
return Task.FromResult(new ChatCompletionAPIRequest
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel);
|
|
||||||
|
|
||||||
return new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
Messages = [systemPrompt, ..messages],
|
||||||
|
Stream = stream,
|
||||||
// Right now, we only support streaming completions:
|
Tools = tools,
|
||||||
Stream = true,
|
ParallelToolCalls = tools is null ? null : true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
token: token))
|
token: token))
|
||||||
yield return content;
|
yield return content;
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
namespace AIStudio.Provider.OpenAI;
|
||||||
|
|
||||||
|
public sealed record AssistantToolCallMessage : IMessageBase
|
||||||
|
{
|
||||||
|
public string Role { get; init; } = "assistant";
|
||||||
|
|
||||||
|
public string? Content { get; init; }
|
||||||
|
|
||||||
|
public IList<ChatCompletionToolCall> ToolCalls { get; init; } = [];
|
||||||
|
}
|
||||||
@ -18,6 +18,10 @@ public record ChatCompletionAPIRequest(
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IList<object>? Tools { get; init; }
|
||||||
|
|
||||||
|
public bool? ParallelToolCalls { get; init; }
|
||||||
|
|
||||||
// Attention: The "required" modifier is not supported for [JsonExtensionData].
|
// Attention: The "required" modifier is not supported for [JsonExtensionData].
|
||||||
[JsonExtensionData]
|
[JsonExtensionData]
|
||||||
public IDictionary<string, object> AdditionalApiParameters { get; init; } = new Dictionary<string, object>();
|
public IDictionary<string, object> AdditionalApiParameters { get; init; } = new Dictionary<string, object>();
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
namespace AIStudio.Provider.OpenAI;
|
||||||
|
|
||||||
|
public sealed record ChatCompletionResponse
|
||||||
|
{
|
||||||
|
public string Id { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
public string Model { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
public IList<ChatCompletionResponseChoice> Choices { get; init; } = [];
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
namespace AIStudio.Provider.OpenAI;
|
||||||
|
|
||||||
|
public sealed record ChatCompletionResponseChoice
|
||||||
|
{
|
||||||
|
public int Index { get; init; }
|
||||||
|
|
||||||
|
public string FinishReason { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
public ChatCompletionResponseMessage Message { get; init; } = new();
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
namespace AIStudio.Provider.OpenAI;
|
||||||
|
|
||||||
|
public sealed record ChatCompletionResponseMessage
|
||||||
|
{
|
||||||
|
public string Role { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
public string? Content { get; init; }
|
||||||
|
|
||||||
|
public IList<ChatCompletionToolCall> ToolCalls { get; init; } = [];
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
namespace AIStudio.Provider.OpenAI;
|
||||||
|
|
||||||
|
public sealed record ChatCompletionToolCall
|
||||||
|
{
|
||||||
|
public string Id { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
public string Type { get; init; } = "function";
|
||||||
|
|
||||||
|
public ChatCompletionToolFunction Function { get; init; } = new();
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
namespace AIStudio.Provider.OpenAI;
|
||||||
|
|
||||||
|
public sealed record ChatCompletionToolFunction
|
||||||
|
{
|
||||||
|
public string Name { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
public string Arguments { get; init; } = string.Empty;
|
||||||
|
}
|
||||||
@ -63,19 +63,18 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, "https
|
|||||||
|
|
||||||
// Check if we are using the Responses API or the Chat Completion API:
|
// Check if we are using the Responses API or the Chat Completion API:
|
||||||
var usingResponsesAPI = modelCapabilities.Contains(Capability.RESPONSES_API);
|
var usingResponsesAPI = modelCapabilities.Contains(Capability.RESPONSES_API);
|
||||||
|
var useChatCompletionsForTools =
|
||||||
|
chatThread.RuntimeSelectedToolIds.Count > 0 &&
|
||||||
|
modelCapabilities.Contains(Capability.CHAT_COMPLETION_API) &&
|
||||||
|
modelCapabilities.Contains(Capability.FUNCTION_CALLING);
|
||||||
|
if (useChatCompletionsForTools)
|
||||||
|
usingResponsesAPI = false;
|
||||||
|
|
||||||
// Prepare the request path based on the API we are using:
|
// Prepare the request path based on the API we are using:
|
||||||
var requestPath = usingResponsesAPI ? "responses" : "chat/completions";
|
var requestPath = usingResponsesAPI ? "responses" : "chat/completions";
|
||||||
|
|
||||||
LOGGER.LogInformation("Using the system prompt role '{SystemPromptRole}' and the '{RequestPath}' API for model '{ChatModelId}'.", systemPromptRole, requestPath, chatModel.Id);
|
LOGGER.LogInformation("Using the system prompt role '{SystemPromptRole}' and the '{RequestPath}' API for model '{ChatModelId}'.", systemPromptRole, requestPath, chatModel.Id);
|
||||||
|
|
||||||
// Prepare the system prompt:
|
|
||||||
var systemPrompt = new TextMessage
|
|
||||||
{
|
|
||||||
Role = systemPromptRole,
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Prepare the tools we want to use:
|
// Prepare the tools we want to use:
|
||||||
//
|
//
|
||||||
@ -89,60 +88,81 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, "https
|
|||||||
// Parse the API parameters:
|
// Parse the API parameters:
|
||||||
var apiParameters = this.ParseAdditionalApiParameters("input", "store", "tools");
|
var apiParameters = this.ParseAdditionalApiParameters("input", "store", "tools");
|
||||||
|
|
||||||
|
if (!usingResponsesAPI)
|
||||||
|
{
|
||||||
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>(
|
||||||
|
"OpenAI",
|
||||||
|
chatModel,
|
||||||
|
chatThread,
|
||||||
|
settingsManager,
|
||||||
|
() => chatThread.Blocks.BuildMessagesAsync(
|
||||||
|
this.Provider,
|
||||||
|
chatModel,
|
||||||
|
role => role switch
|
||||||
|
{
|
||||||
|
ChatRole.USER => "user",
|
||||||
|
ChatRole.AI => "assistant",
|
||||||
|
ChatRole.AGENT => "assistant",
|
||||||
|
ChatRole.SYSTEM => systemPromptRole,
|
||||||
|
_ => "user",
|
||||||
|
},
|
||||||
|
text => new SubContentText
|
||||||
|
{
|
||||||
|
Text = text,
|
||||||
|
},
|
||||||
|
async attachment => new SubContentImageUrlNested
|
||||||
|
{
|
||||||
|
ImageUrl = new SubContentImageUrlData
|
||||||
|
{
|
||||||
|
Url = await attachment.TryAsBase64(token: token) is (true, var base64Content)
|
||||||
|
? $"data:{attachment.DetermineMimeType()};base64,{base64Content}"
|
||||||
|
: string.Empty,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
(systemPrompt, messages, apiParameters, stream, tools) => Task.FromResult(new ChatCompletionAPIRequest
|
||||||
|
{
|
||||||
|
Model = chatModel.Id,
|
||||||
|
Messages = [systemPrompt, ..messages],
|
||||||
|
Stream = stream,
|
||||||
|
Tools = tools,
|
||||||
|
ParallelToolCalls = tools is null ? null : true,
|
||||||
|
AdditionalApiParameters = apiParameters,
|
||||||
|
}),
|
||||||
|
systemPromptRole: systemPromptRole,
|
||||||
|
requestPath: "chat/completions",
|
||||||
|
token: token))
|
||||||
|
yield return content;
|
||||||
|
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the system prompt:
|
||||||
|
var systemPrompt = new TextMessage
|
||||||
|
{
|
||||||
|
Role = systemPromptRole,
|
||||||
|
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
||||||
|
};
|
||||||
|
|
||||||
// Build the list of messages:
|
// Build the list of messages:
|
||||||
var messages = await chatThread.Blocks.BuildMessagesAsync(
|
var messages = await chatThread.Blocks.BuildMessagesAsync(
|
||||||
this.Provider, chatModel,
|
this.Provider, chatModel,
|
||||||
|
|
||||||
// OpenAI-specific role mapping:
|
|
||||||
role => role switch
|
role => role switch
|
||||||
{
|
{
|
||||||
ChatRole.USER => "user",
|
ChatRole.USER => "user",
|
||||||
ChatRole.AI => "assistant",
|
ChatRole.AI => "assistant",
|
||||||
ChatRole.AGENT => "assistant",
|
ChatRole.AGENT => "assistant",
|
||||||
ChatRole.SYSTEM => systemPromptRole,
|
ChatRole.SYSTEM => systemPromptRole,
|
||||||
|
|
||||||
_ => "user",
|
_ => "user",
|
||||||
},
|
},
|
||||||
|
text => new SubContentInputText
|
||||||
// OpenAI's text sub-content depends on the model, whether we are using
|
|
||||||
// the Responses API or the Chat Completion API:
|
|
||||||
text => usingResponsesAPI switch
|
|
||||||
{
|
{
|
||||||
// Responses API uses INPUT_TEXT:
|
Text = text,
|
||||||
true => new SubContentInputText
|
|
||||||
{
|
|
||||||
Text = text,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Chat Completion API uses TEXT:
|
|
||||||
false => new SubContentText
|
|
||||||
{
|
|
||||||
Text = text,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
async attachment => new SubContentInputImage
|
||||||
// OpenAI's image sub-content depends on the model as well,
|
|
||||||
// whether we are using the Responses API or the Chat Completion API:
|
|
||||||
async attachment => usingResponsesAPI switch
|
|
||||||
{
|
{
|
||||||
// Responses API uses INPUT_IMAGE:
|
ImageUrl = await attachment.TryAsBase64(token: token) is (true, var base64Content)
|
||||||
true => new SubContentInputImage
|
? $"data:{attachment.DetermineMimeType()};base64,{base64Content}"
|
||||||
{
|
: string.Empty,
|
||||||
ImageUrl = await attachment.TryAsBase64(token: token) is (true, var base64Content)
|
|
||||||
? $"data:{attachment.DetermineMimeType()};base64,{base64Content}"
|
|
||||||
: string.Empty,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Chat Completion API uses IMAGE_URL:
|
|
||||||
false => new SubContentImageUrlNested
|
|
||||||
{
|
|
||||||
ImageUrl = new SubContentImageUrlData
|
|
||||||
{
|
|
||||||
Url = await attachment.TryAsBase64(token: token) is (true, var base64Content)
|
|
||||||
? $"data:{attachment.DetermineMimeType()};base64,{base64Content}"
|
|
||||||
: string.Empty,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
12
app/MindWork AI Studio/Provider/OpenAI/ToolResultMessage.cs
Normal file
12
app/MindWork AI Studio/Provider/OpenAI/ToolResultMessage.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace AIStudio.Provider.OpenAI;
|
||||||
|
|
||||||
|
public sealed record ToolResultMessage : IMessage<string>
|
||||||
|
{
|
||||||
|
public string Role { get; init; } = "tool";
|
||||||
|
|
||||||
|
public string Content { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
public string ToolCallId { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
public string Name { get; init; } = string.Empty;
|
||||||
|
}
|
||||||
@ -25,30 +25,22 @@ public sealed class ProviderOpenRouter() : BaseProvider(LLMProviders.OPEN_ROUTER
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
{
|
{
|
||||||
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
||||||
"OpenRouter",
|
"OpenRouter",
|
||||||
chatModel,
|
chatModel,
|
||||||
chatThread,
|
chatThread,
|
||||||
settingsManager,
|
settingsManager,
|
||||||
async (systemPrompt, apiParameters) =>
|
() => chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel),
|
||||||
{
|
(systemPrompt, messages, apiParameters, stream, tools) =>
|
||||||
// Build the list of messages:
|
Task.FromResult(new ChatCompletionAPIRequest
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
|
||||||
|
|
||||||
return new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
Messages = [systemPrompt, ..messages],
|
||||||
|
Stream = stream,
|
||||||
// Right now, we only support streaming completions:
|
Tools = tools,
|
||||||
Stream = true,
|
ParallelToolCalls = tools is null ? null : true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
headersAction: headers =>
|
headersAction: headers =>
|
||||||
{
|
{
|
||||||
// Set custom headers for project identification:
|
// Set custom headers for project identification:
|
||||||
|
|||||||
@ -30,28 +30,22 @@ public sealed class ProviderPerplexity() : BaseProvider(LLMProviders.PERPLEXITY,
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
{
|
{
|
||||||
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ResponseStreamLine, NoChatCompletionAnnotationStreamLine>(
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ResponseStreamLine, NoChatCompletionAnnotationStreamLine>(
|
||||||
"Perplexity",
|
"Perplexity",
|
||||||
chatModel,
|
chatModel,
|
||||||
chatThread,
|
chatThread,
|
||||||
settingsManager,
|
settingsManager,
|
||||||
async (systemPrompt, apiParameters) =>
|
() => chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel),
|
||||||
{
|
(systemPrompt, messages, apiParameters, stream, tools) =>
|
||||||
// Build the list of messages:
|
Task.FromResult(new ChatCompletionAPIRequest
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
|
||||||
|
|
||||||
return new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
Messages = [systemPrompt, ..messages],
|
||||||
Stream = true,
|
Stream = stream,
|
||||||
|
Tools = tools,
|
||||||
|
ParallelToolCalls = tools is null ? null : true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
token: token))
|
token: token))
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,36 +23,26 @@ public sealed class ProviderSelfHosted(Host host, string hostname) : BaseProvide
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Provider.Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Provider.Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
{
|
{
|
||||||
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>(
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>(
|
||||||
"self-hosted provider",
|
"self-hosted provider",
|
||||||
chatModel,
|
chatModel,
|
||||||
chatThread,
|
chatThread,
|
||||||
settingsManager,
|
settingsManager,
|
||||||
async (systemPrompt, apiParameters) =>
|
() => host switch
|
||||||
{
|
{
|
||||||
// Build the list of messages. The image format depends on the host:
|
Host.OLLAMA => chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel),
|
||||||
// - Ollama uses the direct image URL format: { "type": "image_url", "image_url": "data:..." }
|
_ => chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel),
|
||||||
// - LM Studio, vLLM, and llama.cpp use the nested image URL format: { "type": "image_url", "image_url": { "url": "data:..." } }
|
},
|
||||||
var messages = host switch
|
(systemPrompt, messages, apiParameters, stream, tools) =>
|
||||||
{
|
Task.FromResult(new ChatCompletionAPIRequest
|
||||||
Host.OLLAMA => await chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel),
|
|
||||||
_ => await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel),
|
|
||||||
};
|
|
||||||
|
|
||||||
return new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
Messages = [systemPrompt, ..messages],
|
||||||
|
Stream = stream,
|
||||||
// Right now, we only support streaming completions:
|
Tools = tools,
|
||||||
Stream = true,
|
ParallelToolCalls = tools is null ? null : true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
isTryingSecret: true,
|
isTryingSecret: true,
|
||||||
requestPath: host.ChatURL(),
|
requestPath: host.ChatURL(),
|
||||||
token: token))
|
token: token))
|
||||||
|
|||||||
@ -22,30 +22,22 @@ public sealed class ProviderX() : BaseProvider(LLMProviders.X, "https://api.x.ai
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||||
{
|
{
|
||||||
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
||||||
"xAI",
|
"xAI",
|
||||||
chatModel,
|
chatModel,
|
||||||
chatThread,
|
chatThread,
|
||||||
settingsManager,
|
settingsManager,
|
||||||
async (systemPrompt, apiParameters) =>
|
() => chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel),
|
||||||
{
|
(systemPrompt, messages, apiParameters, stream, tools) =>
|
||||||
// Build the list of messages:
|
Task.FromResult(new ChatCompletionAPIRequest
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
|
||||||
|
|
||||||
return new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
Messages = [systemPrompt, ..messages],
|
||||||
|
Stream = stream,
|
||||||
// Right now, we only support streaming completions:
|
Tools = tools,
|
||||||
Stream = true,
|
ParallelToolCalls = tools is null ? null : true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
token: token))
|
token: token))
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -136,4 +136,6 @@ public sealed class Data
|
|||||||
public DataBiasOfTheDay BiasOfTheDay { get; init; } = new();
|
public DataBiasOfTheDay BiasOfTheDay { get; init; } = new();
|
||||||
|
|
||||||
public DataI18N I18N { get; init; } = new();
|
public DataI18N I18N { get; init; } = new();
|
||||||
|
|
||||||
|
public DataTools Tools { get; init; } = new();
|
||||||
}
|
}
|
||||||
10
app/MindWork AI Studio/Settings/DataModel/DataTools.cs
Normal file
10
app/MindWork AI Studio/Settings/DataModel/DataTools.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace AIStudio.Settings.DataModel;
|
||||||
|
|
||||||
|
public sealed class DataTools
|
||||||
|
{
|
||||||
|
public Dictionary<string, Dictionary<string, string>> Settings { get; set; } = [];
|
||||||
|
|
||||||
|
public Dictionary<string, HashSet<string>> DefaultToolIdsByComponent { get; set; } = [];
|
||||||
|
|
||||||
|
public HashSet<string> VisibleToolSelectionComponents { get; set; } = [];
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ using System.Text.Json;
|
|||||||
|
|
||||||
using AIStudio.Provider;
|
using AIStudio.Provider;
|
||||||
using AIStudio.Settings.DataModel;
|
using AIStudio.Settings.DataModel;
|
||||||
|
using AIStudio.Tools;
|
||||||
using AIStudio.Tools.PluginSystem;
|
using AIStudio.Tools.PluginSystem;
|
||||||
using AIStudio.Tools.Services;
|
using AIStudio.Tools.Services;
|
||||||
|
|
||||||
@ -344,6 +345,33 @@ public sealed class SettingsManager
|
|||||||
return preselection ?? ChatTemplate.NO_CHAT_TEMPLATE;
|
return preselection ?? ChatTemplate.NO_CHAT_TEMPLATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HashSet<string> GetDefaultToolIds(AIStudio.Tools.Components component)
|
||||||
|
{
|
||||||
|
var key = component.ToString();
|
||||||
|
if (this.ConfigurationData.Tools.DefaultToolIdsByComponent.TryGetValue(key, out var toolIds))
|
||||||
|
return [..toolIds];
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsToolSelectionVisible(AIStudio.Tools.Components component) => component switch
|
||||||
|
{
|
||||||
|
AIStudio.Tools.Components.CHAT => true,
|
||||||
|
_ => this.ConfigurationData.Tools.VisibleToolSelectionComponents.Contains(component.ToString()),
|
||||||
|
};
|
||||||
|
|
||||||
|
public void SetToolSelectionVisibility(AIStudio.Tools.Components component, bool isVisible)
|
||||||
|
{
|
||||||
|
if (component is AIStudio.Tools.Components.CHAT)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var key = component.ToString();
|
||||||
|
if (isVisible)
|
||||||
|
this.ConfigurationData.Tools.VisibleToolSelectionComponents.Add(key);
|
||||||
|
else
|
||||||
|
this.ConfigurationData.Tools.VisibleToolSelectionComponents.Remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
public ConfidenceLevel GetConfiguredConfidenceLevel(LLMProviders llmProvider)
|
public ConfidenceLevel GetConfiguredConfidenceLevel(LLMProviders llmProvider)
|
||||||
{
|
{
|
||||||
if(llmProvider is LLMProviders.NONE)
|
if(llmProvider is LLMProviders.NONE)
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace AIStudio.Tools.ToolCallingSystem;
|
||||||
|
|
||||||
|
public sealed class GetCurrentWeatherTool : IToolImplementation
|
||||||
|
{
|
||||||
|
public string ImplementationKey => "get_current_weather";
|
||||||
|
|
||||||
|
public IReadOnlySet<string> SensitiveTraceArgumentNames => new HashSet<string>(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
public Task<ToolExecutionResult> ExecuteAsync(JsonElement arguments, ToolExecutionContext context, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var city = arguments.TryGetProperty("city", out var cityValue) ? cityValue.GetString() ?? string.Empty : string.Empty;
|
||||||
|
var state = arguments.TryGetProperty("state", out var stateValue) ? stateValue.GetString() ?? string.Empty : string.Empty;
|
||||||
|
var unit = arguments.TryGetProperty("unit", out var unitValue) ? unitValue.GetString() ?? string.Empty : string.Empty;
|
||||||
|
|
||||||
|
if (unit is not ("celsius" or "fahrenheit"))
|
||||||
|
throw new ArgumentException($"Invalid unit '{unit}'.");
|
||||||
|
|
||||||
|
return Task.FromResult(new ToolExecutionResult
|
||||||
|
{
|
||||||
|
TextContent = $"The weather in {city}, {state} is 85 degrees {unit}. It is partly cloudy with highs in the 90's.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace AIStudio.Tools.ToolCallingSystem;
|
||||||
|
|
||||||
|
public interface IToolImplementation
|
||||||
|
{
|
||||||
|
public string ImplementationKey { get; }
|
||||||
|
|
||||||
|
public IReadOnlySet<string> SensitiveTraceArgumentNames { get; }
|
||||||
|
|
||||||
|
public Task<ToolExecutionResult> ExecuteAsync(JsonElement arguments, ToolExecutionContext context, CancellationToken token = default);
|
||||||
|
|
||||||
|
public string FormatTraceResult(string rawResult) => rawResult;
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace AIStudio.Tools.ToolCallingSystem;
|
||||||
|
|
||||||
|
public sealed class ToolDefinition
|
||||||
|
{
|
||||||
|
public int SchemaVersion { get; init; } = 1;
|
||||||
|
|
||||||
|
public string Id { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
public string DisplayName { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
public string Icon { get; init; } = Icons.Material.Filled.Build;
|
||||||
|
|
||||||
|
public string ImplementationKey { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
public ToolVisibilityDefinition VisibleIn { get; init; } = new();
|
||||||
|
|
||||||
|
public ToolSettingsSchema SettingsSchema { get; init; } = new();
|
||||||
|
|
||||||
|
public ToolFunctionDefinition Function { get; init; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ToolVisibilityDefinition
|
||||||
|
{
|
||||||
|
public bool Chat { get; init; } = true;
|
||||||
|
|
||||||
|
public bool Assistants { get; init; } = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ToolFunctionDefinition
|
||||||
|
{
|
||||||
|
public string Name { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
public string Description { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
public bool Strict { get; init; } = true;
|
||||||
|
|
||||||
|
public JsonElement Parameters { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ToolSettingsSchema
|
||||||
|
{
|
||||||
|
public string Type { get; init; } = "object";
|
||||||
|
|
||||||
|
public Dictionary<string, ToolSettingsFieldDefinition> Properties { get; init; } = [];
|
||||||
|
|
||||||
|
public HashSet<string> Required { get; init; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ToolSettingsFieldDefinition
|
||||||
|
{
|
||||||
|
public string Type { get; init; } = "string";
|
||||||
|
|
||||||
|
public string Title { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
public string Description { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("enum")]
|
||||||
|
public List<string> EnumValues { get; init; } = [];
|
||||||
|
|
||||||
|
public bool Secret { get; init; }
|
||||||
|
}
|
||||||
@ -0,0 +1,94 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
|
||||||
|
using AIStudio.Settings;
|
||||||
|
|
||||||
|
namespace AIStudio.Tools.ToolCallingSystem;
|
||||||
|
|
||||||
|
public sealed class ToolExecutionContext
|
||||||
|
{
|
||||||
|
public required ToolDefinition Definition { get; init; }
|
||||||
|
|
||||||
|
public required SettingsManager SettingsManager { get; init; }
|
||||||
|
|
||||||
|
public required IReadOnlyDictionary<string, string> SettingsValues { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ToolExecutionResult
|
||||||
|
{
|
||||||
|
public string? TextContent { get; init; }
|
||||||
|
|
||||||
|
public JsonNode? JsonContent { get; init; }
|
||||||
|
|
||||||
|
public string ToModelContent()
|
||||||
|
{
|
||||||
|
if (this.JsonContent is not null)
|
||||||
|
return this.JsonContent.ToJsonString();
|
||||||
|
|
||||||
|
return this.TextContent ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ToolInvocationTraceStatus
|
||||||
|
{
|
||||||
|
NONE = 0,
|
||||||
|
SUCCESS,
|
||||||
|
ERROR,
|
||||||
|
BLOCKED,
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ToolInvocationTrace
|
||||||
|
{
|
||||||
|
public int Order { get; set; }
|
||||||
|
|
||||||
|
public string ToolId { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string ToolName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string ToolIcon { get; set; } = Icons.Material.Filled.Build;
|
||||||
|
|
||||||
|
public string ToolCallId { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public ToolInvocationTraceStatus Status { get; set; } = ToolInvocationTraceStatus.NONE;
|
||||||
|
|
||||||
|
public bool WasExecuted { get; set; }
|
||||||
|
|
||||||
|
public string StatusMessage { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public Dictionary<string, string> Arguments { get; set; } = [];
|
||||||
|
|
||||||
|
public string Result { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ToolRuntimeStatus
|
||||||
|
{
|
||||||
|
public bool IsRunning { get; set; }
|
||||||
|
|
||||||
|
public List<string> ToolNames { get; set; } = [];
|
||||||
|
|
||||||
|
public string Message => this.ToolNames.Count switch
|
||||||
|
{
|
||||||
|
0 => string.Empty,
|
||||||
|
1 => $"Using tool: {this.ToolNames[0]}",
|
||||||
|
_ => $"Using tools: {string.Join(", ", this.ToolNames)}",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ToolConfigurationState
|
||||||
|
{
|
||||||
|
public bool IsConfigured { get; init; }
|
||||||
|
|
||||||
|
public List<string> MissingRequiredFields { get; init; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ToolCatalogItem
|
||||||
|
{
|
||||||
|
public required ToolDefinition Definition { get; init; }
|
||||||
|
|
||||||
|
public required ToolConfigurationState ConfigurationState { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ToolSelectionState
|
||||||
|
{
|
||||||
|
public HashSet<string> SelectedToolIds { get; init; } = [];
|
||||||
|
}
|
||||||
107
app/MindWork AI Studio/Tools/ToolCallingSystem/ToolExecutor.cs
Normal file
107
app/MindWork AI Studio/Tools/ToolCallingSystem/ToolExecutor.cs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace AIStudio.Tools.ToolCallingSystem;
|
||||||
|
|
||||||
|
public sealed class ToolExecutor(ToolSettingsService toolSettingsService)
|
||||||
|
{
|
||||||
|
public async Task<(string Content, ToolInvocationTrace Trace)> ExecuteAsync(
|
||||||
|
string toolCallId,
|
||||||
|
string toolName,
|
||||||
|
string argumentsJson,
|
||||||
|
IReadOnlyList<(ToolDefinition Definition, IToolImplementation Implementation)> runnableTools,
|
||||||
|
int order,
|
||||||
|
CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var runnableTool = runnableTools.FirstOrDefault(x => x.Definition.Function.Name.Equals(toolName, StringComparison.Ordinal));
|
||||||
|
if (runnableTool.Definition is null || runnableTool.Implementation is null)
|
||||||
|
{
|
||||||
|
return (this.CreateError(toolName), new ToolInvocationTrace
|
||||||
|
{
|
||||||
|
Order = order,
|
||||||
|
ToolId = toolName,
|
||||||
|
ToolName = toolName,
|
||||||
|
ToolCallId = toolCallId,
|
||||||
|
Status = ToolInvocationTraceStatus.BLOCKED,
|
||||||
|
StatusMessage = "Tool is not available in the current context.",
|
||||||
|
Result = this.CreateError(toolName),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var definition = runnableTool.Definition;
|
||||||
|
var implementation = runnableTool.Implementation;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var document = JsonDocument.Parse(string.IsNullOrWhiteSpace(argumentsJson) ? "{}" : argumentsJson);
|
||||||
|
var settingsValues = await toolSettingsService.GetSettingsAsync(definition);
|
||||||
|
var result = await implementation.ExecuteAsync(document.RootElement, new ToolExecutionContext
|
||||||
|
{
|
||||||
|
Definition = definition,
|
||||||
|
SettingsManager = Program.SERVICE_PROVIDER.GetRequiredService<Settings.SettingsManager>(),
|
||||||
|
SettingsValues = settingsValues,
|
||||||
|
}, token);
|
||||||
|
|
||||||
|
return (result.ToModelContent(), new ToolInvocationTrace
|
||||||
|
{
|
||||||
|
Order = order,
|
||||||
|
ToolId = definition.Id,
|
||||||
|
ToolName = definition.DisplayName,
|
||||||
|
ToolIcon = definition.Icon,
|
||||||
|
ToolCallId = toolCallId,
|
||||||
|
Status = ToolInvocationTraceStatus.SUCCESS,
|
||||||
|
WasExecuted = true,
|
||||||
|
Arguments = FormatArguments(document.RootElement, implementation.SensitiveTraceArgumentNames),
|
||||||
|
Result = implementation.FormatTraceResult(result.ToModelContent()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
var error = $"Tool execution failed: {exception.Message}";
|
||||||
|
Dictionary<string, string> formattedArguments = [];
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var document = JsonDocument.Parse(string.IsNullOrWhiteSpace(argumentsJson) ? "{}" : argumentsJson);
|
||||||
|
formattedArguments = FormatArguments(document.RootElement, implementation.SensitiveTraceArgumentNames);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
return (error, new ToolInvocationTrace
|
||||||
|
{
|
||||||
|
Order = order,
|
||||||
|
ToolId = definition.Id,
|
||||||
|
ToolName = definition.DisplayName,
|
||||||
|
ToolIcon = definition.Icon,
|
||||||
|
ToolCallId = toolCallId,
|
||||||
|
Status = ToolInvocationTraceStatus.ERROR,
|
||||||
|
StatusMessage = error,
|
||||||
|
Arguments = formattedArguments,
|
||||||
|
Result = error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string CreateError(string toolName) => $"Tool '{toolName}' is not available.";
|
||||||
|
|
||||||
|
private static Dictionary<string, string> FormatArguments(JsonElement rootElement, IReadOnlySet<string> sensitiveNames)
|
||||||
|
{
|
||||||
|
if (rootElement.ValueKind is not JsonValueKind.Object)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
var arguments = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||||
|
foreach (var property in rootElement.EnumerateObject())
|
||||||
|
{
|
||||||
|
arguments[property.Name] = sensitiveNames.Contains(property.Name)
|
||||||
|
? "*****"
|
||||||
|
: property.Value.ValueKind switch
|
||||||
|
{
|
||||||
|
JsonValueKind.String => property.Value.GetString() ?? string.Empty,
|
||||||
|
_ => property.Value.ToString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
}
|
||||||
135
app/MindWork AI Studio/Tools/ToolCallingSystem/ToolRegistry.cs
Normal file
135
app/MindWork AI Studio/Tools/ToolCallingSystem/ToolRegistry.cs
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
using AIStudio.Provider;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
|
||||||
|
namespace AIStudio.Tools.ToolCallingSystem;
|
||||||
|
|
||||||
|
public sealed class ToolRegistry
|
||||||
|
{
|
||||||
|
private readonly ILogger<ToolRegistry> logger;
|
||||||
|
private readonly ToolSettingsService toolSettingsService;
|
||||||
|
private readonly Dictionary<string, ToolDefinition> definitionsById = new(StringComparer.Ordinal);
|
||||||
|
private readonly Dictionary<string, IToolImplementation> implementationsByKey = new(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
public ToolRegistry(
|
||||||
|
IWebHostEnvironment webHostEnvironment,
|
||||||
|
IEnumerable<IToolImplementation> implementations,
|
||||||
|
ToolSettingsService toolSettingsService,
|
||||||
|
ILogger<ToolRegistry> logger)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
this.toolSettingsService = toolSettingsService;
|
||||||
|
|
||||||
|
foreach (var implementation in implementations)
|
||||||
|
this.implementationsByKey[implementation.ImplementationKey] = implementation;
|
||||||
|
|
||||||
|
var definitionsDirectory = webHostEnvironment.WebRootFileProvider.GetDirectoryContents("tool_definitions");
|
||||||
|
if (!definitionsDirectory.Exists)
|
||||||
|
{
|
||||||
|
this.logger.LogWarning("The tool definitions directory was not found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var serializerOptions = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var file in definitionsDirectory.Where(x => !x.IsDirectory && x.Name.EndsWith(".json", StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var stream = file.CreateReadStream();
|
||||||
|
var definition = JsonSerializer.Deserialize<ToolDefinition>(stream, serializerOptions);
|
||||||
|
if (definition is null || string.IsNullOrWhiteSpace(definition.Id))
|
||||||
|
{
|
||||||
|
this.logger.LogWarning("Skipping tool definition '{ToolFile}' because it could not be deserialized.", file.Name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.implementationsByKey.ContainsKey(definition.ImplementationKey))
|
||||||
|
{
|
||||||
|
this.logger.LogWarning("Skipping tool definition '{ToolId}' because implementation key '{ImplementationKey}' is not registered.", definition.Id, definition.ImplementationKey);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.definitionsById[definition.Id] = definition;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
this.logger.LogWarning(exception, "Skipping invalid tool definition file '{ToolFile}'.", file.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<ToolDefinition> GetDefinitionsForComponent(AIStudio.Tools.Components component)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<ToolDefinition> GetAllDefinitions() => this.definitionsById.Values
|
||||||
|
.OrderBy(x => x.DisplayName, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
public ToolDefinition? GetDefinition(string toolId) => this.definitionsById.GetValueOrDefault(toolId);
|
||||||
|
|
||||||
|
public IToolImplementation? GetImplementation(string implementationKey) => this.implementationsByKey.GetValueOrDefault(implementationKey);
|
||||||
|
|
||||||
|
public async Task<IReadOnlyList<ToolCatalogItem>> GetCatalogAsync(AIStudio.Tools.Components component)
|
||||||
|
{
|
||||||
|
var definitions = this.GetDefinitionsForComponent(component);
|
||||||
|
return await this.GetCatalogAsync(definitions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyList<ToolCatalogItem>> GetCatalogAsync(IEnumerable<ToolDefinition> definitions)
|
||||||
|
{
|
||||||
|
var definitionList = definitions.ToList();
|
||||||
|
var items = new List<ToolCatalogItem>(definitionList.Count);
|
||||||
|
foreach (var definition in definitionList)
|
||||||
|
{
|
||||||
|
items.Add(new ToolCatalogItem
|
||||||
|
{
|
||||||
|
Definition = definition,
|
||||||
|
ConfigurationState = await this.toolSettingsService.GetConfigurationStateAsync(definition),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyList<(ToolDefinition Definition, IToolImplementation Implementation)>> GetRunnableToolsAsync(
|
||||||
|
AIStudio.Tools.Components component,
|
||||||
|
IEnumerable<string> selectedToolIds,
|
||||||
|
IReadOnlyCollection<Capability> modelCapabilities,
|
||||||
|
bool isToolSelectionVisible)
|
||||||
|
{
|
||||||
|
if (!isToolSelectionVisible)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
if (!modelCapabilities.Contains(Capability.CHAT_COMPLETION_API) || !modelCapabilities.Contains(Capability.FUNCTION_CALLING))
|
||||||
|
return [];
|
||||||
|
|
||||||
|
var selectedToolIdSet = selectedToolIds.ToHashSet(StringComparer.Ordinal);
|
||||||
|
var definitions = this.GetDefinitionsForComponent(component).Where(x => selectedToolIdSet.Contains(x.Id)).ToList();
|
||||||
|
var result = new List<(ToolDefinition, IToolImplementation)>(definitions.Count);
|
||||||
|
foreach (var definition in definitions)
|
||||||
|
{
|
||||||
|
if (!this.implementationsByKey.TryGetValue(definition.ImplementationKey, out var implementation))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var configurationState = await this.toolSettingsService.GetConfigurationStateAsync(definition);
|
||||||
|
if (!configurationState.IsConfigured)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
result.Add((definition, implementation));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
using AIStudio.Tools;
|
||||||
|
|
||||||
|
namespace AIStudio.Tools.ToolCallingSystem;
|
||||||
|
|
||||||
|
internal sealed record ToolSettingsSecretId(string ToolId, string FieldName) : ISecretId
|
||||||
|
{
|
||||||
|
public string SecretId => $"tool::{this.ToolId}";
|
||||||
|
|
||||||
|
public string SecretName => this.FieldName;
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
using AIStudio.Settings;
|
||||||
|
using AIStudio.Tools.Services;
|
||||||
|
|
||||||
|
namespace AIStudio.Tools.ToolCallingSystem;
|
||||||
|
|
||||||
|
public sealed class ToolSettingsService(SettingsManager settingsManager, RustService rustService)
|
||||||
|
{
|
||||||
|
public async Task<Dictionary<string, string>> GetSettingsAsync(ToolDefinition definition)
|
||||||
|
{
|
||||||
|
var values = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||||
|
var storedValues = settingsManager.ConfigurationData.Tools.Settings.GetValueOrDefault(definition.Id);
|
||||||
|
foreach (var property in definition.SettingsSchema.Properties)
|
||||||
|
{
|
||||||
|
var fieldName = property.Key;
|
||||||
|
var fieldDefinition = property.Value;
|
||||||
|
if (fieldDefinition.Secret)
|
||||||
|
{
|
||||||
|
var response = await rustService.GetSecret(new ToolSettingsSecretId(definition.Id, fieldName), isTrying: true);
|
||||||
|
if (response.Success)
|
||||||
|
values[fieldName] = await response.Secret.Decrypt(Program.ENCRYPTION);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storedValues?.TryGetValue(fieldName, out var storedValue) is true)
|
||||||
|
values[fieldName] = storedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ToolConfigurationState> GetConfigurationStateAsync(ToolDefinition definition)
|
||||||
|
{
|
||||||
|
var values = await this.GetSettingsAsync(definition);
|
||||||
|
var missing = new List<string>();
|
||||||
|
foreach (var requiredField in definition.SettingsSchema.Required)
|
||||||
|
{
|
||||||
|
if (!values.TryGetValue(requiredField, out var value) || string.IsNullOrWhiteSpace(value))
|
||||||
|
missing.Add(requiredField);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ToolConfigurationState
|
||||||
|
{
|
||||||
|
IsConfigured = missing.Count == 0,
|
||||||
|
MissingRequiredFields = missing,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SaveSettingsAsync(ToolDefinition definition, IReadOnlyDictionary<string, string> values)
|
||||||
|
{
|
||||||
|
if (!settingsManager.ConfigurationData.Tools.Settings.TryGetValue(definition.Id, out var storedValues))
|
||||||
|
{
|
||||||
|
storedValues = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||||
|
settingsManager.ConfigurationData.Tools.Settings[definition.Id] = storedValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var property in definition.SettingsSchema.Properties)
|
||||||
|
{
|
||||||
|
var fieldName = property.Key;
|
||||||
|
var fieldDefinition = property.Value;
|
||||||
|
values.TryGetValue(fieldName, out var value);
|
||||||
|
value ??= string.Empty;
|
||||||
|
|
||||||
|
if (fieldDefinition.Secret)
|
||||||
|
{
|
||||||
|
var secretId = new ToolSettingsSecretId(definition.Id, fieldName);
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
await rustService.DeleteSecret(secretId);
|
||||||
|
else
|
||||||
|
await rustService.SetSecret(secretId, value);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
storedValues[fieldName] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
await settingsManager.StoreSettings();
|
||||||
|
await MessageBus.INSTANCE.SendMessage<object?>(null, Event.CONFIGURATION_CHANGED, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"id": "get_current_weather",
|
||||||
|
"displayName": "Current Weather",
|
||||||
|
"icon": "material-icons:cloud",
|
||||||
|
"implementationKey": "get_current_weather",
|
||||||
|
"visibleIn": {
|
||||||
|
"chat": true,
|
||||||
|
"assistants": true
|
||||||
|
},
|
||||||
|
"settingsSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"demoLabel": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Demo Label",
|
||||||
|
"description": "Required demo setting for validating tool settings in tests.",
|
||||||
|
"secret": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"demoLabel"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"function": {
|
||||||
|
"name": "get_current_weather",
|
||||||
|
"description": "Get the current weather in a given location.",
|
||||||
|
"strict": true,
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"city": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The city to find the weather for, e.g. 'San Francisco'."
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The two-letter abbreviation for the state, e.g. 'CA'."
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The unit to fetch the temperature in.",
|
||||||
|
"enum": [
|
||||||
|
"celsius",
|
||||||
|
"fahrenheit"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"city",
|
||||||
|
"state",
|
||||||
|
"unit"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user