mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-03-14 02:11:37 +00:00
Improved additional API parameter validation (#686)
Some checks are pending
Build and Release / Read metadata (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg updater) (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) (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) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg updater) (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) (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) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
Some checks are pending
Build and Release / Read metadata (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg updater) (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) (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) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg updater) (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) (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) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
Co-authored-by: Thorsten Sommer <SommerEngineering@users.noreply.github.com>
This commit is contained in:
parent
3ea3f20c5b
commit
c120502215
@ -3532,6 +3532,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1870831108"] = "Failed to l
|
||||
-- Please enter a model name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1936099896"] = "Please enter a model name."
|
||||
|
||||
-- Additional API parameters must form a JSON object.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2051143391"] = "Additional API parameters must form a JSON object."
|
||||
|
||||
-- Model
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2189814010"] = "Model"
|
||||
|
||||
@ -3553,12 +3556,18 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2842060373"] = "Instance Na
|
||||
-- Show Expert Settings
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3361153305"] = "Show Expert Settings"
|
||||
|
||||
-- Invalid JSON: 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.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3502745319"] = "Invalid JSON: 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."
|
||||
|
||||
-- Show available models
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3763891899"] = "Show available models"
|
||||
|
||||
-- This host uses the model configured at the provider level. No model selection is available.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3783329915"] = "This host uses the model configured at the provider level. No model selection is available."
|
||||
|
||||
-- Duplicate key '{0}' found.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3804472591"] = "Duplicate key '{0}' found."
|
||||
|
||||
-- Currently, we cannot query the models for the selected provider and/or host. Therefore, please enter the model name manually.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T4116737656"] = "Currently, we cannot query the models for the selected provider and/or host. Therefore, please enter the model name manually."
|
||||
|
||||
|
||||
@ -160,7 +160,7 @@
|
||||
<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" OnBlur="@this.OnInputChangeExpertSettings"/>
|
||||
<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>
|
||||
@ -181,4 +181,4 @@
|
||||
}
|
||||
</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
</MudDialog>
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
using AIStudio.Components;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Provider.HuggingFace;
|
||||
@ -334,7 +337,168 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
|
||||
private void OnInputChangeExpertSettings()
|
||||
{
|
||||
this.AdditionalJsonApiParameters = this.AdditionalJsonApiParameters.Trim().TrimEnd(',', ' ');
|
||||
this.AdditionalJsonApiParameters = NormalizeAdditionalJsonApiParameters(this.AdditionalJsonApiParameters)
|
||||
.Trim()
|
||||
.TrimEnd(',', ' ');
|
||||
}
|
||||
|
||||
private string? ValidateAdditionalJsonApiParameters(string additionalParams)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(additionalParams))
|
||||
return null;
|
||||
|
||||
var normalized = NormalizeAdditionalJsonApiParameters(additionalParams);
|
||||
if (!string.Equals(normalized, additionalParams, StringComparison.Ordinal))
|
||||
this.AdditionalJsonApiParameters = normalized;
|
||||
|
||||
var json = $"{{{normalized}}}";
|
||||
try
|
||||
{
|
||||
if (!this.TryValidateJsonObjectWithDuplicateCheck(json, out var errorMessage))
|
||||
return errorMessage;
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return T("Invalid JSON: 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.");
|
||||
}
|
||||
}
|
||||
|
||||
private static string NormalizeAdditionalJsonApiParameters(string input)
|
||||
{
|
||||
var sb = new StringBuilder(input.Length);
|
||||
var inString = false;
|
||||
var escape = false;
|
||||
for (var i = 0; i < input.Length; i++)
|
||||
{
|
||||
var c = input[i];
|
||||
if (inString)
|
||||
{
|
||||
sb.Append(c);
|
||||
if (escape)
|
||||
{
|
||||
escape = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '\\')
|
||||
{
|
||||
escape = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '"')
|
||||
inString = false;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '"')
|
||||
{
|
||||
inString = true;
|
||||
sb.Append(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (TryReadToken(input, i, "True", out var tokenLength))
|
||||
{
|
||||
sb.Append("true");
|
||||
i += tokenLength - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (TryReadToken(input, i, "False", out tokenLength))
|
||||
{
|
||||
sb.Append("false");
|
||||
i += tokenLength - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (TryReadToken(input, i, "Null", out tokenLength))
|
||||
{
|
||||
sb.Append("null");
|
||||
i += tokenLength - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
sb.Append(c);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static bool TryReadToken(string input, int startIndex, string token, out int tokenLength)
|
||||
{
|
||||
tokenLength = 0;
|
||||
if (startIndex + token.Length > input.Length)
|
||||
return false;
|
||||
|
||||
if (!input.AsSpan(startIndex, token.Length).SequenceEqual(token))
|
||||
return false;
|
||||
|
||||
var beforeIndex = startIndex - 1;
|
||||
if (beforeIndex >= 0 && IsIdentifierChar(input[beforeIndex]))
|
||||
return false;
|
||||
|
||||
var afterIndex = startIndex + token.Length;
|
||||
if (afterIndex < input.Length && IsIdentifierChar(input[afterIndex]))
|
||||
return false;
|
||||
|
||||
tokenLength = token.Length;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsIdentifierChar(char c) => char.IsLetterOrDigit(c) || c == '_';
|
||||
|
||||
private bool TryValidateJsonObjectWithDuplicateCheck(string json, out string? errorMessage)
|
||||
{
|
||||
errorMessage = null;
|
||||
var bytes = Encoding.UTF8.GetBytes(json);
|
||||
var reader = new Utf8JsonReader(bytes, new JsonReaderOptions
|
||||
{
|
||||
AllowTrailingCommas = false,
|
||||
CommentHandling = JsonCommentHandling.Disallow
|
||||
});
|
||||
|
||||
var objectStack = new Stack<HashSet<string>>();
|
||||
while (reader.Read())
|
||||
{
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonTokenType.StartObject:
|
||||
objectStack.Push(new HashSet<string>(StringComparer.Ordinal));
|
||||
break;
|
||||
|
||||
case JsonTokenType.EndObject:
|
||||
if (objectStack.Count > 0)
|
||||
objectStack.Pop();
|
||||
break;
|
||||
|
||||
case JsonTokenType.PropertyName:
|
||||
if (objectStack.Count == 0)
|
||||
{
|
||||
errorMessage = T("Additional API parameters must form a JSON object.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var name = reader.GetString() ?? string.Empty;
|
||||
if (!objectStack.Peek().Add(name))
|
||||
{
|
||||
errorMessage = string.Format(T("Duplicate key '{0}' found."), name);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (objectStack.Count != 0)
|
||||
{
|
||||
errorMessage = T("Invalid JSON: 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.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private string GetExpertStyles => this.showExpertSettings ? "border-2 border-dashed rounded pa-2" : string.Empty;
|
||||
@ -345,4 +509,4 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
||||
"top_p": 0.9,
|
||||
"frequency_penalty": 0.0
|
||||
""";
|
||||
}
|
||||
}
|
||||
|
||||
@ -3534,6 +3534,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1870831108"] = "Der API-Sch
|
||||
-- Please enter a model name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1936099896"] = "Bitte geben Sie einen Modellnamen ein."
|
||||
|
||||
-- Additional API parameters must form a JSON object.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2051143391"] = "Zusätzliche API-Parameter müssen ein JSON-Objekt bilden."
|
||||
|
||||
-- Model
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2189814010"] = "Modell"
|
||||
|
||||
@ -3555,12 +3558,18 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2842060373"] = "Instanzname
|
||||
-- Show Expert Settings
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3361153305"] = "Experten-Einstellungen anzeigen"
|
||||
|
||||
-- Invalid JSON: 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.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3502745319"] = "Ungültiges JSON: Fügen Sie die Parameter in korrektem JSON-Format hinzu, z. B. \"temperature\": 0.5. Entfernen Sie abschließende Kommas. Die üblichen umgebenden geschweiften Klammern {} dürfen jedoch nicht verwendet werden."
|
||||
|
||||
-- Show available models
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3763891899"] = "Verfügbare Modelle anzeigen"
|
||||
|
||||
-- This host uses the model configured at the provider level. No model selection is available.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3783329915"] = "Dieser Host verwendet das auf Anbieterebene konfigurierte Modell. Es ist keine Modellauswahl verfügbar."
|
||||
|
||||
-- Duplicate key '{0}' found.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3804472591"] = "Doppelter Schlüssel '{0}' gefunden."
|
||||
|
||||
-- Currently, we cannot query the models for the selected provider and/or host. Therefore, please enter the model name manually.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T4116737656"] = "Derzeit können wir die Modelle für den ausgewählten Anbieter und/oder Host nicht abfragen. Bitte geben Sie daher den Modellnamen manuell ein."
|
||||
|
||||
|
||||
@ -3534,6 +3534,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1870831108"] = "Failed to l
|
||||
-- Please enter a model name.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T1936099896"] = "Please enter a model name."
|
||||
|
||||
-- Additional API parameters must form a JSON object.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2051143391"] = "Additional API parameters must form a JSON object."
|
||||
|
||||
-- Model
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2189814010"] = "Model"
|
||||
|
||||
@ -3555,12 +3558,18 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2842060373"] = "Instance Na
|
||||
-- Show Expert Settings
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3361153305"] = "Show Expert Settings"
|
||||
|
||||
-- Invalid JSON: 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.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3502745319"] = "Invalid JSON: 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."
|
||||
|
||||
-- Show available models
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3763891899"] = "Show available models"
|
||||
|
||||
-- This host uses the model configured at the provider level. No model selection is available.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3783329915"] = "This host uses the model configured at the provider level. No model selection is available."
|
||||
|
||||
-- Duplicate key '{0}' found.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3804472591"] = "Duplicate key '{0}' found."
|
||||
|
||||
-- Currently, we cannot query the models for the selected provider and/or host. Therefore, please enter the model name manually.
|
||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T4116737656"] = "Currently, we cannot query the models for the selected provider and/or host. Therefore, please enter the model name manually."
|
||||
|
||||
|
||||
@ -29,6 +29,9 @@ public sealed class ProviderAnthropic() : BaseProvider(LLMProviders.ANTHROPIC, "
|
||||
|
||||
// Parse the API parameters:
|
||||
var apiParameters = this.ParseAdditionalApiParameters("system");
|
||||
var maxTokens = 4_096;
|
||||
if (TryPopIntParameter(apiParameters, "max_tokens", out var parsedMaxTokens))
|
||||
maxTokens = parsedMaxTokens;
|
||||
|
||||
// Build the list of messages:
|
||||
var messages = await chatThread.Blocks.BuildMessagesAsync(
|
||||
@ -73,7 +76,7 @@ public sealed class ProviderAnthropic() : BaseProvider(LLMProviders.ANTHROPIC, "
|
||||
Messages = [..messages],
|
||||
|
||||
System = chatThread.PrepareSystemPrompt(settingsManager),
|
||||
MaxTokens = apiParameters.TryGetValue("max_tokens", out var value) && value is int intValue ? intValue : 4_096,
|
||||
MaxTokens = maxTokens,
|
||||
|
||||
// Right now, we only support streaming completions:
|
||||
Stream = true,
|
||||
@ -188,4 +191,4 @@ public sealed class ProviderAnthropic() : BaseProvider(LLMProviders.ANTHROPIC, "
|
||||
var modelResponse = await response.Content.ReadFromJsonAsync<ModelsResponse>(JSON_SERIALIZER_OPTIONS, token);
|
||||
return modelResponse.Data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -731,7 +731,7 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
/// <param name="keysToRemove">Optional list of keys to remove from the final dictionary
|
||||
/// (case-insensitive). The parameters stream, model, and messages are removed by default.</param>
|
||||
protected IDictionary<string, object> ParseAdditionalApiParameters(
|
||||
params List<string> keysToRemove)
|
||||
params string[] keysToRemove)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(this.AdditionalJsonApiParameters))
|
||||
return new Dictionary<string, object>();
|
||||
@ -744,14 +744,23 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
var dict = ConvertToDictionary(jsonDoc);
|
||||
|
||||
// Some keys are always removed because we set them:
|
||||
keysToRemove.Add("stream");
|
||||
keysToRemove.Add("model");
|
||||
keysToRemove.Add("messages");
|
||||
var removeSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
if (keysToRemove.Length > 0)
|
||||
removeSet.UnionWith(keysToRemove);
|
||||
|
||||
removeSet.Add("stream");
|
||||
removeSet.Add("model");
|
||||
removeSet.Add("messages");
|
||||
|
||||
// Remove the specified keys (case-insensitive):
|
||||
var removeSet = new HashSet<string>(keysToRemove, StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var key in removeSet)
|
||||
dict.Remove(key);
|
||||
if (removeSet.Count > 0)
|
||||
{
|
||||
foreach (var key in dict.Keys.ToList())
|
||||
{
|
||||
if (removeSet.Contains(key))
|
||||
dict.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
@ -761,6 +770,85 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
return new Dictionary<string, object>();
|
||||
}
|
||||
}
|
||||
|
||||
protected static bool TryPopIntParameter(IDictionary<string, object> parameters, string key, out int value)
|
||||
{
|
||||
value = default;
|
||||
if (!TryPopParameter(parameters, key, out var raw) || raw is null)
|
||||
return false;
|
||||
|
||||
switch (raw)
|
||||
{
|
||||
case int i:
|
||||
value = i;
|
||||
return true;
|
||||
|
||||
case long l when l is >= int.MinValue and <= int.MaxValue:
|
||||
value = (int)l;
|
||||
return true;
|
||||
|
||||
case double d when d is >= int.MinValue and <= int.MaxValue:
|
||||
value = (int)d;
|
||||
return true;
|
||||
|
||||
case decimal m when m is >= int.MinValue and <= int.MaxValue:
|
||||
value = (int)m;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static bool TryPopBoolParameter(IDictionary<string, object> parameters, string key, out bool value)
|
||||
{
|
||||
value = default;
|
||||
if (!TryPopParameter(parameters, key, out var raw) || raw is null)
|
||||
return false;
|
||||
|
||||
switch (raw)
|
||||
{
|
||||
case bool b:
|
||||
value = b;
|
||||
return true;
|
||||
|
||||
case string s when bool.TryParse(s, out var parsed):
|
||||
value = parsed;
|
||||
return true;
|
||||
|
||||
case int i:
|
||||
value = i != 0;
|
||||
return true;
|
||||
|
||||
case long l:
|
||||
value = l != 0;
|
||||
return true;
|
||||
|
||||
case double d:
|
||||
value = Math.Abs(d) > double.Epsilon;
|
||||
return true;
|
||||
|
||||
case decimal m:
|
||||
value = m != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryPopParameter(IDictionary<string, object> parameters, string key, out object? value)
|
||||
{
|
||||
value = null;
|
||||
if (parameters.Count == 0)
|
||||
return false;
|
||||
|
||||
var foundKey = parameters.Keys.FirstOrDefault(k => string.Equals(k, key, StringComparison.OrdinalIgnoreCase));
|
||||
if (foundKey is null)
|
||||
return false;
|
||||
|
||||
value = parameters[foundKey];
|
||||
parameters.Remove(foundKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static IDictionary<string, object> ConvertToDictionary(JsonElement element)
|
||||
{
|
||||
@ -785,4 +873,4 @@ public abstract class BaseProvider : IProvider, ISecretId
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,11 +14,12 @@ public readonly record struct ChatRequest(
|
||||
string Model,
|
||||
IList<IMessageBase> Messages,
|
||||
bool Stream,
|
||||
int RandomSeed,
|
||||
[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>();
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,6 +36,8 @@ public sealed class ProviderMistral() : BaseProvider(LLMProviders.MISTRAL, "http
|
||||
|
||||
// Parse the API parameters:
|
||||
var apiParameters = this.ParseAdditionalApiParameters();
|
||||
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:
|
||||
var messages = await chatThread.Blocks.BuildMessagesUsingDirectImageUrlAsync(this.Provider, chatModel);
|
||||
@ -52,7 +54,8 @@ public sealed class ProviderMistral() : BaseProvider(LLMProviders.MISTRAL, "http
|
||||
|
||||
// Right now, we only support streaming completions:
|
||||
Stream = true,
|
||||
SafePrompt = apiParameters.TryGetValue("safe_prompt", out var value) && value is true,
|
||||
RandomSeed = randomSeed,
|
||||
SafePrompt = safePrompt,
|
||||
AdditionalApiParameters = apiParameters
|
||||
}, JSON_SERIALIZER_OPTIONS);
|
||||
|
||||
@ -165,4 +168,4 @@ public sealed class ProviderMistral() : BaseProvider(LLMProviders.MISTRAL, "http
|
||||
var modelResponse = await response.Content.ReadFromJsonAsync<ModelsResponse>(token);
|
||||
return modelResponse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,4 +7,5 @@
|
||||
- Improved the user-language logging by limiting language detection logs to a single entry per app start.
|
||||
- Improved the logbook readability by removing non-readable special characters from log entries.
|
||||
- Improved the logbook reliability by significantly reducing duplicate log entries.
|
||||
- Improved the validation of additional API parameters in the advanced provider settings to help catch formatting mistakes earlier.
|
||||
- Fixed an issue where the app could turn white or appear invisible in certain chats after HTML-like content was shown. Thanks Inga for reporting this issue and providing some context on how to reproduce it.
|
||||
Loading…
Reference in New Issue
Block a user