mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-05 02:27:21 +00:00
Adopted further C# 14 and .NET 10 features (#1158)
This commit is contained in:
1 parent
f649febf88
commit
f0fa1411c8
40 files changed
+545
-56
No files matched your search
@@ -70,7 +70,7 @@ public static class ChatArchiveImporter
|
||||
{
|
||||
try
|
||||
{
|
||||
using var archive = ZipFile.OpenRead(archivePath);
|
||||
await using var archive = await ZipFile.OpenReadAsync(archivePath, token);
|
||||
var manifestResult = await DataBackupReader.ReadManifestAsync(archive, token);
|
||||
if (manifestResult.Manifest is not { } manifest)
|
||||
return new(false, new(), manifestResult.Issue);
|
||||
@@ -114,7 +114,7 @@ public static class ChatArchiveImporter
|
||||
try
|
||||
{
|
||||
var manifest = preview.Manifest;
|
||||
using var archive = ZipFile.OpenRead(archivePath);
|
||||
await using var archive = await ZipFile.OpenReadAsync(archivePath, token);
|
||||
var entriesByChat = GroupEntriesByChat(archive);
|
||||
var provenance = await ExtractProvenanceStoreAsync(archive, provenanceStore, provenanceDirectory, token);
|
||||
|
||||
@@ -243,7 +243,7 @@ public static class ChatArchiveImporter
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
private static async Task ExtractEntryAsync(ZipArchiveEntry entry, string destinationPath, CancellationToken token)
|
||||
{
|
||||
await using var entryStream = entry.Open();
|
||||
await using var entryStream = await entry.OpenAsync(token);
|
||||
await using var fileStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None, BUFFER_SIZE, FileOptions.Asynchronous | FileOptions.SequentialScan);
|
||||
await entryStream.CopyToAsync(fileStream, token);
|
||||
}
|
||||
@@ -446,7 +446,7 @@ public static class ChatArchiveImporter
|
||||
|
||||
private static async Task<string> ReadTextAsync(ZipArchiveEntry entry, CancellationToken token)
|
||||
{
|
||||
using var reader = new StreamReader(entry.Open());
|
||||
using var reader = new StreamReader(await entry.OpenAsync(token));
|
||||
return (await reader.ReadToEndAsync(token)).Trim();
|
||||
}
|
||||
|
||||
@@ -550,7 +550,7 @@ public static class ChatArchiveImporter
|
||||
}
|
||||
|
||||
ChatThread? thread;
|
||||
await using (var threadStream = threadEntry.Entry.Open())
|
||||
await using (var threadStream = await threadEntry.Entry.OpenAsync(token))
|
||||
thread = await JsonSerializer.DeserializeAsync<ChatThread>(threadStream, WorkspaceBehaviour.JSON_OPTIONS, token);
|
||||
|
||||
if (thread is null)
|
||||
|
||||
@@ -31,7 +31,7 @@ public static class DataBackupReader
|
||||
DataBackupManifest? manifest;
|
||||
try
|
||||
{
|
||||
await using var manifestStream = manifestEntry.Open();
|
||||
await using var manifestStream = await manifestEntry.OpenAsync(token);
|
||||
manifest = await JsonSerializer.DeserializeAsync<DataBackupManifest>(manifestStream, DataBackupFormat.JSON_OPTIONS, token);
|
||||
}
|
||||
catch (JsonException)
|
||||
|
||||
@@ -50,7 +50,7 @@ public static class DataBackupWriter
|
||||
var buffer = new byte[BUFFER_SIZE];
|
||||
|
||||
progress?.Report(new(DataBackupPhase.WRITING, 0, totalFiles));
|
||||
using (var archive = new ZipArchive(target, ZipArchiveMode.Create, leaveOpen: true))
|
||||
await using (var archive = await ZipArchive.CreateAsync(target, ZipArchiveMode.Create, leaveOpen: true, entryNameEncoding: null, token))
|
||||
{
|
||||
var settingsEntry = DataBackupContents.ENTRIES.First(entry => entry.Kind is DataBackupEntryKind.SETTINGS);
|
||||
await using (var settingsStream = new MemoryStream(Encoding.UTF8.GetBytes(sources.SettingsJson), writable: false))
|
||||
@@ -179,7 +179,7 @@ public static class DataBackupWriter
|
||||
|
||||
using var hash = IncrementalHash.CreateHash(HashAlgorithmName.SHA256);
|
||||
long size = 0;
|
||||
await using (var entryStream = entry.Open())
|
||||
await using (var entryStream = await entry.OpenAsync(token))
|
||||
{
|
||||
int read;
|
||||
while ((read = await source.ReadAsync(buffer, token)) > 0)
|
||||
|
||||
@@ -47,7 +47,7 @@ public static class DataRestorePreparer
|
||||
Discard(target);
|
||||
try
|
||||
{
|
||||
using var archive = ZipFile.OpenRead(archivePath);
|
||||
await using var archive = await ZipFile.OpenReadAsync(archivePath, token);
|
||||
var manifestResult = await DataBackupReader.ReadManifestAsync(archive, token);
|
||||
if (manifestResult.Manifest is not { } manifest)
|
||||
return new(false, false, manifestResult.Issue);
|
||||
@@ -218,7 +218,7 @@ public static class DataRestorePreparer
|
||||
|
||||
using var hash = IncrementalHash.CreateHash(HashAlgorithmName.SHA256);
|
||||
long size = 0;
|
||||
await using (var source = file.Entry.Open())
|
||||
await using (var source = await file.Entry.OpenAsync(token))
|
||||
await using (var destination = new FileStream(file.DestinationPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, BUFFER_SIZE, FileOptions.Asynchronous | FileOptions.SequentialScan))
|
||||
{
|
||||
int read;
|
||||
|
||||
@@ -8,7 +8,15 @@ namespace AIStudio.Tools.ERIClient;
|
||||
public abstract class ERIClientBase(IERIDataSource dataSource) : IDisposable
|
||||
{
|
||||
protected readonly IERIDataSource DataSource = dataSource;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// How AI Studio writes its requests to an ERI server and reads the answers.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An answer which names a field twice is refused, also when the two names differ only in case:
|
||||
/// AI Studio would follow one of the values, such as the provider types a data source allows, while
|
||||
/// the server may have meant the other.
|
||||
/// </remarks>
|
||||
internal static readonly JsonSerializerOptions JSON_OPTIONS = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
@@ -17,6 +25,7 @@ public abstract class ERIClientBase(IERIDataSource dataSource) : IDisposable
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
|
||||
UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow,
|
||||
AllowDuplicateProperties = false,
|
||||
Converters =
|
||||
{
|
||||
new JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseUpper),
|
||||
|
||||
@@ -21,8 +21,6 @@ public readonly record struct FileExtractionResult(FileExtractionOutcome Outcome
|
||||
private static readonly int[] NO_FAILED_PAGES = [];
|
||||
private static readonly PromptInjectionFinding[] NO_FINDINGS = [];
|
||||
|
||||
private readonly IReadOnlyList<PromptInjectionFinding>? promptInjectionFindings;
|
||||
|
||||
/// <summary>
|
||||
/// The prompt-injection attempts the runtime filtered out of the content, if any.
|
||||
/// </summary>
|
||||
@@ -33,8 +31,8 @@ public readonly record struct FileExtractionResult(FileExtractionOutcome Outcome
|
||||
/// </remarks>
|
||||
public IReadOnlyList<PromptInjectionFinding> PromptInjectionFindings
|
||||
{
|
||||
get => this.promptInjectionFindings ?? NO_FINDINGS;
|
||||
init => this.promptInjectionFindings = value;
|
||||
get => field ?? NO_FINDINGS;
|
||||
init;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -196,11 +196,7 @@ public static partial class Pandoc
|
||||
if (uri.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await RunWithRetriesAsync(
|
||||
() =>
|
||||
{
|
||||
ZipFile.ExtractToDirectory(pandocTempDownloadFile, stagingDir, true);
|
||||
return Task.CompletedTask;
|
||||
},
|
||||
() => ZipFile.ExtractToDirectoryAsync(pandocTempDownloadFile, stagingDir, true),
|
||||
"extracting the Pandoc ZIP archive");
|
||||
}
|
||||
else if (uri.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase))
|
||||
|
||||
@@ -17,9 +17,9 @@ public static class PluginArchive
|
||||
// Compatibility shim for Windows-created ZIPs with backslashes in entry names (dotnet/runtime#27620);
|
||||
// remove after dotnet/runtime#27620 and #41914 are fixed.
|
||||
// See documentation/compatibility-shims/2026-07-plugin-archive-zip-backslashes.md.
|
||||
public static void Extract(string sourceArchiveFileName, string destinationDirectory)
|
||||
public static async Task ExtractAsync(string sourceArchiveFileName, string destinationDirectory, CancellationToken token = default)
|
||||
{
|
||||
using var archive = ZipFile.OpenRead(sourceArchiveFileName);
|
||||
await using var archive = await ZipFile.OpenReadAsync(sourceArchiveFileName, token);
|
||||
Directory.CreateDirectory(destinationDirectory);
|
||||
|
||||
foreach (var entry in archive.Entries)
|
||||
@@ -37,7 +37,7 @@ public static class PluginArchive
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)!);
|
||||
entry.ExtractToFile(destinationPath);
|
||||
await entry.ExtractToFileAsync(destinationPath, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,7 @@ public static partial class PluginFactory
|
||||
await response.Content.CopyToAsync(tempFileStream, cancellationToken);
|
||||
}
|
||||
|
||||
ExtractConfigPluginArchive(tempDownloadFile, stagedDirectory);
|
||||
await ExtractConfigPluginArchiveAsync(tempDownloadFile, stagedDirectory, cancellationToken);
|
||||
|
||||
var configDirectory = Path.Join(ENTERPRISE_CONFIGURATION_PLUGINS_ROOT, configPlugId.ToString());
|
||||
if (Directory.Exists(configDirectory))
|
||||
@@ -129,9 +129,9 @@ public static partial class PluginFactory
|
||||
return wasSuccessful;
|
||||
}
|
||||
|
||||
private static void ExtractConfigPluginArchive(string sourceArchiveFileName, string destinationDirectory)
|
||||
private static async Task ExtractConfigPluginArchiveAsync(string sourceArchiveFileName, string destinationDirectory, CancellationToken token)
|
||||
{
|
||||
PluginArchive.Extract(sourceArchiveFileName, destinationDirectory);
|
||||
await PluginArchive.ExtractAsync(sourceArchiveFileName, destinationDirectory, token);
|
||||
|
||||
if (!Directory.EnumerateFiles(destinationDirectory, "plugin.lua", SearchOption.AllDirectories).Any())
|
||||
throw new InvalidDataException("The enterprise configuration plugin archive does not contain a plugin.lua file.");
|
||||
|
||||
@@ -20,7 +20,7 @@ public sealed class GlobalShortcutService : BackgroundService, IMessageBusReceiv
|
||||
}
|
||||
|
||||
private readonly SemaphoreSlim registrationSemaphore = new(1, 1);
|
||||
private readonly object runtimeStateLock = new();
|
||||
private readonly Lock runtimeStateLock = new();
|
||||
private readonly Dictionary<Shortcut, ShortcutState> lastSentStates = [];
|
||||
private readonly Dictionary<Shortcut, string> lastNonEmptyShortcuts = [];
|
||||
private readonly Dictionary<Shortcut, ShortcutRuntimeBinding> runtimeBindings = [];
|
||||
|
||||
@@ -12,7 +12,7 @@ internal sealed partial class FileSourceIndexer
|
||||
|
||||
private readonly ConcurrentDictionary<string, DataSourceWatcherRegistration> watchers = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly Dictionary<string, CancellationTokenSource> watcherDebounceTokens = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly object watcherDebounceLock = new();
|
||||
private readonly Lock watcherDebounceLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// The data sources handed in last, by their id. A watcher which failed is created anew from here.
|
||||
|
||||
@@ -45,7 +45,7 @@ public sealed partial class PluginInstallService
|
||||
if (!this.TryCreateStagingDirectory(PLUGIN_IMPORT_DIRECTORY_PREFIX, out stagingDirectory, out var stagingIssue))
|
||||
return Error(stagingIssue);
|
||||
|
||||
PluginArchive.Extract(archivePath, stagingDirectory);
|
||||
await PluginArchive.ExtractAsync(archivePath, stagingDirectory, token);
|
||||
|
||||
var pluginFiles = Directory.EnumerateFiles(stagingDirectory, PLUGIN_FILE_NAME, SearchOption.AllDirectories).ToArray();
|
||||
if (pluginFiles.Length != 1)
|
||||
|
||||
@@ -140,7 +140,7 @@ public sealed class PluginShareService(NativeShareService nativeShareService, Ru
|
||||
// one next to it, which is what deleting and recreating would come down to.
|
||||
//
|
||||
await using var archiveStream = File.Create(archivePath);
|
||||
ZipFile.CreateFromDirectory(pluginRoot, archiveStream, CompressionLevel.Optimal, false);
|
||||
await ZipFile.CreateFromDirectoryAsync(pluginRoot, archiveStream, CompressionLevel.Optimal, false, token);
|
||||
}, token);
|
||||
|
||||
logger.LogInformation("Exported plugin '{PluginName}' ({PluginId}) to the archive '{ArchivePath}'.", plugin.Name, plugin.Id, archivePath);
|
||||
@@ -177,10 +177,10 @@ public sealed class PluginShareService(NativeShareService nativeShareService, Ru
|
||||
Directory.CreateDirectory(archiveDirectory);
|
||||
this.CleanUpExpiredArchives(archiveDirectory);
|
||||
|
||||
await Task.Run(() =>
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
ZipFile.CreateFromDirectory(pluginRoot, archivePath, CompressionLevel.Optimal, false);
|
||||
await ZipFile.CreateFromDirectoryAsync(pluginRoot, archivePath, CompressionLevel.Optimal, false, token);
|
||||
}, token);
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -14,6 +14,22 @@ public sealed class ToolExecutor(ToolSettingsService toolSettingsService, ILogge
|
||||
{
|
||||
private const string INVALID_TOOL_CALL_ERROR = "The tool call was invalid.";
|
||||
|
||||
/// <summary>
|
||||
/// How the arguments are read for running a tool: every name only once per object.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// JSON allows a name twice, and readers disagree on which of the values counts. A tool reads one
|
||||
/// of them, while the check for addresses the model wrote into its call reads both, and a provider
|
||||
/// which receives the call back may read the other. A call which names an argument twice therefore
|
||||
/// does not run, and the message of the parser, which names the argument but quotes none of the
|
||||
/// values, goes back to the model. The trace reads the arguments without this rule, so that the
|
||||
/// user still sees what the model sent.
|
||||
/// </remarks>
|
||||
private static readonly JsonDocumentOptions EXECUTION_ARGUMENT_OPTIONS = new()
|
||||
{
|
||||
AllowDuplicateProperties = false,
|
||||
};
|
||||
|
||||
public ToolCallOutcome CreateInvalidToolCallResult(string toolCallId, int order)
|
||||
{
|
||||
logger.LogWarning(
|
||||
@@ -116,7 +132,7 @@ public sealed class ToolExecutor(ToolSettingsService toolSettingsService, ILogge
|
||||
var implementation = runnableTool.Implementation;
|
||||
try
|
||||
{
|
||||
using var document = JsonDocument.Parse(string.IsNullOrWhiteSpace(argumentsJson) ? "{}" : argumentsJson);
|
||||
using var document = JsonDocument.Parse(string.IsNullOrWhiteSpace(argumentsJson) ? "{}" : argumentsJson, EXECUTION_ARGUMENT_OPTIONS);
|
||||
var settingsManager = Program.SERVICE_PROVIDER.GetRequiredService<SettingsManager>();
|
||||
|
||||
//
|
||||
|
||||
Reference in new issue
Block a user