mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-08-11 20:32:11 +00:00
Added transparent ProviderTransparency
This commit is contained in:
parent
9fc7eaff99
commit
7d54b9a550
@ -4138,6 +4138,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T2842060373"] = "In
|
||||
-- Currently, we cannot query the embedding models for the selected provider and/or host. Therefore, please enter the model name manually.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T290547799"] = "Currently, we cannot query the embedding models for the selected provider and/or host. Therefore, please enter the model name manually."
|
||||
|
||||
-- This host uses the model configured at the provider level. No model selection is available.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T3783329915"] = "This host uses the model configured at the provider level. No model selection is available."
|
||||
|
||||
-- Model selection
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGPROVIDERDIALOG::T416738168"] = "Model selection"
|
||||
|
||||
@ -6685,6 +6688,12 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::CONFIDENCELEVELEXTENSIONS::T786675843"] = "
|
||||
-- Self-hosted
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T146444217"] = "Self-hosted"
|
||||
|
||||
-- This provider never contacts an external service. It only shows the exact request AI Studio generated locally so you can inspect it for transparency and research. It is not intended to produce real model answers.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T2035654846"] = "This provider never contacts an external service. It only shows the exact request AI Studio generated locally so you can inspect it for transparency and research. It is not intended to produce real model answers."
|
||||
|
||||
-- Transparency
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T2360978711"] = "Transparency"
|
||||
|
||||
-- No provider selected
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T2897045472"] = "No provider selected"
|
||||
|
||||
|
||||
@ -22,6 +22,13 @@
|
||||
@T("Create account")
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(this.DataLLMProvider.Description()))
|
||||
{
|
||||
<MudAlert Severity="Severity.Info" Class="mb-3">
|
||||
@this.DataLLMProvider.Description()
|
||||
</MudAlert>
|
||||
}
|
||||
|
||||
@if (this.DataLLMProvider.IsAPIKeyNeeded(this.DataHost))
|
||||
{
|
||||
@ -57,57 +64,68 @@
|
||||
</MudSelect>
|
||||
}
|
||||
|
||||
<MudField FullWidth="true" Label="@T("Model selection")" Variant="Variant.Outlined" Class="mb-3">
|
||||
<MudStack Row="@true" AlignItems="AlignItems.Center" StretchItems="StretchItems.End">
|
||||
@if (this.DataLLMProvider.IsEmbeddingModelProvidedManually(this.DataHost))
|
||||
{
|
||||
<MudTextField
|
||||
T="string"
|
||||
@bind-Text="@this.dataManuallyModel"
|
||||
Label="@T("Model")"
|
||||
Class="mb-3"
|
||||
Adornment="Adornment.Start"
|
||||
AdornmentIcon="@Icons.Material.Filled.Dns"
|
||||
AdornmentColor="Color.Info"
|
||||
Validation="@this.ValidateManuallyModel"
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
HelperText="@T("Currently, we cannot query the embedding models for the selected provider and/or host. Therefore, please enter the model name manually.")"
|
||||
/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudButton Disabled="@(!this.DataLLMProvider.CanLoadModels(this.DataHost, this.dataAPIKey))" Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.Refresh" OnClick="@this.ReloadModels">
|
||||
@T("Load")
|
||||
</MudButton>
|
||||
@if(this.availableModels.Count is 0)
|
||||
@if (!this.DataLLMProvider.IsEmbeddingModelSelectionHidden(this.DataHost))
|
||||
{
|
||||
<MudField FullWidth="true" Label="@T("Model selection")" Variant="Variant.Outlined" Class="mb-3">
|
||||
<MudStack Row="@true" AlignItems="AlignItems.Center" StretchItems="StretchItems.End">
|
||||
@if (this.DataLLMProvider.IsEmbeddingModelProvidedManually(this.DataHost))
|
||||
{
|
||||
<MudText Typo="Typo.body1">
|
||||
@T("No models loaded or available.")
|
||||
</MudText>
|
||||
<MudTextField
|
||||
T="string"
|
||||
@bind-Text="@this.dataManuallyModel"
|
||||
Label="@T("Model")"
|
||||
Class="mb-3"
|
||||
Adornment="Adornment.Start"
|
||||
AdornmentIcon="@Icons.Material.Filled.Dns"
|
||||
AdornmentColor="Color.Info"
|
||||
Validation="@this.ValidateManuallyModel"
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
HelperText="@T("Currently, we cannot query the embedding models for the selected provider and/or host. Therefore, please enter the model name manually.")"
|
||||
/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudSelect Disabled="@this.IsNoneProvider" @bind-Value="@this.DataModel" Label="@T("Model")"
|
||||
OpenIcon="@Icons.Material.Filled.FaceRetouchingNatural"
|
||||
AdornmentColor="Color.Info" Adornment="Adornment.Start"
|
||||
Validation="@this.providerValidation.ValidatingModel">
|
||||
@foreach (var model in this.availableModels)
|
||||
{
|
||||
<MudSelectItem Value="@model">
|
||||
@model
|
||||
</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
<MudButton Disabled="@(!this.DataLLMProvider.CanLoadModels(this.DataHost, this.dataAPIKey))" Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.Refresh" OnClick="@this.ReloadModels">
|
||||
@T("Load")
|
||||
</MudButton>
|
||||
@if(this.availableModels.Count is 0)
|
||||
{
|
||||
<MudText Typo="Typo.body1">
|
||||
@T("No models loaded or available.")
|
||||
</MudText>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudSelect Disabled="@this.IsNoneProvider" @bind-Value="@this.DataModel" Label="@T("Model")"
|
||||
OpenIcon="@Icons.Material.Filled.FaceRetouchingNatural"
|
||||
AdornmentColor="Color.Info" Adornment="Adornment.Start"
|
||||
Validation="@this.providerValidation.ValidatingModel">
|
||||
@foreach (var model in this.availableModels)
|
||||
{
|
||||
<MudSelectItem Value="@model">
|
||||
@model
|
||||
</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
}
|
||||
}
|
||||
</MudStack>
|
||||
@if (!string.IsNullOrWhiteSpace(this.dataLoadingModelsIssue))
|
||||
{
|
||||
<MudAlert Severity="Severity.Error" Class="mt-3">
|
||||
@this.dataLoadingModelsIssue
|
||||
</MudAlert>
|
||||
}
|
||||
</MudStack>
|
||||
@if (!string.IsNullOrWhiteSpace(this.dataLoadingModelsIssue))
|
||||
{
|
||||
<MudAlert Severity="Severity.Error" Class="mt-3">
|
||||
@this.dataLoadingModelsIssue
|
||||
</MudAlert>
|
||||
}
|
||||
</MudField>
|
||||
</MudField>
|
||||
}
|
||||
else if (!(DataLLMProvider is LLMProviders.TRANSPARENCY))
|
||||
{
|
||||
<MudField FullWidth="true" Label="@T("Model selection")" Variant="Variant.Outlined" Class="mb-3">
|
||||
<MudText Typo="Typo.body1">
|
||||
@T("This host uses the model configured at the provider level. No model selection is available.")
|
||||
</MudText>
|
||||
</MudField>
|
||||
}
|
||||
|
||||
@* ReSharper disable once CSharpWarnings::CS8974 *@
|
||||
<MudTextField
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using AIStudio.Components;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Provider.Transparency;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.Services;
|
||||
using AIStudio.Tools.Validation;
|
||||
@ -114,7 +115,9 @@ public partial class EmbeddingProviderDialog : MSGComponentBase, ISecretId
|
||||
{
|
||||
var cleanedHostname = this.DataHostname.Trim();
|
||||
Model model = default;
|
||||
if(this.DataLLMProvider is LLMProviders.SELF_HOSTED)
|
||||
if (this.DataLLMProvider is LLMProviders.TRANSPARENCY)
|
||||
model = ProviderTransparencyBase.EMBEDDING_PREVIEW_MODEL;
|
||||
else if(this.DataLLMProvider is LLMProviders.SELF_HOSTED)
|
||||
{
|
||||
if (this.DataHost is Host.OLLAMA)
|
||||
model = new Model(this.dataManuallyModel, null);
|
||||
@ -156,6 +159,9 @@ public partial class EmbeddingProviderDialog : MSGComponentBase, ISecretId
|
||||
if(this.IsEditing)
|
||||
{
|
||||
this.dataEditingPreviousInstanceName = this.DataName.ToLowerInvariant();
|
||||
|
||||
if (this.DataLLMProvider is LLMProviders.TRANSPARENCY)
|
||||
return;
|
||||
|
||||
// When using self-hosted embedding, we must copy the model name:
|
||||
if (this.DataLLMProvider is LLMProviders.SELF_HOSTED)
|
||||
|
||||
@ -19,6 +19,13 @@
|
||||
@T("Create account")
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(this.DataLLMProvider.Description()))
|
||||
{
|
||||
<MudAlert Severity="Severity.Info" Class="mb-3">
|
||||
@this.DataLLMProvider.Description()
|
||||
</MudAlert>
|
||||
}
|
||||
|
||||
@if (this.DataLLMProvider.IsAPIKeyNeeded(this.DataHost))
|
||||
{
|
||||
@ -126,7 +133,7 @@
|
||||
}
|
||||
</MudField>
|
||||
}
|
||||
else
|
||||
else if (!(this.DataLLMProvider is LLMProviders.TRANSPARENCY))
|
||||
{
|
||||
<MudField FullWidth="true" Label="@T("Model selection")" Variant="Variant.Outlined" Class="mb-3">
|
||||
<MudText Typo="Typo.body1">
|
||||
@ -151,18 +158,21 @@
|
||||
UserAttributes="@SPELLCHECK_ATTRIBUTES"
|
||||
/>
|
||||
|
||||
<MudStack>
|
||||
<MudButton OnClick="@this.ToggleExpertSettings">
|
||||
@(this.showExpertSettings ? T("Hide Expert Settings") : T("Show Expert Settings"))
|
||||
</MudButton>
|
||||
<MudDivider />
|
||||
<MudCollapse Expanded="@this.showExpertSettings" Class="@this.GetExpertStyles">
|
||||
<MudJustifiedText Class="mb-5">
|
||||
@T("Please be aware: This section is for experts only. You are responsible for verifying the correctness of the additional parameters you provide to the API call. By default, AI Studio uses the OpenAI-compatible chat completions API, when that it is supported by the underlying service and model.")
|
||||
</MudJustifiedText>
|
||||
<MudTextField T="string" Label=@T("Additional API parameters") Variant="Variant.Outlined" Lines="4" AutoGrow="true" MaxLines="10" HelperText=@T("""Add the parameters in proper JSON formatting, e.g., "temperature": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though.""") Placeholder="@GetPlaceholderExpertSettings" @bind-Value="@this.AdditionalJsonApiParameters" Immediate="true" Validation="@this.ValidateAdditionalJsonApiParameters" OnBlur="@this.OnInputChangeExpertSettings"/>
|
||||
</MudCollapse>
|
||||
</MudStack>
|
||||
@if (this.DataLLMProvider.CanConfigureAdditionalJsonApiParameters())
|
||||
{
|
||||
<MudStack>
|
||||
<MudButton OnClick="@this.ToggleExpertSettings">
|
||||
@(this.showExpertSettings ? T("Hide Expert Settings") : T("Show Expert Settings"))
|
||||
</MudButton>
|
||||
<MudDivider />
|
||||
<MudCollapse Expanded="@this.showExpertSettings" Class="@this.GetExpertStyles">
|
||||
<MudJustifiedText Class="mb-5">
|
||||
@T("Please be aware: This section is for experts only. You are responsible for verifying the correctness of the additional parameters you provide to the API call. By default, AI Studio uses the OpenAI-compatible chat completions API, when that it is supported by the underlying service and model.")
|
||||
</MudJustifiedText>
|
||||
<MudTextField T="string" Label=@T("Additional API parameters") Variant="Variant.Outlined" Lines="4" AutoGrow="true" MaxLines="10" HelperText=@T("""Add the parameters in proper JSON formatting, e.g., "temperature": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though.""") Placeholder="@GetPlaceholderExpertSettings" @bind-Value="@this.AdditionalJsonApiParameters" Immediate="true" Validation="@this.ValidateAdditionalJsonApiParameters" OnBlur="@this.OnInputChangeExpertSettings"/>
|
||||
</MudCollapse>
|
||||
</MudStack>
|
||||
}
|
||||
</MudForm>
|
||||
<Issues IssuesData="@this.dataIssues"/>
|
||||
</DialogContent>
|
||||
|
||||
@ -4,6 +4,7 @@ using System.Text.Json;
|
||||
using AIStudio.Components;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Provider.HuggingFace;
|
||||
using AIStudio.Provider.Transparency;
|
||||
using AIStudio.Tools.Services;
|
||||
using AIStudio.Tools.Validation;
|
||||
|
||||
@ -132,7 +133,11 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
|
||||
// Determine the model based on the provider and host configuration:
|
||||
Model model;
|
||||
if (this.DataLLMProvider.IsLLMModelSelectionHidden(this.DataHost))
|
||||
if (this.DataLLMProvider is LLMProviders.TRANSPARENCY)
|
||||
{
|
||||
model = ProviderTransparencyBase.CHAT_PREVIEW_MODEL;
|
||||
}
|
||||
else if (this.DataLLMProvider.IsLLMModelSelectionHidden(this.DataHost))
|
||||
{
|
||||
// Use system model placeholder for hosts that don't support model selection (e.g., llama.cpp):
|
||||
model = Model.SYSTEM_MODEL;
|
||||
@ -157,7 +162,7 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
Hostname = cleanedHostname.EndsWith('/') ? cleanedHostname[..^1] : cleanedHostname,
|
||||
Host = this.DataHost,
|
||||
HFInferenceProvider = this.HFInferenceProviderId,
|
||||
AdditionalJsonApiParameters = this.AdditionalJsonApiParameters,
|
||||
AdditionalJsonApiParameters = this.DataLLMProvider.CanConfigureAdditionalJsonApiParameters() ? this.AdditionalJsonApiParameters : string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
@ -182,6 +187,9 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
if(this.IsEditing)
|
||||
{
|
||||
this.dataEditingPreviousInstanceName = this.DataInstanceName.ToLowerInvariant();
|
||||
|
||||
if (this.DataLLMProvider is LLMProviders.TRANSPARENCY)
|
||||
return;
|
||||
|
||||
// When using Fireworks or Hugging Face, we must copy the model name:
|
||||
if (this.DataLLMProvider.IsLLMModelProvidedManually())
|
||||
|
||||
@ -22,6 +22,13 @@
|
||||
@T("Create account")
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(this.DataLLMProvider.Description()))
|
||||
{
|
||||
<MudAlert Severity="Severity.Info" Class="mb-3">
|
||||
@this.DataLLMProvider.Description()
|
||||
</MudAlert>
|
||||
}
|
||||
|
||||
@if (this.DataLLMProvider.IsAPIKeyNeeded(this.DataHost))
|
||||
{
|
||||
@ -111,7 +118,7 @@
|
||||
}
|
||||
</MudField>
|
||||
}
|
||||
else
|
||||
else if (!(this.DataLLMProvider is LLMProviders.TRANSPARENCY))
|
||||
{
|
||||
<MudField FullWidth="true" Label="@T("Model selection")" Variant="Variant.Outlined" Class="mb-3">
|
||||
<MudText Typo="Typo.body1">
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using AIStudio.Components;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Provider.Transparency;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.Services;
|
||||
using AIStudio.Tools.Validation;
|
||||
@ -116,7 +117,11 @@ public partial class TranscriptionProviderDialog : MSGComponentBase, ISecretId
|
||||
|
||||
// Determine the model based on the provider and host configuration:
|
||||
Model model;
|
||||
if (this.DataLLMProvider.IsTranscriptionModelSelectionHidden(this.DataHost))
|
||||
if (this.DataLLMProvider is LLMProviders.TRANSPARENCY)
|
||||
{
|
||||
model = ProviderTransparencyBase.TRANSCRIPTION_PREVIEW_MODEL;
|
||||
}
|
||||
else if (this.DataLLMProvider.IsTranscriptionModelSelectionHidden(this.DataHost))
|
||||
{
|
||||
// Use system model placeholder for hosts that don't support model selection (e.g., whisper.cpp):
|
||||
model = Model.SYSTEM_MODEL;
|
||||
@ -171,6 +176,9 @@ public partial class TranscriptionProviderDialog : MSGComponentBase, ISecretId
|
||||
if(this.IsEditing)
|
||||
{
|
||||
this.dataEditingPreviousInstanceName = this.DataName.ToLowerInvariant();
|
||||
|
||||
if (this.DataLLMProvider is LLMProviders.TRANSPARENCY)
|
||||
return;
|
||||
|
||||
// When using self-hosted models, we must copy the model name:
|
||||
if (this.DataLLMProvider is LLMProviders.SELF_HOSTED)
|
||||
@ -312,7 +320,7 @@ public partial class TranscriptionProviderDialog : MSGComponentBase, ISecretId
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.Logger.LogError($"Failed to load models from provider '{this.DataLLMProvider}' (host={this.DataHost}, hostname='{this.DataHostname}'): {e.Message}");;
|
||||
this.Logger.LogError($"Failed to load models from provider '{this.DataLLMProvider}' (host={this.DataHost}, hostname='{this.DataHostname}'): {e.Message}");
|
||||
this.dataLoadingModelsIssue = T("We are currently unable to communicate with the provider to load models. Please try again later.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,6 +92,9 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
/// <inheritdoc />
|
||||
public abstract string InstanceName { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Description => this.Provider.Description();
|
||||
|
||||
/// <inheritdoc />
|
||||
public string AdditionalJsonApiParameters { get; init; } = string.Empty;
|
||||
|
||||
|
||||
@ -23,6 +23,11 @@ public interface IProvider
|
||||
/// e.g., to distinguish between different OpenAI API keys.
|
||||
/// </summary>
|
||||
public string InstanceName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional provider description text shown in configuration UI.
|
||||
/// </summary>
|
||||
public string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The additional API parameters.
|
||||
|
||||
@ -16,6 +16,7 @@ public enum LLMProviders
|
||||
ALIBABA_CLOUD = 12,
|
||||
PERPLEXITY = 14,
|
||||
OPEN_ROUTER = 15,
|
||||
TRANSPARENCY = 16,
|
||||
|
||||
FIREWORKS = 5,
|
||||
GROQ = 6,
|
||||
|
||||
@ -12,6 +12,7 @@ using AIStudio.Provider.OpenAI;
|
||||
using AIStudio.Provider.OpenRouter;
|
||||
using AIStudio.Provider.Perplexity;
|
||||
using AIStudio.Provider.SelfHosted;
|
||||
using AIStudio.Provider.Transparency;
|
||||
using AIStudio.Provider.X;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
@ -44,6 +45,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.ALIBABA_CLOUD => "Alibaba Cloud",
|
||||
LLMProviders.PERPLEXITY => "Perplexity",
|
||||
LLMProviders.OPEN_ROUTER => "OpenRouter",
|
||||
LLMProviders.TRANSPARENCY => TB("Transparency"),
|
||||
|
||||
LLMProviders.GROQ => "Groq",
|
||||
LLMProviders.FIREWORKS => "Fireworks.ai",
|
||||
@ -97,6 +99,8 @@ public static class LLMProvidersExtensions
|
||||
|
||||
LLMProviders.OPEN_ROUTER => Confidence.USA_HUB.WithRegion("America, U.S.").WithSources("https://openrouter.ai/privacy", "https://openrouter.ai/terms").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||
|
||||
LLMProviders.TRANSPARENCY => Confidence.SELF_HOSTED.WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||
|
||||
LLMProviders.SELF_HOSTED => Confidence.SELF_HOSTED.WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||
|
||||
LLMProviders.HELMHOLTZ => Confidence.GDPR_NO_TRAINING.WithRegion("Europe, Germany").WithSources("https://helmholtz.cloud/services/?serviceID=d7d5c597-a2f6-4bd1-b71e-4d6499d98570").WithLevel(settingsManager.GetConfiguredConfidenceLevel(llmProvider)),
|
||||
@ -120,6 +124,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.GOOGLE => true,
|
||||
LLMProviders.HELMHOLTZ => true,
|
||||
LLMProviders.ALIBABA_CLOUD => true,
|
||||
LLMProviders.TRANSPARENCY => true,
|
||||
|
||||
//
|
||||
// Providers that do not support embeddings:
|
||||
@ -151,6 +156,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.MISTRAL => true,
|
||||
LLMProviders.FIREWORKS => true,
|
||||
LLMProviders.GWDG => true,
|
||||
LLMProviders.TRANSPARENCY => true,
|
||||
|
||||
//
|
||||
// Providers that support transcription but provide no OpenAI-compatible API yet:
|
||||
@ -186,6 +192,16 @@ public static class LLMProvidersExtensions
|
||||
/// <returns>The provider instance.</returns>
|
||||
public static IProvider CreateProvider(this AIStudio.Settings.Provider providerSettings)
|
||||
{
|
||||
if (providerSettings.UsedLLMProvider is LLMProviders.TRANSPARENCY)
|
||||
{
|
||||
return new ProviderTransparency
|
||||
{
|
||||
InstanceName = providerSettings.InstanceName,
|
||||
AdditionalJsonApiParameters = providerSettings.AdditionalJsonApiParameters,
|
||||
IsEnterpriseConfiguration = providerSettings.IsEnterpriseConfiguration,
|
||||
};
|
||||
}
|
||||
|
||||
return providerSettings.UsedLLMProvider.CreateProvider(providerSettings.InstanceName, providerSettings.Host, providerSettings.Hostname, providerSettings.Model, providerSettings.HFInferenceProvider, providerSettings.AdditionalJsonApiParameters, providerSettings.IsEnterpriseConfiguration);
|
||||
}
|
||||
|
||||
@ -196,6 +212,15 @@ public static class LLMProvidersExtensions
|
||||
/// <returns>The provider instance.</returns>
|
||||
public static IProvider CreateProvider(this EmbeddingProvider embeddingProviderSettings)
|
||||
{
|
||||
if (embeddingProviderSettings.UsedLLMProvider is LLMProviders.TRANSPARENCY)
|
||||
{
|
||||
return new ProviderTransparencyEmbedding
|
||||
{
|
||||
InstanceName = embeddingProviderSettings.Name,
|
||||
IsEnterpriseConfiguration = embeddingProviderSettings.IsEnterpriseConfiguration,
|
||||
};
|
||||
}
|
||||
|
||||
return embeddingProviderSettings.UsedLLMProvider.CreateProvider(embeddingProviderSettings.Name, embeddingProviderSettings.Host, embeddingProviderSettings.Hostname, embeddingProviderSettings.Model, HFInferenceProvider.NONE, isEnterpriseConfiguration: embeddingProviderSettings.IsEnterpriseConfiguration);
|
||||
}
|
||||
|
||||
@ -206,6 +231,15 @@ public static class LLMProvidersExtensions
|
||||
/// <returns>The provider instance.</returns>
|
||||
public static IProvider CreateProvider(this TranscriptionProvider transcriptionProviderSettings)
|
||||
{
|
||||
if (transcriptionProviderSettings.UsedLLMProvider is LLMProviders.TRANSPARENCY)
|
||||
{
|
||||
return new ProviderTransparencyTranscription
|
||||
{
|
||||
InstanceName = transcriptionProviderSettings.Name,
|
||||
IsEnterpriseConfiguration = transcriptionProviderSettings.IsEnterpriseConfiguration,
|
||||
};
|
||||
}
|
||||
|
||||
return transcriptionProviderSettings.UsedLLMProvider.CreateProvider(transcriptionProviderSettings.Name, transcriptionProviderSettings.Host, transcriptionProviderSettings.Hostname, transcriptionProviderSettings.Model, HFInferenceProvider.NONE, isEnterpriseConfiguration: transcriptionProviderSettings.IsEnterpriseConfiguration);
|
||||
}
|
||||
|
||||
@ -224,6 +258,7 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.ALIBABA_CLOUD => new ProviderAlibabaCloud { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||
LLMProviders.PERPLEXITY => new ProviderPerplexity { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||
LLMProviders.OPEN_ROUTER => new ProviderOpenRouter { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||
LLMProviders.TRANSPARENCY => new ProviderTransparency { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||
|
||||
LLMProviders.GROQ => new ProviderGroq { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||
LLMProviders.FIREWORKS => new ProviderFireworks { InstanceName = instanceName, AdditionalJsonApiParameters = expertProviderApiParameter, IsEnterpriseConfiguration = isEnterpriseConfiguration },
|
||||
@ -336,10 +371,17 @@ public static class LLMProvidersExtensions
|
||||
/// <returns>True if model selection should be hidden; otherwise, false.</returns>
|
||||
public static bool IsLLMModelSelectionHidden(this LLMProviders provider, Host host) => provider switch
|
||||
{
|
||||
LLMProviders.TRANSPARENCY => true,
|
||||
LLMProviders.SELF_HOSTED => host is Host.LLAMA_CPP,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
public static bool IsEmbeddingModelSelectionHidden(this LLMProviders provider, Host host) => provider switch
|
||||
{
|
||||
LLMProviders.TRANSPARENCY => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the model selection should be completely hidden for transcription providers.
|
||||
/// This is the case when the host does not support model selection (e.g., whisper.cpp).
|
||||
@ -349,6 +391,7 @@ public static class LLMProvidersExtensions
|
||||
/// <returns>True if model selection should be hidden; otherwise, false.</returns>
|
||||
public static bool IsTranscriptionModelSelectionHidden(this LLMProviders provider, Host host) => provider switch
|
||||
{
|
||||
LLMProviders.TRANSPARENCY => true,
|
||||
LLMProviders.SELF_HOSTED => host is Host.WHISPER_CPP,
|
||||
_ => false,
|
||||
};
|
||||
@ -411,6 +454,9 @@ public static class LLMProvidersExtensions
|
||||
|
||||
public static bool CanLoadModels(this LLMProviders provider, Host host, string? apiKey)
|
||||
{
|
||||
if (provider is LLMProviders.TRANSPARENCY)
|
||||
return false;
|
||||
|
||||
if (provider is LLMProviders.SELF_HOSTED)
|
||||
{
|
||||
switch (host)
|
||||
@ -442,4 +488,16 @@ public static class LLMProvidersExtensions
|
||||
LLMProviders.HUGGINGFACE => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
public static string Description(this LLMProviders provider) => provider switch
|
||||
{
|
||||
LLMProviders.TRANSPARENCY => TB("This provider never contacts an external service. It only shows the exact request AI Studio generated locally so you can inspect it for transparency and research. It is not intended to produce real model answers."),
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
public static bool CanConfigureAdditionalJsonApiParameters(this LLMProviders provider) => provider switch
|
||||
{
|
||||
LLMProviders.TRANSPARENCY => false,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
|
||||
@ -15,6 +15,9 @@ public class NoProvider : IProvider
|
||||
|
||||
public string InstanceName { get; set; } = "None";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Description => this.Provider.Description();
|
||||
|
||||
/// <inheritdoc />
|
||||
public string AdditionalJsonApiParameters { get; init; } = string.Empty;
|
||||
|
||||
|
||||
@ -0,0 +1,244 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Provider.OpenAI;
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider.Transparency;
|
||||
|
||||
public sealed class ProviderTransparency() : ProviderTransparencyBase(LOGGER)
|
||||
{
|
||||
private static readonly ILogger<ProviderTransparency> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ProviderTransparency>();
|
||||
|
||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
var effectiveModel = NormalizeModel(chatModel, CHAT_PREVIEW_MODEL);
|
||||
var requestBlocks = chatThread.Blocks
|
||||
.Where(block => !IsTransparencyPreviewBlock(block))
|
||||
.ToList();
|
||||
var skippedTransparencyPreviewCount = chatThread.Blocks.Count - requestBlocks.Count;
|
||||
var preparedSystemPrompt = chatThread.PrepareSystemPrompt(settingsManager);
|
||||
var systemPrompt = new TextMessage
|
||||
{
|
||||
Role = "system",
|
||||
Content = preparedSystemPrompt,
|
||||
};
|
||||
|
||||
var apiParameters = this.ParseAdditionalApiParameters();
|
||||
var messages = await requestBlocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, effectiveModel);
|
||||
var requestBody = JsonSerializer.Serialize(new ChatCompletionAPIRequest
|
||||
{
|
||||
Model = effectiveModel.Id,
|
||||
Messages = [systemPrompt, ..messages],
|
||||
Stream = true,
|
||||
AdditionalApiParameters = apiParameters,
|
||||
}, JSON_SERIALIZER_OPTIONS);
|
||||
var readableBreakdown = this.BuildReadableBreakdown(chatThread, preparedSystemPrompt, requestBlocks, skippedTransparencyPreviewCount, settingsManager);
|
||||
|
||||
yield return new ContentStreamChunk(
|
||||
this.BuildJsonRequestPreview(
|
||||
"chat/completions",
|
||||
effectiveModel,
|
||||
requestBody,
|
||||
readableBreakdown,
|
||||
("Message count", (messages.Count + 1).ToString()),
|
||||
("Stream", bool.TrueString),
|
||||
("Additional API parameters", apiParameters.Count.ToString())),
|
||||
[]);
|
||||
}
|
||||
|
||||
private string BuildReadableBreakdown(ChatThread chatThread, string preparedSystemPrompt, IReadOnlyList<ContentBlock> requestBlocks, int skippedTransparencyPreviewCount, SettingsManager settingsManager)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
AppendSystemPromptBreakdown(builder, chatThread, preparedSystemPrompt, settingsManager);
|
||||
AppendConversationBreakdown(builder, chatThread, requestBlocks, skippedTransparencyPreviewCount, settingsManager);
|
||||
return builder.ToString().TrimEnd();
|
||||
}
|
||||
|
||||
private static void AppendSystemPromptBreakdown(StringBuilder builder, ChatThread chatThread, string preparedSystemPrompt, SettingsManager settingsManager)
|
||||
{
|
||||
var baseSystemPrompt = chatThread.SystemPrompt;
|
||||
var selectedTemplate = ResolveSelectedChatTemplate(chatThread, settingsManager);
|
||||
var selectedProfile = ResolveSelectedProfile(chatThread, settingsManager);
|
||||
|
||||
builder.AppendLine("Final system prompt sent to the provider:");
|
||||
builder.AppendLine("```text");
|
||||
builder.AppendLine(preparedSystemPrompt);
|
||||
builder.AppendLine("```");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("System prompt sources:");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(baseSystemPrompt))
|
||||
{
|
||||
builder.AppendLine("- Base chat system prompt:");
|
||||
builder.AppendLine("```text");
|
||||
builder.AppendLine(baseSystemPrompt);
|
||||
builder.AppendLine("```");
|
||||
}
|
||||
|
||||
if (selectedTemplate is not null)
|
||||
{
|
||||
builder.AppendLine($"- Chat template system prompt from `{selectedTemplate.GetSafeName()}`:");
|
||||
builder.AppendLine("```text");
|
||||
builder.AppendLine(selectedTemplate.ToSystemPrompt());
|
||||
builder.AppendLine("```");
|
||||
|
||||
if (selectedTemplate.ExampleConversation.Count > 0)
|
||||
builder.AppendLine($"- Chat template example conversation adds `{selectedTemplate.ExampleConversation.Count}` hidden message(s) to the request history.");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(chatThread.AugmentedData))
|
||||
{
|
||||
builder.AppendLine("- Augmented context added by AI Studio:");
|
||||
builder.AppendLine("```text");
|
||||
builder.AppendLine(chatThread.AugmentedData);
|
||||
builder.AppendLine("```");
|
||||
}
|
||||
|
||||
if (selectedProfile is not null && selectedTemplate?.AllowProfileUsage != false)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(selectedProfile.NeedToKnow))
|
||||
{
|
||||
builder.AppendLine($"- Profile `{selectedProfile.GetSafeName()}`: What should you know about the user?");
|
||||
builder.AppendLine("```text");
|
||||
builder.AppendLine(selectedProfile.NeedToKnow);
|
||||
builder.AppendLine("```");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(selectedProfile.Actions))
|
||||
{
|
||||
builder.AppendLine($"- Profile `{selectedProfile.GetSafeName()}`: The user wants you to consider the following things.");
|
||||
builder.AppendLine("```text");
|
||||
builder.AppendLine(selectedProfile.Actions);
|
||||
builder.AppendLine("```");
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedTemplate is not null && !selectedTemplate.AllowProfileUsage)
|
||||
builder.AppendLine($"- Profile instructions are disabled by chat template `{selectedTemplate.GetSafeName()}`.");
|
||||
|
||||
if (chatThread.IncludeDateTime)
|
||||
builder.AppendLine("- AI Studio prepends the current UTC and local date/time before the system prompt.");
|
||||
}
|
||||
|
||||
private static void AppendConversationBreakdown(StringBuilder builder, ChatThread chatThread, IReadOnlyList<ContentBlock> requestBlocks, int skippedTransparencyPreviewCount, SettingsManager settingsManager)
|
||||
{
|
||||
var selectedTemplate = ResolveSelectedChatTemplate(chatThread, settingsManager);
|
||||
var orderedBlocks = requestBlocks
|
||||
.Where(block => block.ContentType is ContentType.TEXT && block.Content is ContentText text && !string.IsNullOrWhiteSpace(text.Text))
|
||||
.OrderBy(block => block.Time)
|
||||
.ToList();
|
||||
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("Conversation history sources:");
|
||||
|
||||
if (skippedTransparencyPreviewCount > 0)
|
||||
builder.AppendLine($"- Previous transparency preview responses are excluded from the generated request: `{skippedTransparencyPreviewCount}`.");
|
||||
|
||||
if (orderedBlocks.Count == 0)
|
||||
{
|
||||
builder.AppendLine("- No prior chat messages are part of this request.");
|
||||
return;
|
||||
}
|
||||
|
||||
var templateMessageCount = CountTemplateExampleMessages(orderedBlocks, selectedTemplate);
|
||||
if (templateMessageCount > 0)
|
||||
{
|
||||
builder.AppendLine($"- Chat template example conversation from `{selectedTemplate!.GetSafeName()}`:");
|
||||
builder.AppendLine("```text");
|
||||
foreach (var block in orderedBlocks.Take(templateMessageCount))
|
||||
builder.AppendLine(SummarizeBlock(block));
|
||||
builder.AppendLine("```");
|
||||
}
|
||||
|
||||
var remainingBlocks = orderedBlocks.Skip(templateMessageCount).ToList();
|
||||
var visibleBlocks = remainingBlocks.Where(block => !block.HideFromUser).ToList();
|
||||
var hiddenBlocks = remainingBlocks.Where(block => block.HideFromUser).ToList();
|
||||
|
||||
if (visibleBlocks.Count > 0)
|
||||
{
|
||||
builder.AppendLine("- Visible chat history and current user input:");
|
||||
builder.AppendLine("```text");
|
||||
foreach (var block in visibleBlocks)
|
||||
builder.AppendLine(SummarizeBlock(block));
|
||||
builder.AppendLine("```");
|
||||
}
|
||||
|
||||
if (hiddenBlocks.Count > 0)
|
||||
{
|
||||
builder.AppendLine("- Hidden messages included in the request:");
|
||||
builder.AppendLine("```text");
|
||||
foreach (var block in hiddenBlocks)
|
||||
builder.AppendLine(SummarizeBlock(block));
|
||||
builder.AppendLine("```");
|
||||
}
|
||||
}
|
||||
|
||||
private static ChatTemplate? ResolveSelectedChatTemplate(ChatThread chatThread, SettingsManager settingsManager)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(chatThread.SelectedChatTemplate))
|
||||
return null;
|
||||
|
||||
if (!Guid.TryParse(chatThread.SelectedChatTemplate, out var templateId) || templateId == Guid.Empty || chatThread.SelectedChatTemplate == ChatTemplate.NO_CHAT_TEMPLATE.Id)
|
||||
return null;
|
||||
|
||||
return settingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(template => template.Id == chatThread.SelectedChatTemplate);
|
||||
}
|
||||
|
||||
private static Profile? ResolveSelectedProfile(ChatThread chatThread, SettingsManager settingsManager)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(chatThread.SelectedProfile))
|
||||
return null;
|
||||
|
||||
if (!Guid.TryParse(chatThread.SelectedProfile, out var profileId) || profileId == Guid.Empty || chatThread.SelectedProfile == Profile.NO_PROFILE.Id)
|
||||
return null;
|
||||
|
||||
return settingsManager.ConfigurationData.Profiles.FirstOrDefault(profile => profile.Id == chatThread.SelectedProfile);
|
||||
}
|
||||
|
||||
private static int CountTemplateExampleMessages(IReadOnlyList<ContentBlock> orderedBlocks, ChatTemplate? selectedTemplate)
|
||||
{
|
||||
if (selectedTemplate is null || selectedTemplate.ExampleConversation.Count == 0)
|
||||
return 0;
|
||||
|
||||
var matchingCount = 0;
|
||||
foreach (var pair in orderedBlocks.Zip(selectedTemplate.ExampleConversation))
|
||||
{
|
||||
if (pair.First.Role != pair.Second.Role)
|
||||
break;
|
||||
|
||||
if (pair.First.Content is not ContentText firstText || pair.Second.Content is not ContentText secondText)
|
||||
break;
|
||||
|
||||
if (!string.Equals(firstText.Text.Trim(), secondText.Text.Trim(), StringComparison.Ordinal))
|
||||
break;
|
||||
|
||||
matchingCount++;
|
||||
}
|
||||
|
||||
return matchingCount;
|
||||
}
|
||||
|
||||
private static string SummarizeBlock(ContentBlock block)
|
||||
{
|
||||
if (block.Content is not ContentText contentText)
|
||||
return $"{block.Role.ToChatTemplateName()}: [unsupported content]";
|
||||
|
||||
var attachmentSuffix = contentText.FileAttachments.Count == 0
|
||||
? string.Empty
|
||||
: $" [attachments: {contentText.FileAttachments.Count}]";
|
||||
|
||||
var visibilitySuffix = block.HideFromUser ? " [hidden]" : string.Empty;
|
||||
return $"{block.Role.ToChatTemplateName()}: {contentText.Text.Trim()}{attachmentSuffix}{visibilitySuffix}";
|
||||
}
|
||||
|
||||
private static bool IsTransparencyPreviewBlock(ContentBlock block)
|
||||
{
|
||||
if (block.Role is not ChatRole.AI || block.Content is not ContentText contentText)
|
||||
return false;
|
||||
|
||||
return contentText.Text.StartsWith(PREVIEW_NOTICE, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,169 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider.Transparency;
|
||||
|
||||
public abstract class ProviderTransparencyBase(ILogger logger) : BaseProvider(LLMProviders.TRANSPARENCY, new Uri("https://transparency.invalid/v1/"), ExternalHttpTrustPolicy.SYSTEM_TRUST_ONLY, logger)
|
||||
{
|
||||
protected const string PREVIEW_NOTICE = "Transparency preview only. AI Studio generated this request locally and did not contact an external provider.";
|
||||
|
||||
private static readonly JsonSerializerOptions PRETTY_JSON_SERIALIZER_OPTIONS = new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
};
|
||||
|
||||
protected readonly ILogger Logger = logger;
|
||||
|
||||
public static readonly Model CHAT_PREVIEW_MODEL = new("transparency-preview", "Transparency Preview");
|
||||
|
||||
public static readonly Model EMBEDDING_PREVIEW_MODEL = new("transparency-embedding-preview", "Transparency Embedding Preview");
|
||||
|
||||
public static readonly Model TRANSCRIPTION_PREVIEW_MODEL = new("transparency-transcription-preview", "Transparency Transcription Preview");
|
||||
|
||||
public override string Id => LLMProviders.TRANSPARENCY.ToName();
|
||||
|
||||
public override string InstanceName { get; set; } = "Transparency";
|
||||
|
||||
public override bool HasModelLoadingCapability => false;
|
||||
|
||||
public override Task<ModelLoadResult> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default) => Task.FromResult(SuccessfulModelLoadResult([CHAT_PREVIEW_MODEL]));
|
||||
|
||||
public override Task<ModelLoadResult> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default) => Task.FromResult(SuccessfulModelLoadResult([]));
|
||||
|
||||
public override Task<ModelLoadResult> GetEmbeddingModels(string? apiKeyProvisional = null, CancellationToken token = default) => Task.FromResult(SuccessfulModelLoadResult([EMBEDDING_PREVIEW_MODEL]));
|
||||
|
||||
public override Task<ModelLoadResult> GetTranscriptionModels(string? apiKeyProvisional = null, CancellationToken token = default) => Task.FromResult(SuccessfulModelLoadResult([TRANSCRIPTION_PREVIEW_MODEL]));
|
||||
|
||||
public override async IAsyncEnumerable<ContentStreamChunk> StreamChatCompletion(Model chatModel, ChatThread chatThread, SettingsManager settingsManager, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
yield break;
|
||||
}
|
||||
|
||||
public override async IAsyncEnumerable<ImageURL> StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
yield break;
|
||||
}
|
||||
|
||||
public override Task<TranscriptionResult> TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default) => Task.FromResult(TranscriptionResult.Failure());
|
||||
|
||||
public override Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts) => Task.FromResult<IReadOnlyList<IReadOnlyList<float>>>([]);
|
||||
|
||||
protected static Model NormalizeModel(Model model, Model fallbackModel)
|
||||
{
|
||||
if (model.IsSystemModel || string.IsNullOrWhiteSpace(model.Id))
|
||||
return fallbackModel;
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
protected string BuildJsonRequestPreview(string requestPath, Model model, string rawJson, string? readableBreakdown = null, params (string Label, string Value)[] details)
|
||||
{
|
||||
var requestUri = new Uri(this.BaseUri, requestPath);
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine(PREVIEW_NOTICE);
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("**Request metadata**");
|
||||
builder.AppendLine($"- URL: `{requestUri}`");
|
||||
builder.AppendLine($"- Instance: `{this.InstanceName}`");
|
||||
builder.AppendLine($"- Model: `{model.Id}`");
|
||||
|
||||
foreach (var (label, value) in details)
|
||||
builder.AppendLine($"- {label}: `{value}`");
|
||||
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("**Readable body**");
|
||||
if (!string.IsNullOrWhiteSpace(readableBreakdown))
|
||||
{
|
||||
builder.AppendLine(readableBreakdown.TrimEnd());
|
||||
builder.AppendLine();
|
||||
}
|
||||
builder.AppendLine("```json");
|
||||
builder.AppendLine(PrettyPrintJson(rawJson));
|
||||
builder.AppendLine("```");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("**Original unchanged JSON**");
|
||||
builder.AppendLine("```json");
|
||||
builder.AppendLine(rawJson);
|
||||
builder.AppendLine("```");
|
||||
return builder.ToString().TrimEnd();
|
||||
}
|
||||
|
||||
protected string BuildMultipartRequestPreview(string requestPath, Model model, string audioFilePath, string mimeType)
|
||||
{
|
||||
var requestUri = new Uri(this.BaseUri, requestPath);
|
||||
var fileName = Path.GetFileName(audioFilePath);
|
||||
var readableBody = JsonSerializer.Serialize(new
|
||||
{
|
||||
model = model.Id,
|
||||
file = new
|
||||
{
|
||||
source_path = audioFilePath,
|
||||
file_name = fileName,
|
||||
mime_type = mimeType,
|
||||
},
|
||||
}, PRETTY_JSON_SERIALIZER_OPTIONS);
|
||||
|
||||
var rawBody = $$"""
|
||||
file=@"{{audioFilePath}}"; filename="{{fileName}}"; content-type="{{mimeType}}"
|
||||
model={{model.Id}}
|
||||
""";
|
||||
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine(PREVIEW_NOTICE);
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("**Request metadata**");
|
||||
builder.AppendLine($"- URL: `{requestUri}`");
|
||||
builder.AppendLine($"- Instance: `{this.InstanceName}`");
|
||||
builder.AppendLine($"- Model: `{model.Id}`");
|
||||
builder.AppendLine($"- Audio file: `{audioFilePath}`");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("**Readable body**");
|
||||
builder.AppendLine("```json");
|
||||
builder.AppendLine(readableBody);
|
||||
builder.AppendLine("```");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("**Original request body**");
|
||||
builder.AppendLine("This request does not use JSON. These are the multipart fields AI Studio prepared:");
|
||||
builder.AppendLine("```text");
|
||||
builder.AppendLine(rawBody);
|
||||
builder.AppendLine("```");
|
||||
return builder.ToString().TrimEnd();
|
||||
}
|
||||
|
||||
protected static IReadOnlyList<IReadOnlyList<float>> CreateDummyEmbeddings(int count)
|
||||
{
|
||||
var vectorCount = Math.Max(count, 1);
|
||||
return Enumerable.Range(0, vectorCount)
|
||||
.Select(index => (IReadOnlyList<float>)new float[]
|
||||
{
|
||||
0.125f + index,
|
||||
0.25f + index,
|
||||
0.375f + index,
|
||||
0.5f + index,
|
||||
0.625f + index,
|
||||
0.75f + index,
|
||||
0.875f + index,
|
||||
1f + index,
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private static string PrettyPrintJson(string rawJson)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var jsonDocument = JsonDocument.Parse(rawJson);
|
||||
return JsonSerializer.Serialize(jsonDocument.RootElement, PRETTY_JSON_SERIALIZER_OPTIONS);
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return rawJson;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
using System.Text.Json;
|
||||
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider.Transparency;
|
||||
|
||||
public sealed class ProviderTransparencyEmbedding() : ProviderTransparencyBase(LOGGER)
|
||||
{
|
||||
private static readonly ILogger<ProviderTransparencyEmbedding> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ProviderTransparencyEmbedding>();
|
||||
|
||||
public override Task<IReadOnlyList<IReadOnlyList<float>>> EmbedTextAsync(Model embeddingModel, SettingsManager settingsManager, CancellationToken token = default, params List<string> texts)
|
||||
{
|
||||
var effectiveModel = NormalizeModel(embeddingModel, EMBEDDING_PREVIEW_MODEL);
|
||||
var requestBody = JsonSerializer.Serialize(new
|
||||
{
|
||||
model = effectiveModel.Id,
|
||||
input = texts,
|
||||
encoding_format = "float",
|
||||
}, JSON_SERIALIZER_OPTIONS);
|
||||
|
||||
var preview = this.BuildJsonRequestPreview(
|
||||
"embeddings",
|
||||
effectiveModel,
|
||||
requestBody,
|
||||
readableBreakdown: null,
|
||||
("Input collection count", texts.Count.ToString()),
|
||||
("Stream", bool.FalseString));
|
||||
|
||||
this.Logger.LogInformation("Transparency embedding preview for '{ProviderInstanceName}' (provider={ProviderType}).{NewLine}{Preview}", this.InstanceName, this.Provider, Environment.NewLine, preview);
|
||||
return Task.FromResult(CreateDummyEmbeddings(texts.Count));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.MIME;
|
||||
|
||||
namespace AIStudio.Provider.Transparency;
|
||||
|
||||
public sealed class ProviderTransparencyTranscription() : ProviderTransparencyBase(LOGGER)
|
||||
{
|
||||
private static readonly ILogger<ProviderTransparencyTranscription> LOGGER = Program.LOGGER_FACTORY.CreateLogger<ProviderTransparencyTranscription>();
|
||||
|
||||
public override Task<TranscriptionResult> TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default)
|
||||
{
|
||||
var effectiveModel = NormalizeModel(transcriptionModel, TRANSCRIPTION_PREVIEW_MODEL);
|
||||
var mimeType = Builder.FromFilename(audioFilePath);
|
||||
var preview = this.BuildMultipartRequestPreview("audio/transcriptions", effectiveModel, audioFilePath, mimeType);
|
||||
return Task.FromResult(TranscriptionResult.FromText(preview));
|
||||
}
|
||||
}
|
||||
@ -32,6 +32,13 @@ public static partial class ProviderExtensions
|
||||
LLMProviders.GROQ => GetModelCapabilitiesOpenSource(model),
|
||||
LLMProviders.FIREWORKS => GetModelCapabilitiesOpenSource(model),
|
||||
LLMProviders.HUGGINGFACE => GetModelCapabilitiesOpenSource(model),
|
||||
LLMProviders.TRANSPARENCY =>
|
||||
[
|
||||
Capability.TEXT_INPUT,
|
||||
Capability.MULTIPLE_IMAGE_INPUT,
|
||||
Capability.TEXT_OUTPUT,
|
||||
Capability.CHAT_COMPLETION_API,
|
||||
],
|
||||
|
||||
LLMProviders.HELMHOLTZ => GetModelCapabilitiesOpenSource(model),
|
||||
LLMProviders.GWDG => GetModelCapabilitiesOpenSource(model),
|
||||
@ -40,4 +47,4 @@ public static partial class ProviderExtensions
|
||||
|
||||
_ => []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -391,6 +391,7 @@ public sealed class SettingsManager
|
||||
case ConfidenceSchemes.TRUST_ALL:
|
||||
return llmProvider switch
|
||||
{
|
||||
LLMProviders.TRANSPARENCY => ConfidenceLevel.HIGH,
|
||||
LLMProviders.SELF_HOSTED => ConfidenceLevel.HIGH,
|
||||
|
||||
_ => ConfidenceLevel.MEDIUM,
|
||||
@ -399,6 +400,7 @@ public sealed class SettingsManager
|
||||
case ConfidenceSchemes.TRUST_USA_EUROPE:
|
||||
return llmProvider switch
|
||||
{
|
||||
LLMProviders.TRANSPARENCY => ConfidenceLevel.HIGH,
|
||||
LLMProviders.SELF_HOSTED => ConfidenceLevel.HIGH,
|
||||
LLMProviders.DEEP_SEEK => ConfidenceLevel.LOW,
|
||||
|
||||
@ -408,6 +410,7 @@ public sealed class SettingsManager
|
||||
case ConfidenceSchemes.TRUST_USA:
|
||||
return llmProvider switch
|
||||
{
|
||||
LLMProviders.TRANSPARENCY => ConfidenceLevel.HIGH,
|
||||
LLMProviders.SELF_HOSTED => ConfidenceLevel.HIGH,
|
||||
LLMProviders.MISTRAL => ConfidenceLevel.LOW,
|
||||
LLMProviders.HELMHOLTZ => ConfidenceLevel.LOW,
|
||||
@ -420,6 +423,7 @@ public sealed class SettingsManager
|
||||
case ConfidenceSchemes.TRUST_EUROPE:
|
||||
return llmProvider switch
|
||||
{
|
||||
LLMProviders.TRANSPARENCY => ConfidenceLevel.HIGH,
|
||||
LLMProviders.SELF_HOSTED => ConfidenceLevel.HIGH,
|
||||
LLMProviders.MISTRAL => ConfidenceLevel.MEDIUM,
|
||||
LLMProviders.HELMHOLTZ => ConfidenceLevel.MEDIUM,
|
||||
@ -431,6 +435,7 @@ public sealed class SettingsManager
|
||||
case ConfidenceSchemes.TRUST_ASIA:
|
||||
return llmProvider switch
|
||||
{
|
||||
LLMProviders.TRANSPARENCY => ConfidenceLevel.HIGH,
|
||||
LLMProviders.SELF_HOSTED => ConfidenceLevel.HIGH,
|
||||
LLMProviders.DEEP_SEEK => ConfidenceLevel.MEDIUM,
|
||||
|
||||
@ -440,6 +445,7 @@ public sealed class SettingsManager
|
||||
case ConfidenceSchemes.LOCAL_TRUST_ONLY:
|
||||
return llmProvider switch
|
||||
{
|
||||
LLMProviders.TRANSPARENCY => ConfidenceLevel.HIGH,
|
||||
LLMProviders.SELF_HOSTED => ConfidenceLevel.HIGH,
|
||||
|
||||
_ => ConfidenceLevel.VERY_LOW,
|
||||
|
||||
@ -73,7 +73,7 @@ public sealed class ProviderValidation
|
||||
public string? ValidatingModel(Model model)
|
||||
{
|
||||
// For NONE providers, no validation is needed:
|
||||
if (this.GetProvider() is LLMProviders.NONE)
|
||||
if (this.GetProvider() is LLMProviders.NONE or LLMProviders.TRANSPARENCY)
|
||||
return null;
|
||||
|
||||
// For self-hosted llama.cpp or whisper.cpp, no model selection needed
|
||||
|
||||
Loading…
Reference in New Issue
Block a user