mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-06-27 15:36:26 +00:00
Merge Error Fixes
This commit is contained in:
parent
5ae8d7e2a7
commit
1c0c2a3855
@ -165,7 +165,7 @@
|
||||
|
||||
@if (this.SettingsManager.IsToolSelectionVisible(this.Component))
|
||||
{
|
||||
<ToolSelection Component="@this.Component" LLMProvider="@this.providerSettings" SelectedToolIds="@this.selectedToolIds" SelectedToolIdsChanged="@this.SelectedToolIdsChanged" Disabled="@this.isProcessing" />
|
||||
<ToolSelection Component="@this.Component" LLMProvider="@this.ProviderSettings" SelectedToolIds="@this.selectedToolIds" SelectedToolIdsChanged="@this.SelectedToolIdsChanged" Disabled="@this.isProcessing" />
|
||||
}
|
||||
|
||||
<MudSpacer />
|
||||
|
||||
@ -124,7 +124,7 @@
|
||||
|
||||
<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" />
|
||||
<ToolSelection Component="Components.CHAT" LLMProvider="@this.Provider" SelectedToolIds="@this.selectedToolIds" SelectedToolIdsChanged="@this.SelectedToolIdsChanged" Disabled="@this.IsCurrentChatStreaming" />
|
||||
|
||||
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
|
||||
{
|
||||
|
||||
@ -78,6 +78,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
private Guid loadedParameterWorkspaceId = Guid.Empty;
|
||||
private Guid foregroundChatId = Guid.Empty;
|
||||
private int workspaceHeaderSyncVersion;
|
||||
private CancellationTokenSource? cancellationTokenSource;
|
||||
|
||||
// Unfortunately, we need the input field reference to blur the focus away. Without
|
||||
// this, we cannot clear the input field.
|
||||
@ -702,7 +703,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
// ProviderSettings = this.Provider,
|
||||
// IsForeground = true,
|
||||
//});
|
||||
using (this.cancellationTokenSource = new())
|
||||
using (this.cancellationTokenSource = new CancellationTokenSource())
|
||||
{
|
||||
this.StateHasChanged();
|
||||
this.ChatThread!.RuntimeComponent = Tools.Components.CHAT;
|
||||
@ -719,12 +720,8 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
// Save the chat:
|
||||
if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY)
|
||||
{
|
||||
ChatThread = this.ChatThread!,
|
||||
AIText = aiText,
|
||||
LastUserPrompt = lastUserPrompt,
|
||||
ProviderSettings = this.Provider,
|
||||
IsForeground = true,
|
||||
});
|
||||
await this.SaveThread();
|
||||
}
|
||||
|
||||
await this.SyncForegroundChatAsync();
|
||||
this.StateHasChanged();
|
||||
@ -1133,4 +1130,4 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@ -957,7 +957,6 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
/// <param name="chatModel">The selected chat model.</param>
|
||||
/// <param name="chatThread">The current chat thread.</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="storeType">The secret store type.</param>
|
||||
/// <param name="isTryingSecret">Whether the API key is optional.</param>
|
||||
@ -969,19 +968,19 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
/// <typeparam name="TDelta">The delta stream line type.</typeparam>
|
||||
/// <typeparam name="TAnnotation">The annotation stream line type.</typeparam>
|
||||
/// <returns>The streamed content chunks.</returns>
|
||||
protected async IAsyncEnumerable<ContentStreamChunk> StreamOpenAICompatibleChatCompletion<TDelta, TAnnotation>(
|
||||
protected async IAsyncEnumerable<ContentStreamChunk> StreamOpenAICompatibleChatCompletion<TRequest, TDelta, TAnnotation>(
|
||||
string providerName,
|
||||
Model chatModel,
|
||||
ChatThread chatThread,
|
||||
SettingsManager settingsManager,
|
||||
Func<Task<IList<IMessageBase>>> messagesFactory,
|
||||
Func<TextMessage, IDictionary<string, object>, Task<TRequest>> requestFactory,
|
||||
Func<TextMessage, IDictionary<string, object>, IList<object>?, Task<TRequest>> requestFactory,
|
||||
SecretStoreType storeType = SecretStoreType.LLM_PROVIDER,
|
||||
bool isTryingSecret = false,
|
||||
string systemPromptRole = "system",
|
||||
string requestPath = "chat/completions",
|
||||
Action<HttpRequestHeaders>? headersAction = null,
|
||||
[EnumeratorCancellation] CancellationToken token = default)
|
||||
where TRequest : ChatCompletionAPIRequest
|
||||
where TDelta : IResponseStreamLine
|
||||
where TAnnotation : IAnnotationStreamLine
|
||||
{
|
||||
@ -1000,7 +999,6 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
// Parse the API parameters:
|
||||
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;
|
||||
@ -1023,7 +1021,12 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
var toolCallCount = 0;
|
||||
while (true)
|
||||
{
|
||||
var requestDto = await requestFactory(systemPrompt, [..baseMessages, ..internalMessages], apiParameters, false, providerTools);
|
||||
ChatCompletionAPIRequest requestDtoBase = await requestFactory(systemPrompt, apiParameters, providerTools);
|
||||
var requestDto = requestDtoBase with
|
||||
{
|
||||
Messages = [..requestDtoBase.Messages, ..internalMessages],
|
||||
Stream = false,
|
||||
};
|
||||
var response = await this.ExecuteChatCompletionRequest(requestDto, requestPath, requestedSecret, headersAction, token);
|
||||
var responseMessage = response?.Choices.FirstOrDefault()?.Message;
|
||||
if (responseMessage is null)
|
||||
@ -1106,7 +1109,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
}
|
||||
|
||||
// Prepare the provider HTTP chat request:
|
||||
var providerChatRequest = JsonSerializer.Serialize(await requestFactory(systemPrompt, baseMessages, apiParameters, true, null), JSON_SERIALIZER_OPTIONS);
|
||||
var providerChatRequest = JsonSerializer.Serialize(await requestFactory(systemPrompt, apiParameters, null), JSON_SERIALIZER_OPTIONS);
|
||||
|
||||
async Task<HttpRequestMessage> RequestBuilder()
|
||||
{
|
||||
@ -1143,7 +1146,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
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);
|
||||
using var response = await this.HttpClient.SendAsync(request, token);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var responseBody = await response.Content.ReadAsStringAsync(token);
|
||||
@ -1477,4 +1480,4 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ using AIStudio.Tools.ToolCallingSystem;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
@ -24,8 +23,6 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, new Ur
|
||||
private static readonly ILogger<ProviderOpenAI> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ProviderOpenAI>();
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ProviderOpenAI).Namespace, nameof(ProviderOpenAI));
|
||||
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ProviderOpenAI).Namespace, nameof(ProviderOpenAI));
|
||||
|
||||
#region Implementation of IProvider
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -121,44 +118,48 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, new Ur
|
||||
|
||||
if (!usingResponsesAPI)
|
||||
{
|
||||
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>(
|
||||
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, 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
|
||||
async (systemPrompt, apiParameters, tools) =>
|
||||
{
|
||||
Model = chatModel.Id,
|
||||
Messages = [systemPrompt, ..messages],
|
||||
Stream = stream,
|
||||
Tools = tools,
|
||||
ParallelToolCalls = tools is null ? null : true,
|
||||
AdditionalApiParameters = apiParameters,
|
||||
}),
|
||||
var messages = await 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,
|
||||
},
|
||||
});
|
||||
|
||||
return new ChatCompletionAPIRequest
|
||||
{
|
||||
Model = chatModel.Id,
|
||||
Messages = [systemPrompt, ..messages],
|
||||
Stream = true,
|
||||
Tools = tools,
|
||||
ParallelToolCalls = tools is null ? null : true,
|
||||
AdditionalApiParameters = apiParameters,
|
||||
};
|
||||
},
|
||||
systemPromptRole: systemPromptRole,
|
||||
requestPath: "chat/completions",
|
||||
token: token))
|
||||
@ -174,19 +175,6 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, new Ur
|
||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
||||
};
|
||||
|
||||
//
|
||||
// Prepare the tools we want to use:
|
||||
//
|
||||
IList<ProviderTool> providerTools = modelCapabilities.Contains(Capability.WEB_SEARCH) switch
|
||||
{
|
||||
true => [ ProviderTools.WEB_SEARCH ],
|
||||
_ => []
|
||||
};
|
||||
|
||||
|
||||
// Parse the API parameters:
|
||||
var apiParameters = this.ParseAdditionalApiParameters("input", "store", "tools");
|
||||
|
||||
// Build the list of messages:
|
||||
var messages = await chatThread.Blocks.BuildMessagesAsync(
|
||||
this.Provider, chatModel,
|
||||
@ -279,7 +267,6 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, new Ur
|
||||
Store = false,
|
||||
|
||||
// Tools we want to use:
|
||||
ProviderTools = providerTools,
|
||||
Tools = providerTools,
|
||||
|
||||
// Additional API parameters:
|
||||
@ -438,7 +425,7 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, new Ur
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
||||
request.Content = new StringContent(JsonSerializer.Serialize(requestDto, JSON_SERIALIZER_OPTIONS), Encoding.UTF8, "application/json");
|
||||
|
||||
using var response = await this.httpClient.SendAsync(request, token);
|
||||
using var response = await this.HttpClient.SendAsync(request, token);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var responseBody = await response.Content.ReadAsStringAsync(token);
|
||||
|
||||
@ -10,13 +10,11 @@ namespace AIStudio.Provider.OpenAI;
|
||||
/// <param name="Stream">Whether to stream the response.</param>
|
||||
/// <param name="Store">Whether to store the response on the server (usually OpenAI's infrastructure).</param>
|
||||
/// <param name="Tools">The provider-side tools and local function tools to use for the request.</param>
|
||||
/// <param name="ProviderTools">The provider-side tools to use for the request.</param>
|
||||
public record ResponsesAPIRequest(
|
||||
string Model,
|
||||
IList<object> Input,
|
||||
bool Stream,
|
||||
bool Store,
|
||||
[property: JsonPropertyName("tools")] IList<ProviderTool> ProviderTools)
|
||||
IList<object> Tools)
|
||||
{
|
||||
public ResponsesAPIRequest() : this(string.Empty, [], true, false, [])
|
||||
|
||||
@ -34,4 +34,9 @@ public enum SecretStoreType
|
||||
/// Data source secrets. Uses the "data-source::" prefix.
|
||||
/// </summary>
|
||||
DATA_SOURCE,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tool setting secrets. Uses the "tool::" prefix.
|
||||
/// </summary>
|
||||
TOOL_SETTINGS,
|
||||
}
|
||||
|
||||
@ -17,7 +17,8 @@ public static class SecretStoreTypeExtensions
|
||||
SecretStoreType.TRANSCRIPTION_PROVIDER => "transcription",
|
||||
SecretStoreType.IMAGE_PROVIDER => "image",
|
||||
SecretStoreType.DATA_SOURCE => "data-source",
|
||||
SecretStoreType.TOOL_SETTINGS => "tool",
|
||||
|
||||
_ => "provider",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ namespace AIStudio.Tools.ToolCallingSystem;
|
||||
|
||||
internal sealed record ToolSettingsSecretId(string ToolId, string FieldName) : ISecretId
|
||||
{
|
||||
public string SecretId => $"tool::{this.ToolId}";
|
||||
public string SecretId => this.ToolId;
|
||||
|
||||
public string SecretName => this.FieldName;
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ public sealed class ToolSettingsService(SettingsManager settingsManager, RustSer
|
||||
|
||||
if (fieldDefinition.Secret)
|
||||
{
|
||||
var response = await rustService.GetSecret(new ToolSettingsSecretId(definition.Id, fieldName), isTrying: true);
|
||||
var response = await rustService.GetSecret(new ToolSettingsSecretId(definition.Id, fieldName), SecretStoreType.TOOL_SETTINGS, isTrying: true);
|
||||
if (response.Success)
|
||||
values[fieldName] = await response.Secret.Decrypt(Program.ENCRYPTION);
|
||||
|
||||
@ -99,9 +99,9 @@ public sealed class ToolSettingsService(SettingsManager settingsManager, RustSer
|
||||
{
|
||||
var secretId = new ToolSettingsSecretId(definition.Id, fieldName);
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
await rustService.DeleteSecret(secretId);
|
||||
await rustService.DeleteSecret(secretId, SecretStoreType.TOOL_SETTINGS);
|
||||
else
|
||||
await rustService.SetSecret(secretId, value);
|
||||
await rustService.SetSecret(secretId, value, SecretStoreType.TOOL_SETTINGS);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user