mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-08-11 19:32:10 +00:00
Added support for background running assistant sessions
This commit is contained in:
parent
2ccf1d0617
commit
947db222a0
@ -1,6 +1,7 @@
|
||||
using System.Text;
|
||||
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
|
||||
namespace AIStudio.Assistants.Agenda;
|
||||
|
||||
@ -185,6 +186,85 @@ public partial class AssistantAgenda : AssistantBaseCore<SettingsDialogAgenda>
|
||||
private string inputWhoIsPresenting = string.Empty;
|
||||
|
||||
private readonly List<string> contentLines = [];
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_TOPIC_STATE_KEY = new(nameof(inputTopic));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_NAME_STATE_KEY = new(nameof(inputName));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_CONTENT_STATE_KEY = new(nameof(inputContent));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_DURATION_STATE_KEY = new(nameof(inputDuration));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_START_TIME_STATE_KEY = new(nameof(inputStartTime));
|
||||
private static readonly AssistantSessionStateKey<HashSet<string>> SELECTED_FOCI_STATE_KEY = new(nameof(selectedFoci));
|
||||
private static readonly AssistantSessionStateKey<HashSet<string>> JUST_BRIEFLY_STATE_KEY = new(nameof(justBriefly));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_OBJECTIVE_STATE_KEY = new(nameof(inputObjective));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_MODERATOR_STATE_KEY = new(nameof(inputModerator));
|
||||
private static readonly AssistantSessionStateKey<CommonLanguages> SELECTED_TARGET_LANGUAGE_STATE_KEY = new(nameof(selectedTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<string> CUSTOM_TARGET_LANGUAGE_STATE_KEY = new(nameof(customTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<bool> INTRODUCE_PARTICIPANTS_STATE_KEY = new(nameof(introduceParticipants));
|
||||
private static readonly AssistantSessionStateKey<bool> IS_MEETING_VIRTUAL_STATE_KEY = new(nameof(isMeetingVirtual));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_LOCATION_STATE_KEY = new(nameof(inputLocation));
|
||||
private static readonly AssistantSessionStateKey<bool> GOING_TO_DINNER_STATE_KEY = new(nameof(goingToDinner));
|
||||
private static readonly AssistantSessionStateKey<bool> DOING_SOCIAL_ACTIVITY_STATE_KEY = new(nameof(doingSocialActivity));
|
||||
private static readonly AssistantSessionStateKey<bool> NEED_TO_ARRIVE_AND_DEPART_STATE_KEY = new(nameof(needToArriveAndDepart));
|
||||
private static readonly AssistantSessionStateKey<int> DURATION_LUNCH_BREAK_STATE_KEY = new(nameof(durationLunchBreak));
|
||||
private static readonly AssistantSessionStateKey<int> DURATION_BREAKS_STATE_KEY = new(nameof(durationBreaks));
|
||||
private static readonly AssistantSessionStateKey<bool> ACTIVE_PARTICIPATION_STATE_KEY = new(nameof(activeParticipation));
|
||||
private static readonly AssistantSessionStateKey<NumberParticipants> NUMBER_PARTICIPANTS_STATE_KEY = new(nameof(numberParticipants));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_WHO_IS_PRESENTING_STATE_KEY = new(nameof(inputWhoIsPresenting));
|
||||
private static readonly AssistantSessionStateKey<List<string>> CONTENT_LINES_STATE_KEY = new(nameof(contentLines));
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state)
|
||||
{
|
||||
state.Set(INPUT_TOPIC_STATE_KEY, this.inputTopic);
|
||||
state.Set(INPUT_NAME_STATE_KEY, this.inputName);
|
||||
state.Set(INPUT_CONTENT_STATE_KEY, this.inputContent);
|
||||
state.Set(INPUT_DURATION_STATE_KEY, this.inputDuration);
|
||||
state.Set(INPUT_START_TIME_STATE_KEY, this.inputStartTime);
|
||||
state.SetHashSet(SELECTED_FOCI_STATE_KEY, this.selectedFoci);
|
||||
state.SetHashSet(JUST_BRIEFLY_STATE_KEY, this.justBriefly);
|
||||
state.Set(INPUT_OBJECTIVE_STATE_KEY, this.inputObjective);
|
||||
state.Set(INPUT_MODERATOR_STATE_KEY, this.inputModerator);
|
||||
state.Set(SELECTED_TARGET_LANGUAGE_STATE_KEY, this.selectedTargetLanguage);
|
||||
state.Set(CUSTOM_TARGET_LANGUAGE_STATE_KEY, this.customTargetLanguage);
|
||||
state.Set(INTRODUCE_PARTICIPANTS_STATE_KEY, this.introduceParticipants);
|
||||
state.Set(IS_MEETING_VIRTUAL_STATE_KEY, this.isMeetingVirtual);
|
||||
state.Set(INPUT_LOCATION_STATE_KEY, this.inputLocation);
|
||||
state.Set(GOING_TO_DINNER_STATE_KEY, this.goingToDinner);
|
||||
state.Set(DOING_SOCIAL_ACTIVITY_STATE_KEY, this.doingSocialActivity);
|
||||
state.Set(NEED_TO_ARRIVE_AND_DEPART_STATE_KEY, this.needToArriveAndDepart);
|
||||
state.Set(DURATION_LUNCH_BREAK_STATE_KEY, this.durationLunchBreak);
|
||||
state.Set(DURATION_BREAKS_STATE_KEY, this.durationBreaks);
|
||||
state.Set(ACTIVE_PARTICIPATION_STATE_KEY, this.activeParticipation);
|
||||
state.Set(NUMBER_PARTICIPANTS_STATE_KEY, this.numberParticipants);
|
||||
state.Set(INPUT_WHO_IS_PRESENTING_STATE_KEY, this.inputWhoIsPresenting);
|
||||
state.SetList(CONTENT_LINES_STATE_KEY, this.contentLines);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RestoreCustomAssistantSessionState(AssistantSessionStateReader state)
|
||||
{
|
||||
state.Restore(INPUT_TOPIC_STATE_KEY, value => this.inputTopic = value);
|
||||
state.Restore(INPUT_NAME_STATE_KEY, value => this.inputName = value);
|
||||
state.Restore(INPUT_CONTENT_STATE_KEY, value => this.inputContent = value);
|
||||
state.Restore(INPUT_DURATION_STATE_KEY, value => this.inputDuration = value);
|
||||
state.Restore(INPUT_START_TIME_STATE_KEY, value => this.inputStartTime = value);
|
||||
state.Restore(SELECTED_FOCI_STATE_KEY, value => this.selectedFoci = value);
|
||||
state.Restore(JUST_BRIEFLY_STATE_KEY, value => this.justBriefly = value);
|
||||
state.Restore(INPUT_OBJECTIVE_STATE_KEY, value => this.inputObjective = value);
|
||||
state.Restore(INPUT_MODERATOR_STATE_KEY, value => this.inputModerator = value);
|
||||
state.Restore(SELECTED_TARGET_LANGUAGE_STATE_KEY, value => this.selectedTargetLanguage = value);
|
||||
state.Restore(CUSTOM_TARGET_LANGUAGE_STATE_KEY, value => this.customTargetLanguage = value);
|
||||
state.Restore(INTRODUCE_PARTICIPANTS_STATE_KEY, value => this.introduceParticipants = value);
|
||||
state.Restore(IS_MEETING_VIRTUAL_STATE_KEY, value => this.isMeetingVirtual = value);
|
||||
state.Restore(INPUT_LOCATION_STATE_KEY, value => this.inputLocation = value);
|
||||
state.Restore(GOING_TO_DINNER_STATE_KEY, value => this.goingToDinner = value);
|
||||
state.Restore(DOING_SOCIAL_ACTIVITY_STATE_KEY, value => this.doingSocialActivity = value);
|
||||
state.Restore(NEED_TO_ARRIVE_AND_DEPART_STATE_KEY, value => this.needToArriveAndDepart = value);
|
||||
state.Restore(DURATION_LUNCH_BREAK_STATE_KEY, value => this.durationLunchBreak = value);
|
||||
state.Restore(DURATION_BREAKS_STATE_KEY, value => this.durationBreaks = value);
|
||||
state.Restore(ACTIVE_PARTICIPATION_STATE_KEY, value => this.activeParticipation = value);
|
||||
state.Restore(NUMBER_PARTICIPANTS_STATE_KEY, value => this.numberParticipants = value);
|
||||
state.Restore(INPUT_WHO_IS_PRESENTING_STATE_KEY, value => this.inputWhoIsPresenting = value);
|
||||
state.RestoreList(CONTENT_LINES_STATE_KEY, this.contentLines);
|
||||
}
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
<MudButton Disabled="@(this.SubmitDisabled || this.isProcessing)" Variant="Variant.Filled" OnClick="@(async () => await this.Start())" Style="@this.SubmitButtonStyle">
|
||||
@this.SubmitText
|
||||
</MudButton>
|
||||
@if (this.isProcessing && this.CancellationTokenSource is not null)
|
||||
@if (this.isProcessing)
|
||||
{
|
||||
<MudTooltip Text="@TB("Stop generation")">
|
||||
<MudIconButton Variant="Variant.Filled" Icon="@Icons.Material.Filled.Stop" Color="Color.Error" OnClick="@(async () => await this.CancelStreaming())"/>
|
||||
|
||||
@ -2,6 +2,7 @@ using AIStudio.Chat;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
@ -36,6 +37,9 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
|
||||
[Inject]
|
||||
private MudTheme ColorTheme { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected AssistantSessionService AssistantSessionService { get; init; } = null!;
|
||||
|
||||
protected abstract string Title { get; }
|
||||
|
||||
@ -119,12 +123,35 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
protected ChatThread? ChatThread;
|
||||
protected IContent? LastUserPrompt;
|
||||
protected CancellationTokenSource? CancellationTokenSource;
|
||||
|
||||
private static readonly AssistantSessionStateKey<AIStudio.Settings.Provider> PROVIDER_SETTINGS_STATE_KEY = new(nameof(ProviderSettings));
|
||||
private static readonly AssistantSessionStateKey<bool> INPUT_IS_VALID_STATE_KEY = new(nameof(InputIsValid));
|
||||
private static readonly AssistantSessionStateKey<Profile> CURRENT_PROFILE_STATE_KEY = new(nameof(CurrentProfile));
|
||||
private static readonly AssistantSessionStateKey<ChatTemplate> CURRENT_CHAT_TEMPLATE_STATE_KEY = new(nameof(CurrentChatTemplate));
|
||||
private static readonly AssistantSessionStateKey<ChatThread?> CHAT_THREAD_STATE_KEY = new(nameof(ChatThread));
|
||||
private static readonly AssistantSessionStateKey<IContent?> LAST_USER_PROMPT_STATE_KEY = new(nameof(LastUserPrompt));
|
||||
private static readonly AssistantSessionStateKey<ContentBlock?> RESULTING_CONTENT_BLOCK_STATE_KEY = new(nameof(resultingContentBlock));
|
||||
private static readonly AssistantSessionStateKey<string[]> INPUT_ISSUES_STATE_KEY = new(nameof(inputIssues));
|
||||
private static readonly AssistantSessionStateKey<bool> IS_PROCESSING_STATE_KEY = new(nameof(isProcessing));
|
||||
|
||||
private readonly Timer formChangeTimer = new(TimeSpan.FromSeconds(1.6));
|
||||
|
||||
private ContentBlock? resultingContentBlock;
|
||||
private string[] inputIssues = [];
|
||||
private bool isProcessing;
|
||||
private bool isDisposed;
|
||||
private AssistantSessionKey assistantSessionKey;
|
||||
private Guid? assistantSessionId;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the Blazor component instance has already been disposed.
|
||||
/// </summary>
|
||||
protected bool IsAssistantComponentDisposed => this.isDisposed;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the assistant-specific identifier used to distinguish session slots.
|
||||
/// </summary>
|
||||
protected virtual string AssistantSessionInstanceId => this.GetType().FullName ?? this.Component.ToString();
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
@ -150,6 +177,8 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
this.ProviderSettings = this.SettingsManager.GetPreselectedProvider(this.Component);
|
||||
this.CurrentProfile = this.SettingsManager.GetPreselectedProfile(this.Component);
|
||||
this.CurrentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(this.Component);
|
||||
this.assistantSessionKey = new(this.Component, this.AssistantSessionInstanceId);
|
||||
await this.AttachAssistantSessionIfAvailable();
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
@ -191,12 +220,63 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
|
||||
private async Task Start()
|
||||
{
|
||||
using (this.CancellationTokenSource = new())
|
||||
var activeSession = this.AssistantSessionService.TryGetSnapshot(this.assistantSessionKey);
|
||||
if (activeSession?.IsActive ?? false)
|
||||
{
|
||||
await this.AttachAssistantSession(activeSession, restoreClientOnlyContent: true);
|
||||
return;
|
||||
}
|
||||
|
||||
this.CancellationTokenSource = new();
|
||||
this.isProcessing = true;
|
||||
var startedSession = this.AssistantSessionService.TryBegin(this.assistantSessionKey, this.Title, this.CancellationTokenSource, this.ChatThread, this.CaptureAssistantSessionState());
|
||||
if (startedSession.IsActive is not true || startedSession.Key != this.assistantSessionKey)
|
||||
{
|
||||
this.CancellationTokenSource.Dispose();
|
||||
this.CancellationTokenSource = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.assistantSessionId = startedSession.SessionId;
|
||||
await this.RefreshAssistantUIAsync();
|
||||
|
||||
var sessionStatus = AssistantSessionStatus.COMPLETED;
|
||||
var errorMessage = string.Empty;
|
||||
try
|
||||
{
|
||||
await this.SubmitAction();
|
||||
|
||||
if (this.CancellationTokenSource?.IsCancellationRequested ?? false)
|
||||
sessionStatus = AssistantSessionStatus.CANCELED;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
sessionStatus = AssistantSessionStatus.CANCELED;
|
||||
}
|
||||
catch (ProviderRequestException e)
|
||||
{
|
||||
sessionStatus = AssistantSessionStatus.FAILED;
|
||||
errorMessage = e.UserMessage;
|
||||
this.Logger.LogError(e, "The provider request failed for assistant '{AssistantTitle}'. Status={StatusCode}, Reason='{ReasonPhrase}', Body='{ResponseBody}'", this.Title, e.StatusCode, e.ReasonPhrase, e.ResponseBody);
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.CloudOff, e.UserMessage));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
sessionStatus = AssistantSessionStatus.FAILED;
|
||||
errorMessage = e.Message;
|
||||
this.Logger.LogError(e, "The assistant session '{AssistantTitle}' failed.", this.Title);
|
||||
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.Stream, string.Format(this.TB("The assistant failed. The message is: '{0}'"), e.Message)));
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.isProcessing = false;
|
||||
var sessionCancellationTokenSource = this.CancellationTokenSource;
|
||||
this.CancellationTokenSource = null;
|
||||
if (this.assistantSessionId is { } sessionId)
|
||||
await this.AssistantSessionService.CompleteAsync(this.assistantSessionKey, sessionId, sessionStatus, errorMessage, this.ChatThread, this.CaptureAssistantSessionState());
|
||||
sessionCancellationTokenSource?.Dispose();
|
||||
await this.RefreshAssistantUIAsync();
|
||||
}
|
||||
|
||||
this.CancellationTokenSource = null;
|
||||
}
|
||||
|
||||
private void TriggerFormChange(FormFieldChangedEventArgs _)
|
||||
@ -224,7 +304,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
Array.Resize(ref this.inputIssues, this.inputIssues.Length + 1);
|
||||
this.inputIssues[^1] = issue;
|
||||
this.InputIsValid = false;
|
||||
this.StateHasChanged();
|
||||
_ = this.RefreshAssistantUIAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -234,7 +314,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
{
|
||||
this.inputIssues = [];
|
||||
this.InputIsValid = true;
|
||||
this.StateHasChanged();
|
||||
_ = this.RefreshAssistantUIAsync();
|
||||
}
|
||||
|
||||
protected void CreateChatThread()
|
||||
@ -310,6 +390,18 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
InitialRemoteWait = true,
|
||||
};
|
||||
|
||||
aiText.StreamingEvent = async () =>
|
||||
{
|
||||
await this.CheckpointAssistantSession();
|
||||
await this.RefreshAssistantUIAsync();
|
||||
};
|
||||
|
||||
aiText.StreamingDone = async () =>
|
||||
{
|
||||
await this.CheckpointAssistantSession();
|
||||
await this.RefreshAssistantUIAsync();
|
||||
};
|
||||
|
||||
this.resultingContentBlock = new ContentBlock
|
||||
{
|
||||
Time = time,
|
||||
@ -326,7 +418,8 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
}
|
||||
|
||||
this.isProcessing = true;
|
||||
this.StateHasChanged();
|
||||
await this.CheckpointAssistantSession();
|
||||
await this.RefreshAssistantUIAsync();
|
||||
|
||||
try
|
||||
{
|
||||
@ -353,8 +446,9 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.isProcessing = false;
|
||||
this.StateHasChanged();
|
||||
this.isProcessing = this.assistantSessionId is not null && (this.AssistantSessionService.TryGetSnapshot(this.assistantSessionKey)?.IsActive ?? false);
|
||||
await this.CheckpointAssistantSession();
|
||||
await this.RefreshAssistantUIAsync();
|
||||
|
||||
if(manageCancellationLocally)
|
||||
{
|
||||
@ -366,9 +460,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
|
||||
private async Task CancelStreaming()
|
||||
{
|
||||
if (this.CancellationTokenSource is not null)
|
||||
if(!this.CancellationTokenSource.IsCancellationRequested)
|
||||
await this.CancellationTokenSource.CancelAsync();
|
||||
await this.AssistantSessionService.CancelAsync(this.assistantSessionKey);
|
||||
}
|
||||
|
||||
protected async Task CopyToClipboard()
|
||||
@ -434,10 +526,10 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
await this.DialogService.ShowAsync<TSettings>(null, dialogParameters, DialogOptions.FULLSCREEN);
|
||||
}
|
||||
|
||||
protected Task SendToAssistant(Tools.Components destination, SendToButton sendToButton)
|
||||
protected async Task SendToAssistant(Tools.Components destination, SendToButton sendToButton)
|
||||
{
|
||||
if (!this.CanSendToAssistant(destination))
|
||||
return Task.CompletedTask;
|
||||
return;
|
||||
|
||||
var contentToSend = sendToButton == default ? string.Empty : sendToButton.UseResultingContentBlockData switch
|
||||
{
|
||||
@ -450,6 +542,16 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
};
|
||||
|
||||
var sendToData = destination.GetData();
|
||||
if (destination is not Tools.Components.CHAT && this.AssistantSessionService.GetSnapshots().Any(snapshot => snapshot.IsActive && snapshot.Key.Component == destination))
|
||||
{
|
||||
await this.MessageBus.SendWarning(new(Icons.Material.Filled.Apps, this.TB("This assistant is already running. AI Studio opens the running session instead.")));
|
||||
this.NavigationManager.NavigateTo(sendToData.Route);
|
||||
return;
|
||||
}
|
||||
|
||||
if (destination is not Tools.Components.CHAT)
|
||||
await this.AssistantSessionService.ClearInactiveSessionsForComponentAsync(destination);
|
||||
|
||||
switch (destination)
|
||||
{
|
||||
case Tools.Components.CHAT:
|
||||
@ -469,7 +571,6 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
}
|
||||
|
||||
this.NavigationManager.NavigateTo(sendToData.Route);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private bool CanSendToAssistant(Tools.Components component)
|
||||
@ -482,6 +583,11 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
|
||||
private async Task InnerResetForm()
|
||||
{
|
||||
if (this.AssistantSessionService.TryGetSnapshot(this.assistantSessionKey)?.IsActive ?? false)
|
||||
return;
|
||||
|
||||
await this.AssistantSessionService.ClearAsync(this.assistantSessionKey);
|
||||
this.assistantSessionId = null;
|
||||
this.resultingContentBlock = null;
|
||||
this.ProviderSettings = Settings.Provider.NONE;
|
||||
|
||||
@ -495,7 +601,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
this.inputIssues = [];
|
||||
|
||||
this.Form?.ResetValidation();
|
||||
this.StateHasChanged();
|
||||
await this.RefreshAssistantUIAsync();
|
||||
this.Form?.ResetValidation();
|
||||
}
|
||||
|
||||
@ -515,6 +621,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
this.isDisposed = true;
|
||||
try
|
||||
{
|
||||
this.formChangeTimer.Stop();
|
||||
@ -529,4 +636,153 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region Assistant sessions
|
||||
|
||||
/// <summary>
|
||||
/// Stores the current assistant UI and chat state in the active assistant session.
|
||||
/// </summary>
|
||||
/// <returns>A task that completes after the checkpoint was stored and published.</returns>
|
||||
private Task CheckpointAssistantSession()
|
||||
{
|
||||
if (this.assistantSessionId is null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
return this.AssistantSessionService.CheckpointAsync(this.assistantSessionKey, this.assistantSessionId.Value, this.Title, this.ChatThread, this.CaptureAssistantSessionState());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows derived assistants to restore client-only UI after a session was attached.
|
||||
/// </summary>
|
||||
/// <param name="snapshot">The assistant session snapshot that was attached.</param>
|
||||
/// <returns>A task that completes after derived UI restore work has finished.</returns>
|
||||
protected virtual Task OnAssistantSessionAttachedAsync(AssistantSessionSnapshot snapshot) => Task.CompletedTask;
|
||||
|
||||
/// <summary>
|
||||
/// Handles assistant session change events for the current assistant instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The message payload type.</typeparam>
|
||||
/// <param name="sendingComponent">The component that sent the message, if any.</param>
|
||||
/// <param name="triggeredEvent">The event that was triggered.</param>
|
||||
/// <param name="data">The message payload.</param>
|
||||
/// <returns>A task that completes after the message was processed.</returns>
|
||||
protected override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||
{
|
||||
switch (triggeredEvent)
|
||||
{
|
||||
case Event.ASSISTANT_SESSION_CHANGED:
|
||||
case Event.ASSISTANT_SESSION_FINISHED:
|
||||
if (data is AssistantSessionSnapshot snapshot && snapshot.Key == this.assistantSessionKey)
|
||||
await this.AttachAssistantSession(snapshot, restoreClientOnlyContent: triggeredEvent is Event.ASSISTANT_SESSION_FINISHED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches the component to an existing assistant session if one is available.
|
||||
/// </summary>
|
||||
/// <returns>A task that completes after the session was attached.</returns>
|
||||
private async Task AttachAssistantSessionIfAvailable()
|
||||
{
|
||||
var snapshot = this.AssistantSessionService.TryGetSnapshot(this.assistantSessionKey);
|
||||
if (snapshot is null)
|
||||
return;
|
||||
|
||||
await this.AttachAssistantSession(snapshot, restoreClientOnlyContent: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies an assistant session snapshot to this component instance.
|
||||
/// </summary>
|
||||
/// <param name="snapshot">The snapshot to attach.</param>
|
||||
/// <param name="restoreClientOnlyContent">Whether derived assistants should restore client-only UI state.</param>
|
||||
/// <returns>A task that completes after the component was refreshed.</returns>
|
||||
private async Task AttachAssistantSession(AssistantSessionSnapshot snapshot, bool restoreClientOnlyContent)
|
||||
{
|
||||
this.assistantSessionId = snapshot.SessionId;
|
||||
this.ImportAssistantSessionState(snapshot.State);
|
||||
this.ChatThread = snapshot.ChatThread ?? this.ChatThread;
|
||||
this.isProcessing = snapshot.IsActive;
|
||||
|
||||
if (!snapshot.IsActive)
|
||||
this.CancellationTokenSource = null;
|
||||
|
||||
if (restoreClientOnlyContent)
|
||||
await this.OnAssistantSessionAttachedAsync(snapshot);
|
||||
|
||||
await this.RefreshAssistantUIAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the component when it is still mounted.
|
||||
/// </summary>
|
||||
/// <returns>A task that completes after the renderer was notified.</returns>
|
||||
private async Task RefreshAssistantUIAsync()
|
||||
{
|
||||
if (this.isDisposed)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// The component may already have left the renderer while a background session is finishing.
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures the base assistant state and assistant-specific typed state values for session restore.
|
||||
/// </summary>
|
||||
/// <returns>A dictionary containing the current assistant state.</returns>
|
||||
private Dictionary<string, IAssistantSessionSnapshotField> CaptureAssistantSessionState()
|
||||
{
|
||||
var state = new AssistantSessionStateWriter();
|
||||
state.Set(PROVIDER_SETTINGS_STATE_KEY, this.ProviderSettings);
|
||||
state.Set(INPUT_IS_VALID_STATE_KEY, this.InputIsValid);
|
||||
state.Set(CURRENT_PROFILE_STATE_KEY, this.CurrentProfile);
|
||||
state.Set(CURRENT_CHAT_TEMPLATE_STATE_KEY, this.CurrentChatTemplate);
|
||||
state.Set(CHAT_THREAD_STATE_KEY, this.ChatThread);
|
||||
state.Set(LAST_USER_PROMPT_STATE_KEY, this.LastUserPrompt);
|
||||
state.Set(RESULTING_CONTENT_BLOCK_STATE_KEY, this.resultingContentBlock);
|
||||
state.Set(INPUT_ISSUES_STATE_KEY, this.inputIssues);
|
||||
state.Set(IS_PROCESSING_STATE_KEY, this.isProcessing);
|
||||
this.CaptureCustomAssistantSessionState(state);
|
||||
|
||||
return state.ToDictionary();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures assistant-specific state values.
|
||||
/// </summary>
|
||||
/// <param name="state">The typed state writer to update.</param>
|
||||
protected virtual void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state) { }
|
||||
|
||||
/// <summary>
|
||||
/// Restores the base assistant state and assistant-specific typed state values from a session snapshot.
|
||||
/// </summary>
|
||||
/// <param name="state">The captured assistant state to import.</param>
|
||||
private void ImportAssistantSessionState(IReadOnlyDictionary<string, IAssistantSessionSnapshotField> state)
|
||||
{
|
||||
var reader = new AssistantSessionStateReader(state, this.Title);
|
||||
reader.Restore(PROVIDER_SETTINGS_STATE_KEY, value => this.ProviderSettings = value);
|
||||
reader.Restore(INPUT_IS_VALID_STATE_KEY, value => this.InputIsValid = value);
|
||||
reader.Restore(CURRENT_PROFILE_STATE_KEY, value => this.CurrentProfile = value);
|
||||
reader.Restore(CURRENT_CHAT_TEMPLATE_STATE_KEY, value => this.CurrentChatTemplate = value);
|
||||
reader.Restore(CHAT_THREAD_STATE_KEY, value => this.ChatThread = value);
|
||||
reader.Restore(LAST_USER_PROMPT_STATE_KEY, value => this.LastUserPrompt = value);
|
||||
reader.Restore(RESULTING_CONTENT_BLOCK_STATE_KEY, value => this.resultingContentBlock = value);
|
||||
reader.Restore(INPUT_ISSUES_STATE_KEY, value => this.inputIssues = value);
|
||||
reader.Restore(IS_PROCESSING_STATE_KEY, value => this.isProcessing = value);
|
||||
this.RestoreCustomAssistantSessionState(reader);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores assistant-specific state values.
|
||||
/// </summary>
|
||||
/// <param name="state">The typed state reader to read from.</param>
|
||||
protected virtual void RestoreCustomAssistantSessionState(AssistantSessionStateReader state) { }
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -3,6 +3,7 @@ using System.Text;
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
|
||||
namespace AIStudio.Assistants.BiasDay;
|
||||
|
||||
@ -66,6 +67,25 @@ public partial class BiasOfTheDayAssistant : AssistantBaseCore<SettingsDialogAss
|
||||
private Bias biasOfTheDay = BiasCatalog.NONE;
|
||||
private CommonLanguages selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
private string customTargetLanguage = string.Empty;
|
||||
private static readonly AssistantSessionStateKey<Bias> BIAS_OF_THE_DAY_STATE_KEY = new(nameof(biasOfTheDay));
|
||||
private static readonly AssistantSessionStateKey<CommonLanguages> SELECTED_TARGET_LANGUAGE_STATE_KEY = new(nameof(selectedTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<string> CUSTOM_TARGET_LANGUAGE_STATE_KEY = new(nameof(customTargetLanguage));
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state)
|
||||
{
|
||||
state.Set(BIAS_OF_THE_DAY_STATE_KEY, this.biasOfTheDay);
|
||||
state.Set(SELECTED_TARGET_LANGUAGE_STATE_KEY, this.selectedTargetLanguage);
|
||||
state.Set(CUSTOM_TARGET_LANGUAGE_STATE_KEY, this.customTargetLanguage);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RestoreCustomAssistantSessionState(AssistantSessionStateReader state)
|
||||
{
|
||||
state.Restore(BIAS_OF_THE_DAY_STATE_KEY, value => this.biasOfTheDay = value);
|
||||
state.Restore(SELECTED_TARGET_LANGUAGE_STATE_KEY, value => this.selectedTargetLanguage = value);
|
||||
state.Restore(CUSTOM_TARGET_LANGUAGE_STATE_KEY, value => this.customTargetLanguage = value);
|
||||
}
|
||||
|
||||
private string? ValidateTargetLanguage(CommonLanguages language)
|
||||
{
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System.Text;
|
||||
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
|
||||
namespace AIStudio.Assistants.Coding;
|
||||
|
||||
@ -59,6 +60,28 @@ public partial class AssistantCoding : AssistantBaseCore<SettingsDialogCoding>
|
||||
private bool provideCompilerMessages;
|
||||
private string compilerMessages = string.Empty;
|
||||
private string questions = string.Empty;
|
||||
private static readonly AssistantSessionStateKey<List<CodingContext>> CODING_CONTEXTS_STATE_KEY = new(nameof(codingContexts));
|
||||
private static readonly AssistantSessionStateKey<bool> PROVIDE_COMPILER_MESSAGES_STATE_KEY = new(nameof(provideCompilerMessages));
|
||||
private static readonly AssistantSessionStateKey<string> COMPILER_MESSAGES_STATE_KEY = new(nameof(compilerMessages));
|
||||
private static readonly AssistantSessionStateKey<string> QUESTIONS_STATE_KEY = new(nameof(questions));
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state)
|
||||
{
|
||||
state.SetList(CODING_CONTEXTS_STATE_KEY, this.codingContexts);
|
||||
state.Set(PROVIDE_COMPILER_MESSAGES_STATE_KEY, this.provideCompilerMessages);
|
||||
state.Set(COMPILER_MESSAGES_STATE_KEY, this.compilerMessages);
|
||||
state.Set(QUESTIONS_STATE_KEY, this.questions);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RestoreCustomAssistantSessionState(AssistantSessionStateReader state)
|
||||
{
|
||||
state.RestoreList(CODING_CONTEXTS_STATE_KEY, this.codingContexts);
|
||||
state.Restore(PROVIDE_COMPILER_MESSAGES_STATE_KEY, value => this.provideCompilerMessages = value);
|
||||
state.Restore(COMPILER_MESSAGES_STATE_KEY, value => this.compilerMessages = value);
|
||||
state.Restore(QUESTIONS_STATE_KEY, value => this.questions = value);
|
||||
}
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
@ -279,6 +280,55 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<NoSettingsPan
|
||||
private ProfilePreselection policyPreselectedProfile = ProfilePreselection.NoProfile;
|
||||
private HashSet<FileAttachment> loadedDocumentPaths = [];
|
||||
private readonly List<ConfigurationSelectData<string>> availableLLMProviders = new();
|
||||
private static readonly AssistantSessionStateKey<DataDocumentAnalysisPolicy?> SELECTED_POLICY_STATE_KEY = new(nameof(selectedPolicy));
|
||||
private static readonly AssistantSessionStateKey<bool> POLICY_IS_PROTECTED_STATE_KEY = new(nameof(policyIsProtected));
|
||||
private static readonly AssistantSessionStateKey<bool> POLICY_HIDE_POLICY_DEFINITION_STATE_KEY = new(nameof(policyHidePolicyDefinition));
|
||||
private static readonly AssistantSessionStateKey<bool> POLICY_DEFINITION_EXPANDED_STATE_KEY = new(nameof(policyDefinitionExpanded));
|
||||
private static readonly AssistantSessionStateKey<string> POLICY_NAME_STATE_KEY = new(nameof(policyName));
|
||||
private static readonly AssistantSessionStateKey<string> POLICY_DESCRIPTION_STATE_KEY = new(nameof(policyDescription));
|
||||
private static readonly AssistantSessionStateKey<string> POLICY_ANALYSIS_RULES_STATE_KEY = new(nameof(policyAnalysisRules));
|
||||
private static readonly AssistantSessionStateKey<string> POLICY_OUTPUT_RULES_STATE_KEY = new(nameof(policyOutputRules));
|
||||
private static readonly AssistantSessionStateKey<ConfidenceLevel> POLICY_MINIMUM_PROVIDER_CONFIDENCE_STATE_KEY = new(nameof(policyMinimumProviderConfidence));
|
||||
private static readonly AssistantSessionStateKey<string> POLICY_PRESELECTED_PROVIDER_ID_STATE_KEY = new(nameof(policyPreselectedProviderId));
|
||||
private static readonly AssistantSessionStateKey<ProfilePreselection> POLICY_PRESELECTED_PROFILE_STATE_KEY = new(nameof(policyPreselectedProfile));
|
||||
private static readonly AssistantSessionStateKey<HashSet<FileAttachment>> LOADED_DOCUMENT_PATHS_STATE_KEY = new(nameof(loadedDocumentPaths));
|
||||
private static readonly AssistantSessionStateKey<List<ConfigurationSelectData<string>>> AVAILABLE_LLM_PROVIDERS_STATE_KEY = new(nameof(availableLLMProviders));
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state)
|
||||
{
|
||||
state.Set(SELECTED_POLICY_STATE_KEY, this.selectedPolicy);
|
||||
state.Set(POLICY_IS_PROTECTED_STATE_KEY, this.policyIsProtected);
|
||||
state.Set(POLICY_HIDE_POLICY_DEFINITION_STATE_KEY, this.policyHidePolicyDefinition);
|
||||
state.Set(POLICY_DEFINITION_EXPANDED_STATE_KEY, this.policyDefinitionExpanded);
|
||||
state.Set(POLICY_NAME_STATE_KEY, this.policyName);
|
||||
state.Set(POLICY_DESCRIPTION_STATE_KEY, this.policyDescription);
|
||||
state.Set(POLICY_ANALYSIS_RULES_STATE_KEY, this.policyAnalysisRules);
|
||||
state.Set(POLICY_OUTPUT_RULES_STATE_KEY, this.policyOutputRules);
|
||||
state.Set(POLICY_MINIMUM_PROVIDER_CONFIDENCE_STATE_KEY, this.policyMinimumProviderConfidence);
|
||||
state.Set(POLICY_PRESELECTED_PROVIDER_ID_STATE_KEY, this.policyPreselectedProviderId);
|
||||
state.Set(POLICY_PRESELECTED_PROFILE_STATE_KEY, this.policyPreselectedProfile);
|
||||
state.SetHashSet(LOADED_DOCUMENT_PATHS_STATE_KEY, this.loadedDocumentPaths);
|
||||
state.SetList(AVAILABLE_LLM_PROVIDERS_STATE_KEY, this.availableLLMProviders);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RestoreCustomAssistantSessionState(AssistantSessionStateReader state)
|
||||
{
|
||||
state.Restore(SELECTED_POLICY_STATE_KEY, value => this.selectedPolicy = value);
|
||||
state.Restore(POLICY_IS_PROTECTED_STATE_KEY, value => this.policyIsProtected = value);
|
||||
state.Restore(POLICY_HIDE_POLICY_DEFINITION_STATE_KEY, value => this.policyHidePolicyDefinition = value);
|
||||
state.Restore(POLICY_DEFINITION_EXPANDED_STATE_KEY, value => this.policyDefinitionExpanded = value);
|
||||
state.Restore(POLICY_NAME_STATE_KEY, value => this.policyName = value);
|
||||
state.Restore(POLICY_DESCRIPTION_STATE_KEY, value => this.policyDescription = value);
|
||||
state.Restore(POLICY_ANALYSIS_RULES_STATE_KEY, value => this.policyAnalysisRules = value);
|
||||
state.Restore(POLICY_OUTPUT_RULES_STATE_KEY, value => this.policyOutputRules = value);
|
||||
state.Restore(POLICY_MINIMUM_PROVIDER_CONFIDENCE_STATE_KEY, value => this.policyMinimumProviderConfidence = value);
|
||||
state.Restore(POLICY_PRESELECTED_PROVIDER_ID_STATE_KEY, value => this.policyPreselectedProviderId = value);
|
||||
state.Restore(POLICY_PRESELECTED_PROFILE_STATE_KEY, value => this.policyPreselectedProfile = value);
|
||||
state.RestoreHashSet(LOADED_DOCUMENT_PATHS_STATE_KEY, this.loadedDocumentPaths);
|
||||
state.RestoreList(AVAILABLE_LLM_PROVIDERS_STATE_KEY, this.availableLLMProviders);
|
||||
}
|
||||
|
||||
private bool IsNoPolicySelectedOrProtected => this.selectedPolicy is null || this.selectedPolicy.IsProtected;
|
||||
|
||||
@ -515,7 +565,7 @@ public partial class DocumentAnalysisAssistant : AssistantBaseCore<NoSettingsPan
|
||||
break;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
return base.ProcessIncomingMessage(sendingComponent, triggeredEvent, data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System.Text;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
using AIStudio.Tools.PluginSystem.Assistants;
|
||||
using AIStudio.Tools.PluginSystem.Assistants.DataModel;
|
||||
@ -27,6 +28,11 @@ public partial class AssistantDynamic : AssistantBaseCore<NoSettingsPanel>
|
||||
// Reuse chat-level provider filtering/preselection instead of NONE.
|
||||
protected override Tools.Components Component => Tools.Components.CHAT;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin ID as the assistant session instance ID.
|
||||
/// </summary>
|
||||
protected override string AssistantSessionInstanceId => this.assistantPlugin is null ? base.AssistantSessionInstanceId : this.assistantPlugin.Id.ToString();
|
||||
|
||||
private string title = string.Empty;
|
||||
private string description = string.Empty;
|
||||
private string systemPrompt = string.Empty;
|
||||
@ -44,6 +50,61 @@ public partial class AssistantDynamic : AssistantBaseCore<NoSettingsPanel>
|
||||
private string securityMessage = string.Empty;
|
||||
private bool isSecurityBlocked;
|
||||
private const string ASSISTANT_QUERY_KEY = "assistantId";
|
||||
private static readonly AssistantSessionStateKey<string> TITLE_STATE_KEY = new(nameof(title));
|
||||
private static readonly AssistantSessionStateKey<string> DESCRIPTION_STATE_KEY = new(nameof(description));
|
||||
private static readonly AssistantSessionStateKey<string> SYSTEM_PROMPT_STATE_KEY = new(nameof(systemPrompt));
|
||||
private static readonly AssistantSessionStateKey<bool> ALLOW_PROFILES_STATE_KEY = new(nameof(allowProfiles));
|
||||
private static readonly AssistantSessionStateKey<string> SUBMIT_TEXT_STATE_KEY = new(nameof(submitText));
|
||||
private static readonly AssistantSessionStateKey<bool> SHOW_FOOTER_PROFILE_SELECTION_STATE_KEY = new(nameof(showFooterProfileSelection));
|
||||
private static readonly AssistantSessionStateKey<PluginAssistants?> ASSISTANT_PLUGIN_STATE_KEY = new(nameof(assistantPlugin));
|
||||
private static readonly AssistantSessionStateKey<AssistantState> ASSISTANT_STATE_STATE_KEY = new(nameof(assistantState));
|
||||
private static readonly AssistantSessionStateKey<Dictionary<string, string>> IMAGE_CACHE_STATE_KEY = new(nameof(imageCache));
|
||||
private static readonly AssistantSessionStateKey<HashSet<string>> EXECUTING_BUTTON_ACTIONS_STATE_KEY = new(nameof(executingButtonActions));
|
||||
private static readonly AssistantSessionStateKey<HashSet<string>> EXECUTING_SWITCH_ACTIONS_STATE_KEY = new(nameof(executingSwitchActions));
|
||||
private static readonly AssistantSessionStateKey<string> PLUGIN_PATH_STATE_KEY = new(nameof(pluginPath));
|
||||
private static readonly AssistantSessionStateKey<PluginAssistantAudit?> AUDIT_STATE_KEY = new(nameof(audit));
|
||||
private static readonly AssistantSessionStateKey<string> SECURITY_MESSAGE_STATE_KEY = new(nameof(securityMessage));
|
||||
private static readonly AssistantSessionStateKey<bool> IS_SECURITY_BLOCKED_STATE_KEY = new(nameof(isSecurityBlocked));
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state)
|
||||
{
|
||||
state.Set(TITLE_STATE_KEY, this.title);
|
||||
state.Set(DESCRIPTION_STATE_KEY, this.description);
|
||||
state.Set(SYSTEM_PROMPT_STATE_KEY, this.systemPrompt);
|
||||
state.Set(ALLOW_PROFILES_STATE_KEY, this.allowProfiles);
|
||||
state.Set(SUBMIT_TEXT_STATE_KEY, this.submitText);
|
||||
state.Set(SHOW_FOOTER_PROFILE_SELECTION_STATE_KEY, this.showFooterProfileSelection);
|
||||
state.Set(ASSISTANT_PLUGIN_STATE_KEY, this.assistantPlugin);
|
||||
state.Set(ASSISTANT_STATE_STATE_KEY, this.assistantState.Clone());
|
||||
state.SetDictionary(IMAGE_CACHE_STATE_KEY, this.imageCache);
|
||||
state.SetHashSet(EXECUTING_BUTTON_ACTIONS_STATE_KEY, this.executingButtonActions);
|
||||
state.SetHashSet(EXECUTING_SWITCH_ACTIONS_STATE_KEY, this.executingSwitchActions);
|
||||
state.Set(PLUGIN_PATH_STATE_KEY, this.pluginPath);
|
||||
state.Set(AUDIT_STATE_KEY, this.audit);
|
||||
state.Set(SECURITY_MESSAGE_STATE_KEY, this.securityMessage);
|
||||
state.Set(IS_SECURITY_BLOCKED_STATE_KEY, this.isSecurityBlocked);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RestoreCustomAssistantSessionState(AssistantSessionStateReader state)
|
||||
{
|
||||
state.Restore(TITLE_STATE_KEY, value => this.title = value);
|
||||
state.Restore(DESCRIPTION_STATE_KEY, value => this.description = value);
|
||||
state.Restore(SYSTEM_PROMPT_STATE_KEY, value => this.systemPrompt = value);
|
||||
state.Restore(ALLOW_PROFILES_STATE_KEY, value => this.allowProfiles = value);
|
||||
state.Restore(SUBMIT_TEXT_STATE_KEY, value => this.submitText = value);
|
||||
state.Restore(SHOW_FOOTER_PROFILE_SELECTION_STATE_KEY, value => this.showFooterProfileSelection = value);
|
||||
state.Restore(ASSISTANT_PLUGIN_STATE_KEY, value => this.assistantPlugin = value);
|
||||
state.Restore(ASSISTANT_STATE_STATE_KEY, value => this.assistantState.CopyFrom(value));
|
||||
state.RestoreDictionary(IMAGE_CACHE_STATE_KEY, this.imageCache);
|
||||
state.RestoreHashSet(EXECUTING_BUTTON_ACTIONS_STATE_KEY, this.executingButtonActions);
|
||||
state.RestoreHashSet(EXECUTING_SWITCH_ACTIONS_STATE_KEY, this.executingSwitchActions);
|
||||
state.Restore(PLUGIN_PATH_STATE_KEY, value => this.pluginPath = value);
|
||||
state.Restore(AUDIT_STATE_KEY, value => this.audit = value);
|
||||
state.Restore(SECURITY_MESSAGE_STATE_KEY, value => this.securityMessage = value);
|
||||
state.Restore(IS_SECURITY_BLOCKED_STATE_KEY, value => this.isSecurityBlocked = value);
|
||||
}
|
||||
|
||||
#region Implementation of AssistantBase
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System.Text;
|
||||
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
|
||||
namespace AIStudio.Assistants.EMail;
|
||||
|
||||
@ -78,6 +79,46 @@ public partial class AssistantEMail : AssistantBaseCore<SettingsDialogWritingEMa
|
||||
private string customTargetLanguage = string.Empty;
|
||||
private bool provideHistory;
|
||||
private string inputHistory = string.Empty;
|
||||
private static readonly AssistantSessionStateKey<WritingStyles> SELECTED_WRITING_STYLE_STATE_KEY = new(nameof(selectedWritingStyle));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_GREETING_STATE_KEY = new(nameof(inputGreeting));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_BULLET_POINTS_STATE_KEY = new(nameof(inputBulletPoints));
|
||||
private static readonly AssistantSessionStateKey<List<string>> BULLET_POINTS_LINES_STATE_KEY = new(nameof(bulletPointsLines));
|
||||
private static readonly AssistantSessionStateKey<HashSet<string>> SELECTED_FOCI_STATE_KEY = new(nameof(selectedFoci));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_NAME_STATE_KEY = new(nameof(inputName));
|
||||
private static readonly AssistantSessionStateKey<CommonLanguages> SELECTED_TARGET_LANGUAGE_STATE_KEY = new(nameof(selectedTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<string> CUSTOM_TARGET_LANGUAGE_STATE_KEY = new(nameof(customTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<bool> PROVIDE_HISTORY_STATE_KEY = new(nameof(provideHistory));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_HISTORY_STATE_KEY = new(nameof(inputHistory));
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state)
|
||||
{
|
||||
state.Set(SELECTED_WRITING_STYLE_STATE_KEY, this.selectedWritingStyle);
|
||||
state.Set(INPUT_GREETING_STATE_KEY, this.inputGreeting);
|
||||
state.Set(INPUT_BULLET_POINTS_STATE_KEY, this.inputBulletPoints);
|
||||
state.SetList(BULLET_POINTS_LINES_STATE_KEY, this.bulletPointsLines);
|
||||
state.SetHashSet(SELECTED_FOCI_STATE_KEY, this.selectedFoci);
|
||||
state.Set(INPUT_NAME_STATE_KEY, this.inputName);
|
||||
state.Set(SELECTED_TARGET_LANGUAGE_STATE_KEY, this.selectedTargetLanguage);
|
||||
state.Set(CUSTOM_TARGET_LANGUAGE_STATE_KEY, this.customTargetLanguage);
|
||||
state.Set(PROVIDE_HISTORY_STATE_KEY, this.provideHistory);
|
||||
state.Set(INPUT_HISTORY_STATE_KEY, this.inputHistory);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RestoreCustomAssistantSessionState(AssistantSessionStateReader state)
|
||||
{
|
||||
state.Restore(SELECTED_WRITING_STYLE_STATE_KEY, value => this.selectedWritingStyle = value);
|
||||
state.Restore(INPUT_GREETING_STATE_KEY, value => this.inputGreeting = value);
|
||||
state.Restore(INPUT_BULLET_POINTS_STATE_KEY, value => this.inputBulletPoints = value);
|
||||
state.RestoreList(BULLET_POINTS_LINES_STATE_KEY, this.bulletPointsLines);
|
||||
state.Restore(SELECTED_FOCI_STATE_KEY, value => this.selectedFoci = value);
|
||||
state.Restore(INPUT_NAME_STATE_KEY, value => this.inputName = value);
|
||||
state.Restore(SELECTED_TARGET_LANGUAGE_STATE_KEY, value => this.selectedTargetLanguage = value);
|
||||
state.Restore(CUSTOM_TARGET_LANGUAGE_STATE_KEY, value => this.customTargetLanguage = value);
|
||||
state.Restore(PROVIDE_HISTORY_STATE_KEY, value => this.provideHistory = value);
|
||||
state.Restore(INPUT_HISTORY_STATE_KEY, value => this.inputHistory = value);
|
||||
}
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ using AIStudio.Chat;
|
||||
using AIStudio.Dialogs;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
@ -449,6 +450,88 @@ public partial class AssistantERI : AssistantBaseCore<SettingsDialogERIServer>
|
||||
private bool writeToFilesystem;
|
||||
private string baseDirectory = string.Empty;
|
||||
private List<string> previouslyGeneratedFiles = new();
|
||||
private static readonly AssistantSessionStateKey<DataERIServer?> SELECTED_ERI_SERVER_STATE_KEY = new(nameof(selectedERIServer));
|
||||
private static readonly AssistantSessionStateKey<bool> AUTO_SAVE_STATE_KEY = new(nameof(autoSave));
|
||||
private static readonly AssistantSessionStateKey<string> SERVER_NAME_STATE_KEY = new(nameof(serverName));
|
||||
private static readonly AssistantSessionStateKey<string> SERVER_DESCRIPTION_STATE_KEY = new(nameof(serverDescription));
|
||||
private static readonly AssistantSessionStateKey<ERIVersion> SELECTED_ERI_VERSION_STATE_KEY = new(nameof(selectedERIVersion));
|
||||
private static readonly AssistantSessionStateKey<string?> ERI_SPECIFICATION_STATE_KEY = new(nameof(eriSpecification));
|
||||
private static readonly AssistantSessionStateKey<ProgrammingLanguages> SELECTED_PROGRAMMING_LANGUAGE_STATE_KEY = new(nameof(selectedProgrammingLanguage));
|
||||
private static readonly AssistantSessionStateKey<string> OTHER_PROGRAMMING_LANGUAGE_STATE_KEY = new(nameof(otherProgrammingLanguage));
|
||||
private static readonly AssistantSessionStateKey<DataSources> SELECTED_DATA_SOURCE_STATE_KEY = new(nameof(selectedDataSource));
|
||||
private static readonly AssistantSessionStateKey<string> OTHER_DATA_SOURCE_STATE_KEY = new(nameof(otherDataSource));
|
||||
private static readonly AssistantSessionStateKey<string> DATA_SOURCE_PRODUCT_NAME_STATE_KEY = new(nameof(dataSourceProductName));
|
||||
private static readonly AssistantSessionStateKey<string> DATA_SOURCE_HOSTNAME_STATE_KEY = new(nameof(dataSourceHostname));
|
||||
private static readonly AssistantSessionStateKey<int?> DATA_SOURCE_PORT_STATE_KEY = new(nameof(dataSourcePort));
|
||||
private static readonly AssistantSessionStateKey<bool> USER_TYPED_PORT_STATE_KEY = new(nameof(userTypedPort));
|
||||
private static readonly AssistantSessionStateKey<HashSet<Auth>> SELECTED_AUTHENTICATION_METHODS_STATE_KEY = new(nameof(selectedAuthenticationMethods));
|
||||
private static readonly AssistantSessionStateKey<string> AUTH_DESCRIPTION_STATE_KEY = new(nameof(authDescription));
|
||||
private static readonly AssistantSessionStateKey<OperatingSystem> SELECTED_OPERATING_SYSTEM_STATE_KEY = new(nameof(selectedOperatingSystem));
|
||||
private static readonly AssistantSessionStateKey<AllowedLLMProviders> ALLOWED_LLM_PROVIDERS_STATE_KEY = new(nameof(allowedLLMProviders));
|
||||
private static readonly AssistantSessionStateKey<List<EmbeddingInfo>> EMBEDDINGS_STATE_KEY = new(nameof(embeddings));
|
||||
private static readonly AssistantSessionStateKey<List<RetrievalInfo>> RETRIEVAL_PROCESSES_STATE_KEY = new(nameof(retrievalProcesses));
|
||||
private static readonly AssistantSessionStateKey<string> ADDITIONAL_LIBRARIES_STATE_KEY = new(nameof(additionalLibraries));
|
||||
private static readonly AssistantSessionStateKey<bool> WRITE_TO_FILESYSTEM_STATE_KEY = new(nameof(writeToFilesystem));
|
||||
private static readonly AssistantSessionStateKey<string> BASE_DIRECTORY_STATE_KEY = new(nameof(baseDirectory));
|
||||
private static readonly AssistantSessionStateKey<List<string>> PREVIOUSLY_GENERATED_FILES_STATE_KEY = new(nameof(previouslyGeneratedFiles));
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state)
|
||||
{
|
||||
state.Set(SELECTED_ERI_SERVER_STATE_KEY, this.selectedERIServer);
|
||||
state.Set(AUTO_SAVE_STATE_KEY, this.autoSave);
|
||||
state.Set(SERVER_NAME_STATE_KEY, this.serverName);
|
||||
state.Set(SERVER_DESCRIPTION_STATE_KEY, this.serverDescription);
|
||||
state.Set(SELECTED_ERI_VERSION_STATE_KEY, this.selectedERIVersion);
|
||||
state.Set(ERI_SPECIFICATION_STATE_KEY, this.eriSpecification);
|
||||
state.Set(SELECTED_PROGRAMMING_LANGUAGE_STATE_KEY, this.selectedProgrammingLanguage);
|
||||
state.Set(OTHER_PROGRAMMING_LANGUAGE_STATE_KEY, this.otherProgrammingLanguage);
|
||||
state.Set(SELECTED_DATA_SOURCE_STATE_KEY, this.selectedDataSource);
|
||||
state.Set(OTHER_DATA_SOURCE_STATE_KEY, this.otherDataSource);
|
||||
state.Set(DATA_SOURCE_PRODUCT_NAME_STATE_KEY, this.dataSourceProductName);
|
||||
state.Set(DATA_SOURCE_HOSTNAME_STATE_KEY, this.dataSourceHostname);
|
||||
state.Set(DATA_SOURCE_PORT_STATE_KEY, this.dataSourcePort);
|
||||
state.Set(USER_TYPED_PORT_STATE_KEY, this.userTypedPort);
|
||||
state.SetHashSet(SELECTED_AUTHENTICATION_METHODS_STATE_KEY, this.selectedAuthenticationMethods);
|
||||
state.Set(AUTH_DESCRIPTION_STATE_KEY, this.authDescription);
|
||||
state.Set(SELECTED_OPERATING_SYSTEM_STATE_KEY, this.selectedOperatingSystem);
|
||||
state.Set(ALLOWED_LLM_PROVIDERS_STATE_KEY, this.allowedLLMProviders);
|
||||
state.SetList(EMBEDDINGS_STATE_KEY, this.embeddings);
|
||||
state.SetList(RETRIEVAL_PROCESSES_STATE_KEY, this.retrievalProcesses);
|
||||
state.Set(ADDITIONAL_LIBRARIES_STATE_KEY, this.additionalLibraries);
|
||||
state.Set(WRITE_TO_FILESYSTEM_STATE_KEY, this.writeToFilesystem);
|
||||
state.Set(BASE_DIRECTORY_STATE_KEY, this.baseDirectory);
|
||||
state.SetList(PREVIOUSLY_GENERATED_FILES_STATE_KEY, this.previouslyGeneratedFiles);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RestoreCustomAssistantSessionState(AssistantSessionStateReader state)
|
||||
{
|
||||
state.Restore(SELECTED_ERI_SERVER_STATE_KEY, value => this.selectedERIServer = value);
|
||||
state.Restore(AUTO_SAVE_STATE_KEY, value => this.autoSave = value);
|
||||
state.Restore(SERVER_NAME_STATE_KEY, value => this.serverName = value);
|
||||
state.Restore(SERVER_DESCRIPTION_STATE_KEY, value => this.serverDescription = value);
|
||||
state.Restore(SELECTED_ERI_VERSION_STATE_KEY, value => this.selectedERIVersion = value);
|
||||
state.Restore(ERI_SPECIFICATION_STATE_KEY, value => this.eriSpecification = value);
|
||||
state.Restore(SELECTED_PROGRAMMING_LANGUAGE_STATE_KEY, value => this.selectedProgrammingLanguage = value);
|
||||
state.Restore(OTHER_PROGRAMMING_LANGUAGE_STATE_KEY, value => this.otherProgrammingLanguage = value);
|
||||
state.Restore(SELECTED_DATA_SOURCE_STATE_KEY, value => this.selectedDataSource = value);
|
||||
state.Restore(OTHER_DATA_SOURCE_STATE_KEY, value => this.otherDataSource = value);
|
||||
state.Restore(DATA_SOURCE_PRODUCT_NAME_STATE_KEY, value => this.dataSourceProductName = value);
|
||||
state.Restore(DATA_SOURCE_HOSTNAME_STATE_KEY, value => this.dataSourceHostname = value);
|
||||
state.Restore(DATA_SOURCE_PORT_STATE_KEY, value => this.dataSourcePort = value);
|
||||
state.Restore(USER_TYPED_PORT_STATE_KEY, value => this.userTypedPort = value);
|
||||
state.Restore(SELECTED_AUTHENTICATION_METHODS_STATE_KEY, value => this.selectedAuthenticationMethods = value);
|
||||
state.Restore(AUTH_DESCRIPTION_STATE_KEY, value => this.authDescription = value);
|
||||
state.Restore(SELECTED_OPERATING_SYSTEM_STATE_KEY, value => this.selectedOperatingSystem = value);
|
||||
state.Restore(ALLOWED_LLM_PROVIDERS_STATE_KEY, value => this.allowedLLMProviders = value);
|
||||
state.RestoreList(EMBEDDINGS_STATE_KEY, this.embeddings);
|
||||
state.RestoreList(RETRIEVAL_PROCESSES_STATE_KEY, this.retrievalProcesses);
|
||||
state.Restore(ADDITIONAL_LIBRARIES_STATE_KEY, value => this.additionalLibraries = value);
|
||||
state.Restore(WRITE_TO_FILESYSTEM_STATE_KEY, value => this.writeToFilesystem = value);
|
||||
state.Restore(BASE_DIRECTORY_STATE_KEY, value => this.baseDirectory = value);
|
||||
state.RestoreList(PREVIOUSLY_GENERATED_FILES_STATE_KEY, this.previouslyGeneratedFiles);
|
||||
}
|
||||
|
||||
private bool AreServerPresetsBlocked => !this.SettingsManager.ConfigurationData.ERI.PreselectOptions;
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
|
||||
namespace AIStudio.Assistants.GrammarSpelling;
|
||||
|
||||
@ -84,6 +85,28 @@ public partial class AssistantGrammarSpelling : AssistantBaseCore<SettingsDialog
|
||||
private CommonLanguages selectedTargetLanguage;
|
||||
private string customTargetLanguage = string.Empty;
|
||||
private string correctedText = string.Empty;
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_TEXT_STATE_KEY = new(nameof(inputText));
|
||||
private static readonly AssistantSessionStateKey<CommonLanguages> SELECTED_TARGET_LANGUAGE_STATE_KEY = new(nameof(selectedTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<string> CUSTOM_TARGET_LANGUAGE_STATE_KEY = new(nameof(customTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<string> CORRECTED_TEXT_STATE_KEY = new(nameof(correctedText));
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state)
|
||||
{
|
||||
state.Set(INPUT_TEXT_STATE_KEY, this.inputText);
|
||||
state.Set(SELECTED_TARGET_LANGUAGE_STATE_KEY, this.selectedTargetLanguage);
|
||||
state.Set(CUSTOM_TARGET_LANGUAGE_STATE_KEY, this.customTargetLanguage);
|
||||
state.Set(CORRECTED_TEXT_STATE_KEY, this.correctedText);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RestoreCustomAssistantSessionState(AssistantSessionStateReader state)
|
||||
{
|
||||
state.Restore(INPUT_TEXT_STATE_KEY, value => this.inputText = value);
|
||||
state.Restore(SELECTED_TARGET_LANGUAGE_STATE_KEY, value => this.selectedTargetLanguage = value);
|
||||
state.Restore(CUSTOM_TARGET_LANGUAGE_STATE_KEY, value => this.customTargetLanguage = value);
|
||||
state.Restore(CORRECTED_TEXT_STATE_KEY, value => this.correctedText = value);
|
||||
}
|
||||
|
||||
private string? ValidateText(string text)
|
||||
{
|
||||
@ -127,6 +150,13 @@ public partial class AssistantGrammarSpelling : AssistantBaseCore<SettingsDialog
|
||||
var time = this.AddUserRequest(this.inputText);
|
||||
|
||||
this.correctedText = await this.AddAIResponseAsync(time);
|
||||
await this.JsRuntime.GenerateAndShowDiff(this.inputText, this.correctedText);
|
||||
if (!this.IsAssistantComponentDisposed)
|
||||
await this.JsRuntime.GenerateAndShowDiff(this.inputText, this.correctedText);
|
||||
}
|
||||
|
||||
protected override async Task OnAssistantSessionAttachedAsync(AssistantSessionSnapshot snapshot)
|
||||
{
|
||||
if (!snapshot.IsActive && !string.IsNullOrWhiteSpace(this.inputText) && !string.IsNullOrWhiteSpace(this.correctedText))
|
||||
await this.JsRuntime.GenerateAndShowDiff(this.inputText, this.correctedText);
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@ using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
@ -117,6 +118,52 @@ public partial class AssistantI18N : AssistantBaseCore<SettingsDialogI18N>
|
||||
private Dictionary<string, string> removedContent = [];
|
||||
private Dictionary<string, string> localizedContent = [];
|
||||
private StringBuilder finalLuaCode = new();
|
||||
private static readonly AssistantSessionStateKey<CommonLanguages> SELECTED_TARGET_LANGUAGE_STATE_KEY = new(nameof(selectedTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<string> CUSTOM_TARGET_LANGUAGE_STATE_KEY = new(nameof(customTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<bool> IS_LOADING_STATE_KEY = new(nameof(isLoading));
|
||||
private static readonly AssistantSessionStateKey<string> LOADING_ISSUE_STATE_KEY = new(nameof(loadingIssue));
|
||||
private static readonly AssistantSessionStateKey<bool> LOCALIZATION_POSSIBLE_STATE_KEY = new(nameof(localizationPossible));
|
||||
private static readonly AssistantSessionStateKey<string> SEARCH_STRING_STATE_KEY = new(nameof(searchString));
|
||||
private static readonly AssistantSessionStateKey<Guid> SELECTED_LANGUAGE_PLUGIN_ID_STATE_KEY = new(nameof(selectedLanguagePluginId));
|
||||
private static readonly AssistantSessionStateKey<ILanguagePlugin?> SELECTED_LANGUAGE_PLUGIN_STATE_KEY = new(nameof(selectedLanguagePlugin));
|
||||
private static readonly AssistantSessionStateKey<Dictionary<string, string>> ADDED_CONTENT_STATE_KEY = new(nameof(addedContent));
|
||||
private static readonly AssistantSessionStateKey<Dictionary<string, string>> REMOVED_CONTENT_STATE_KEY = new(nameof(removedContent));
|
||||
private static readonly AssistantSessionStateKey<Dictionary<string, string>> LOCALIZED_CONTENT_STATE_KEY = new(nameof(localizedContent));
|
||||
private static readonly AssistantSessionStateKey<string> FINAL_LUA_CODE_STATE_KEY = new(nameof(finalLuaCode));
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state)
|
||||
{
|
||||
state.Set(SELECTED_TARGET_LANGUAGE_STATE_KEY, this.selectedTargetLanguage);
|
||||
state.Set(CUSTOM_TARGET_LANGUAGE_STATE_KEY, this.customTargetLanguage);
|
||||
state.Set(IS_LOADING_STATE_KEY, this.isLoading);
|
||||
state.Set(LOADING_ISSUE_STATE_KEY, this.loadingIssue);
|
||||
state.Set(LOCALIZATION_POSSIBLE_STATE_KEY, this.localizationPossible);
|
||||
state.Set(SEARCH_STRING_STATE_KEY, this.searchString);
|
||||
state.Set(SELECTED_LANGUAGE_PLUGIN_ID_STATE_KEY, this.selectedLanguagePluginId);
|
||||
state.Set(SELECTED_LANGUAGE_PLUGIN_STATE_KEY, this.selectedLanguagePlugin);
|
||||
state.SetDictionary(ADDED_CONTENT_STATE_KEY, this.addedContent);
|
||||
state.SetDictionary(REMOVED_CONTENT_STATE_KEY, this.removedContent);
|
||||
state.SetDictionary(LOCALIZED_CONTENT_STATE_KEY, this.localizedContent);
|
||||
state.SetStringBuilder(FINAL_LUA_CODE_STATE_KEY, this.finalLuaCode);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RestoreCustomAssistantSessionState(AssistantSessionStateReader state)
|
||||
{
|
||||
state.Restore(SELECTED_TARGET_LANGUAGE_STATE_KEY, value => this.selectedTargetLanguage = value);
|
||||
state.Restore(CUSTOM_TARGET_LANGUAGE_STATE_KEY, value => this.customTargetLanguage = value);
|
||||
state.Restore(IS_LOADING_STATE_KEY, value => this.isLoading = value);
|
||||
state.Restore(LOADING_ISSUE_STATE_KEY, value => this.loadingIssue = value);
|
||||
state.Restore(LOCALIZATION_POSSIBLE_STATE_KEY, value => this.localizationPossible = value);
|
||||
state.Restore(SEARCH_STRING_STATE_KEY, value => this.searchString = value);
|
||||
state.Restore(SELECTED_LANGUAGE_PLUGIN_ID_STATE_KEY, value => this.selectedLanguagePluginId = value);
|
||||
state.Restore(SELECTED_LANGUAGE_PLUGIN_STATE_KEY, value => this.selectedLanguagePlugin = value);
|
||||
state.RestoreDictionary(ADDED_CONTENT_STATE_KEY, this.addedContent);
|
||||
state.RestoreDictionary(REMOVED_CONTENT_STATE_KEY, this.removedContent);
|
||||
state.RestoreDictionary(LOCALIZED_CONTENT_STATE_KEY, this.localizedContent);
|
||||
state.RestoreStringBuilder(FINAL_LUA_CODE_STATE_KEY, this.finalLuaCode);
|
||||
}
|
||||
|
||||
#region Overrides of AssistantBase<SettingsDialogI18N>
|
||||
|
||||
|
||||
@ -310,6 +310,12 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ASSISTANTBASE::T180921696"] = "Reset"
|
||||
-- Please select a provider.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ASSISTANTBASE::T1809312323"] = "Please select a provider."
|
||||
|
||||
-- The assistant failed. The message is: '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ASSISTANTBASE::T1833836281"] = "The assistant failed. The message is: '{0}'"
|
||||
|
||||
-- This assistant is already running. AI Studio opens the running session instead.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ASSISTANTBASE::T2575715765"] = "This assistant is already running. AI Studio opens the running session instead."
|
||||
|
||||
-- Assistant - {0}
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::ASSISTANTBASE::T3043922"] = "Assistant - {0}"
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
|
||||
namespace AIStudio.Assistants.IconFinder;
|
||||
|
||||
@ -56,6 +57,22 @@ public partial class AssistantIconFinder : AssistantBaseCore<SettingsDialogIconF
|
||||
|
||||
private string inputContext = string.Empty;
|
||||
private IconSources selectedIconSource;
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_CONTEXT_STATE_KEY = new(nameof(inputContext));
|
||||
private static readonly AssistantSessionStateKey<IconSources> SELECTED_ICON_SOURCE_STATE_KEY = new(nameof(selectedIconSource));
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state)
|
||||
{
|
||||
state.Set(INPUT_CONTEXT_STATE_KEY, this.inputContext);
|
||||
state.Set(SELECTED_ICON_SOURCE_STATE_KEY, this.selectedIconSource);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RestoreCustomAssistantSessionState(AssistantSessionStateReader state)
|
||||
{
|
||||
state.Restore(INPUT_CONTEXT_STATE_KEY, value => this.inputContext = value);
|
||||
state.Restore(SELECTED_ICON_SOURCE_STATE_KEY, value => this.selectedIconSource = value);
|
||||
}
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
|
||||
namespace AIStudio.Assistants.JobPosting;
|
||||
|
||||
@ -128,6 +129,49 @@ public partial class AssistantJobPostings : AssistantBaseCore<SettingsDialogJobP
|
||||
private string inputCountryLegalFramework = string.Empty;
|
||||
private CommonLanguages selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
private string customTargetLanguage = string.Empty;
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_MANDATORY_INFORMATION_STATE_KEY = new(nameof(inputMandatoryInformation));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_JOB_DESCRIPTION_STATE_KEY = new(nameof(inputJobDescription));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_QUALIFICATIONS_STATE_KEY = new(nameof(inputQualifications));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_RESPONSIBILITIES_STATE_KEY = new(nameof(inputResponsibilities));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_COMPANY_NAME_STATE_KEY = new(nameof(inputCompanyName));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_ENTRY_DATE_STATE_KEY = new(nameof(inputEntryDate));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_VALID_UNTIL_STATE_KEY = new(nameof(inputValidUntil));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_WORK_LOCATION_STATE_KEY = new(nameof(inputWorkLocation));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_COUNTRY_LEGAL_FRAMEWORK_STATE_KEY = new(nameof(inputCountryLegalFramework));
|
||||
private static readonly AssistantSessionStateKey<CommonLanguages> SELECTED_TARGET_LANGUAGE_STATE_KEY = new(nameof(selectedTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<string> CUSTOM_TARGET_LANGUAGE_STATE_KEY = new(nameof(customTargetLanguage));
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state)
|
||||
{
|
||||
state.Set(INPUT_MANDATORY_INFORMATION_STATE_KEY, this.inputMandatoryInformation);
|
||||
state.Set(INPUT_JOB_DESCRIPTION_STATE_KEY, this.inputJobDescription);
|
||||
state.Set(INPUT_QUALIFICATIONS_STATE_KEY, this.inputQualifications);
|
||||
state.Set(INPUT_RESPONSIBILITIES_STATE_KEY, this.inputResponsibilities);
|
||||
state.Set(INPUT_COMPANY_NAME_STATE_KEY, this.inputCompanyName);
|
||||
state.Set(INPUT_ENTRY_DATE_STATE_KEY, this.inputEntryDate);
|
||||
state.Set(INPUT_VALID_UNTIL_STATE_KEY, this.inputValidUntil);
|
||||
state.Set(INPUT_WORK_LOCATION_STATE_KEY, this.inputWorkLocation);
|
||||
state.Set(INPUT_COUNTRY_LEGAL_FRAMEWORK_STATE_KEY, this.inputCountryLegalFramework);
|
||||
state.Set(SELECTED_TARGET_LANGUAGE_STATE_KEY, this.selectedTargetLanguage);
|
||||
state.Set(CUSTOM_TARGET_LANGUAGE_STATE_KEY, this.customTargetLanguage);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RestoreCustomAssistantSessionState(AssistantSessionStateReader state)
|
||||
{
|
||||
state.Restore(INPUT_MANDATORY_INFORMATION_STATE_KEY, value => this.inputMandatoryInformation = value);
|
||||
state.Restore(INPUT_JOB_DESCRIPTION_STATE_KEY, value => this.inputJobDescription = value);
|
||||
state.Restore(INPUT_QUALIFICATIONS_STATE_KEY, value => this.inputQualifications = value);
|
||||
state.Restore(INPUT_RESPONSIBILITIES_STATE_KEY, value => this.inputResponsibilities = value);
|
||||
state.Restore(INPUT_COMPANY_NAME_STATE_KEY, value => this.inputCompanyName = value);
|
||||
state.Restore(INPUT_ENTRY_DATE_STATE_KEY, value => this.inputEntryDate = value);
|
||||
state.Restore(INPUT_VALID_UNTIL_STATE_KEY, value => this.inputValidUntil = value);
|
||||
state.Restore(INPUT_WORK_LOCATION_STATE_KEY, value => this.inputWorkLocation = value);
|
||||
state.Restore(INPUT_COUNTRY_LEGAL_FRAMEWORK_STATE_KEY, value => this.inputCountryLegalFramework = value);
|
||||
state.Restore(SELECTED_TARGET_LANGUAGE_STATE_KEY, value => this.selectedTargetLanguage = value);
|
||||
state.Restore(CUSTOM_TARGET_LANGUAGE_STATE_KEY, value => this.customTargetLanguage = value);
|
||||
}
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
|
||||
namespace AIStudio.Assistants.LegalCheck;
|
||||
|
||||
@ -59,6 +60,31 @@ public partial class AssistantLegalCheck : AssistantBaseCore<SettingsDialogLegal
|
||||
private bool isAgentRunning;
|
||||
private string inputLegalDocument = string.Empty;
|
||||
private string inputQuestions = string.Empty;
|
||||
private static readonly AssistantSessionStateKey<bool> SHOW_WEB_CONTENT_READER_STATE_KEY = new(nameof(showWebContentReader));
|
||||
private static readonly AssistantSessionStateKey<bool> USE_CONTENT_CLEANER_AGENT_STATE_KEY = new(nameof(useContentCleanerAgent));
|
||||
private static readonly AssistantSessionStateKey<bool> IS_AGENT_RUNNING_STATE_KEY = new(nameof(isAgentRunning));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_LEGAL_DOCUMENT_STATE_KEY = new(nameof(inputLegalDocument));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_QUESTIONS_STATE_KEY = new(nameof(inputQuestions));
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state)
|
||||
{
|
||||
state.Set(SHOW_WEB_CONTENT_READER_STATE_KEY, this.showWebContentReader);
|
||||
state.Set(USE_CONTENT_CLEANER_AGENT_STATE_KEY, this.useContentCleanerAgent);
|
||||
state.Set(IS_AGENT_RUNNING_STATE_KEY, this.isAgentRunning);
|
||||
state.Set(INPUT_LEGAL_DOCUMENT_STATE_KEY, this.inputLegalDocument);
|
||||
state.Set(INPUT_QUESTIONS_STATE_KEY, this.inputQuestions);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RestoreCustomAssistantSessionState(AssistantSessionStateReader state)
|
||||
{
|
||||
state.Restore(SHOW_WEB_CONTENT_READER_STATE_KEY, value => this.showWebContentReader = value);
|
||||
state.Restore(USE_CONTENT_CLEANER_AGENT_STATE_KEY, value => this.useContentCleanerAgent = value);
|
||||
state.Restore(IS_AGENT_RUNNING_STATE_KEY, value => this.isAgentRunning = value);
|
||||
state.Restore(INPUT_LEGAL_DOCUMENT_STATE_KEY, value => this.inputLegalDocument = value);
|
||||
state.Restore(INPUT_QUESTIONS_STATE_KEY, value => this.inputQuestions = value);
|
||||
}
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
|
||||
namespace AIStudio.Assistants.MyTasks;
|
||||
|
||||
@ -59,6 +60,25 @@ public partial class AssistantMyTasks : AssistantBaseCore<SettingsDialogMyTasks>
|
||||
private string inputText = string.Empty;
|
||||
private CommonLanguages selectedTargetLanguage = CommonLanguages.AS_IS;
|
||||
private string customTargetLanguage = string.Empty;
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_TEXT_STATE_KEY = new(nameof(inputText));
|
||||
private static readonly AssistantSessionStateKey<CommonLanguages> SELECTED_TARGET_LANGUAGE_STATE_KEY = new(nameof(selectedTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<string> CUSTOM_TARGET_LANGUAGE_STATE_KEY = new(nameof(customTargetLanguage));
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state)
|
||||
{
|
||||
state.Set(INPUT_TEXT_STATE_KEY, this.inputText);
|
||||
state.Set(SELECTED_TARGET_LANGUAGE_STATE_KEY, this.selectedTargetLanguage);
|
||||
state.Set(CUSTOM_TARGET_LANGUAGE_STATE_KEY, this.customTargetLanguage);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RestoreCustomAssistantSessionState(AssistantSessionStateReader state)
|
||||
{
|
||||
state.Restore(INPUT_TEXT_STATE_KEY, value => this.inputText = value);
|
||||
state.Restore(SELECTED_TARGET_LANGUAGE_STATE_KEY, value => this.selectedTargetLanguage = value);
|
||||
state.Restore(CUSTOM_TARGET_LANGUAGE_STATE_KEY, value => this.customTargetLanguage = value);
|
||||
}
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ using System.Text.RegularExpressions;
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
#if !DEBUG
|
||||
@ -176,6 +177,67 @@ public partial class AssistantPromptOptimizer : AssistantBaseCore<SettingsDialog
|
||||
private string recStructureMarkers = string.Empty;
|
||||
private string recRoleDefinition = string.Empty;
|
||||
private string recLanguageChoice = string.Empty;
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_PROMPT_STATE_KEY = new(nameof(inputPrompt));
|
||||
private static readonly AssistantSessionStateKey<CommonLanguages> SELECTED_TARGET_LANGUAGE_STATE_KEY = new(nameof(selectedTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<string> CUSTOM_TARGET_LANGUAGE_STATE_KEY = new(nameof(customTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<string> IMPORTANT_ASPECTS_STATE_KEY = new(nameof(importantAspects));
|
||||
private static readonly AssistantSessionStateKey<bool> USE_CUSTOM_PROMPT_GUIDE_STATE_KEY = new(nameof(useCustomPromptGuide));
|
||||
private static readonly AssistantSessionStateKey<HashSet<FileAttachment>> CUSTOM_PROMPT_GUIDE_FILES_STATE_KEY = new(nameof(customPromptGuideFiles));
|
||||
private static readonly AssistantSessionStateKey<string> CURRENT_CUSTOM_PROMPT_GUIDE_PATH_STATE_KEY = new(nameof(currentCustomPromptGuidePath));
|
||||
private static readonly AssistantSessionStateKey<string> CUSTOM_PROMPTING_GUIDELINE_CONTENT_STATE_KEY = new(nameof(customPromptingGuidelineContent));
|
||||
private static readonly AssistantSessionStateKey<bool> IS_LOADING_CUSTOM_PROMPT_GUIDE_STATE_KEY = new(nameof(isLoadingCustomPromptGuide));
|
||||
private static readonly AssistantSessionStateKey<bool> HAS_UPDATED_DEFAULT_RECOMMENDATIONS_STATE_KEY = new(nameof(hasUpdatedDefaultRecommendations));
|
||||
private static readonly AssistantSessionStateKey<string> OPTIMIZED_PROMPT_STATE_KEY = new(nameof(optimizedPrompt));
|
||||
private static readonly AssistantSessionStateKey<string> REC_CLARITY_DIRECTNESS_STATE_KEY = new(nameof(recClarityDirectness));
|
||||
private static readonly AssistantSessionStateKey<string> REC_EXAMPLES_CONTEXT_STATE_KEY = new(nameof(recExamplesContext));
|
||||
private static readonly AssistantSessionStateKey<string> REC_SEQUENTIAL_STEPS_STATE_KEY = new(nameof(recSequentialSteps));
|
||||
private static readonly AssistantSessionStateKey<string> REC_STRUCTURE_MARKERS_STATE_KEY = new(nameof(recStructureMarkers));
|
||||
private static readonly AssistantSessionStateKey<string> REC_ROLE_DEFINITION_STATE_KEY = new(nameof(recRoleDefinition));
|
||||
private static readonly AssistantSessionStateKey<string> REC_LANGUAGE_CHOICE_STATE_KEY = new(nameof(recLanguageChoice));
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state)
|
||||
{
|
||||
state.Set(INPUT_PROMPT_STATE_KEY, this.inputPrompt);
|
||||
state.Set(SELECTED_TARGET_LANGUAGE_STATE_KEY, this.selectedTargetLanguage);
|
||||
state.Set(CUSTOM_TARGET_LANGUAGE_STATE_KEY, this.customTargetLanguage);
|
||||
state.Set(IMPORTANT_ASPECTS_STATE_KEY, this.importantAspects);
|
||||
state.Set(USE_CUSTOM_PROMPT_GUIDE_STATE_KEY, this.useCustomPromptGuide);
|
||||
state.SetHashSet(CUSTOM_PROMPT_GUIDE_FILES_STATE_KEY, this.customPromptGuideFiles);
|
||||
state.Set(CURRENT_CUSTOM_PROMPT_GUIDE_PATH_STATE_KEY, this.currentCustomPromptGuidePath);
|
||||
state.Set(CUSTOM_PROMPTING_GUIDELINE_CONTENT_STATE_KEY, this.customPromptingGuidelineContent);
|
||||
state.Set(IS_LOADING_CUSTOM_PROMPT_GUIDE_STATE_KEY, this.isLoadingCustomPromptGuide);
|
||||
state.Set(HAS_UPDATED_DEFAULT_RECOMMENDATIONS_STATE_KEY, this.hasUpdatedDefaultRecommendations);
|
||||
state.Set(OPTIMIZED_PROMPT_STATE_KEY, this.optimizedPrompt);
|
||||
state.Set(REC_CLARITY_DIRECTNESS_STATE_KEY, this.recClarityDirectness);
|
||||
state.Set(REC_EXAMPLES_CONTEXT_STATE_KEY, this.recExamplesContext);
|
||||
state.Set(REC_SEQUENTIAL_STEPS_STATE_KEY, this.recSequentialSteps);
|
||||
state.Set(REC_STRUCTURE_MARKERS_STATE_KEY, this.recStructureMarkers);
|
||||
state.Set(REC_ROLE_DEFINITION_STATE_KEY, this.recRoleDefinition);
|
||||
state.Set(REC_LANGUAGE_CHOICE_STATE_KEY, this.recLanguageChoice);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RestoreCustomAssistantSessionState(AssistantSessionStateReader state)
|
||||
{
|
||||
state.Restore(INPUT_PROMPT_STATE_KEY, value => this.inputPrompt = value);
|
||||
state.Restore(SELECTED_TARGET_LANGUAGE_STATE_KEY, value => this.selectedTargetLanguage = value);
|
||||
state.Restore(CUSTOM_TARGET_LANGUAGE_STATE_KEY, value => this.customTargetLanguage = value);
|
||||
state.Restore(IMPORTANT_ASPECTS_STATE_KEY, value => this.importantAspects = value);
|
||||
state.Restore(USE_CUSTOM_PROMPT_GUIDE_STATE_KEY, value => this.useCustomPromptGuide = value);
|
||||
state.RestoreHashSet(CUSTOM_PROMPT_GUIDE_FILES_STATE_KEY, this.customPromptGuideFiles);
|
||||
state.Restore(CURRENT_CUSTOM_PROMPT_GUIDE_PATH_STATE_KEY, value => this.currentCustomPromptGuidePath = value);
|
||||
state.Restore(CUSTOM_PROMPTING_GUIDELINE_CONTENT_STATE_KEY, value => this.customPromptingGuidelineContent = value);
|
||||
state.Restore(IS_LOADING_CUSTOM_PROMPT_GUIDE_STATE_KEY, value => this.isLoadingCustomPromptGuide = value);
|
||||
state.Restore(HAS_UPDATED_DEFAULT_RECOMMENDATIONS_STATE_KEY, value => this.hasUpdatedDefaultRecommendations = value);
|
||||
state.Restore(OPTIMIZED_PROMPT_STATE_KEY, value => this.optimizedPrompt = value);
|
||||
state.Restore(REC_CLARITY_DIRECTNESS_STATE_KEY, value => this.recClarityDirectness = value);
|
||||
state.Restore(REC_EXAMPLES_CONTEXT_STATE_KEY, value => this.recExamplesContext = value);
|
||||
state.Restore(REC_SEQUENTIAL_STEPS_STATE_KEY, value => this.recSequentialSteps = value);
|
||||
state.Restore(REC_STRUCTURE_MARKERS_STATE_KEY, value => this.recStructureMarkers = value);
|
||||
state.Restore(REC_ROLE_DEFINITION_STATE_KEY, value => this.recRoleDefinition = value);
|
||||
state.Restore(REC_LANGUAGE_CHOICE_STATE_KEY, value => this.recLanguageChoice = value);
|
||||
}
|
||||
|
||||
private bool ShowUpdatedPromptGuidelinesIndicator => !this.useCustomPromptGuide && this.hasUpdatedDefaultRecommendations;
|
||||
private bool CanPreviewCustomPromptGuide => this.useCustomPromptGuide && this.customPromptGuideFiles.Count > 0;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
|
||||
namespace AIStudio.Assistants.RewriteImprove;
|
||||
|
||||
@ -91,6 +92,34 @@ public partial class AssistantRewriteImprove : AssistantBaseCore<SettingsDialogR
|
||||
private string rewrittenText = string.Empty;
|
||||
private WritingStyles selectedWritingStyle;
|
||||
private SentenceStructure selectedSentenceStructure;
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_TEXT_STATE_KEY = new(nameof(inputText));
|
||||
private static readonly AssistantSessionStateKey<CommonLanguages> SELECTED_TARGET_LANGUAGE_STATE_KEY = new(nameof(selectedTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<string> CUSTOM_TARGET_LANGUAGE_STATE_KEY = new(nameof(customTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<string> REWRITTEN_TEXT_STATE_KEY = new(nameof(rewrittenText));
|
||||
private static readonly AssistantSessionStateKey<WritingStyles> SELECTED_WRITING_STYLE_STATE_KEY = new(nameof(selectedWritingStyle));
|
||||
private static readonly AssistantSessionStateKey<SentenceStructure> SELECTED_SENTENCE_STRUCTURE_STATE_KEY = new(nameof(selectedSentenceStructure));
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state)
|
||||
{
|
||||
state.Set(INPUT_TEXT_STATE_KEY, this.inputText);
|
||||
state.Set(SELECTED_TARGET_LANGUAGE_STATE_KEY, this.selectedTargetLanguage);
|
||||
state.Set(CUSTOM_TARGET_LANGUAGE_STATE_KEY, this.customTargetLanguage);
|
||||
state.Set(REWRITTEN_TEXT_STATE_KEY, this.rewrittenText);
|
||||
state.Set(SELECTED_WRITING_STYLE_STATE_KEY, this.selectedWritingStyle);
|
||||
state.Set(SELECTED_SENTENCE_STRUCTURE_STATE_KEY, this.selectedSentenceStructure);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RestoreCustomAssistantSessionState(AssistantSessionStateReader state)
|
||||
{
|
||||
state.Restore(INPUT_TEXT_STATE_KEY, value => this.inputText = value);
|
||||
state.Restore(SELECTED_TARGET_LANGUAGE_STATE_KEY, value => this.selectedTargetLanguage = value);
|
||||
state.Restore(CUSTOM_TARGET_LANGUAGE_STATE_KEY, value => this.customTargetLanguage = value);
|
||||
state.Restore(REWRITTEN_TEXT_STATE_KEY, value => this.rewrittenText = value);
|
||||
state.Restore(SELECTED_WRITING_STYLE_STATE_KEY, value => this.selectedWritingStyle = value);
|
||||
state.Restore(SELECTED_SENTENCE_STRUCTURE_STATE_KEY, value => this.selectedSentenceStructure = value);
|
||||
}
|
||||
|
||||
private string? ValidateText(string text)
|
||||
{
|
||||
@ -134,6 +163,13 @@ public partial class AssistantRewriteImprove : AssistantBaseCore<SettingsDialogR
|
||||
var time = this.AddUserRequest(this.inputText);
|
||||
|
||||
this.rewrittenText = await this.AddAIResponseAsync(time);
|
||||
await this.JsRuntime.GenerateAndShowDiff(this.inputText, this.rewrittenText);
|
||||
if (!this.IsAssistantComponentDisposed)
|
||||
await this.JsRuntime.GenerateAndShowDiff(this.inputText, this.rewrittenText);
|
||||
}
|
||||
|
||||
protected override async Task OnAssistantSessionAttachedAsync(AssistantSessionSnapshot snapshot)
|
||||
{
|
||||
if (!snapshot.IsActive && !string.IsNullOrWhiteSpace(this.inputText) && !string.IsNullOrWhiteSpace(this.rewrittenText))
|
||||
await this.JsRuntime.GenerateAndShowDiff(this.inputText, this.rewrittenText);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
using System.Text;
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
|
||||
namespace AIStudio.Assistants.SlideBuilder;
|
||||
|
||||
@ -197,6 +198,58 @@ public partial class SlideAssistant : AssistantBaseCore<SettingsDialogSlideBuild
|
||||
private int calculatedNumberOfSlides;
|
||||
private string importantAspects = string.Empty;
|
||||
private HashSet<FileAttachment> loadedDocumentPaths = [];
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_TITLE_STATE_KEY = new(nameof(inputTitle));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_CONTENT_STATE_KEY = new(nameof(inputContent));
|
||||
private static readonly AssistantSessionStateKey<string> CUSTOM_TARGET_LANGUAGE_STATE_KEY = new(nameof(customTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<AudienceProfile> SELECTED_AUDIENCE_PROFILE_STATE_KEY = new(nameof(selectedAudienceProfile));
|
||||
private static readonly AssistantSessionStateKey<AudienceAgeGroup> SELECTED_AUDIENCE_AGE_GROUP_STATE_KEY = new(nameof(selectedAudienceAgeGroup));
|
||||
private static readonly AssistantSessionStateKey<AudienceOrganizationalLevel> SELECTED_AUDIENCE_ORGANIZATIONAL_LEVEL_STATE_KEY = new(nameof(selectedAudienceOrganizationalLevel));
|
||||
private static readonly AssistantSessionStateKey<AudienceExpertise> SELECTED_AUDIENCE_EXPERTISE_STATE_KEY = new(nameof(selectedAudienceExpertise));
|
||||
private static readonly AssistantSessionStateKey<CommonLanguages> SELECTED_TARGET_LANGUAGE_STATE_KEY = new(nameof(selectedTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<int> NUMBER_OF_SHEETS_STATE_KEY = new(nameof(numberOfSheets));
|
||||
private static readonly AssistantSessionStateKey<int> NUMBER_OF_BULLET_POINTS_STATE_KEY = new(nameof(numberOfBulletPoints));
|
||||
private static readonly AssistantSessionStateKey<int> TIME_SPECIFICATION_STATE_KEY = new(nameof(timeSpecification));
|
||||
private static readonly AssistantSessionStateKey<int> CALCULATED_NUMBER_OF_SLIDES_STATE_KEY = new(nameof(calculatedNumberOfSlides));
|
||||
private static readonly AssistantSessionStateKey<string> IMPORTANT_ASPECTS_STATE_KEY = new(nameof(importantAspects));
|
||||
private static readonly AssistantSessionStateKey<HashSet<FileAttachment>> LOADED_DOCUMENT_PATHS_STATE_KEY = new(nameof(loadedDocumentPaths));
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state)
|
||||
{
|
||||
state.Set(INPUT_TITLE_STATE_KEY, this.inputTitle);
|
||||
state.Set(INPUT_CONTENT_STATE_KEY, this.inputContent);
|
||||
state.Set(CUSTOM_TARGET_LANGUAGE_STATE_KEY, this.customTargetLanguage);
|
||||
state.Set(SELECTED_AUDIENCE_PROFILE_STATE_KEY, this.selectedAudienceProfile);
|
||||
state.Set(SELECTED_AUDIENCE_AGE_GROUP_STATE_KEY, this.selectedAudienceAgeGroup);
|
||||
state.Set(SELECTED_AUDIENCE_ORGANIZATIONAL_LEVEL_STATE_KEY, this.selectedAudienceOrganizationalLevel);
|
||||
state.Set(SELECTED_AUDIENCE_EXPERTISE_STATE_KEY, this.selectedAudienceExpertise);
|
||||
state.Set(SELECTED_TARGET_LANGUAGE_STATE_KEY, this.selectedTargetLanguage);
|
||||
state.Set(NUMBER_OF_SHEETS_STATE_KEY, this.numberOfSheets);
|
||||
state.Set(NUMBER_OF_BULLET_POINTS_STATE_KEY, this.numberOfBulletPoints);
|
||||
state.Set(TIME_SPECIFICATION_STATE_KEY, this.timeSpecification);
|
||||
state.Set(CALCULATED_NUMBER_OF_SLIDES_STATE_KEY, this.calculatedNumberOfSlides);
|
||||
state.Set(IMPORTANT_ASPECTS_STATE_KEY, this.importantAspects);
|
||||
state.SetHashSet(LOADED_DOCUMENT_PATHS_STATE_KEY, this.loadedDocumentPaths);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RestoreCustomAssistantSessionState(AssistantSessionStateReader state)
|
||||
{
|
||||
state.Restore(INPUT_TITLE_STATE_KEY, value => this.inputTitle = value);
|
||||
state.Restore(INPUT_CONTENT_STATE_KEY, value => this.inputContent = value);
|
||||
state.Restore(CUSTOM_TARGET_LANGUAGE_STATE_KEY, value => this.customTargetLanguage = value);
|
||||
state.Restore(SELECTED_AUDIENCE_PROFILE_STATE_KEY, value => this.selectedAudienceProfile = value);
|
||||
state.Restore(SELECTED_AUDIENCE_AGE_GROUP_STATE_KEY, value => this.selectedAudienceAgeGroup = value);
|
||||
state.Restore(SELECTED_AUDIENCE_ORGANIZATIONAL_LEVEL_STATE_KEY, value => this.selectedAudienceOrganizationalLevel = value);
|
||||
state.Restore(SELECTED_AUDIENCE_EXPERTISE_STATE_KEY, value => this.selectedAudienceExpertise = value);
|
||||
state.Restore(SELECTED_TARGET_LANGUAGE_STATE_KEY, value => this.selectedTargetLanguage = value);
|
||||
state.Restore(NUMBER_OF_SHEETS_STATE_KEY, value => this.numberOfSheets = value);
|
||||
state.Restore(NUMBER_OF_BULLET_POINTS_STATE_KEY, value => this.numberOfBulletPoints = value);
|
||||
state.Restore(TIME_SPECIFICATION_STATE_KEY, value => this.timeSpecification = value);
|
||||
state.Restore(CALCULATED_NUMBER_OF_SLIDES_STATE_KEY, value => this.calculatedNumberOfSlides = value);
|
||||
state.Restore(IMPORTANT_ASPECTS_STATE_KEY, value => this.importantAspects = value);
|
||||
state.RestoreHashSet(LOADED_DOCUMENT_PATHS_STATE_KEY, this.loadedDocumentPaths);
|
||||
}
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
|
||||
namespace AIStudio.Assistants.Synonym;
|
||||
|
||||
@ -103,6 +104,28 @@ public partial class AssistantSynonyms : AssistantBaseCore<SettingsDialogSynonym
|
||||
private string inputContext = string.Empty;
|
||||
private CommonLanguages selectedLanguage;
|
||||
private string customTargetLanguage = string.Empty;
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_TEXT_STATE_KEY = new(nameof(inputText));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_CONTEXT_STATE_KEY = new(nameof(inputContext));
|
||||
private static readonly AssistantSessionStateKey<CommonLanguages> SELECTED_LANGUAGE_STATE_KEY = new(nameof(selectedLanguage));
|
||||
private static readonly AssistantSessionStateKey<string> CUSTOM_TARGET_LANGUAGE_STATE_KEY = new(nameof(customTargetLanguage));
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state)
|
||||
{
|
||||
state.Set(INPUT_TEXT_STATE_KEY, this.inputText);
|
||||
state.Set(INPUT_CONTEXT_STATE_KEY, this.inputContext);
|
||||
state.Set(SELECTED_LANGUAGE_STATE_KEY, this.selectedLanguage);
|
||||
state.Set(CUSTOM_TARGET_LANGUAGE_STATE_KEY, this.customTargetLanguage);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RestoreCustomAssistantSessionState(AssistantSessionStateReader state)
|
||||
{
|
||||
state.Restore(INPUT_TEXT_STATE_KEY, value => this.inputText = value);
|
||||
state.Restore(INPUT_CONTEXT_STATE_KEY, value => this.inputContext = value);
|
||||
state.Restore(SELECTED_LANGUAGE_STATE_KEY, value => this.selectedLanguage = value);
|
||||
state.Restore(CUSTOM_TARGET_LANGUAGE_STATE_KEY, value => this.customTargetLanguage = value);
|
||||
}
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
|
||||
namespace AIStudio.Assistants.TextSummarizer;
|
||||
|
||||
@ -72,6 +73,43 @@ public partial class AssistantTextSummarizer : AssistantBaseCore<SettingsDialogT
|
||||
private Complexity selectedComplexity;
|
||||
private string expertInField = string.Empty;
|
||||
private string importantAspects = string.Empty;
|
||||
private static readonly AssistantSessionStateKey<bool> SHOW_WEB_CONTENT_READER_STATE_KEY = new(nameof(showWebContentReader));
|
||||
private static readonly AssistantSessionStateKey<bool> USE_CONTENT_CLEANER_AGENT_STATE_KEY = new(nameof(useContentCleanerAgent));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_TEXT_STATE_KEY = new(nameof(inputText));
|
||||
private static readonly AssistantSessionStateKey<bool> IS_AGENT_RUNNING_STATE_KEY = new(nameof(isAgentRunning));
|
||||
private static readonly AssistantSessionStateKey<CommonLanguages> SELECTED_TARGET_LANGUAGE_STATE_KEY = new(nameof(selectedTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<string> CUSTOM_TARGET_LANGUAGE_STATE_KEY = new(nameof(customTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<Complexity> SELECTED_COMPLEXITY_STATE_KEY = new(nameof(selectedComplexity));
|
||||
private static readonly AssistantSessionStateKey<string> EXPERT_IN_FIELD_STATE_KEY = new(nameof(expertInField));
|
||||
private static readonly AssistantSessionStateKey<string> IMPORTANT_ASPECTS_STATE_KEY = new(nameof(importantAspects));
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state)
|
||||
{
|
||||
state.Set(SHOW_WEB_CONTENT_READER_STATE_KEY, this.showWebContentReader);
|
||||
state.Set(USE_CONTENT_CLEANER_AGENT_STATE_KEY, this.useContentCleanerAgent);
|
||||
state.Set(INPUT_TEXT_STATE_KEY, this.inputText);
|
||||
state.Set(IS_AGENT_RUNNING_STATE_KEY, this.isAgentRunning);
|
||||
state.Set(SELECTED_TARGET_LANGUAGE_STATE_KEY, this.selectedTargetLanguage);
|
||||
state.Set(CUSTOM_TARGET_LANGUAGE_STATE_KEY, this.customTargetLanguage);
|
||||
state.Set(SELECTED_COMPLEXITY_STATE_KEY, this.selectedComplexity);
|
||||
state.Set(EXPERT_IN_FIELD_STATE_KEY, this.expertInField);
|
||||
state.Set(IMPORTANT_ASPECTS_STATE_KEY, this.importantAspects);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RestoreCustomAssistantSessionState(AssistantSessionStateReader state)
|
||||
{
|
||||
state.Restore(SHOW_WEB_CONTENT_READER_STATE_KEY, value => this.showWebContentReader = value);
|
||||
state.Restore(USE_CONTENT_CLEANER_AGENT_STATE_KEY, value => this.useContentCleanerAgent = value);
|
||||
state.Restore(INPUT_TEXT_STATE_KEY, value => this.inputText = value);
|
||||
state.Restore(IS_AGENT_RUNNING_STATE_KEY, value => this.isAgentRunning = value);
|
||||
state.Restore(SELECTED_TARGET_LANGUAGE_STATE_KEY, value => this.selectedTargetLanguage = value);
|
||||
state.Restore(CUSTOM_TARGET_LANGUAGE_STATE_KEY, value => this.customTargetLanguage = value);
|
||||
state.Restore(SELECTED_COMPLEXITY_STATE_KEY, value => this.selectedComplexity = value);
|
||||
state.Restore(EXPERT_IN_FIELD_STATE_KEY, value => this.expertInField = value);
|
||||
state.Restore(IMPORTANT_ASPECTS_STATE_KEY, value => this.importantAspects = value);
|
||||
}
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
|
||||
namespace AIStudio.Assistants.Translation;
|
||||
|
||||
@ -79,6 +80,40 @@ public partial class AssistantTranslation : AssistantBaseCore<SettingsDialogTran
|
||||
private string inputTextLastTranslation = string.Empty;
|
||||
private CommonLanguages selectedTargetLanguage;
|
||||
private string customTargetLanguage = string.Empty;
|
||||
private static readonly AssistantSessionStateKey<bool> SHOW_WEB_CONTENT_READER_STATE_KEY = new(nameof(showWebContentReader));
|
||||
private static readonly AssistantSessionStateKey<bool> USE_CONTENT_CLEANER_AGENT_STATE_KEY = new(nameof(useContentCleanerAgent));
|
||||
private static readonly AssistantSessionStateKey<bool> LIVE_TRANSLATION_STATE_KEY = new(nameof(liveTranslation));
|
||||
private static readonly AssistantSessionStateKey<bool> IS_AGENT_RUNNING_STATE_KEY = new(nameof(isAgentRunning));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_TEXT_STATE_KEY = new(nameof(inputText));
|
||||
private static readonly AssistantSessionStateKey<string> INPUT_TEXT_LAST_TRANSLATION_STATE_KEY = new(nameof(inputTextLastTranslation));
|
||||
private static readonly AssistantSessionStateKey<CommonLanguages> SELECTED_TARGET_LANGUAGE_STATE_KEY = new(nameof(selectedTargetLanguage));
|
||||
private static readonly AssistantSessionStateKey<string> CUSTOM_TARGET_LANGUAGE_STATE_KEY = new(nameof(customTargetLanguage));
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CaptureCustomAssistantSessionState(AssistantSessionStateWriter state)
|
||||
{
|
||||
state.Set(SHOW_WEB_CONTENT_READER_STATE_KEY, this.showWebContentReader);
|
||||
state.Set(USE_CONTENT_CLEANER_AGENT_STATE_KEY, this.useContentCleanerAgent);
|
||||
state.Set(LIVE_TRANSLATION_STATE_KEY, this.liveTranslation);
|
||||
state.Set(IS_AGENT_RUNNING_STATE_KEY, this.isAgentRunning);
|
||||
state.Set(INPUT_TEXT_STATE_KEY, this.inputText);
|
||||
state.Set(INPUT_TEXT_LAST_TRANSLATION_STATE_KEY, this.inputTextLastTranslation);
|
||||
state.Set(SELECTED_TARGET_LANGUAGE_STATE_KEY, this.selectedTargetLanguage);
|
||||
state.Set(CUSTOM_TARGET_LANGUAGE_STATE_KEY, this.customTargetLanguage);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void RestoreCustomAssistantSessionState(AssistantSessionStateReader state)
|
||||
{
|
||||
state.Restore(SHOW_WEB_CONTENT_READER_STATE_KEY, value => this.showWebContentReader = value);
|
||||
state.Restore(USE_CONTENT_CLEANER_AGENT_STATE_KEY, value => this.useContentCleanerAgent = value);
|
||||
state.Restore(LIVE_TRANSLATION_STATE_KEY, value => this.liveTranslation = value);
|
||||
state.Restore(IS_AGENT_RUNNING_STATE_KEY, value => this.isAgentRunning = value);
|
||||
state.Restore(INPUT_TEXT_STATE_KEY, value => this.inputText = value);
|
||||
state.Restore(INPUT_TEXT_LAST_TRANSLATION_STATE_KEY, value => this.inputTextLastTranslation = value);
|
||||
state.Restore(SELECTED_TARGET_LANGUAGE_STATE_KEY, value => this.selectedTargetLanguage = value);
|
||||
state.Restore(CUSTOM_TARGET_LANGUAGE_STATE_KEY, value => this.customTargetLanguage = value);
|
||||
}
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
@ -31,6 +32,12 @@ public partial class AssistantBlock<TSettings> : MSGComponentBase where TSetting
|
||||
[Parameter]
|
||||
public Tools.Components Component { get; set; } = Tools.Components.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the optional assistant session instance ID represented by this block.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string AssistantSessionInstanceId { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public PreviewFeatures RequiredPreviewFeature { get; set; } = PreviewFeatures.NONE;
|
||||
|
||||
@ -39,6 +46,9 @@ public partial class AssistantBlock<TSettings> : MSGComponentBase where TSetting
|
||||
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private AssistantSessionService AssistantSessionService { get; init; } = null!;
|
||||
|
||||
private async Task OpenSettingsDialog()
|
||||
{
|
||||
@ -50,7 +60,7 @@ public partial class AssistantBlock<TSettings> : MSGComponentBase where TSetting
|
||||
await this.DialogService.ShowAsync<TSettings>(T("Open Settings"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
}
|
||||
|
||||
private string BorderColor => this.SettingsManager.IsDarkMode switch
|
||||
private string BorderColor => this.HasActiveSession ? this.ColorTheme.GetCurrentPalette(this.SettingsManager).Warning.Value : this.SettingsManager.IsDarkMode switch
|
||||
{
|
||||
true => this.ColorTheme.GetCurrentPalette(this.SettingsManager).GrayLight,
|
||||
false => this.ColorTheme.GetCurrentPalette(this.SettingsManager).Primary.Value,
|
||||
@ -61,4 +71,27 @@ public partial class AssistantBlock<TSettings> : MSGComponentBase where TSetting
|
||||
private bool IsVisible => this.SettingsManager.IsAssistantVisible(this.Component, assistantName: this.Name, requiredPreviewFeature: this.RequiredPreviewFeature);
|
||||
|
||||
private bool HasSettingsPanel => typeof(TSettings) != typeof(NoSettingsPanel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether this block represents an active assistant session.
|
||||
/// </summary>
|
||||
private bool HasActiveSession => string.IsNullOrWhiteSpace(this.AssistantSessionInstanceId)
|
||||
? this.AssistantSessionService.GetSnapshots().Any(snapshot => snapshot.IsActive && snapshot.Key.Component == this.Component)
|
||||
: this.AssistantSessionService.GetSnapshots().Any(snapshot => snapshot.IsActive && snapshot.Key.InstanceId == this.AssistantSessionInstanceId);
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the block when assistant session activity changes.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The message payload type.</typeparam>
|
||||
/// <param name="sendingComponent">The component that sent the message, if any.</param>
|
||||
/// <param name="triggeredEvent">The event that was triggered.</param>
|
||||
/// <param name="data">The message payload.</param>
|
||||
/// <returns>A task that completes after the message was processed.</returns>
|
||||
protected override Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||
{
|
||||
if (triggeredEvent is Event.ASSISTANT_SESSION_CHANGED or Event.ASSISTANT_SESSION_FINISHED)
|
||||
this.StateHasChanged();
|
||||
|
||||
return base.ProcessIncomingMessage(sendingComponent, triggeredEvent, data);
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@ using AIStudio.Dialogs;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.AIJobs;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
using AIStudio.Tools.Rust;
|
||||
using AIStudio.Tools.Services;
|
||||
@ -30,6 +31,9 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
|
||||
[Inject]
|
||||
private AIJobService AIJobService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private AssistantSessionService AssistantSessionService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ISnackbar Snackbar { get; init; } = null!;
|
||||
@ -102,7 +106,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
Event.UPDATE_AVAILABLE, Event.CONFIGURATION_CHANGED, Event.COLOR_THEME_CHANGED, Event.SHOW_ERROR,
|
||||
Event.SHOW_WARNING, Event.SHOW_SUCCESS, Event.STARTUP_PLUGIN_SYSTEM, Event.PLUGINS_RELOADED,
|
||||
Event.INSTALL_UPDATE, Event.STARTUP_COMPLETED, Event.AI_JOB_CHANGED, Event.AI_JOB_FINISHED,
|
||||
Event.CHAT_GENERATION_CHANGED,
|
||||
Event.CHAT_GENERATION_CHANGED, Event.ASSISTANT_SESSION_CHANGED, Event.ASSISTANT_SESSION_FINISHED,
|
||||
]);
|
||||
|
||||
// Set the snackbar for the update service:
|
||||
@ -228,6 +232,8 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
case Event.AI_JOB_CHANGED:
|
||||
case Event.AI_JOB_FINISHED:
|
||||
case Event.CHAT_GENERATION_CHANGED:
|
||||
case Event.ASSISTANT_SESSION_CHANGED:
|
||||
case Event.ASSISTANT_SESSION_FINISHED:
|
||||
this.LoadNavItems();
|
||||
this.StateHasChanged();
|
||||
break;
|
||||
@ -344,7 +350,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan
|
||||
|
||||
yield return new(T("Home"), Icons.Material.Filled.Home, palette.DarkLighten, palette.GrayLight, Routes.HOME, true);
|
||||
yield return new(T("Chat"), this.AIJobService.HasActiveJobs ? Icons.Material.Filled.Chat : Icons.Material.Outlined.Chat, palette.DarkLighten, palette.GrayLight, Routes.CHAT, false);
|
||||
yield return new(T("Assistants"), Icons.Material.Filled.Apps, palette.DarkLighten, palette.GrayLight, Routes.ASSISTANTS, false);
|
||||
yield return new(T("Assistants"), this.AssistantSessionService.HasActiveSessions ? Icons.Material.Filled.Apps : Icons.Material.Outlined.Apps, palette.DarkLighten, palette.GrayLight, Routes.ASSISTANTS, false);
|
||||
|
||||
if (PreviewFeatures.PRE_WRITER_MODE_2024.IsEnabled(this.SettingsManager))
|
||||
yield return new(T("Writer"), Icons.Material.Filled.Create, palette.DarkLighten, palette.GrayLight, Routes.WRITER, false);
|
||||
|
||||
@ -47,6 +47,7 @@
|
||||
Description="@T(assistantPlugin.Description)"
|
||||
Icon="@Icons.Material.Filled.FindInPage"
|
||||
Disabled="@(!securityState.CanStartAssistant)"
|
||||
AssistantSessionInstanceId="@assistantPlugin.Id.ToString()"
|
||||
Link="@($"{Routes.ASSISTANT_DYNAMIC}?assistantId={assistantPlugin.Id}")">
|
||||
<SecurityBadge>
|
||||
<AssistantPluginSecurityCard Plugin="@assistantPlugin" Compact="@true" />
|
||||
|
||||
@ -3,6 +3,7 @@ using AIStudio.Agents.AssistantAudit;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.Databases;
|
||||
using AIStudio.Tools.AIJobs;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
using AIStudio.Tools.PluginSystem.Assistants;
|
||||
using AIStudio.Tools.Services;
|
||||
@ -130,6 +131,7 @@ internal sealed class Program
|
||||
builder.Services.AddSingleton<SettingsManager>();
|
||||
builder.Services.AddSingleton<ThreadSafeRandom>();
|
||||
builder.Services.AddSingleton<AIJobService>();
|
||||
builder.Services.AddSingleton<AssistantSessionService>();
|
||||
builder.Services.AddSingleton<VoiceRecordingAvailabilityService>();
|
||||
builder.Services.AddSingleton<DataSourceService>();
|
||||
builder.Services.AddScoped<PandocAvailabilityService>();
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
namespace AIStudio.Tools.AssistantSessions;
|
||||
|
||||
/// <summary>
|
||||
/// Identifies one logical assistant session slot.
|
||||
/// </summary>
|
||||
public readonly record struct AssistantSessionKey
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new assistant session key.
|
||||
/// </summary>
|
||||
/// <param name="component">The application component the assistant belongs to.</param>
|
||||
/// <param name="instanceId">The assistant-specific instance ID, such as a component type or plugin ID.</param>
|
||||
public AssistantSessionKey(Components component, string instanceId)
|
||||
{
|
||||
this.Component = component;
|
||||
this.InstanceId = instanceId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the application component the assistant belongs to.
|
||||
/// </summary>
|
||||
public Components Component { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the assistant-specific instance ID, such as a component type or plugin ID.
|
||||
/// </summary>
|
||||
public string InstanceId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Converts the key into a compact diagnostic string.
|
||||
/// </summary>
|
||||
/// <returns>The component and instance ID joined by a colon.</returns>
|
||||
public override string ToString() => $"{this.Component}:{this.InstanceId}";
|
||||
}
|
||||
@ -0,0 +1,320 @@
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
using AIStudio.Chat;
|
||||
|
||||
namespace AIStudio.Tools.AssistantSessions;
|
||||
|
||||
/// <summary>
|
||||
/// Keeps assistant sessions alive while their Blazor components are not mounted.
|
||||
/// </summary>
|
||||
/// <param name="messageBus">The message bus used to publish assistant session changes.</param>
|
||||
public sealed class AssistantSessionService(MessageBus messageBus)
|
||||
{
|
||||
/// <summary>
|
||||
/// Mutable runtime state owned exclusively by <see cref="AssistantSessionService"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This type intentionally exists in addition to <see cref="AssistantSessionSnapshot"/>.
|
||||
/// It holds runtime-only data such as the cancellation token source and lock, while
|
||||
/// snapshots are copied DTOs for UI components and message bus payloads.
|
||||
/// </remarks>
|
||||
private sealed class AssistantSessionState
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifies this concrete run of an assistant session.
|
||||
/// </summary>
|
||||
public required Guid SessionId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the assistant and logical session slot this run belongs to.
|
||||
/// </summary>
|
||||
public required AssistantSessionKey Key { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the active assistant run.
|
||||
/// </summary>
|
||||
public required CancellationTokenSource CancellationTokenSource { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Stores when the session run started.
|
||||
/// </summary>
|
||||
public required DateTimeOffset StartedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Stores when the session state was last changed.
|
||||
/// </summary>
|
||||
public DateTimeOffset UpdatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Stores when the session reached a terminal state.
|
||||
/// </summary>
|
||||
public DateTimeOffset? FinishedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Stores the user-visible assistant title.
|
||||
/// </summary>
|
||||
public string Title { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the current lifecycle state of the session.
|
||||
/// </summary>
|
||||
public AssistantSessionStatus Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Stores the user-visible error message for failed sessions.
|
||||
/// </summary>
|
||||
public string ErrorMessage { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the current assistant chat thread, including streamed output.
|
||||
/// </summary>
|
||||
public ChatThread? ChatThread { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Stores the assistant component state captured from the running UI instance.
|
||||
/// </summary>
|
||||
public Dictionary<string, IAssistantSessionSnapshotField> State { get; set; } = new(StringComparer.Ordinal);
|
||||
|
||||
/// <summary>
|
||||
/// Guards mutable fields while snapshots are created or updates are applied.
|
||||
/// </summary>
|
||||
public readonly Lock SyncRoot = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores one assistant session per session key.
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<AssistantSessionKey, AssistantSessionState> sessions = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether at least one assistant session is still active.
|
||||
/// </summary>
|
||||
public bool HasActiveSessions => this.sessions.Values.Any(session => session.Status is AssistantSessionStatus.RUNNING or AssistantSessionStatus.CANCELING);
|
||||
|
||||
/// <summary>
|
||||
/// Gets copied snapshots for all known assistant sessions.
|
||||
/// </summary>
|
||||
/// <returns>Session snapshots ordered by newest update first.</returns>
|
||||
public IReadOnlyCollection<AssistantSessionSnapshot> GetSnapshots()
|
||||
{
|
||||
return this.sessions.Values
|
||||
.Select(CreateSnapshot)
|
||||
.OrderByDescending(snapshot => snapshot.UpdatedAt)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the current snapshot for an assistant session key.
|
||||
/// </summary>
|
||||
/// <param name="key">The assistant session key to look up.</param>
|
||||
/// <returns>The current snapshot, or <c>null</c> when no session exists.</returns>
|
||||
public AssistantSessionSnapshot? TryGetSnapshot(AssistantSessionKey key)
|
||||
{
|
||||
return this.sessions.TryGetValue(key, out var session) ? CreateSnapshot(session) : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a new assistant session when no active session exists for the key.
|
||||
/// </summary>
|
||||
/// <param name="key">The assistant session key.</param>
|
||||
/// <param name="title">The user-visible assistant title.</param>
|
||||
/// <param name="cancellationTokenSource">The cancellation token source owned by the new runtime session.</param>
|
||||
/// <param name="chatThread">The current assistant chat thread, if one already exists.</param>
|
||||
/// <param name="state">The initial assistant component state.</param>
|
||||
/// <returns>The new session snapshot, or the existing active session snapshot.</returns>
|
||||
public AssistantSessionSnapshot TryBegin(AssistantSessionKey key, string title, CancellationTokenSource cancellationTokenSource, ChatThread? chatThread, Dictionary<string, IAssistantSessionSnapshotField> state)
|
||||
{
|
||||
if (this.sessions.TryGetValue(key, out var existing) && existing.Status is AssistantSessionStatus.RUNNING or AssistantSessionStatus.CANCELING)
|
||||
return CreateSnapshot(existing);
|
||||
|
||||
var now = DateTimeOffset.Now;
|
||||
var session = new AssistantSessionState
|
||||
{
|
||||
SessionId = Guid.NewGuid(),
|
||||
Key = key,
|
||||
CancellationTokenSource = cancellationTokenSource,
|
||||
StartedAt = now,
|
||||
UpdatedAt = now,
|
||||
Title = title,
|
||||
Status = AssistantSessionStatus.RUNNING,
|
||||
ChatThread = chatThread,
|
||||
State = state,
|
||||
};
|
||||
|
||||
this.sessions[key] = session;
|
||||
_ = this.NotifyChangedAsync(session);
|
||||
return CreateSnapshot(session);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a running assistant session with the latest UI and chat state.
|
||||
/// </summary>
|
||||
/// <param name="key">The assistant session key.</param>
|
||||
/// <param name="sessionId">The concrete run ID that is allowed to write the checkpoint.</param>
|
||||
/// <param name="title">The current user-visible assistant title.</param>
|
||||
/// <param name="chatThread">The current assistant chat thread.</param>
|
||||
/// <param name="state">The current assistant component state.</param>
|
||||
public async Task CheckpointAsync(AssistantSessionKey key, Guid sessionId, string title, ChatThread? chatThread, Dictionary<string, IAssistantSessionSnapshotField> state)
|
||||
{
|
||||
if (!this.sessions.TryGetValue(key, out var session))
|
||||
return;
|
||||
|
||||
lock (session.SyncRoot)
|
||||
{
|
||||
if (session.SessionId != sessionId)
|
||||
return;
|
||||
|
||||
session.Title = title;
|
||||
session.ChatThread = chatThread;
|
||||
session.State = state;
|
||||
session.UpdatedAt = DateTimeOffset.Now;
|
||||
}
|
||||
|
||||
await this.NotifyChangedAsync(session);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Requests cancellation for an active assistant session.
|
||||
/// </summary>
|
||||
/// <param name="key">The assistant session key to cancel.</param>
|
||||
public async Task CancelAsync(AssistantSessionKey key)
|
||||
{
|
||||
if (!this.sessions.TryGetValue(key, out var session))
|
||||
return;
|
||||
|
||||
lock (session.SyncRoot)
|
||||
{
|
||||
if (session.Status is not AssistantSessionStatus.RUNNING)
|
||||
return;
|
||||
|
||||
session.Status = AssistantSessionStatus.CANCELING;
|
||||
session.UpdatedAt = DateTimeOffset.Now;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!session.CancellationTokenSource.IsCancellationRequested)
|
||||
await session.CancellationTokenSource.CancelAsync();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await this.NotifyChangedAsync(session);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves an assistant session into a terminal state and publishes completion.
|
||||
/// </summary>
|
||||
/// <param name="key">The assistant session key.</param>
|
||||
/// <param name="sessionId">The concrete run ID that is allowed to complete the session.</param>
|
||||
/// <param name="status">The terminal status to store.</param>
|
||||
/// <param name="errorMessage">The user-visible error message for failed sessions.</param>
|
||||
/// <param name="chatThread">The final assistant chat thread.</param>
|
||||
/// <param name="state">The final assistant component state.</param>
|
||||
public async Task CompleteAsync(AssistantSessionKey key, Guid sessionId, AssistantSessionStatus status, string errorMessage, ChatThread? chatThread, Dictionary<string, IAssistantSessionSnapshotField> state)
|
||||
{
|
||||
if (!this.sessions.TryGetValue(key, out var session))
|
||||
return;
|
||||
|
||||
lock (session.SyncRoot)
|
||||
{
|
||||
if (session.SessionId != sessionId)
|
||||
return;
|
||||
|
||||
session.Status = status;
|
||||
session.ErrorMessage = errorMessage;
|
||||
session.ChatThread = chatThread;
|
||||
session.State = state;
|
||||
session.UpdatedAt = DateTimeOffset.Now;
|
||||
session.FinishedAt = session.UpdatedAt;
|
||||
}
|
||||
|
||||
await this.NotifyChangedAsync(session);
|
||||
await messageBus.SendMessage(null, Event.ASSISTANT_SESSION_FINISHED, CreateSnapshot(session));
|
||||
|
||||
try
|
||||
{
|
||||
session.CancellationTokenSource.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears an inactive assistant session.
|
||||
/// </summary>
|
||||
/// <param name="key">The assistant session key to clear.</param>
|
||||
public async Task ClearAsync(AssistantSessionKey key)
|
||||
{
|
||||
if (!this.sessions.TryGetValue(key, out var session))
|
||||
return;
|
||||
|
||||
if (session.Status is AssistantSessionStatus.RUNNING or AssistantSessionStatus.CANCELING)
|
||||
return;
|
||||
|
||||
lock (session.SyncRoot)
|
||||
{
|
||||
session.Status = AssistantSessionStatus.NONE;
|
||||
session.ChatThread = null;
|
||||
session.State = new(StringComparer.Ordinal);
|
||||
session.UpdatedAt = DateTimeOffset.Now;
|
||||
session.FinishedAt = session.UpdatedAt;
|
||||
}
|
||||
|
||||
this.sessions.TryRemove(key, out _);
|
||||
await messageBus.SendMessage(null, Event.ASSISTANT_SESSION_CHANGED, CreateSnapshot(session));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all inactive sessions for a component.
|
||||
/// </summary>
|
||||
/// <param name="component">The component whose inactive sessions should be cleared.</param>
|
||||
public async Task ClearInactiveSessionsForComponentAsync(Components component)
|
||||
{
|
||||
var matchingKeys = this.sessions
|
||||
.Where(pair => pair.Key.Component == component && pair.Value.Status is not AssistantSessionStatus.RUNNING and not AssistantSessionStatus.CANCELING)
|
||||
.Select(pair => pair.Key)
|
||||
.ToList();
|
||||
|
||||
foreach (var key in matchingKeys)
|
||||
await this.ClearAsync(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Publishes an assistant session change event.
|
||||
/// </summary>
|
||||
/// <param name="session">The runtime session whose copied snapshot should be published.</param>
|
||||
private async Task NotifyChangedAsync(AssistantSessionState session)
|
||||
{
|
||||
await messageBus.SendMessage(null, Event.ASSISTANT_SESSION_CHANGED, CreateSnapshot(session));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copied, external snapshot from the internal runtime state.
|
||||
/// </summary>
|
||||
/// <param name="session">The runtime session to copy.</param>
|
||||
/// <returns>A snapshot safe to send to UI components.</returns>
|
||||
private static AssistantSessionSnapshot CreateSnapshot(AssistantSessionState session)
|
||||
{
|
||||
lock (session.SyncRoot)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
SessionId = session.SessionId,
|
||||
Key = session.Key,
|
||||
Title = session.Title,
|
||||
Status = session.Status,
|
||||
StartedAt = session.StartedAt,
|
||||
UpdatedAt = session.UpdatedAt,
|
||||
FinishedAt = session.FinishedAt,
|
||||
ErrorMessage = session.ErrorMessage,
|
||||
ChatThread = session.ChatThread,
|
||||
State = new Dictionary<string, IAssistantSessionSnapshotField>(session.State, StringComparer.Ordinal),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
using AIStudio.Chat;
|
||||
|
||||
namespace AIStudio.Tools.AssistantSessions;
|
||||
|
||||
/// <summary>
|
||||
/// Immutable-style view of an assistant session for UI consumers and message bus events.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The service creates snapshots by copying its internal runtime state. Consumers must
|
||||
/// treat the contained chat thread and state objects as read-only views of the session.
|
||||
/// </remarks>
|
||||
public sealed record AssistantSessionSnapshot
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifies the concrete run represented by this snapshot.
|
||||
/// </summary>
|
||||
public required Guid SessionId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the assistant and logical session slot represented by this snapshot.
|
||||
/// </summary>
|
||||
public required AssistantSessionKey Key { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user-visible assistant title.
|
||||
/// </summary>
|
||||
public required string Title { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current lifecycle status.
|
||||
/// </summary>
|
||||
public required AssistantSessionStatus Status { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets when the session run started.
|
||||
/// </summary>
|
||||
public required DateTimeOffset StartedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets when the session was last changed.
|
||||
/// </summary>
|
||||
public required DateTimeOffset UpdatedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets when the session reached a terminal state.
|
||||
/// </summary>
|
||||
public DateTimeOffset? FinishedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user-visible error message for failed sessions.
|
||||
/// </summary>
|
||||
public string ErrorMessage { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the assistant chat thread captured for this session.
|
||||
/// </summary>
|
||||
public ChatThread? ChatThread { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the assistant component state captured for this session.
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, IAssistantSessionSnapshotField> State { get; init; } = new Dictionary<string, IAssistantSessionSnapshotField>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the session is still running or canceling.
|
||||
/// </summary>
|
||||
public bool IsActive => this.Status is AssistantSessionStatus.RUNNING or AssistantSessionStatus.CANCELING;
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
namespace AIStudio.Tools.AssistantSessions;
|
||||
|
||||
/// <summary>
|
||||
/// Stores one typed value in an assistant session snapshot.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The captured value type.</typeparam>
|
||||
public sealed record AssistantSessionSnapshotField<T> : IAssistantSessionSnapshotField
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new typed snapshot field.
|
||||
/// </summary>
|
||||
/// <param name="value">The captured value.</param>
|
||||
public AssistantSessionSnapshotField(T value)
|
||||
{
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the captured value.
|
||||
/// </summary>
|
||||
public T Value { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Type ValueType => typeof(T);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryRead<TValue>(out TValue? value)
|
||||
{
|
||||
if (this.Value is TValue typedValue)
|
||||
{
|
||||
value = typedValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.Value is null && default(TValue) is null)
|
||||
{
|
||||
value = default;
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
namespace AIStudio.Tools.AssistantSessions;
|
||||
|
||||
/// <summary>
|
||||
/// Identifies a typed assistant session state value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The value type stored for this key.</typeparam>
|
||||
public readonly record struct AssistantSessionStateKey<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new assistant session state key.
|
||||
/// </summary>
|
||||
/// <param name="name">The stable dictionary name used in assistant session snapshots.</param>
|
||||
public AssistantSessionStateKey(string name)
|
||||
{
|
||||
this.Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the stable dictionary name used in assistant session snapshots.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the stable dictionary name.
|
||||
/// </summary>
|
||||
/// <returns>The stable dictionary name.</returns>
|
||||
public override string ToString() => this.Name;
|
||||
}
|
||||
@ -0,0 +1,111 @@
|
||||
using System.Text;
|
||||
|
||||
namespace AIStudio.Tools.AssistantSessions;
|
||||
|
||||
/// <summary>
|
||||
/// Restores typed assistant session state values from a snapshot.
|
||||
/// </summary>
|
||||
/// <param name="fields">The captured snapshot fields.</param>
|
||||
/// <param name="assistantTitle">The user-visible assistant title.</param>
|
||||
public sealed class AssistantSessionStateReader(IReadOnlyDictionary<string, IAssistantSessionSnapshotField> fields, string assistantTitle)
|
||||
{
|
||||
private static readonly ILogger<AssistantSessionStateReader> LOG = Program.LOGGER_FACTORY.CreateLogger<AssistantSessionStateReader>();
|
||||
|
||||
/// <summary>
|
||||
/// Restores a typed value when it exists in the snapshot.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The value type.</typeparam>
|
||||
/// <param name="key">The typed state key.</param>
|
||||
/// <param name="apply">The action that applies the restored value.</param>
|
||||
public void Restore<T>(AssistantSessionStateKey<T> key, Action<T> apply)
|
||||
{
|
||||
if (this.TryRead(key, out var value))
|
||||
apply(value!);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores a list into an existing list instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The list item type.</typeparam>
|
||||
/// <param name="key">The typed state key.</param>
|
||||
/// <param name="target">The existing list to update.</param>
|
||||
public void RestoreList<T>(AssistantSessionStateKey<List<T>> key, List<T> target)
|
||||
{
|
||||
this.Restore(key, values =>
|
||||
{
|
||||
target.Clear();
|
||||
target.AddRange(values);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores a hash set into an existing hash set instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The set item type.</typeparam>
|
||||
/// <param name="key">The typed state key.</param>
|
||||
/// <param name="target">The existing hash set to update.</param>
|
||||
public void RestoreHashSet<T>(AssistantSessionStateKey<HashSet<T>> key, HashSet<T> target)
|
||||
{
|
||||
this.Restore(key, values =>
|
||||
{
|
||||
target.Clear();
|
||||
target.UnionWith(values);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores a dictionary into an existing dictionary instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The dictionary key type.</typeparam>
|
||||
/// <typeparam name="TValue">The dictionary value type.</typeparam>
|
||||
/// <param name="key">The typed state key.</param>
|
||||
/// <param name="target">The existing dictionary to update.</param>
|
||||
public void RestoreDictionary<TKey, TValue>(AssistantSessionStateKey<Dictionary<TKey, TValue>> key, Dictionary<TKey, TValue> target) where TKey : notnull
|
||||
{
|
||||
this.Restore(key, values =>
|
||||
{
|
||||
target.Clear();
|
||||
foreach (var (itemKey, itemValue) in values)
|
||||
target[itemKey] = itemValue;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores text into an existing string builder instance.
|
||||
/// </summary>
|
||||
/// <param name="key">The typed state key.</param>
|
||||
/// <param name="target">The existing string builder to update.</param>
|
||||
public void RestoreStringBuilder(AssistantSessionStateKey<string> key, StringBuilder target)
|
||||
{
|
||||
this.Restore(key, value =>
|
||||
{
|
||||
target.Clear();
|
||||
target.Append(value);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to read a typed value from the snapshot.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The requested value type.</typeparam>
|
||||
/// <param name="key">The typed state key.</param>
|
||||
/// <param name="value">The restored value when reading succeeds.</param>
|
||||
/// <returns><c>true</c> when a value exists and matches the requested type; otherwise, <c>false</c>.</returns>
|
||||
private bool TryRead<T>(AssistantSessionStateKey<T> key, out T? value)
|
||||
{
|
||||
value = default;
|
||||
if (!fields.TryGetValue(key.Name, out var field))
|
||||
return false;
|
||||
|
||||
if (field.TryRead(out value))
|
||||
return true;
|
||||
|
||||
LOG.LogWarning(
|
||||
"Could not restore assistant session field '{FieldStateKey}' for assistant '{AssistantTitle}'. ExpectedType='{ExpectedType}', CapturedValueType='{CapturedValueType}'. The current value is kept.",
|
||||
key.Name,
|
||||
assistantTitle,
|
||||
typeof(T).FullName,
|
||||
field.ValueType.FullName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
using System.Text;
|
||||
|
||||
namespace AIStudio.Tools.AssistantSessions;
|
||||
|
||||
/// <summary>
|
||||
/// Collects typed assistant session state values for a snapshot.
|
||||
/// </summary>
|
||||
public sealed class AssistantSessionStateWriter
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores captured fields by their stable dictionary names.
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, IAssistantSessionSnapshotField> fields = new(StringComparer.Ordinal);
|
||||
|
||||
/// <summary>
|
||||
/// Stores a typed state value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The value type.</typeparam>
|
||||
/// <param name="key">The typed state key.</param>
|
||||
/// <param name="value">The captured value.</param>
|
||||
public void Set<T>(AssistantSessionStateKey<T> key, T value)
|
||||
{
|
||||
this.fields[key.Name] = new AssistantSessionSnapshotField<T>(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores a list copy.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The list item type.</typeparam>
|
||||
/// <param name="key">The typed state key.</param>
|
||||
/// <param name="values">The values to copy.</param>
|
||||
public void SetList<T>(AssistantSessionStateKey<List<T>> key, IEnumerable<T> values)
|
||||
{
|
||||
this.Set(key, values.ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores a hash set copy.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The set item type.</typeparam>
|
||||
/// <param name="key">The typed state key.</param>
|
||||
/// <param name="values">The values to copy.</param>
|
||||
public void SetHashSet<T>(AssistantSessionStateKey<HashSet<T>> key, IEnumerable<T> values)
|
||||
{
|
||||
this.Set(key, values.ToHashSet());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores a dictionary copy.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The dictionary key type.</typeparam>
|
||||
/// <typeparam name="TValue">The dictionary value type.</typeparam>
|
||||
/// <param name="key">The typed state key.</param>
|
||||
/// <param name="values">The values to copy.</param>
|
||||
public void SetDictionary<TKey, TValue>(AssistantSessionStateKey<Dictionary<TKey, TValue>> key, IDictionary<TKey, TValue> values) where TKey : notnull
|
||||
{
|
||||
this.Set(key, new Dictionary<TKey, TValue>(values));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores the current text from a string builder.
|
||||
/// </summary>
|
||||
/// <param name="key">The typed state key.</param>
|
||||
/// <param name="value">The string builder to read.</param>
|
||||
public void SetStringBuilder(AssistantSessionStateKey<string> key, StringBuilder value)
|
||||
{
|
||||
this.Set(key, value.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the captured fields as a dictionary.
|
||||
/// </summary>
|
||||
/// <returns>A copied dictionary containing the captured fields.</returns>
|
||||
public Dictionary<string, IAssistantSessionSnapshotField> ToDictionary()
|
||||
{
|
||||
return new(this.fields, StringComparer.Ordinal);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
namespace AIStudio.Tools.AssistantSessions;
|
||||
|
||||
/// <summary>
|
||||
/// Describes the lifecycle state of an assistant session.
|
||||
/// </summary>
|
||||
public enum AssistantSessionStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// No session state is available.
|
||||
/// </summary>
|
||||
NONE,
|
||||
|
||||
/// <summary>
|
||||
/// The assistant session is running.
|
||||
/// </summary>
|
||||
RUNNING,
|
||||
|
||||
/// <summary>
|
||||
/// Cancellation was requested and the assistant is shutting down.
|
||||
/// </summary>
|
||||
CANCELING,
|
||||
|
||||
/// <summary>
|
||||
/// The assistant session completed successfully.
|
||||
/// </summary>
|
||||
COMPLETED,
|
||||
|
||||
/// <summary>
|
||||
/// The assistant session was canceled.
|
||||
/// </summary>
|
||||
CANCELED,
|
||||
|
||||
/// <summary>
|
||||
/// The assistant session failed.
|
||||
/// </summary>
|
||||
FAILED,
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
namespace AIStudio.Tools.AssistantSessions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a typed value stored in an assistant session snapshot.
|
||||
/// </summary>
|
||||
public interface IAssistantSessionSnapshotField
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the type used when the value was captured.
|
||||
/// </summary>
|
||||
Type ValueType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Tries to read the captured value as the requested type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The requested value type.</typeparam>
|
||||
/// <param name="value">The typed value when reading succeeds.</param>
|
||||
/// <returns><c>true</c> when the captured value matches <typeparamref name="T"/>; otherwise, <c>false</c>.</returns>
|
||||
bool TryRead<T>(out T? value);
|
||||
}
|
||||
@ -144,6 +144,16 @@ public enum Event
|
||||
/// Notifies receivers that chat generation state changed.
|
||||
/// </summary>
|
||||
CHAT_GENERATION_CHANGED,
|
||||
|
||||
/// <summary>
|
||||
/// Notifies receivers that an assistant session changed.
|
||||
/// </summary>
|
||||
ASSISTANT_SESSION_CHANGED,
|
||||
|
||||
/// <summary>
|
||||
/// Notifies receivers that an assistant session finished.
|
||||
/// </summary>
|
||||
ASSISTANT_SESSION_FINISHED,
|
||||
|
||||
// Workspace events:
|
||||
/// <summary>
|
||||
|
||||
@ -30,6 +30,49 @@ public sealed class AssistantState
|
||||
this.Times.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies all dynamic assistant state values from another state instance.
|
||||
/// </summary>
|
||||
/// <param name="other">The state instance to copy from.</param>
|
||||
public void CopyFrom(AssistantState other)
|
||||
{
|
||||
this.Clear();
|
||||
CopyDictionary(other.Text, this.Text);
|
||||
CopyDictionary(other.SingleSelect, this.SingleSelect);
|
||||
CopyDictionary(other.MultiSelect, this.MultiSelect);
|
||||
CopyDictionary(other.Booleans, this.Booleans);
|
||||
CopyDictionary(other.WebContent, this.WebContent);
|
||||
CopyDictionary(other.FileContent, this.FileContent);
|
||||
CopyDictionary(other.Colors, this.Colors);
|
||||
CopyDictionary(other.Dates, this.Dates);
|
||||
CopyDictionary(other.DateRanges, this.DateRanges);
|
||||
CopyDictionary(other.Times, this.Times);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy of the dynamic assistant state.
|
||||
/// </summary>
|
||||
/// <returns>A copied assistant state instance.</returns>
|
||||
public AssistantState Clone()
|
||||
{
|
||||
var clone = new AssistantState();
|
||||
clone.CopyFrom(this);
|
||||
return clone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies all entries from one dictionary into another dictionary.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The dictionary key type.</typeparam>
|
||||
/// <typeparam name="TValue">The dictionary value type.</typeparam>
|
||||
/// <param name="source">The source dictionary.</param>
|
||||
/// <param name="target">The target dictionary.</param>
|
||||
private static void CopyDictionary<TKey, TValue>(Dictionary<TKey, TValue> source, Dictionary<TKey, TValue> target) where TKey : notnull
|
||||
{
|
||||
foreach (var (key, value) in source)
|
||||
target[key] = value;
|
||||
}
|
||||
|
||||
public bool TryApplyValue(string fieldName, LuaValue value, out string expectedType)
|
||||
{
|
||||
expectedType = string.Empty;
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
# v26.6.3, build 243 (2026-06-xx xx:xx UTC)
|
||||
- Improved assistants so running tasks can continue when you leave the assistant and return later.
|
||||
- Improved the chat experience by automatically focusing the message composer again when it becomes available. Thanks, Dominic Neuburg (`donework`), for the contribution.
|
||||
Loading…
Reference in New Issue
Block a user