mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-04-14 23:21:37 +00:00
Chat request refactoring for OpenAI-compatible providers (#722)
Some checks are pending
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,deb,updater, appimage,deb) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,deb,updater, appimage,deb) (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
Some checks are pending
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,deb,updater, appimage,deb) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,deb,updater, appimage,deb) (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
This commit is contained in:
parent
a3bf308a76
commit
d494fe4bc7
@ -1,7 +1,5 @@
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
using AIStudio.Chat;
|
using AIStudio.Chat;
|
||||||
using AIStudio.Provider.OpenAI;
|
using AIStudio.Provider.OpenAI;
|
||||||
@ -24,52 +22,30 @@ 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)
|
||||||
{
|
{
|
||||||
// Get the API key:
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
||||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
|
"AlibabaCloud",
|
||||||
if(!requestedSecret.Success)
|
chatModel,
|
||||||
yield break;
|
chatThread,
|
||||||
|
settingsManager,
|
||||||
|
async (systemPrompt, apiParameters) =>
|
||||||
|
{
|
||||||
|
// Build the list of messages:
|
||||||
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the system prompt:
|
return new ChatCompletionAPIRequest
|
||||||
var systemPrompt = new TextMessage
|
{
|
||||||
{
|
Model = chatModel.Id,
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
// Build the messages:
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
// - First of all the system prompt
|
||||||
|
// - Then none-empty user and AI messages
|
||||||
|
Messages = [systemPrompt, ..messages],
|
||||||
|
|
||||||
// Build the list of messages:
|
Stream = true,
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
AdditionalApiParameters = apiParameters
|
||||||
|
};
|
||||||
// Prepare the AlibabaCloud HTTP chat request:
|
},
|
||||||
var alibabaCloudChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
token: token))
|
||||||
{
|
|
||||||
Model = chatModel.Id,
|
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
|
||||||
|
|
||||||
Stream = true,
|
|
||||||
AdditionalApiParameters = apiParameters
|
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
|
||||||
|
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
|
||||||
{
|
|
||||||
// Build the HTTP post request:
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
|
|
||||||
|
|
||||||
// Set the authorization header:
|
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
|
||||||
|
|
||||||
// Set the content:
|
|
||||||
request.Content = new StringContent(alibabaCloudChatRequest, Encoding.UTF8, "application/json");
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>("AlibabaCloud", RequestBuilder, token))
|
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -565,6 +565,78 @@ public abstract class BaseProvider : IProvider, ISecretId
|
|||||||
streamReader.Dispose();
|
streamReader.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Streams the chat completion from an OpenAI-compatible provider using the Chat Completion API.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="providerName">The provider name for logging and error reporting.</param>
|
||||||
|
/// <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="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>
|
||||||
|
/// <param name="systemPromptRole">The system prompt role to use.</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="token">The cancellation token.</param>
|
||||||
|
/// <typeparam name="TRequest">The request DTO type.</typeparam>
|
||||||
|
/// <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<TRequest, TDelta, TAnnotation>(
|
||||||
|
string providerName,
|
||||||
|
Model chatModel,
|
||||||
|
ChatThread chatThread,
|
||||||
|
SettingsManager settingsManager,
|
||||||
|
Func<TextMessage, IDictionary<string, 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 TDelta : IResponseStreamLine
|
||||||
|
where TAnnotation : IAnnotationStreamLine
|
||||||
|
{
|
||||||
|
// Get the API key:
|
||||||
|
var requestedSecret = await RUST_SERVICE.GetAPIKey(this, storeType, isTrying: isTryingSecret);
|
||||||
|
if(!requestedSecret.Success && !isTryingSecret)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
// Prepare the system prompt:
|
||||||
|
var systemPrompt = new TextMessage
|
||||||
|
{
|
||||||
|
Role = systemPromptRole,
|
||||||
|
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse the API parameters:
|
||||||
|
var apiParameters = this.ParseAdditionalApiParameters();
|
||||||
|
|
||||||
|
// Prepare the provider HTTP chat request:
|
||||||
|
var providerChatRequest = JsonSerializer.Serialize(await requestFactory(systemPrompt, apiParameters), JSON_SERIALIZER_OPTIONS);
|
||||||
|
|
||||||
|
async Task<HttpRequestMessage> RequestBuilder()
|
||||||
|
{
|
||||||
|
// Build the HTTP post request:
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, requestPath);
|
||||||
|
|
||||||
|
// Set the authorization header:
|
||||||
|
if (requestedSecret.Success)
|
||||||
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
||||||
|
|
||||||
|
// Set provider-specific headers:
|
||||||
|
headersAction?.Invoke(request.Headers);
|
||||||
|
|
||||||
|
// Set the content:
|
||||||
|
request.Content = new StringContent(providerChatRequest, Encoding.UTF8, "application/json");
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
await foreach (var content in this.StreamChatCompletionInternal<TDelta, TAnnotation>(providerName, RequestBuilder, token))
|
||||||
|
yield return content;
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
using AIStudio.Chat;
|
using AIStudio.Chat;
|
||||||
using AIStudio.Provider.OpenAI;
|
using AIStudio.Provider.OpenAI;
|
||||||
@ -24,52 +22,30 @@ 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)
|
||||||
{
|
{
|
||||||
// Get the API key:
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
||||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
|
"DeepSeek",
|
||||||
if(!requestedSecret.Success)
|
chatModel,
|
||||||
yield break;
|
chatThread,
|
||||||
|
settingsManager,
|
||||||
|
async (systemPrompt, apiParameters) =>
|
||||||
|
{
|
||||||
|
// Build the list of messages:
|
||||||
|
var messages = await chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the system prompt:
|
return new ChatCompletionAPIRequest
|
||||||
var systemPrompt = new TextMessage
|
{
|
||||||
{
|
Model = chatModel.Id,
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
// Build the messages:
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
// - First of all the system prompt
|
||||||
|
// - Then none-empty user and AI messages
|
||||||
|
Messages = [systemPrompt, ..messages],
|
||||||
|
|
||||||
// Build the list of messages:
|
Stream = true,
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel);
|
AdditionalApiParameters = apiParameters
|
||||||
|
};
|
||||||
// Prepare the DeepSeek HTTP chat request:
|
},
|
||||||
var deepSeekChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
token: token))
|
||||||
{
|
|
||||||
Model = chatModel.Id,
|
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
|
||||||
|
|
||||||
Stream = true,
|
|
||||||
AdditionalApiParameters = apiParameters
|
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
|
||||||
|
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
|
||||||
{
|
|
||||||
// Build the HTTP post request:
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
|
|
||||||
|
|
||||||
// Set the authorization header:
|
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
|
||||||
|
|
||||||
// Set the content:
|
|
||||||
request.Content = new StringContent(deepSeekChatRequest, Encoding.UTF8, "application/json");
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>("DeepSeek", RequestBuilder, token))
|
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,20 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace AIStudio.Provider.Fireworks;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The Fireworks chat request model.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="Model">Which model to use for chat completion.</param>
|
|
||||||
/// <param name="Messages">The chat messages.</param>
|
|
||||||
/// <param name="Stream">Whether to stream the chat completion.</param>
|
|
||||||
public readonly record struct ChatRequest(
|
|
||||||
string Model,
|
|
||||||
IList<IMessageBase> Messages,
|
|
||||||
bool Stream
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Attention: The "required" modifier is not supported for [JsonExtensionData].
|
|
||||||
[JsonExtensionData]
|
|
||||||
public IDictionary<string, object> AdditionalApiParameters { get; init; } = new Dictionary<string, object>();
|
|
||||||
}
|
|
||||||
@ -1,7 +1,4 @@
|
|||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
using AIStudio.Chat;
|
using AIStudio.Chat;
|
||||||
using AIStudio.Provider.OpenAI;
|
using AIStudio.Provider.OpenAI;
|
||||||
@ -24,53 +21,31 @@ 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)
|
||||||
{
|
{
|
||||||
// Get the API key:
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ResponseStreamLine, ChatCompletionAnnotationStreamLine>(
|
||||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
|
"Fireworks",
|
||||||
if(!requestedSecret.Success)
|
chatModel,
|
||||||
yield break;
|
chatThread,
|
||||||
|
settingsManager,
|
||||||
|
async (systemPrompt, apiParameters) =>
|
||||||
|
{
|
||||||
|
// Build the list of messages:
|
||||||
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the system prompt:
|
return new ChatCompletionAPIRequest
|
||||||
var systemPrompt = new TextMessage
|
{
|
||||||
{
|
Model = chatModel.Id,
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
// Build the messages:
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
// - First of all the system prompt
|
||||||
|
// - Then none-empty user and AI messages
|
||||||
|
Messages = [systemPrompt, ..messages],
|
||||||
|
|
||||||
// Build the list of messages:
|
// Right now, we only support streaming completions:
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
Stream = true,
|
||||||
|
AdditionalApiParameters = apiParameters
|
||||||
// Prepare the Fireworks HTTP chat request:
|
};
|
||||||
var fireworksChatRequest = JsonSerializer.Serialize(new ChatRequest
|
},
|
||||||
{
|
token: token))
|
||||||
Model = chatModel.Id,
|
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
|
||||||
|
|
||||||
// Right now, we only support streaming completions:
|
|
||||||
Stream = true,
|
|
||||||
AdditionalApiParameters = apiParameters
|
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
|
||||||
|
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
|
||||||
{
|
|
||||||
// Build the HTTP post request:
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
|
|
||||||
|
|
||||||
// Set the authorization header:
|
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
|
||||||
|
|
||||||
// Set the content:
|
|
||||||
request.Content = new StringContent(fireworksChatRequest, Encoding.UTF8, "application/json");
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine, ChatCompletionAnnotationStreamLine>("Fireworks", RequestBuilder, token))
|
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
using AIStudio.Chat;
|
using AIStudio.Chat;
|
||||||
using AIStudio.Provider.OpenAI;
|
using AIStudio.Provider.OpenAI;
|
||||||
@ -24,52 +22,30 @@ 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)
|
||||||
{
|
{
|
||||||
// Get the API key:
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>(
|
||||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
|
"GWDG",
|
||||||
if(!requestedSecret.Success)
|
chatModel,
|
||||||
yield break;
|
chatThread,
|
||||||
|
settingsManager,
|
||||||
|
async (systemPrompt, apiParameters) =>
|
||||||
|
{
|
||||||
|
// Build the list of messages:
|
||||||
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the system prompt:
|
return new ChatCompletionAPIRequest
|
||||||
var systemPrompt = new TextMessage
|
{
|
||||||
{
|
Model = chatModel.Id,
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
// Build the messages:
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
// - First of all the system prompt
|
||||||
|
// - Then none-empty user and AI messages
|
||||||
|
Messages = [systemPrompt, ..messages],
|
||||||
|
|
||||||
// Build the list of messages:
|
Stream = true,
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
AdditionalApiParameters = apiParameters
|
||||||
|
};
|
||||||
// Prepare the GWDG HTTP chat request:
|
},
|
||||||
var gwdgChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
token: token))
|
||||||
{
|
|
||||||
Model = chatModel.Id,
|
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
|
||||||
|
|
||||||
Stream = true,
|
|
||||||
AdditionalApiParameters = apiParameters
|
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
|
||||||
|
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
|
||||||
{
|
|
||||||
// Build the HTTP post request:
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
|
|
||||||
|
|
||||||
// Set the authorization header:
|
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
|
||||||
|
|
||||||
// Set the content:
|
|
||||||
request.Content = new StringContent(gwdgChatRequest, Encoding.UTF8, "application/json");
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>("GWDG", RequestBuilder, token))
|
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,20 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace AIStudio.Provider.Google;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The Google chat request model.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="Model">Which model to use for chat completion.</param>
|
|
||||||
/// <param name="Messages">The chat messages.</param>
|
|
||||||
/// <param name="Stream">Whether to stream the chat completion.</param>
|
|
||||||
public readonly record struct ChatRequest(
|
|
||||||
string Model,
|
|
||||||
IList<IMessageBase> Messages,
|
|
||||||
bool Stream
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Attention: The "required" modifier is not supported for [JsonExtensionData].
|
|
||||||
[JsonExtensionData]
|
|
||||||
public IDictionary<string, object> AdditionalApiParameters { get; init; } = new Dictionary<string, object>();
|
|
||||||
}
|
|
||||||
@ -24,53 +24,31 @@ 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)
|
||||||
{
|
{
|
||||||
// Get the API key:
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
||||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
|
"Google",
|
||||||
if(!requestedSecret.Success)
|
chatModel,
|
||||||
yield break;
|
chatThread,
|
||||||
|
settingsManager,
|
||||||
|
async (systemPrompt, apiParameters) =>
|
||||||
|
{
|
||||||
|
// Build the list of messages:
|
||||||
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the system prompt:
|
return new ChatCompletionAPIRequest
|
||||||
var systemPrompt = new TextMessage
|
{
|
||||||
{
|
Model = chatModel.Id,
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
// Build the messages:
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
// - First of all the system prompt
|
||||||
|
// - Then none-empty user and AI messages
|
||||||
|
Messages = [systemPrompt, ..messages],
|
||||||
|
|
||||||
// Build the list of messages:
|
// Right now, we only support streaming completions:
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
Stream = true,
|
||||||
|
AdditionalApiParameters = apiParameters
|
||||||
// Prepare the Google HTTP chat request:
|
};
|
||||||
var geminiChatRequest = JsonSerializer.Serialize(new ChatRequest
|
},
|
||||||
{
|
token: token))
|
||||||
Model = chatModel.Id,
|
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
|
||||||
|
|
||||||
// Right now, we only support streaming completions:
|
|
||||||
Stream = true,
|
|
||||||
AdditionalApiParameters = apiParameters
|
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
|
||||||
|
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
|
||||||
{
|
|
||||||
// Build the HTTP post request:
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
|
|
||||||
|
|
||||||
// Set the authorization header:
|
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
|
||||||
|
|
||||||
// Set the content:
|
|
||||||
request.Content = new StringContent(geminiChatRequest, Encoding.UTF8, "application/json");
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>("Google", RequestBuilder, token))
|
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace AIStudio.Provider.Groq;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The Groq chat request model.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="Model">Which model to use for chat completion.</param>
|
|
||||||
/// <param name="Messages">The chat messages.</param>
|
|
||||||
/// <param name="Stream">Whether to stream the chat completion.</param>
|
|
||||||
/// <param name="Seed">The seed for the chat completion.</param>
|
|
||||||
public readonly record struct ChatRequest(
|
|
||||||
string Model,
|
|
||||||
IList<IMessageBase> Messages,
|
|
||||||
bool Stream,
|
|
||||||
int Seed
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Attention: The "required" modifier is not supported for [JsonExtensionData].
|
|
||||||
[JsonExtensionData]
|
|
||||||
public IDictionary<string, object> AdditionalApiParameters { get; init; } = new Dictionary<string, object>();
|
|
||||||
}
|
|
||||||
@ -1,7 +1,5 @@
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
using AIStudio.Chat;
|
using AIStudio.Chat;
|
||||||
using AIStudio.Provider.OpenAI;
|
using AIStudio.Provider.OpenAI;
|
||||||
@ -24,53 +22,34 @@ 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)
|
||||||
{
|
{
|
||||||
// Get the API key:
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>(
|
||||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
|
"Groq",
|
||||||
if(!requestedSecret.Success)
|
chatModel,
|
||||||
yield break;
|
chatThread,
|
||||||
|
settingsManager,
|
||||||
|
async (systemPrompt, apiParameters) =>
|
||||||
|
{
|
||||||
|
if (TryPopIntParameter(apiParameters, "seed", out var parsedSeed))
|
||||||
|
apiParameters["seed"] = parsedSeed;
|
||||||
|
|
||||||
// Prepare the system prompt:
|
// Build the list of messages:
|
||||||
var systemPrompt = new TextMessage
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
{
|
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
return new ChatCompletionAPIRequest
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
{
|
||||||
|
Model = chatModel.Id,
|
||||||
|
|
||||||
// Build the list of messages:
|
// Build the messages:
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
// - First of all the system prompt
|
||||||
|
// - Then none-empty user and AI messages
|
||||||
|
Messages = [systemPrompt, ..messages],
|
||||||
|
|
||||||
// Prepare the OpenAI HTTP chat request:
|
// Right now, we only support streaming completions:
|
||||||
var groqChatRequest = JsonSerializer.Serialize(new ChatRequest
|
Stream = true,
|
||||||
{
|
AdditionalApiParameters = apiParameters
|
||||||
Model = chatModel.Id,
|
};
|
||||||
|
},
|
||||||
// Build the messages:
|
token: token))
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
|
||||||
|
|
||||||
// Right now, we only support streaming completions:
|
|
||||||
Stream = true,
|
|
||||||
AdditionalApiParameters = apiParameters
|
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
|
||||||
|
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
|
||||||
{
|
|
||||||
// Build the HTTP post request:
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
|
|
||||||
|
|
||||||
// Set the authorization header:
|
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
|
||||||
|
|
||||||
// Set the content:
|
|
||||||
request.Content = new StringContent(groqChatRequest, Encoding.UTF8, "application/json");
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>("Groq", RequestBuilder, token))
|
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
using AIStudio.Chat;
|
using AIStudio.Chat;
|
||||||
using AIStudio.Provider.OpenAI;
|
using AIStudio.Provider.OpenAI;
|
||||||
@ -24,52 +22,30 @@ 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)
|
||||||
{
|
{
|
||||||
// Get the API key:
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>(
|
||||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
|
"Helmholtz",
|
||||||
if(!requestedSecret.Success)
|
chatModel,
|
||||||
yield break;
|
chatThread,
|
||||||
|
settingsManager,
|
||||||
|
async (systemPrompt, apiParameters) =>
|
||||||
|
{
|
||||||
|
// Build the list of messages:
|
||||||
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the system prompt:
|
return new ChatCompletionAPIRequest
|
||||||
var systemPrompt = new TextMessage
|
{
|
||||||
{
|
Model = chatModel.Id,
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
// Build the messages:
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
// - First of all the system prompt
|
||||||
|
// - Then none-empty user and AI messages
|
||||||
|
Messages = [systemPrompt, ..messages],
|
||||||
|
|
||||||
// Build the list of messages:
|
Stream = true,
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
AdditionalApiParameters = apiParameters
|
||||||
|
};
|
||||||
// Prepare the Helmholtz HTTP chat request:
|
},
|
||||||
var helmholtzChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
token: token))
|
||||||
{
|
|
||||||
Model = chatModel.Id,
|
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
|
||||||
|
|
||||||
Stream = true,
|
|
||||||
AdditionalApiParameters = apiParameters
|
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
|
||||||
|
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
|
||||||
{
|
|
||||||
// Build the HTTP post request:
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
|
|
||||||
|
|
||||||
// Set the authorization header:
|
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
|
||||||
|
|
||||||
// Set the content:
|
|
||||||
request.Content = new StringContent(helmholtzChatRequest, Encoding.UTF8, "application/json");
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>("Helmholtz", RequestBuilder, token))
|
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,4 @@
|
|||||||
using System.Net.Http.Headers;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
using AIStudio.Chat;
|
using AIStudio.Chat;
|
||||||
using AIStudio.Provider.OpenAI;
|
using AIStudio.Provider.OpenAI;
|
||||||
@ -29,52 +26,30 @@ 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)
|
||||||
{
|
{
|
||||||
// Get the API key:
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>(
|
||||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
|
"HuggingFace",
|
||||||
if(!requestedSecret.Success)
|
chatModel,
|
||||||
yield break;
|
chatThread,
|
||||||
|
settingsManager,
|
||||||
|
async (systemPrompt, apiParameters) =>
|
||||||
|
{
|
||||||
|
// Build the list of messages:
|
||||||
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the system prompt:
|
return new ChatCompletionAPIRequest
|
||||||
var systemPrompt = new TextMessage
|
{
|
||||||
{
|
Model = chatModel.Id,
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
// Build the messages:
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
// - First of all the system prompt
|
||||||
|
// - Then none-empty user and AI messages
|
||||||
|
Messages = [systemPrompt, ..messages],
|
||||||
|
|
||||||
// Build the list of messages:
|
Stream = true,
|
||||||
var message = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
AdditionalApiParameters = apiParameters
|
||||||
|
};
|
||||||
// Prepare the HuggingFace HTTP chat request:
|
},
|
||||||
var huggingfaceChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
token: token))
|
||||||
{
|
|
||||||
Model = chatModel.Id,
|
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..message],
|
|
||||||
|
|
||||||
Stream = true,
|
|
||||||
AdditionalApiParameters = apiParameters
|
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
|
||||||
|
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
|
||||||
{
|
|
||||||
// Build the HTTP post request:
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
|
|
||||||
|
|
||||||
// Set the authorization header:
|
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
|
||||||
|
|
||||||
// Set the content:
|
|
||||||
request.Content = new StringContent(huggingfaceChatRequest, Encoding.UTF8, "application/json");
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>("HuggingFace", RequestBuilder, token))
|
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,25 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace AIStudio.Provider.Mistral;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The OpenAI chat request model.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="Model">Which model to use for chat completion.</param>
|
|
||||||
/// <param name="Messages">The chat messages.</param>
|
|
||||||
/// <param name="Stream">Whether to stream the chat completion.</param>
|
|
||||||
/// <param name="RandomSeed">The seed for the chat completion.</param>
|
|
||||||
/// <param name="SafePrompt">Whether to inject a safety prompt before all conversations.</param>
|
|
||||||
public readonly record struct ChatRequest(
|
|
||||||
string Model,
|
|
||||||
IList<IMessageBase> Messages,
|
|
||||||
bool Stream,
|
|
||||||
[property: JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
||||||
int? RandomSeed,
|
|
||||||
bool SafePrompt = false
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Attention: The "required" modifier is not supported for [JsonExtensionData].
|
|
||||||
[JsonExtensionData]
|
|
||||||
public IDictionary<string, object> AdditionalApiParameters { get; init; } = new Dictionary<string, object>();
|
|
||||||
}
|
|
||||||
@ -1,7 +1,5 @@
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
using AIStudio.Chat;
|
using AIStudio.Chat;
|
||||||
using AIStudio.Provider.OpenAI;
|
using AIStudio.Provider.OpenAI;
|
||||||
@ -22,58 +20,37 @@ 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)
|
||||||
{
|
{
|
||||||
// Get the API key:
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
||||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
|
"Mistral",
|
||||||
if(!requestedSecret.Success)
|
chatModel,
|
||||||
yield break;
|
chatThread,
|
||||||
|
settingsManager,
|
||||||
|
async (systemPrompt, apiParameters) =>
|
||||||
|
{
|
||||||
|
if (TryPopBoolParameter(apiParameters, "safe_prompt", out var parsedSafePrompt))
|
||||||
|
apiParameters["safe_prompt"] = parsedSafePrompt;
|
||||||
|
|
||||||
// Prepare the system prompt:
|
if (TryPopIntParameter(apiParameters, "random_seed", out var parsedRandomSeed))
|
||||||
var systemPrompt = new TextMessage
|
apiParameters["random_seed"] = parsedRandomSeed;
|
||||||
{
|
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
// Build the list of messages:
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
var messages = await chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel);
|
||||||
var safePrompt = TryPopBoolParameter(apiParameters, "safe_prompt", out var parsedSafePrompt) && parsedSafePrompt;
|
|
||||||
var randomSeed = TryPopIntParameter(apiParameters, "random_seed", out var parsedRandomSeed) ? parsedRandomSeed : (int?)null;
|
|
||||||
|
|
||||||
// Build the list of messages:
|
return new ChatCompletionAPIRequest
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel);
|
{
|
||||||
|
Model = chatModel.Id,
|
||||||
|
|
||||||
// Prepare the Mistral HTTP chat request:
|
// Build the messages:
|
||||||
var mistralChatRequest = JsonSerializer.Serialize(new ChatRequest
|
// - First of all the system prompt
|
||||||
{
|
// - Then none-empty user and AI messages
|
||||||
Model = chatModel.Id,
|
Messages = [systemPrompt, ..messages],
|
||||||
|
|
||||||
// Build the messages:
|
// Right now, we only support streaming completions:
|
||||||
// - First of all the system prompt
|
Stream = true,
|
||||||
// - Then none-empty user and AI messages
|
AdditionalApiParameters = apiParameters
|
||||||
Messages = [systemPrompt, ..messages],
|
};
|
||||||
|
},
|
||||||
// Right now, we only support streaming completions:
|
token: token))
|
||||||
Stream = true,
|
|
||||||
RandomSeed = randomSeed,
|
|
||||||
SafePrompt = safePrompt,
|
|
||||||
AdditionalApiParameters = apiParameters
|
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
|
||||||
|
|
||||||
|
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
|
||||||
{
|
|
||||||
// Build the HTTP post request:
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
|
|
||||||
|
|
||||||
// Set the authorization header:
|
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
|
||||||
|
|
||||||
// Set the content:
|
|
||||||
request.Content = new StringContent(mistralChatRequest, Encoding.UTF8, "application/json");
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>("Mistral", RequestBuilder, token))
|
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -79,9 +79,9 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, "https
|
|||||||
//
|
//
|
||||||
// Prepare the tools we want to use:
|
// Prepare the tools we want to use:
|
||||||
//
|
//
|
||||||
IList<Tool> tools = modelCapabilities.Contains(Capability.WEB_SEARCH) switch
|
IList<ProviderTool> providerTools = modelCapabilities.Contains(Capability.WEB_SEARCH) switch
|
||||||
{
|
{
|
||||||
true => [ Tools.WEB_SEARCH ],
|
true => [ ProviderTools.WEB_SEARCH ],
|
||||||
_ => []
|
_ => []
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -178,7 +178,7 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, "https
|
|||||||
Store = false,
|
Store = false,
|
||||||
|
|
||||||
// Tools we want to use:
|
// Tools we want to use:
|
||||||
Tools = tools,
|
ProviderTools = providerTools,
|
||||||
|
|
||||||
// Additional API parameters:
|
// Additional API parameters:
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
namespace AIStudio.Provider.OpenAI;
|
namespace AIStudio.Provider.OpenAI;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a tool used by the AI model.
|
/// Represents a tool executed on the provider side.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Right now, only our OpenAI provider is using tools. Thus, this class is located in the
|
/// Right now, only our OpenAI provider is using tools. Thus, this class is located in the
|
||||||
@ -9,4 +9,4 @@ namespace AIStudio.Provider.OpenAI;
|
|||||||
/// be moved into the provider namespace.
|
/// be moved into the provider namespace.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="Type">The type of the tool.</param>
|
/// <param name="Type">The type of the tool.</param>
|
||||||
public record Tool(string Type);
|
public record ProviderTool(string Type);
|
||||||
@ -1,14 +1,14 @@
|
|||||||
namespace AIStudio.Provider.OpenAI;
|
namespace AIStudio.Provider.OpenAI;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Known tools for LLM providers.
|
/// Known provider-side tools for LLM providers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Right now, only our OpenAI provider is using tools. Thus, this class is located in the
|
/// Right now, only our OpenAI provider is using tools. Thus, this class is located in the
|
||||||
/// OpenAI namespace. In the future, when other providers also support tools, this class can
|
/// OpenAI namespace. In the future, when other providers also support tools, this class can
|
||||||
/// be moved into the provider namespace.
|
/// be moved into the provider namespace.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public static class Tools
|
public static class ProviderTools
|
||||||
{
|
{
|
||||||
public static readonly Tool WEB_SEARCH = new("web_search");
|
public static readonly ProviderTool WEB_SEARCH = new("web_search");
|
||||||
}
|
}
|
||||||
@ -9,13 +9,13 @@ namespace AIStudio.Provider.OpenAI;
|
|||||||
/// <param name="Input">The chat messages.</param>
|
/// <param name="Input">The chat messages.</param>
|
||||||
/// <param name="Stream">Whether to stream the response.</param>
|
/// <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="Store">Whether to store the response on the server (usually OpenAI's infrastructure).</param>
|
||||||
/// <param name="Tools">The tools to use for the request.</param>
|
/// <param name="ProviderTools">The provider-side tools to use for the request.</param>
|
||||||
public record ResponsesAPIRequest(
|
public record ResponsesAPIRequest(
|
||||||
string Model,
|
string Model,
|
||||||
IList<IMessageBase> Input,
|
IList<IMessageBase> Input,
|
||||||
bool Stream,
|
bool Stream,
|
||||||
bool Store,
|
bool Store,
|
||||||
IList<Tool> Tools)
|
[property: JsonPropertyName("tools")] IList<ProviderTool> ProviderTools)
|
||||||
{
|
{
|
||||||
public ResponsesAPIRequest() : this(string.Empty, [], true, false, [])
|
public ResponsesAPIRequest() : this(string.Empty, [], true, false, [])
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
using AIStudio.Chat;
|
using AIStudio.Chat;
|
||||||
using AIStudio.Provider.OpenAI;
|
using AIStudio.Provider.OpenAI;
|
||||||
@ -27,57 +25,37 @@ 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)
|
||||||
{
|
{
|
||||||
// Get the API key:
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
||||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
|
"OpenRouter",
|
||||||
if(!requestedSecret.Success)
|
chatModel,
|
||||||
yield break;
|
chatThread,
|
||||||
|
settingsManager,
|
||||||
|
async (systemPrompt, apiParameters) =>
|
||||||
|
{
|
||||||
|
// Build the list of messages:
|
||||||
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the system prompt:
|
return new ChatCompletionAPIRequest
|
||||||
var systemPrompt = new TextMessage
|
{
|
||||||
{
|
Model = chatModel.Id,
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
// Build the messages:
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
// - First of all the system prompt
|
||||||
|
// - Then none-empty user and AI messages
|
||||||
|
Messages = [systemPrompt, ..messages],
|
||||||
|
|
||||||
// Build the list of messages:
|
// Right now, we only support streaming completions:
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
Stream = true,
|
||||||
|
AdditionalApiParameters = apiParameters
|
||||||
// Prepare the OpenRouter HTTP chat request:
|
};
|
||||||
var openRouterChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
},
|
||||||
{
|
headersAction: headers =>
|
||||||
Model = chatModel.Id,
|
{
|
||||||
|
// Set custom headers for project identification:
|
||||||
// Build the messages:
|
headers.Add("HTTP-Referer", PROJECT_WEBSITE);
|
||||||
// - First of all the system prompt
|
headers.Add("X-Title", PROJECT_NAME);
|
||||||
// - Then none-empty user and AI messages
|
},
|
||||||
Messages = [systemPrompt, ..messages],
|
token: token))
|
||||||
|
|
||||||
// Right now, we only support streaming completions:
|
|
||||||
Stream = true,
|
|
||||||
AdditionalApiParameters = apiParameters
|
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
|
||||||
|
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
|
||||||
{
|
|
||||||
// Build the HTTP post request:
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
|
|
||||||
|
|
||||||
// Set the authorization header:
|
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
|
||||||
|
|
||||||
// Set custom headers for project identification:
|
|
||||||
request.Headers.Add("HTTP-Referer", PROJECT_WEBSITE);
|
|
||||||
request.Headers.Add("X-Title", PROJECT_NAME);
|
|
||||||
|
|
||||||
// Set the content:
|
|
||||||
request.Content = new StringContent(openRouterChatRequest, Encoding.UTF8, "application/json");
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>("OpenRouter", RequestBuilder, token))
|
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,4 @@
|
|||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
using AIStudio.Chat;
|
using AIStudio.Chat;
|
||||||
using AIStudio.Provider.OpenAI;
|
using AIStudio.Provider.OpenAI;
|
||||||
@ -33,51 +30,29 @@ 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)
|
||||||
{
|
{
|
||||||
// Get the API key:
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ResponseStreamLine, NoChatCompletionAnnotationStreamLine>(
|
||||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
|
"Perplexity",
|
||||||
if(!requestedSecret.Success)
|
chatModel,
|
||||||
yield break;
|
chatThread,
|
||||||
|
settingsManager,
|
||||||
|
async (systemPrompt, apiParameters) =>
|
||||||
|
{
|
||||||
|
// Build the list of messages:
|
||||||
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the system prompt:
|
return new ChatCompletionAPIRequest
|
||||||
var systemPrompt = new TextMessage
|
{
|
||||||
{
|
Model = chatModel.Id,
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
// Build the messages:
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
// - First of all the system prompt
|
||||||
|
// - Then none-empty user and AI messages
|
||||||
// Build the list of messages:
|
Messages = [systemPrompt, ..messages],
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
Stream = true,
|
||||||
|
AdditionalApiParameters = apiParameters
|
||||||
// Prepare the Perplexity HTTP chat request:
|
};
|
||||||
var perplexityChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
},
|
||||||
{
|
token: token))
|
||||||
Model = chatModel.Id,
|
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
|
||||||
Stream = true,
|
|
||||||
AdditionalApiParameters = apiParameters
|
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
|
||||||
|
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
|
||||||
{
|
|
||||||
// Build the HTTP post request:
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
|
|
||||||
|
|
||||||
// Set the authorization header:
|
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
|
||||||
|
|
||||||
// Set the content:
|
|
||||||
request.Content = new StringContent(perplexityChatRequest, Encoding.UTF8, "application/json");
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
await foreach (var content in this.StreamChatCompletionInternal<ResponseStreamLine, NoChatCompletionAnnotationStreamLine>("Perplexity", RequestBuilder, token))
|
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,20 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace AIStudio.Provider.SelfHosted;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The chat request model.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="Model">Which model to use for chat completion.</param>
|
|
||||||
/// <param name="Messages">The chat messages.</param>
|
|
||||||
/// <param name="Stream">Whether to stream the chat completion.</param>
|
|
||||||
public readonly record struct ChatRequest(
|
|
||||||
string Model,
|
|
||||||
IList<IMessageBase> Messages,
|
|
||||||
bool Stream
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Attention: The "required" modifier is not supported for [JsonExtensionData].
|
|
||||||
[JsonExtensionData]
|
|
||||||
public IDictionary<string, object> AdditionalApiParameters { get; init; } = new Dictionary<string, object>();
|
|
||||||
}
|
|
||||||
@ -1,7 +1,5 @@
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
using AIStudio.Chat;
|
using AIStudio.Chat;
|
||||||
using AIStudio.Provider.OpenAI;
|
using AIStudio.Provider.OpenAI;
|
||||||
@ -25,58 +23,39 @@ 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)
|
||||||
{
|
{
|
||||||
// Get the API key:
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>(
|
||||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER, isTrying: true);
|
"self-hosted provider",
|
||||||
|
chatModel,
|
||||||
|
chatThread,
|
||||||
|
settingsManager,
|
||||||
|
async (systemPrompt, apiParameters) =>
|
||||||
|
{
|
||||||
|
// Build the list of messages. The image format depends on the host:
|
||||||
|
// - Ollama uses the direct image URL format: { "type": "image_url", "image_url": "data:..." }
|
||||||
|
// - LM Studio, vLLM, and llama.cpp use the nested image URL format: { "type": "image_url", "image_url": { "url": "data:..." } }
|
||||||
|
var messages = host switch
|
||||||
|
{
|
||||||
|
Host.OLLAMA => await chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel),
|
||||||
|
_ => await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel),
|
||||||
|
};
|
||||||
|
|
||||||
// Prepare the system prompt:
|
return new ChatCompletionAPIRequest
|
||||||
var systemPrompt = new TextMessage
|
{
|
||||||
{
|
Model = chatModel.Id,
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
// Build the messages:
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
// - First of all the system prompt
|
||||||
|
// - Then none-empty user and AI messages
|
||||||
|
Messages = [systemPrompt, ..messages],
|
||||||
|
|
||||||
// Build the list of messages. The image format depends on the host:
|
// Right now, we only support streaming completions:
|
||||||
// - Ollama uses the direct image URL format: { "type": "image_url", "image_url": "data:..." }
|
Stream = true,
|
||||||
// - LM Studio, vLLM, and llama.cpp use the nested image URL format: { "type": "image_url", "image_url": { "url": "data:..." } }
|
AdditionalApiParameters = apiParameters
|
||||||
var messages = host switch
|
};
|
||||||
{
|
},
|
||||||
Host.OLLAMA => await chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel),
|
isTryingSecret: true,
|
||||||
_ => await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel),
|
requestPath: host.ChatURL(),
|
||||||
};
|
token: token))
|
||||||
|
|
||||||
// Prepare the OpenAI HTTP chat request:
|
|
||||||
var providerChatRequest = JsonSerializer.Serialize(new ChatRequest
|
|
||||||
{
|
|
||||||
Model = chatModel.Id,
|
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
|
||||||
|
|
||||||
// Right now, we only support streaming completions:
|
|
||||||
Stream = true,
|
|
||||||
AdditionalApiParameters = apiParameters
|
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
|
||||||
|
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
|
||||||
{
|
|
||||||
// Build the HTTP post request:
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Post, host.ChatURL());
|
|
||||||
|
|
||||||
// Set the authorization header:
|
|
||||||
if (requestedSecret.Success)
|
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
|
||||||
|
|
||||||
// Set the content:
|
|
||||||
request.Content = new StringContent(providerChatRequest, Encoding.UTF8, "application/json");
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, ChatCompletionAnnotationStreamLine>("self-hosted provider", RequestBuilder, token))
|
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
using AIStudio.Chat;
|
using AIStudio.Chat;
|
||||||
using AIStudio.Provider.OpenAI;
|
using AIStudio.Provider.OpenAI;
|
||||||
@ -24,53 +22,31 @@ 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)
|
||||||
{
|
{
|
||||||
// Get the API key:
|
await foreach (var content in this.StreamOpenAICompatibleChatCompletion<ChatCompletionAPIRequest, ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>(
|
||||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this, SecretStoreType.LLM_PROVIDER);
|
"xAI",
|
||||||
if(!requestedSecret.Success)
|
chatModel,
|
||||||
yield break;
|
chatThread,
|
||||||
|
settingsManager,
|
||||||
|
async (systemPrompt, apiParameters) =>
|
||||||
|
{
|
||||||
|
// Build the list of messages:
|
||||||
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the system prompt:
|
return new ChatCompletionAPIRequest
|
||||||
var systemPrompt = new TextMessage
|
{
|
||||||
{
|
Model = chatModel.Id,
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
// Build the messages:
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
// - First of all the system prompt
|
||||||
|
// - Then none-empty user and AI messages
|
||||||
|
Messages = [systemPrompt, ..messages],
|
||||||
|
|
||||||
// Build the list of messages:
|
// Right now, we only support streaming completions:
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
Stream = true,
|
||||||
|
AdditionalApiParameters = apiParameters
|
||||||
// Prepare the xAI HTTP chat request:
|
};
|
||||||
var xChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
},
|
||||||
{
|
token: token))
|
||||||
Model = chatModel.Id,
|
|
||||||
|
|
||||||
// Build the messages:
|
|
||||||
// - First of all the system prompt
|
|
||||||
// - Then none-empty user and AI messages
|
|
||||||
Messages = [systemPrompt, ..messages],
|
|
||||||
|
|
||||||
// Right now, we only support streaming completions:
|
|
||||||
Stream = true,
|
|
||||||
AdditionalApiParameters = apiParameters
|
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
|
||||||
|
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
|
||||||
{
|
|
||||||
// Build the HTTP post request:
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions");
|
|
||||||
|
|
||||||
// Set the authorization header:
|
|
||||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
|
|
||||||
|
|
||||||
// Set the content:
|
|
||||||
request.Content = new StringContent(xChatRequest, Encoding.UTF8, "application/json");
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
await foreach (var content in this.StreamChatCompletionInternal<ChatCompletionDeltaStreamLine, NoChatCompletionAnnotationStreamLine>("xAI", RequestBuilder, token))
|
|
||||||
yield return content;
|
yield return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
- Improved the validation of additional API parameters in the advanced provider settings to help catch formatting mistakes earlier.
|
- Improved the validation of additional API parameters in the advanced provider settings to help catch formatting mistakes earlier.
|
||||||
- Improved the app startup resilience by allowing AI Studio to continue without Qdrant if it fails to initialize.
|
- Improved the app startup resilience by allowing AI Studio to continue without Qdrant if it fails to initialize.
|
||||||
- Improved the translation assistant by updating the system and user prompts.
|
- Improved the translation assistant by updating the system and user prompts.
|
||||||
|
- Improved OpenAI-compatible providers by refactoring their streaming request handling to be more consistent and reliable.
|
||||||
- Fixed an issue where assistants hidden via configuration plugins still appear in "Send to ..." menus. Thanks, Gunnar, for reporting this issue.
|
- Fixed an issue where assistants hidden via configuration plugins still appear in "Send to ..." menus. Thanks, Gunnar, for reporting this issue.
|
||||||
- Fixed an issue with chat templates that could stop working because the stored validation result for attached files was reused. AI Studio now checks attached files again when you use a chat template.
|
- Fixed an issue with chat templates that could stop working because the stored validation result for attached files was reused. AI Studio now checks attached files again when you use a chat template.
|
||||||
- Fixed an issue with voice recording where AI Studio could log errors and keep the feature available even though required parts failed to initialize. Voice recording is now disabled automatically for the current session in that case.
|
- Fixed an issue with voice recording where AI Studio could log errors and keep the feature available even though required parts failed to initialize. Voice recording is now disabled automatically for the current session in that case.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user