mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-04-15 03:01: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,26 +22,17 @@ 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,
|
||||||
// Prepare the system prompt:
|
async (systemPrompt, apiParameters) =>
|
||||||
var systemPrompt = new TextMessage
|
|
||||||
{
|
{
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
|
||||||
|
|
||||||
// Build the list of messages:
|
// Build the list of messages:
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the AlibabaCloud HTTP chat request:
|
return new ChatCompletionAPIRequest
|
||||||
var alibabaCloudChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
@ -54,22 +43,9 @@ public sealed class ProviderAlibabaCloud() : BaseProvider(LLMProviders.ALIBABA_C
|
|||||||
|
|
||||||
Stream = true,
|
Stream = true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
};
|
||||||
|
},
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
token: token))
|
||||||
{
|
|
||||||
// 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,26 +22,17 @@ 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,
|
||||||
// Prepare the system prompt:
|
async (systemPrompt, apiParameters) =>
|
||||||
var systemPrompt = new TextMessage
|
|
||||||
{
|
{
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
|
||||||
|
|
||||||
// Build the list of messages:
|
// Build the list of messages:
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel);
|
var messages = await chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the DeepSeek HTTP chat request:
|
return new ChatCompletionAPIRequest
|
||||||
var deepSeekChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
@ -54,22 +43,9 @@ public sealed class ProviderDeepSeek() : BaseProvider(LLMProviders.DEEP_SEEK, "h
|
|||||||
|
|
||||||
Stream = true,
|
Stream = true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
};
|
||||||
|
},
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
token: token))
|
||||||
{
|
|
||||||
// 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,26 +21,17 @@ 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,
|
||||||
// Prepare the system prompt:
|
async (systemPrompt, apiParameters) =>
|
||||||
var systemPrompt = new TextMessage
|
|
||||||
{
|
{
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
|
||||||
|
|
||||||
// Build the list of messages:
|
// Build the list of messages:
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the Fireworks HTTP chat request:
|
return new ChatCompletionAPIRequest
|
||||||
var fireworksChatRequest = JsonSerializer.Serialize(new ChatRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
@ -55,22 +43,9 @@ public class ProviderFireworks() : BaseProvider(LLMProviders.FIREWORKS, "https:/
|
|||||||
// Right now, we only support streaming completions:
|
// Right now, we only support streaming completions:
|
||||||
Stream = true,
|
Stream = true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
};
|
||||||
|
},
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
token: token))
|
||||||
{
|
|
||||||
// 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,26 +22,17 @@ 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,
|
||||||
// Prepare the system prompt:
|
async (systemPrompt, apiParameters) =>
|
||||||
var systemPrompt = new TextMessage
|
|
||||||
{
|
{
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
|
||||||
|
|
||||||
// Build the list of messages:
|
// Build the list of messages:
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the GWDG HTTP chat request:
|
return new ChatCompletionAPIRequest
|
||||||
var gwdgChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
@ -54,22 +43,9 @@ public sealed class ProviderGWDG() : BaseProvider(LLMProviders.GWDG, "https://ch
|
|||||||
|
|
||||||
Stream = true,
|
Stream = true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
};
|
||||||
|
},
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
token: token))
|
||||||
{
|
|
||||||
// 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,26 +24,17 @@ 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,
|
||||||
// Prepare the system prompt:
|
async (systemPrompt, apiParameters) =>
|
||||||
var systemPrompt = new TextMessage
|
|
||||||
{
|
{
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
|
||||||
|
|
||||||
// Build the list of messages:
|
// Build the list of messages:
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the Google HTTP chat request:
|
return new ChatCompletionAPIRequest
|
||||||
var geminiChatRequest = JsonSerializer.Serialize(new ChatRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
@ -55,22 +46,9 @@ public class ProviderGoogle() : BaseProvider(LLMProviders.GOOGLE, "https://gener
|
|||||||
// Right now, we only support streaming completions:
|
// Right now, we only support streaming completions:
|
||||||
Stream = true,
|
Stream = true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
};
|
||||||
|
},
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
token: token))
|
||||||
{
|
|
||||||
// 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,26 +22,20 @@ 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,
|
||||||
// Prepare the system prompt:
|
async (systemPrompt, apiParameters) =>
|
||||||
var systemPrompt = new TextMessage
|
|
||||||
{
|
{
|
||||||
Role = "system",
|
if (TryPopIntParameter(apiParameters, "seed", out var parsedSeed))
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
apiParameters["seed"] = parsedSeed;
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
|
||||||
|
|
||||||
// Build the list of messages:
|
// Build the list of messages:
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the OpenAI HTTP chat request:
|
return new ChatCompletionAPIRequest
|
||||||
var groqChatRequest = JsonSerializer.Serialize(new ChatRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
@ -55,22 +47,9 @@ public class ProviderGroq() : BaseProvider(LLMProviders.GROQ, "https://api.groq.
|
|||||||
// Right now, we only support streaming completions:
|
// Right now, we only support streaming completions:
|
||||||
Stream = true,
|
Stream = true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
};
|
||||||
|
},
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
token: token))
|
||||||
{
|
|
||||||
// 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,26 +22,17 @@ 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,
|
||||||
// Prepare the system prompt:
|
async (systemPrompt, apiParameters) =>
|
||||||
var systemPrompt = new TextMessage
|
|
||||||
{
|
{
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
|
||||||
|
|
||||||
// Build the list of messages:
|
// Build the list of messages:
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the Helmholtz HTTP chat request:
|
return new ChatCompletionAPIRequest
|
||||||
var helmholtzChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
@ -54,22 +43,9 @@ public sealed class ProviderHelmholtz() : BaseProvider(LLMProviders.HELMHOLTZ, "
|
|||||||
|
|
||||||
Stream = true,
|
Stream = true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
};
|
||||||
|
},
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
token: token))
|
||||||
{
|
|
||||||
// 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,
|
||||||
// Prepare the system prompt:
|
async (systemPrompt, apiParameters) =>
|
||||||
var systemPrompt = new TextMessage
|
|
||||||
{
|
{
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
|
||||||
|
|
||||||
// Build the list of messages:
|
// Build the list of messages:
|
||||||
var message = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the HuggingFace HTTP chat request:
|
return new ChatCompletionAPIRequest
|
||||||
var huggingfaceChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
// Build the messages:
|
// Build the messages:
|
||||||
// - First of all the system prompt
|
// - First of all the system prompt
|
||||||
// - Then none-empty user and AI messages
|
// - Then none-empty user and AI messages
|
||||||
Messages = [systemPrompt, ..message],
|
Messages = [systemPrompt, ..messages],
|
||||||
|
|
||||||
Stream = true,
|
Stream = true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
};
|
||||||
|
},
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
token: token))
|
||||||
{
|
|
||||||
// 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,28 +20,23 @@ 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,
|
||||||
// Prepare the system prompt:
|
async (systemPrompt, apiParameters) =>
|
||||||
var systemPrompt = new TextMessage
|
|
||||||
{
|
{
|
||||||
Role = "system",
|
if (TryPopBoolParameter(apiParameters, "safe_prompt", out var parsedSafePrompt))
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
apiParameters["safe_prompt"] = parsedSafePrompt;
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
if (TryPopIntParameter(apiParameters, "random_seed", out var parsedRandomSeed))
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
apiParameters["random_seed"] = parsedRandomSeed;
|
||||||
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:
|
// Build the list of messages:
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel);
|
var messages = await chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the Mistral HTTP chat request:
|
return new ChatCompletionAPIRequest
|
||||||
var mistralChatRequest = JsonSerializer.Serialize(new ChatRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
@ -54,26 +47,10 @@ public sealed class ProviderMistral() : BaseProvider(LLMProviders.MISTRAL, "http
|
|||||||
|
|
||||||
// Right now, we only support streaming completions:
|
// Right now, we only support streaming completions:
|
||||||
Stream = true,
|
Stream = true,
|
||||||
RandomSeed = randomSeed,
|
|
||||||
SafePrompt = safePrompt,
|
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
};
|
||||||
|
},
|
||||||
|
token: token))
|
||||||
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,26 +25,17 @@ 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,
|
||||||
// Prepare the system prompt:
|
async (systemPrompt, apiParameters) =>
|
||||||
var systemPrompt = new TextMessage
|
|
||||||
{
|
{
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
|
||||||
|
|
||||||
// Build the list of messages:
|
// Build the list of messages:
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the OpenRouter HTTP chat request:
|
return new ChatCompletionAPIRequest
|
||||||
var openRouterChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
@ -58,26 +47,15 @@ public sealed class ProviderOpenRouter() : BaseProvider(LLMProviders.OPEN_ROUTER
|
|||||||
// Right now, we only support streaming completions:
|
// Right now, we only support streaming completions:
|
||||||
Stream = true,
|
Stream = true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
};
|
||||||
|
},
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
headersAction: headers =>
|
||||||
{
|
{
|
||||||
// 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:
|
// Set custom headers for project identification:
|
||||||
request.Headers.Add("HTTP-Referer", PROJECT_WEBSITE);
|
headers.Add("HTTP-Referer", PROJECT_WEBSITE);
|
||||||
request.Headers.Add("X-Title", PROJECT_NAME);
|
headers.Add("X-Title", PROJECT_NAME);
|
||||||
|
},
|
||||||
// Set the content:
|
token: token))
|
||||||
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,26 +30,17 @@ 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,
|
||||||
// Prepare the system prompt:
|
async (systemPrompt, apiParameters) =>
|
||||||
var systemPrompt = new TextMessage
|
|
||||||
{
|
{
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
|
||||||
|
|
||||||
// Build the list of messages:
|
// Build the list of messages:
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the Perplexity HTTP chat request:
|
return new ChatCompletionAPIRequest
|
||||||
var perplexityChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
@ -62,22 +50,9 @@ public sealed class ProviderPerplexity() : BaseProvider(LLMProviders.PERPLEXITY,
|
|||||||
Messages = [systemPrompt, ..messages],
|
Messages = [systemPrompt, ..messages],
|
||||||
Stream = true,
|
Stream = true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
};
|
||||||
|
},
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
token: token))
|
||||||
{
|
|
||||||
// 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,19 +23,13 @@ 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,
|
||||||
// Prepare the system prompt:
|
chatThread,
|
||||||
var systemPrompt = new TextMessage
|
settingsManager,
|
||||||
|
async (systemPrompt, apiParameters) =>
|
||||||
{
|
{
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
|
||||||
|
|
||||||
// Build the list of messages. The image format depends on the host:
|
// 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:..." }
|
// - 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:..." } }
|
// - LM Studio, vLLM, and llama.cpp use the nested image URL format: { "type": "image_url", "image_url": { "url": "data:..." } }
|
||||||
@ -47,8 +39,7 @@ public sealed class ProviderSelfHosted(Host host, string hostname) : BaseProvide
|
|||||||
_ => await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel),
|
_ => await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prepare the OpenAI HTTP chat request:
|
return new ChatCompletionAPIRequest
|
||||||
var providerChatRequest = JsonSerializer.Serialize(new ChatRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
@ -60,23 +51,11 @@ public sealed class ProviderSelfHosted(Host host, string hostname) : BaseProvide
|
|||||||
// Right now, we only support streaming completions:
|
// Right now, we only support streaming completions:
|
||||||
Stream = true,
|
Stream = true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
};
|
||||||
|
},
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
isTryingSecret: true,
|
||||||
{
|
requestPath: host.ChatURL(),
|
||||||
// Build the HTTP post request:
|
token: token))
|
||||||
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,26 +22,17 @@ 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,
|
||||||
// Prepare the system prompt:
|
async (systemPrompt, apiParameters) =>
|
||||||
var systemPrompt = new TextMessage
|
|
||||||
{
|
{
|
||||||
Role = "system",
|
|
||||||
Content = chatThread.PrepareSystemPrompt(settingsManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the API parameters:
|
|
||||||
var apiParameters = this.ParseAdditionalApiParameters();
|
|
||||||
|
|
||||||
// Build the list of messages:
|
// Build the list of messages:
|
||||||
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
var messages = await chatThread.Blocks.BuildMessagesUsingNestedImageUrlAsync(this.Provider, chatModel);
|
||||||
|
|
||||||
// Prepare the xAI HTTP chat request:
|
return new ChatCompletionAPIRequest
|
||||||
var xChatRequest = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
|
||||||
{
|
{
|
||||||
Model = chatModel.Id,
|
Model = chatModel.Id,
|
||||||
|
|
||||||
@ -55,22 +44,9 @@ public sealed class ProviderX() : BaseProvider(LLMProviders.X, "https://api.x.ai
|
|||||||
// Right now, we only support streaming completions:
|
// Right now, we only support streaming completions:
|
||||||
Stream = true,
|
Stream = true,
|
||||||
AdditionalApiParameters = apiParameters
|
AdditionalApiParameters = apiParameters
|
||||||
}, JSON_SERIALIZER_OPTIONS);
|
};
|
||||||
|
},
|
||||||
async Task<HttpRequestMessage> RequestBuilder()
|
token: token))
|
||||||
{
|
|
||||||
// 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