mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-15 13:53:48 +00:00
Added support for parallel AI job processing (#774)
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (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, nsis) (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,updater, appimage) (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,app,updater, dmg) (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, nsis) (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,updater, appimage) (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
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (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, nsis) (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,updater, appimage) (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,app,updater, dmg) (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, nsis) (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,updater, appimage) (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
This commit is contained in:
1 parent
8853ea0cfe
commit
a0753488b3
19 files changed
+883
-69
No files matched your search
@@ -0,0 +1,7 @@
|
||||
namespace AIStudio.Tools.AIJobs;
|
||||
|
||||
public enum AIJobKind
|
||||
{
|
||||
NONE,
|
||||
CHAT_GENERATION,
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace AIStudio.Tools.AIJobs;
|
||||
|
||||
public enum AIJobSchedulingClass
|
||||
{
|
||||
NONE,
|
||||
TOP_LEVEL_USER_JOB,
|
||||
INTERNAL_DEPENDENCY,
|
||||
}
|
||||
@@ -0,0 +1,384 @@
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
using AIStudio.Tools.RAG.RAGProcesses;
|
||||
|
||||
namespace AIStudio.Tools.AIJobs;
|
||||
|
||||
public sealed class AIJobService(
|
||||
SettingsManager settingsManager,
|
||||
MessageBus messageBus,
|
||||
ILogger<AIJobService> logger)
|
||||
{
|
||||
private sealed class AIJobState
|
||||
{
|
||||
public required CancellationTokenSource CancellationTokenSource { get; init; }
|
||||
|
||||
public required ChatGenerationRequest ChatGenerationRequest { get; init; }
|
||||
|
||||
public required AIJobSnapshot Snapshot { get; set; }
|
||||
|
||||
public DateTimeOffset LastCheckpoint { get; set; }
|
||||
|
||||
public readonly Lock SyncRoot = new();
|
||||
}
|
||||
|
||||
private static readonly TimeSpan STREAMING_EVENT_MIN_TIME = TimeSpan.FromSeconds(3);
|
||||
|
||||
private static readonly TimeSpan CHECKPOINT_MIN_TIME = TimeSpan.FromSeconds(3);
|
||||
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(AIJobService).Namespace, nameof(AIJobService));
|
||||
|
||||
private readonly ConcurrentDictionary<Guid, AIJobState> jobs = new();
|
||||
private readonly ConcurrentDictionary<Guid, Guid> activeChatJobsByChatId = new();
|
||||
|
||||
public IReadOnlyCollection<AIJobSnapshot> GetSnapshots()
|
||||
{
|
||||
return this.jobs.Values
|
||||
.Select(job => job.Snapshot)
|
||||
.OrderByDescending(snapshot => snapshot.UpdatedAt)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public bool HasActiveJobs => this.jobs.Values.Any(job => job.Snapshot.IsActive);
|
||||
|
||||
public bool IsChatGenerationActive(Guid chatId)
|
||||
{
|
||||
if (!this.activeChatJobsByChatId.TryGetValue(chatId, out var jobId))
|
||||
return false;
|
||||
|
||||
return this.jobs.TryGetValue(jobId, out var job) && job.Snapshot.IsActive;
|
||||
}
|
||||
|
||||
public AIJobSnapshot? TryGetChatSnapshot(Guid chatId)
|
||||
{
|
||||
if (!this.activeChatJobsByChatId.TryGetValue(chatId, out var jobId))
|
||||
return this.jobs.Values
|
||||
.Select(job => job.Snapshot)
|
||||
.Where(snapshot => snapshot.Kind is AIJobKind.CHAT_GENERATION && snapshot.SubjectId == chatId)
|
||||
.MaxBy(snapshot => snapshot.UpdatedAt);
|
||||
|
||||
return this.jobs.TryGetValue(jobId, out var activeJob) ? activeJob.Snapshot : null;
|
||||
}
|
||||
|
||||
public ChatThread? TryGetLiveChatThread(Guid chatId)
|
||||
{
|
||||
if (!this.activeChatJobsByChatId.TryGetValue(chatId, out var jobId))
|
||||
return null;
|
||||
|
||||
return this.jobs.TryGetValue(jobId, out var job) ? job.ChatGenerationRequest.ChatThread : null;
|
||||
}
|
||||
|
||||
public async Task<AIJobSnapshot?> TryStartChatGenerationAsync(ChatGenerationRequest request)
|
||||
{
|
||||
if (this.activeChatJobsByChatId.TryGetValue(request.ChatThread.ChatId, out var existingJobId))
|
||||
return this.jobs.TryGetValue(existingJobId, out var existingJob) ? existingJob.Snapshot : null;
|
||||
|
||||
var jobId = Guid.NewGuid();
|
||||
var rootJobId = request.ParentJobId ?? jobId;
|
||||
var snapshot = new AIJobSnapshot
|
||||
{
|
||||
JobId = jobId,
|
||||
Kind = AIJobKind.CHAT_GENERATION,
|
||||
SubjectId = request.ChatThread.ChatId,
|
||||
ParentJobId = request.ParentJobId,
|
||||
RootJobId = rootJobId,
|
||||
Priority = request.Priority,
|
||||
IsForeground = request.IsForeground,
|
||||
SchedulingClass = AIJobSchedulingClass.TOP_LEVEL_USER_JOB,
|
||||
Status = AIJobStatus.WAITING_FOR_REMOTE,
|
||||
Title = request.ChatThread.Name,
|
||||
ProviderId = request.ProviderSettings.Id,
|
||||
ModelId = request.ProviderSettings.Model.Id,
|
||||
UpdatedAt = DateTimeOffset.Now,
|
||||
};
|
||||
|
||||
var state = new AIJobState
|
||||
{
|
||||
CancellationTokenSource = new CancellationTokenSource(),
|
||||
ChatGenerationRequest = request,
|
||||
Snapshot = snapshot,
|
||||
LastCheckpoint = DateTimeOffset.MinValue,
|
||||
};
|
||||
|
||||
if (!this.activeChatJobsByChatId.TryAdd(request.ChatThread.ChatId, jobId))
|
||||
{
|
||||
state.CancellationTokenSource.Dispose();
|
||||
return this.TryGetChatSnapshot(request.ChatThread.ChatId);
|
||||
}
|
||||
|
||||
if (!this.jobs.TryAdd(jobId, state))
|
||||
{
|
||||
this.activeChatJobsByChatId.TryRemove(request.ChatThread.ChatId, out _);
|
||||
state.CancellationTokenSource.Dispose();
|
||||
return null;
|
||||
}
|
||||
|
||||
request.AIText.InitialRemoteWait = true;
|
||||
request.AIText.IsStreaming = false;
|
||||
await CheckpointChatAsync(state, force: true);
|
||||
await this.NotifyChangedAsync(state);
|
||||
|
||||
_ = Task.Factory.StartNew(async () => await this.RunChatGenerationAsync(state), TaskCreationOptions.LongRunning);
|
||||
return state.Snapshot;
|
||||
}
|
||||
|
||||
public async Task CancelAsync(Guid jobId)
|
||||
{
|
||||
if (!this.jobs.TryGetValue(jobId, out var job))
|
||||
return;
|
||||
|
||||
if (!job.CancellationTokenSource.IsCancellationRequested)
|
||||
await job.CancellationTokenSource.CancelAsync();
|
||||
}
|
||||
|
||||
public async Task CancelChatGenerationAsync(Guid chatId)
|
||||
{
|
||||
if (!this.activeChatJobsByChatId.TryGetValue(chatId, out var jobId))
|
||||
return;
|
||||
|
||||
await this.CancelAsync(jobId);
|
||||
}
|
||||
|
||||
public async Task SetForegroundAsync(AIJobKind kind, Guid subjectId, bool isForeground)
|
||||
{
|
||||
var matchingJobs = this.jobs.Values
|
||||
.Where(job => job.Snapshot.Kind == kind && job.Snapshot.SubjectId == subjectId && job.Snapshot.IsActive)
|
||||
.ToList();
|
||||
|
||||
foreach (var job in matchingJobs)
|
||||
{
|
||||
lock (job.SyncRoot)
|
||||
{
|
||||
job.Snapshot = job.Snapshot with
|
||||
{
|
||||
IsForeground = isForeground,
|
||||
UpdatedAt = DateTimeOffset.Now,
|
||||
};
|
||||
}
|
||||
|
||||
await this.NotifyChangedAsync(job);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RunChatGenerationAsync(AIJobState state)
|
||||
{
|
||||
var request = state.ChatGenerationRequest;
|
||||
var token = state.CancellationTokenSource.Token;
|
||||
|
||||
try
|
||||
{
|
||||
var provider = request.ProviderSettings.CreateProvider();
|
||||
var chatThread = request.ChatThread;
|
||||
var aiText = request.AIText;
|
||||
|
||||
if (!chatThread.IsLLMProviderAllowed(provider))
|
||||
{
|
||||
logger.LogError("The provider is not allowed for chat '{ChatId}' due to data security reasons. Skipping the AI process.", chatThread.ChatId);
|
||||
await this.CompleteChatGenerationAsync(state, AIJobStatus.FAILED, TB("The selected provider is not allowed for this chat."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await this.CheckSelectedModelAvailability(provider, request.ProviderSettings.Model, token))
|
||||
{
|
||||
await this.CompleteChatGenerationAsync(state, AIJobStatus.FAILED, TB("The selected model is not available."));
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var rag = new AISrcSelWithRetCtxVal();
|
||||
if (request.LastUserPrompt is not null)
|
||||
{
|
||||
chatThread = await rag.ProcessAsync(provider, request.LastUserPrompt, chatThread, token);
|
||||
request.ChatThread = chatThread;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) when (token.IsCancellationRequested)
|
||||
{
|
||||
await this.CompleteChatGenerationAsync(state, AIJobStatus.CANCELED);
|
||||
return;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, "Skipping the RAG process due to an error.");
|
||||
}
|
||||
|
||||
var lastStreamingEvent = DateTimeOffset.MinValue;
|
||||
aiText.InitialRemoteWait = true;
|
||||
|
||||
await this.NotifyChangedAsync(state);
|
||||
await foreach (var contentStreamChunk in provider.StreamChatCompletion(request.ProviderSettings.Model, chatThread, settingsManager, token))
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
break;
|
||||
|
||||
aiText.InitialRemoteWait = false;
|
||||
aiText.IsStreaming = true;
|
||||
aiText.Text += contentStreamChunk;
|
||||
aiText.Sources.MergeSources(contentStreamChunk.Sources);
|
||||
|
||||
UpdateStatus(state, AIJobStatus.RUNNING);
|
||||
var now = DateTimeOffset.Now;
|
||||
if (!settingsManager.ConfigurationData.App.IsSavingEnergy || now - lastStreamingEvent > STREAMING_EVENT_MIN_TIME)
|
||||
{
|
||||
lastStreamingEvent = now;
|
||||
await this.NotifyChangedAsync(state);
|
||||
}
|
||||
|
||||
await CheckpointChatAsync(state);
|
||||
}
|
||||
|
||||
await this.CompleteChatGenerationAsync(state, token.IsCancellationRequested ? AIJobStatus.CANCELED : AIJobStatus.COMPLETED);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
await this.CompleteChatGenerationAsync(state, AIJobStatus.CANCELED);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, "The chat generation job '{JobId}' failed.", state.Snapshot.JobId);
|
||||
await this.CompleteChatGenerationAsync(state, AIJobStatus.FAILED, e.Message);
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Stream, string.Format(TB("The AI job failed. The message is: '{0}'"), e.Message)));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CompleteChatGenerationAsync(AIJobState state, AIJobStatus status, string errorMessage = "")
|
||||
{
|
||||
var aiText = state.ChatGenerationRequest.AIText;
|
||||
aiText.InitialRemoteWait = false;
|
||||
aiText.IsStreaming = false;
|
||||
aiText.Text = aiText.Text.RemoveThinkTags().Trim();
|
||||
|
||||
lock (state.SyncRoot)
|
||||
{
|
||||
state.Snapshot = state.Snapshot with
|
||||
{
|
||||
Status = status,
|
||||
ErrorMessage = errorMessage,
|
||||
UpdatedAt = DateTimeOffset.Now,
|
||||
};
|
||||
}
|
||||
|
||||
this.activeChatJobsByChatId.TryRemove(state.ChatGenerationRequest.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();
|
||||
}
|
||||
|
||||
private static void UpdateStatus(AIJobState state, AIJobStatus status)
|
||||
{
|
||||
lock (state.SyncRoot)
|
||||
{
|
||||
if (state.Snapshot.Status == status)
|
||||
return;
|
||||
|
||||
state.Snapshot = state.Snapshot with
|
||||
{
|
||||
Status = status,
|
||||
UpdatedAt = DateTimeOffset.Now,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private async Task NotifyChangedAsync(AIJobState state)
|
||||
{
|
||||
lock (state.SyncRoot)
|
||||
{
|
||||
state.Snapshot = state.Snapshot with
|
||||
{
|
||||
Title = state.ChatGenerationRequest.ChatThread.Name,
|
||||
UpdatedAt = DateTimeOffset.Now,
|
||||
};
|
||||
}
|
||||
|
||||
await messageBus.SendMessage(null, Event.AI_JOB_CHANGED, state.Snapshot);
|
||||
}
|
||||
|
||||
private static async Task CheckpointChatAsync(AIJobState state, bool force = false)
|
||||
{
|
||||
var now = DateTimeOffset.Now;
|
||||
if (!force && now - state.LastCheckpoint < CHECKPOINT_MIN_TIME)
|
||||
return;
|
||||
|
||||
state.LastCheckpoint = now;
|
||||
await WorkspaceBehaviour.StoreChatAsync(state.ChatGenerationRequest.ChatThread);
|
||||
}
|
||||
|
||||
private static bool ModelsMatch(Model modelA, Model modelB)
|
||||
{
|
||||
var idA = modelA.Id.Trim();
|
||||
var idB = modelB.Id.Trim();
|
||||
return string.Equals(idA, idB, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private async Task<bool> CheckSelectedModelAvailability(IProvider provider, Model chatModel, CancellationToken token = default)
|
||||
{
|
||||
if (chatModel.IsSystemModel)
|
||||
return true;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(chatModel.Id))
|
||||
{
|
||||
logger.LogWarning("Skipping AI request because model ID is null or white space.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!provider.HasModelLoadingCapability)
|
||||
return true;
|
||||
|
||||
IReadOnlyList<Model> loadedModels;
|
||||
try
|
||||
{
|
||||
var modelLoadResult = await provider.GetTextModels(token: token);
|
||||
if (!modelLoadResult.Success)
|
||||
{
|
||||
var userMessage = modelLoadResult.FailureReason.ToUserMessage(provider.InstanceName);
|
||||
if (!string.IsNullOrWhiteSpace(userMessage))
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, userMessage));
|
||||
|
||||
logger.LogWarning("Skipping selected model availability check for '{ProviderInstanceName}' (provider={ProviderType}) because loading the model list failed with reason {FailureReason}.", provider.InstanceName, provider.Provider, modelLoadResult.FailureReason);
|
||||
return false;
|
||||
}
|
||||
|
||||
loadedModels = modelLoadResult.Models;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogWarning(e, "Skipping selected model availability check for '{ProviderInstanceName}' (provider={ProviderType}) because the model list could not be loaded.", provider.InstanceName, provider.Provider);
|
||||
return true;
|
||||
}
|
||||
|
||||
var availableModels = loadedModels.Where(model => !string.IsNullOrWhiteSpace(model.Id)).ToList();
|
||||
if (availableModels.Count == 0)
|
||||
{
|
||||
var emptyModelsMessage = string.Format(
|
||||
TB("We could load models from '{0}', but the provider did not return any usable text models."),
|
||||
provider.InstanceName);
|
||||
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, emptyModelsMessage));
|
||||
logger.LogWarning("Skipping AI request because there are no models available from '{ProviderInstanceName}' (provider={ProviderType}).", provider.InstanceName, provider.Provider);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (availableModels.Any(model => ModelsMatch(model, chatModel)))
|
||||
return true;
|
||||
|
||||
var message = string.Format(
|
||||
TB("The selected model '{0}' is no longer available from '{1}' (provider={2}). Please adapt your provider settings."),
|
||||
chatModel.Id,
|
||||
provider.InstanceName,
|
||||
provider.Provider);
|
||||
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, message));
|
||||
logger.LogWarning("Skipping AI request because model '{ModelId}' is not available from '{ProviderInstanceName}' (provider={ProviderType}).", chatModel.Id, provider.InstanceName, provider.Provider);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
namespace AIStudio.Tools.AIJobs;
|
||||
|
||||
public sealed record AIJobSnapshot
|
||||
{
|
||||
public Guid JobId { get; init; }
|
||||
|
||||
public AIJobKind Kind { get; init; }
|
||||
|
||||
public Guid SubjectId { get; init; }
|
||||
|
||||
public Guid? ParentJobId { get; init; }
|
||||
|
||||
public Guid RootJobId { get; init; }
|
||||
|
||||
public int Priority { get; init; }
|
||||
|
||||
public bool IsForeground { get; init; }
|
||||
|
||||
public AIJobSchedulingClass SchedulingClass { get; init; }
|
||||
|
||||
public AIJobStatus Status { get; init; }
|
||||
|
||||
public string Title { get; init; } = string.Empty;
|
||||
|
||||
public string ProviderId { get; init; } = string.Empty;
|
||||
|
||||
public string ModelId { get; init; } = string.Empty;
|
||||
|
||||
public DateTimeOffset UpdatedAt { get; init; }
|
||||
|
||||
public string ErrorMessage { get; init; } = string.Empty;
|
||||
|
||||
public bool IsActive => this.Status is AIJobStatus.QUEUED or AIJobStatus.WAITING_FOR_REMOTE or AIJobStatus.RUNNING;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace AIStudio.Tools.AIJobs;
|
||||
|
||||
public enum AIJobStatus
|
||||
{
|
||||
NONE,
|
||||
QUEUED,
|
||||
WAITING_FOR_REMOTE,
|
||||
RUNNING,
|
||||
COMPLETED,
|
||||
CANCELED,
|
||||
FAILED,
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using AIStudio.Chat;
|
||||
|
||||
namespace AIStudio.Tools.AIJobs;
|
||||
|
||||
public sealed record ChatGenerationRequest
|
||||
{
|
||||
public required ChatThread ChatThread { get; set; }
|
||||
|
||||
public required ContentText AIText { get; init; }
|
||||
|
||||
public IContent? LastUserPrompt { get; init; }
|
||||
|
||||
public required AIStudio.Settings.Provider ProviderSettings { get; init; }
|
||||
|
||||
public Guid? ParentJobId { get; init; }
|
||||
|
||||
public int Priority { get; init; }
|
||||
|
||||
public bool IsForeground { get; init; } = true;
|
||||
}
|
||||
@@ -1,63 +1,281 @@
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
/// <summary>
|
||||
/// Defines message bus events used for communication between UI components and services.
|
||||
/// </summary>
|
||||
public enum Event
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the absence of a message bus event.
|
||||
/// </summary>
|
||||
NONE,
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Common events:
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Requests registered receivers to refresh state that depends on the current UI state.
|
||||
/// </summary>
|
||||
STATE_HAS_CHANGED,
|
||||
|
||||
/// <summary>
|
||||
/// Notifies receivers that the application configuration was changed and should be reloaded or re-applied.
|
||||
/// </summary>
|
||||
CONFIGURATION_CHANGED,
|
||||
|
||||
/// <summary>
|
||||
/// Notifies receivers that the active color theme changed.
|
||||
/// </summary>
|
||||
COLOR_THEME_CHANGED,
|
||||
|
||||
/// <summary>
|
||||
/// Requests startup initialization of the plugin system.
|
||||
/// </summary>
|
||||
STARTUP_PLUGIN_SYSTEM,
|
||||
|
||||
/// <summary>
|
||||
/// Notifies receivers that the startup initialization completed.
|
||||
/// </summary>
|
||||
STARTUP_COMPLETED,
|
||||
|
||||
/// <summary>
|
||||
/// Carries an enterprise environment that should be processed during startup.
|
||||
/// </summary>
|
||||
STARTUP_ENTERPRISE_ENVIRONMENT,
|
||||
|
||||
/// <summary>
|
||||
/// Notifies receivers that the known enterprise environments changed.
|
||||
/// </summary>
|
||||
ENTERPRISE_ENVIRONMENTS_CHANGED,
|
||||
|
||||
/// <summary>
|
||||
/// Notifies receivers that plugins were reloaded.
|
||||
/// </summary>
|
||||
PLUGINS_RELOADED,
|
||||
|
||||
/// <summary>
|
||||
/// Requests display of an error notification.
|
||||
/// </summary>
|
||||
SHOW_ERROR,
|
||||
|
||||
/// <summary>
|
||||
/// Requests display of a warning notification.
|
||||
/// </summary>
|
||||
SHOW_WARNING,
|
||||
|
||||
/// <summary>
|
||||
/// Requests display of a success notification.
|
||||
/// </summary>
|
||||
SHOW_SUCCESS,
|
||||
|
||||
/// <summary>
|
||||
/// Carries an event received from the Tauri runtime.
|
||||
/// </summary>
|
||||
TAURI_EVENT_RECEIVED,
|
||||
|
||||
/// <summary>
|
||||
/// Notifies receivers that the Rust service is unavailable or failed a health check.
|
||||
/// </summary>
|
||||
RUST_SERVICE_UNAVAILABLE,
|
||||
|
||||
/// <summary>
|
||||
/// Notifies receivers that voice recording availability changed.
|
||||
/// </summary>
|
||||
VOICE_RECORDING_AVAILABILITY_CHANGED,
|
||||
|
||||
// Update events:
|
||||
/// <summary>
|
||||
/// Requests a user-triggered search for application updates.
|
||||
/// </summary>
|
||||
USER_SEARCH_FOR_UPDATE,
|
||||
|
||||
/// <summary>
|
||||
/// Notifies receivers that an application update is available.
|
||||
/// </summary>
|
||||
UPDATE_AVAILABLE,
|
||||
|
||||
/// <summary>
|
||||
/// Requests installation of the available application update.
|
||||
/// </summary>
|
||||
INSTALL_UPDATE,
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Chat events:
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Queries whether the current chat has unsaved changes.
|
||||
/// </summary>
|
||||
HAS_CHAT_UNSAVED_CHANGES,
|
||||
|
||||
/// <summary>
|
||||
/// Requests the current chat state to be reset.
|
||||
/// </summary>
|
||||
RESET_CHAT_STATE,
|
||||
|
||||
/// <summary>
|
||||
/// Carries a chat that should be loaded by the chat component.
|
||||
/// </summary>
|
||||
LOAD_CHAT,
|
||||
|
||||
/// <summary>
|
||||
/// Notifies receivers that chat response streaming has completed.
|
||||
/// </summary>
|
||||
CHAT_STREAMING_DONE,
|
||||
|
||||
/// <summary>
|
||||
/// Notifies receivers that an AI job changed.
|
||||
/// </summary>
|
||||
AI_JOB_CHANGED,
|
||||
|
||||
/// <summary>
|
||||
/// Notifies receivers that an AI job finished.
|
||||
/// </summary>
|
||||
AI_JOB_FINISHED,
|
||||
|
||||
/// <summary>
|
||||
/// Notifies receivers that chat generation state changed.
|
||||
/// </summary>
|
||||
CHAT_GENERATION_CHANGED,
|
||||
|
||||
// Workspace events:
|
||||
/// <summary>
|
||||
/// Notifies receivers that the chat loaded in the workspace changed.
|
||||
/// </summary>
|
||||
WORKSPACE_LOADED_CHAT_CHANGED,
|
||||
|
||||
/// <summary>
|
||||
/// Requests the chat workspace overlay to be toggled.
|
||||
/// </summary>
|
||||
WORKSPACE_TOGGLE_OVERLAY,
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// RAG events:
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Carries data sources that were automatically selected for retrieval-augmented generation.
|
||||
/// </summary>
|
||||
RAG_AUTO_DATA_SOURCES_SELECTED,
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// File attachment events:
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Registers a file drop area for file attachment handling.
|
||||
/// </summary>
|
||||
REGISTER_FILE_DROP_AREA,
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a file drop area from file attachment handling.
|
||||
/// </summary>
|
||||
UNREGISTER_FILE_DROP_AREA,
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Send events:
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Sends content to the grammar and spelling assistant.
|
||||
/// </summary>
|
||||
SEND_TO_GRAMMAR_SPELLING_ASSISTANT,
|
||||
|
||||
/// <summary>
|
||||
/// Sends content to the icon finder assistant.
|
||||
/// </summary>
|
||||
SEND_TO_ICON_FINDER_ASSISTANT,
|
||||
|
||||
/// <summary>
|
||||
/// Sends content to the rewrite assistant.
|
||||
/// </summary>
|
||||
SEND_TO_REWRITE_ASSISTANT,
|
||||
|
||||
/// <summary>
|
||||
/// Sends content to the prompt optimizer assistant.
|
||||
/// </summary>
|
||||
SEND_TO_PROMPT_OPTIMIZER_ASSISTANT,
|
||||
|
||||
/// <summary>
|
||||
/// Sends content to the translation assistant.
|
||||
/// </summary>
|
||||
SEND_TO_TRANSLATION_ASSISTANT,
|
||||
|
||||
/// <summary>
|
||||
/// Sends content to the agenda assistant.
|
||||
/// </summary>
|
||||
SEND_TO_AGENDA_ASSISTANT,
|
||||
|
||||
/// <summary>
|
||||
/// Sends content to the coding assistant.
|
||||
/// </summary>
|
||||
SEND_TO_CODING_ASSISTANT,
|
||||
|
||||
/// <summary>
|
||||
/// Sends content to the text summarizer assistant.
|
||||
/// </summary>
|
||||
SEND_TO_TEXT_SUMMARIZER_ASSISTANT,
|
||||
|
||||
/// <summary>
|
||||
/// Sends the result of the current assistant to the chat component.
|
||||
/// </summary>
|
||||
SEND_TO_CHAT,
|
||||
|
||||
/// <summary>
|
||||
/// Sends text to the chat input field, aka the user prompt.
|
||||
/// </summary>
|
||||
SEND_TO_CHAT_INPUT,
|
||||
|
||||
/// <summary>
|
||||
/// Sends content to the email assistant.
|
||||
/// </summary>
|
||||
SEND_TO_EMAIL_ASSISTANT,
|
||||
|
||||
/// <summary>
|
||||
/// Sends content to the legal check assistant.
|
||||
/// </summary>
|
||||
SEND_TO_LEGAL_CHECK_ASSISTANT,
|
||||
|
||||
/// <summary>
|
||||
/// Sends content to the synonym assistant.
|
||||
/// </summary>
|
||||
SEND_TO_SYNONYMS_ASSISTANT,
|
||||
|
||||
/// <summary>
|
||||
/// Sends content to the "my tasks assistant".
|
||||
/// </summary>
|
||||
SEND_TO_MY_TASKS_ASSISTANT,
|
||||
|
||||
/// <summary>
|
||||
/// Sends content to the job posting assistant.
|
||||
/// </summary>
|
||||
SEND_TO_JOB_POSTING_ASSISTANT,
|
||||
|
||||
/// <summary>
|
||||
/// Sends content to the document analysis assistant.
|
||||
/// </summary>
|
||||
SEND_TO_DOCUMENT_ANALYSIS_ASSISTANT,
|
||||
|
||||
/// <summary>
|
||||
/// Sends content to the slide builder assistant.
|
||||
/// </summary>
|
||||
SEND_TO_SLIDE_BUILDER_ASSISTANT
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user