mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-17 10:38:00 +00:00
Reduced memory usage and fixed several memory leaks (#933)
Build and Release / Determine run mode (push) Has been cancelled
Build and Release / Read metadata (push) Has been cancelled
Build and Release / Sync Flatpak repo (push) Has been cancelled
Build and Release / Collect Flatpak artifacts (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Prepare & create release (push) Has been cancelled
Build and Release / Publish release (push) Has been cancelled
Build and Release / Determine run mode (push) Has been cancelled
Build and Release / Read metadata (push) Has been cancelled
Build and Release / Sync Flatpak repo (push) Has been cancelled
Build and Release / Collect Flatpak artifacts (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Prepare & create release (push) Has been cancelled
Build and Release / Publish release (push) Has been cancelled
This commit is contained in:
1 parent
902a01a4d0
commit
7d9a4f5ab1
51 files changed
+531
-85
No files matched your search
@@ -16,7 +16,13 @@ public sealed class AIJobService(SettingsManager settingsManager, MessageBus mes
|
||||
|
||||
public required CancellationToken CancellationToken { get; init; }
|
||||
|
||||
public required ChatGenerationRequest ChatGenerationRequest { get; init; }
|
||||
/// <summary>
|
||||
/// What the job works on. This is the heavy part of a job: it holds the entire chat thread.
|
||||
/// We release it once the job is done, so a finished job does not keep a chat alive for as
|
||||
/// long as the app runs. Everything a finished job still has to answer lives in the
|
||||
/// snapshot, which is small.
|
||||
/// </summary>
|
||||
public ChatGenerationRequest? ChatGenerationRequest { get; set; }
|
||||
|
||||
public required AIJobSnapshot Snapshot { get; set; }
|
||||
|
||||
@@ -70,7 +76,7 @@ public sealed class AIJobService(SettingsManager settingsManager, MessageBus mes
|
||||
if (!this.activeChatJobsByChatId.TryGetValue(chatId, out var jobId))
|
||||
return null;
|
||||
|
||||
return this.jobs.TryGetValue(jobId, out var job) ? job.ChatGenerationRequest.ChatThread : null;
|
||||
return this.jobs.TryGetValue(jobId, out var job) ? job.ChatGenerationRequest?.ChatThread : null;
|
||||
}
|
||||
|
||||
public async Task<AIJobSnapshot?> TryStartChatGenerationAsync(ChatGenerationRequest request)
|
||||
@@ -185,6 +191,9 @@ public sealed class AIJobService(SettingsManager settingsManager, MessageBus mes
|
||||
private async Task RunChatGenerationAsync(AIJobState state)
|
||||
{
|
||||
var request = state.ChatGenerationRequest;
|
||||
if (request is null)
|
||||
return;
|
||||
|
||||
var token = state.CancellationToken;
|
||||
|
||||
try
|
||||
@@ -281,7 +290,11 @@ public sealed class AIJobService(SettingsManager settingsManager, MessageBus mes
|
||||
state.IsCompletionStarted = true;
|
||||
}
|
||||
|
||||
var aiText = state.ChatGenerationRequest.AIText;
|
||||
var request = state.ChatGenerationRequest;
|
||||
if (request is null)
|
||||
return;
|
||||
|
||||
var aiText = request.AIText;
|
||||
aiText.InitialRemoteWait = false;
|
||||
aiText.IsStreaming = false;
|
||||
aiText.Text = aiText.Text.RemoveThinkTags().Trim();
|
||||
@@ -298,31 +311,72 @@ public sealed class AIJobService(SettingsManager settingsManager, MessageBus mes
|
||||
};
|
||||
}
|
||||
|
||||
this.activeChatJobsByChatId.TryRemove(state.ChatGenerationRequest.ChatThread.ChatId, out _);
|
||||
this.activeChatJobsByChatId.TryRemove(request.ChatThread.ChatId, out _);
|
||||
await CheckpointChatAsync(state, force: true);
|
||||
await this.NotifyChangedAsync(state);
|
||||
await messageBus.SendMessage(null, Event.AI_JOB_FINISHED, state.Snapshot);
|
||||
state.CancellationTokenSource.Dispose();
|
||||
|
||||
//
|
||||
// The chat is stored and everyone was told about it, so nothing needs the request anymore.
|
||||
// Releasing it here is what keeps a finished job from holding an entire chat thread — even
|
||||
// one the user has deleted in the meantime. We do it under the lock, because that is where
|
||||
// every other access to the state happens:
|
||||
//
|
||||
lock (state.SyncRoot)
|
||||
{
|
||||
state.ChatGenerationRequest = null;
|
||||
}
|
||||
|
||||
this.PruneCompletedJobs(state.Snapshot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drops the finished jobs which nothing needs anymore.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// What the app asks for is the outcome of the last generation of a chat, cf. TryGetChatSnapshot.
|
||||
/// Everything older than that is a history no one reads, and it would grow for as long as the
|
||||
/// app runs. Active jobs are never touched, and neither is the job we just finished.
|
||||
/// </remarks>
|
||||
/// <param name="latest">The snapshot of the job which just finished.</param>
|
||||
private void PruneCompletedJobs(AIJobSnapshot latest)
|
||||
{
|
||||
var supersededJobIds = this.jobs.Values
|
||||
.Select(job => job.Snapshot)
|
||||
.Where(snapshot => snapshot.Kind == latest.Kind)
|
||||
.Where(snapshot => snapshot.SubjectId == latest.SubjectId)
|
||||
.Where(snapshot => snapshot.JobId != latest.JobId)
|
||||
.Where(snapshot => !snapshot.IsActive)
|
||||
.Select(snapshot => snapshot.JobId)
|
||||
.ToList();
|
||||
|
||||
foreach (var jobId in supersededJobIds)
|
||||
this.jobs.TryRemove(jobId, out _);
|
||||
}
|
||||
|
||||
private static void RemoveEmptyAIResponse(AIJobState state)
|
||||
{
|
||||
var aiText = state.ChatGenerationRequest.AIText;
|
||||
var request = state.ChatGenerationRequest;
|
||||
if (request is null)
|
||||
return;
|
||||
|
||||
var aiText = request.AIText;
|
||||
if (!string.IsNullOrWhiteSpace(aiText.Text))
|
||||
return;
|
||||
|
||||
var aiBlock = state.ChatGenerationRequest.ChatThread.Blocks
|
||||
var aiBlock = request.ChatThread.Blocks
|
||||
.LastOrDefault(block => ReferenceEquals(block.Content, aiText));
|
||||
|
||||
if (aiBlock is not null)
|
||||
state.ChatGenerationRequest.ChatThread.Blocks.Remove(aiBlock);
|
||||
request.ChatThread.Blocks.Remove(aiBlock);
|
||||
}
|
||||
|
||||
private static bool TrySetWaitingForRemote(AIJobState state, CancellationToken token)
|
||||
{
|
||||
lock (state.SyncRoot)
|
||||
{
|
||||
if (state.IsCompletionStarted || token.IsCancellationRequested)
|
||||
if (state.IsCompletionStarted || token.IsCancellationRequested || state.ChatGenerationRequest is null)
|
||||
return false;
|
||||
|
||||
state.ChatGenerationRequest.AIText.InitialRemoteWait = true;
|
||||
@@ -334,7 +388,7 @@ public sealed class AIJobService(SettingsManager settingsManager, MessageBus mes
|
||||
{
|
||||
lock (state.SyncRoot)
|
||||
{
|
||||
if (state.IsCompletionStarted || token.IsCancellationRequested)
|
||||
if (state.IsCompletionStarted || token.IsCancellationRequested || state.ChatGenerationRequest is null)
|
||||
return false;
|
||||
|
||||
var aiText = state.ChatGenerationRequest.AIText;
|
||||
@@ -360,9 +414,13 @@ public sealed class AIJobService(SettingsManager settingsManager, MessageBus mes
|
||||
{
|
||||
lock (state.SyncRoot)
|
||||
{
|
||||
//
|
||||
// A released request keeps its last known title: the job is done, so there is nothing
|
||||
// left to read a newer one from.
|
||||
//
|
||||
state.Snapshot = state.Snapshot with
|
||||
{
|
||||
Title = state.ChatGenerationRequest.ChatThread.Name,
|
||||
Title = state.ChatGenerationRequest?.ChatThread.Name ?? state.Snapshot.Title,
|
||||
UpdatedAt = DateTimeOffset.Now,
|
||||
};
|
||||
}
|
||||
@@ -376,8 +434,12 @@ public sealed class AIJobService(SettingsManager settingsManager, MessageBus mes
|
||||
if (!force && now - state.LastCheckpoint < CHECKPOINT_MIN_TIME)
|
||||
return;
|
||||
|
||||
var request = state.ChatGenerationRequest;
|
||||
if (request is null)
|
||||
return;
|
||||
|
||||
state.LastCheckpoint = now;
|
||||
await WorkspaceBehaviour.StoreChatAsync(state.ChatGenerationRequest.ChatThread);
|
||||
await WorkspaceBehaviour.StoreChatAsync(request.ChatThread);
|
||||
}
|
||||
|
||||
private static bool ModelsMatch(Model modelA, Model modelB)
|
||||
|
||||
Reference in new issue
Block a user