mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-03-29 12:31:38 +00:00
Merge branch 'main' into tauri_update
This commit is contained in:
commit
7a6181cfc1
@ -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."
|
||||
|
||||
|
||||
@ -84,6 +84,9 @@ public record FileAttachment(FileAttachmentType Type, string FileName, string Fi
|
||||
{
|
||||
var extension = Path.GetExtension(filePath).TrimStart('.').ToLowerInvariant();
|
||||
|
||||
if (FileTypeFilter.Executables.FilterExtensions.Contains(extension))
|
||||
return FileAttachmentType.FORBIDDEN;
|
||||
|
||||
// Check if it's an image file:
|
||||
if (FileTypeFilter.AllImages.FilterExtensions.Contains(extension))
|
||||
return FileAttachmentType.IMAGE;
|
||||
@ -96,7 +99,8 @@ public record FileAttachment(FileAttachmentType Type, string FileName, string Fi
|
||||
if (FileTypeFilter.PDF.FilterExtensions.Contains(extension) ||
|
||||
FileTypeFilter.Text.FilterExtensions.Contains(extension) ||
|
||||
FileTypeFilter.AllOffice.FilterExtensions.Contains(extension) ||
|
||||
FileTypeFilter.AllSourceCode.FilterExtensions.Contains(extension))
|
||||
FileTypeFilter.AllSourceCode.FilterExtensions.Contains(extension) ||
|
||||
FileTypeFilter.IsAllowedSourceLikeFileName(filePath))
|
||||
return FileAttachmentType.DOCUMENT;
|
||||
|
||||
// All other file types are forbidden:
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -253,7 +253,7 @@ public sealed record Provider(
|
||||
["AdditionalJsonApiParameters"] = "{{LuaTools.EscapeLuaString(this.AdditionalJsonApiParameters)}}",
|
||||
["Model"] = {
|
||||
["Id"] = "{{LuaTools.EscapeLuaString(this.Model.Id)}}",
|
||||
["DisplayName"] = "{{LuaTools.EscapeLuaString(this.Model.DisplayName ?? string.Empty)}}",
|
||||
["DisplayName"] = "{{LuaTools.EscapeLuaString(this.Model.DisplayName ?? this.Model.Id)}}",
|
||||
},
|
||||
}
|
||||
""";
|
||||
|
||||
@ -12,6 +12,51 @@ namespace AIStudio.Tools.Rust;
|
||||
public readonly record struct FileTypeFilter(string FilterName, string[] FilterExtensions)
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(FileTypeFilter).Namespace, nameof(FileTypeFilter));
|
||||
|
||||
private static string[] AllowedSourceLikeFileNames =>
|
||||
[
|
||||
"Dockerfile",
|
||||
"Containerfile",
|
||||
"Jenkinsfile",
|
||||
"Makefile",
|
||||
"GNUmakefile",
|
||||
"Procfile",
|
||||
"Vagrantfile",
|
||||
"Tiltfile",
|
||||
"Justfile",
|
||||
"Brewfile",
|
||||
"Caddyfile",
|
||||
"Gemfile",
|
||||
"Podfile",
|
||||
"Fastfile",
|
||||
"Appfile",
|
||||
"Rakefile",
|
||||
"Dangerfile",
|
||||
"BUILD",
|
||||
"WORKSPACE",
|
||||
"BUCK",
|
||||
];
|
||||
|
||||
private static string[] AllowedSourceLikeFileNamePrefixes =>
|
||||
[
|
||||
"Dockerfile",
|
||||
"Containerfile",
|
||||
"Jenkinsfile",
|
||||
"Procfile",
|
||||
"Caddyfile",
|
||||
];
|
||||
|
||||
public static bool IsAllowedSourceLikeFileName(string filePath)
|
||||
{
|
||||
var fileName = Path.GetFileName(filePath);
|
||||
if (string.IsNullOrWhiteSpace(fileName))
|
||||
return false;
|
||||
|
||||
if (AllowedSourceLikeFileNames.Any(name => string.Equals(name, fileName, StringComparison.OrdinalIgnoreCase)))
|
||||
return true;
|
||||
|
||||
return AllowedSourceLikeFileNamePrefixes.Any(prefix => fileName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public static FileTypeFilter PDF => new(TB("PDF Files"), ["pdf"]);
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
# v26.3.1, build 235 (2026-03-xx xx:xx UTC)
|
||||
- Added support for the new Qwen 3.5 model family.
|
||||
- Improved the performance by caching the OS language detection and requesting the user language only once per app start.
|
||||
- Improved the chat performance by reducing unnecessary UI updates, making chats smoother and more responsive, especially in longer conversations.
|
||||
- Improved the workspace loading experience: when opening the chat for the first time, your workspaces now appear faster and load step by step in the background, with placeholder rows so the app feels responsive right away.
|
||||
@ -6,6 +7,6 @@
|
||||
- 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.
|
||||
- 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.
|
||||
- Upgraded to Rust v1.94.0
|
||||
- Added the model capabilities of the new Qwen3.5 model family.
|
||||
- Improved file attachments in chats: configuration and project files such as `Dockerfile`, `Caddyfile`, `Makefile`, or `Jenkinsfile` are now included more reliably when you send them to the AI.
|
||||
- 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.
|
||||
@ -3,7 +3,7 @@
|
||||
234
|
||||
9.0.114 (commit 4c5aac3d56)
|
||||
9.0.13 (commit 9ecbfd4f3f)
|
||||
1.94.0 (commit 4a4ef493e)
|
||||
1.93.1 (commit 01f6ddf75)
|
||||
8.15.0
|
||||
2.10.3
|
||||
3eb367d4c9e, release
|
||||
|
||||
Loading…
Reference in New Issue
Block a user