mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-05 03:27:41 +00:00
Added a Send to menu to every chat message and kept restrictions when sending content on (#1124)
This commit is contained in:
1 parent
d06688ca08
commit
bf54b8cd0d
73 files changed
+1923
-390
No files matched your search
@@ -19,6 +19,7 @@ internal sealed class StructuredLlmStageRunner(
|
||||
/// <typeparam name="T">The strict response type.</typeparam>
|
||||
/// <param name="provider">The selected provider configuration.</param>
|
||||
/// <param name="profile">The selected user profile.</param>
|
||||
/// <param name="restrictions">What the content of the briefing demands, see VisualBriefingManifest.Restrictions.</param>
|
||||
/// <param name="systemContract">The stage-specific system contract.</param>
|
||||
/// <param name="prompt">The user prompt containing stage inputs.</param>
|
||||
/// <param name="attachments">The first-turn attachments.</param>
|
||||
@@ -31,6 +32,7 @@ internal sealed class StructuredLlmStageRunner(
|
||||
public async Task<StructuredLlmStageResult<T>> RunAsync<T>(
|
||||
ProviderSettings provider,
|
||||
Profile profile,
|
||||
ChatRestrictions restrictions,
|
||||
string systemContract,
|
||||
string prompt,
|
||||
IReadOnlyList<FileAttachment> attachments,
|
||||
@@ -79,6 +81,10 @@ internal sealed class StructuredLlmStageRunner(
|
||||
],
|
||||
};
|
||||
|
||||
// The thread of the stage holds what the briefing holds. The orchestrator checked the provider before the
|
||||
// build started; the provider call checks it against the thread once more:
|
||||
thread.RequireRestrictions(restrictions);
|
||||
|
||||
VisualBriefingContractIssue? repairIssue = null;
|
||||
for (var attempt = 1; attempt <= 2; attempt++)
|
||||
{
|
||||
|
||||
+9
-2
@@ -1,5 +1,6 @@
|
||||
using System.Text.Json;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Dialogs;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Tools.Media;
|
||||
@@ -58,7 +59,13 @@ public partial class VisualBriefingAssistant
|
||||
/// <summary>
|
||||
/// Defines <c>CreateBriefingAsync</c> for the visual briefing feature.
|
||||
/// </summary>
|
||||
private async Task CreateBriefingAsync()
|
||||
private Task CreateBriefingAsync() => this.CreateBriefingAsync(ChatRestrictions.NONE);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a briefing which holds content with the given restrictions, see VisualBriefingManifest.Restrictions.
|
||||
/// </summary>
|
||||
/// <param name="restrictions">What the content of the briefing demands.</param>
|
||||
private async Task CreateBriefingAsync(ChatRestrictions restrictions)
|
||||
{
|
||||
var defaults = this.SettingsManager.ConfigurationData.VisualBriefing;
|
||||
var defaultProvider = this.SettingsManager.GetPreselectedProvider(ComponentKind.VISUAL_BRIEFING_ASSISTANT);
|
||||
@@ -79,7 +86,7 @@ public partial class VisualBriefingAssistant
|
||||
OptimizeImages = defaults.OptimizeImages,
|
||||
};
|
||||
|
||||
var briefing = await this.Store.CreateAsync(suggestedName, string.Empty, settings);
|
||||
var briefing = await this.Store.CreateAsync(suggestedName, string.Empty, settings, restrictions);
|
||||
await this.ReloadListAsync(briefing.BriefingId);
|
||||
}
|
||||
|
||||
|
||||
+16
-4
@@ -1,3 +1,4 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.Rust;
|
||||
@@ -109,10 +110,21 @@ public partial class VisualBriefingAssistant
|
||||
private string? ValidateProjectName(string name) => string.IsNullOrWhiteSpace(name) ? T("Please provide a briefing name.") : null;
|
||||
|
||||
/// <summary>Validates the selected generation provider.</summary>
|
||||
private string? ValidateProvider(ProviderSettings value) =>
|
||||
value == ProviderSettings.NONE || value.UsedLLMProvider is LLMProviders.NONE
|
||||
? T("Please select a provider.")
|
||||
: null;
|
||||
/// <remarks>
|
||||
/// A provider which may not see a text sent to this briefing is reported here, before the build starts. A provider the
|
||||
/// organization prohibited is not: it fails for every briefing alike, and the build reports it.
|
||||
/// </remarks>
|
||||
private string? ValidateProvider(ProviderSettings value)
|
||||
{
|
||||
if (value == ProviderSettings.NONE || value.UsedLLMProvider is LLMProviders.NONE)
|
||||
return T("Please select a provider.");
|
||||
|
||||
var restrictions = this.selectedBriefing?.Restrictions ?? ChatRestrictions.NONE;
|
||||
if (!restrictions.IsLLMProviderAllowed(value) && ChatRestrictions.NONE.IsLLMProviderAllowed(value))
|
||||
return T("This provider may not see the text sent to this briefing, because of where the text came from. Please select another provider.");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Validates the free-form target language when Other is selected.</summary>
|
||||
private string? ValidateCustomTargetLanguage(string language) =>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Components;
|
||||
using AIStudio.Dialogs;
|
||||
using AIStudio.Dialogs.Settings;
|
||||
using AIStudio.Tools.AssistantLaunch;
|
||||
using AIStudio.Tools.AssistantSessions;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
@@ -152,20 +154,18 @@ public partial class VisualBriefingAssistant : MSGComponentBase
|
||||
return;
|
||||
}
|
||||
|
||||
this.ApplyFilters([], [Event.SEND_TO_VISUAL_BRIEFING_ASSISTANT, Event.CONFIGURATION_CHANGED]);
|
||||
this.ApplyFilters([], [Event.CONFIGURATION_CHANGED]);
|
||||
this.MediaTranscriptionService.StateChanged += this.MediaStateChanged;
|
||||
this.BuildProgressService.Changed += this.BuildProgressChanged;
|
||||
await this.ReloadListAsync();
|
||||
await this.ConsumePendingMediaOutcomesAsync();
|
||||
this.MonitorSourceStatusAsync(this.sourceMonitorCancellation.Token).Observe($"{nameof(VisualBriefingAssistant)}: monitoring the source status");
|
||||
var deferredInstruction = this.MessageBus.TakeDeferredMessages<string>(Event.SEND_TO_VISUAL_BRIEFING_ASSISTANT).LastOrDefault();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(deferredInstruction))
|
||||
if (AssistantLaunchService.TakeTextPayload(ComponentKind.VISUAL_BRIEFING_ASSISTANT) is { } textLaunchPayload && !string.IsNullOrWhiteSpace(textLaunchPayload.Text))
|
||||
{
|
||||
if (this.selectedBriefing is null)
|
||||
await this.CreateBriefingAsync();
|
||||
|
||||
this.editor.Instruction = deferredInstruction;
|
||||
// A text sent here starts a briefing of its own, which holds what the text demands. The briefing
|
||||
// open last belongs to other work, and its instruction would be gone:
|
||||
await this.CreateBriefingAsync(textLaunchPayload.Restrictions);
|
||||
this.editor.Instruction = textLaunchPayload.Text;
|
||||
await this.SaveCurrentAsync();
|
||||
}
|
||||
|
||||
@@ -235,17 +235,6 @@ public partial class VisualBriefingAssistant : MSGComponentBase
|
||||
/// </summary>
|
||||
protected override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
||||
{
|
||||
if (triggeredEvent is Event.SEND_TO_VISUAL_BRIEFING_ASSISTANT && data is string text)
|
||||
{
|
||||
if (this.selectedBriefing is null)
|
||||
await this.CreateBriefingAsync();
|
||||
|
||||
this.editor.Instruction = text;
|
||||
await this.SaveCurrentAsync();
|
||||
this.StateHasChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
if (triggeredEvent is Event.CONFIGURATION_CHANGED)
|
||||
{
|
||||
// The spellchecking setting might have changed. Since this page is not re-parameterized
|
||||
|
||||
+23
@@ -1,3 +1,4 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Tools.Rust;
|
||||
@@ -233,6 +234,28 @@ internal sealed partial class VisualBriefingBuildOrchestrator
|
||||
"No provider is selected.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the selected provider may see what the briefing holds.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A text sent to the briefing demands what it demanded where it came from, see VisualBriefingManifest.Restrictions,
|
||||
/// and a provider the organization prohibited may see nothing at all. The provider call of each stage would refuse
|
||||
/// such a provider as well, but only once the build runs, and the stage would fail without telling why.
|
||||
/// </remarks>
|
||||
/// <param name="manifest">The briefing manifest.</param>
|
||||
/// <param name="provider">The provider.</param>
|
||||
internal static void ValidateProviderMaySeeContent(VisualBriefingManifest manifest, ProviderSettings provider)
|
||||
{
|
||||
if (manifest.Restrictions.IsLLMProviderAllowed(provider))
|
||||
return;
|
||||
|
||||
throw new VisualBriefingBuildException(
|
||||
VisualBriefingFailureCode.PROVIDER_NOT_ALLOWED,
|
||||
VisualBriefingBuildStage.SOURCE_PREPARATION,
|
||||
"The selected provider may not see the content of this briefing.",
|
||||
$"ProviderFamily={provider.UsedLLMProvider}; RequiredConfidence={manifest.Restrictions.ProviderConfidence}; DataSecurity={manifest.Restrictions.DataSecurity}.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures content-generating builds have at least one source-material file.
|
||||
/// </summary>
|
||||
|
||||
@@ -114,6 +114,7 @@ internal sealed partial class VisualBriefingBuildOrchestrator
|
||||
try
|
||||
{
|
||||
ValidateProvider(provider);
|
||||
ValidateProviderMaySeeContent(manifest, provider);
|
||||
ValidateSourceMaterial(manifest, mode);
|
||||
var parentContext = await this.LoadParentContextAsync(manifest, mode, parentRevisionId, token);
|
||||
VisualBriefingEvidenceArtifact? reusableEvidence = null;
|
||||
|
||||
@@ -37,7 +37,7 @@ internal sealed class VisualBriefingContentStage(StructuredLlmStageRunner stageR
|
||||
await store.SaveBuildAsync(build, token);
|
||||
progressService.Publish(build);
|
||||
|
||||
var run = await stageRunner.RunAsync<VisualBriefingContentResponse>(provider, profile, BuildSystemContract(),
|
||||
var run = await stageRunner.RunAsync<VisualBriefingContentResponse>(provider, profile, manifest.Restrictions, BuildSystemContract(),
|
||||
BuildPrompt(manifest, evidence, plan), [], VisualBriefingBuildStage.CONTENT, build.OperationId, build.BuildId,
|
||||
response => this.ValidateResponseAndProject(manifest, plan, evidence, response), token);
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ internal sealed class VisualBriefingEvidenceStage(StructuredLlmStageRunner stage
|
||||
progressService.Publish(build);
|
||||
|
||||
var run = await stageRunner.RunAsync<VisualBriefingEvidenceResponse>(
|
||||
provider, profile, BuildSystemContract(), BuildPrompt(manifest, preparedSources), preparedSources.Attachments, VisualBriefingBuildStage.EVIDENCE,
|
||||
provider, profile, manifest.Restrictions, BuildSystemContract(), BuildPrompt(manifest, preparedSources), preparedSources.Attachments, VisualBriefingBuildStage.EVIDENCE,
|
||||
build.OperationId, build.BuildId, response => VisualBriefingValidation.ValidateEvidence(manifest, response), token);
|
||||
|
||||
stage.Attempts = run.Attempts;
|
||||
|
||||
@@ -18,6 +18,11 @@ public enum VisualBriefingFailureCode
|
||||
/// </summary>
|
||||
PROVIDER_NOT_SELECTED,
|
||||
|
||||
/// <summary>
|
||||
/// The selected provider may not see the content of the briefing.
|
||||
/// </summary>
|
||||
PROVIDER_NOT_ALLOWED,
|
||||
|
||||
/// <summary>
|
||||
/// The selected model lacks a required capability.
|
||||
/// </summary>
|
||||
|
||||
@@ -83,6 +83,7 @@ internal static class VisualBriefingFailureExtensions
|
||||
private static string ToUserMessage(this VisualBriefingFailureCode code) => code switch
|
||||
{
|
||||
VisualBriefingFailureCode.PROVIDER_NOT_SELECTED => TB("This briefing has no provider selected. Please select a provider before you generate a briefing."),
|
||||
VisualBriefingFailureCode.PROVIDER_NOT_ALLOWED => TB("The selected provider may not see the content of this briefing. Please select another provider."),
|
||||
VisualBriefingFailureCode.MODEL_CAPABILITY_MISSING => TB("The selected model lacks a capability this briefing needs. Please select another model."),
|
||||
VisualBriefingFailureCode.SOURCE_UNREACHABLE => TB("A source of this briefing can no longer be reached. Please relink or remove the affected source."),
|
||||
VisualBriefingFailureCode.TRANSCRIPT_UNAVAILABLE => TB("A media transcript of this briefing is missing or outdated. Please transcribe the affected media again."),
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using AIStudio.Chat;
|
||||
|
||||
namespace AIStudio.Assistants.VisualBriefing;
|
||||
|
||||
/// <summary>
|
||||
@@ -40,6 +42,18 @@ public sealed class VisualBriefingManifest
|
||||
/// </summary>
|
||||
public VisualBriefingLocalSettings Settings { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// What the text sent to this briefing demands, because of where it came from.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An answer written from mails demands here what it demanded in its chat, and every stage of every build
|
||||
/// holds to it. The restrictions are set when the briefing is created for the text, and they never loosen:
|
||||
/// the text may still be part of the instruction, and of the versions built from it. They live here rather
|
||||
/// than in the settings, which the editor replaces with every save. A briefing nothing was sent to, or one
|
||||
/// written before this property existed, demands nothing.
|
||||
/// </remarks>
|
||||
public ChatRestrictions Restrictions { get; init; } = ChatRestrictions.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// Defines <c>Sources</c> for the visual briefing feature.
|
||||
/// </summary>
|
||||
|
||||
@@ -42,7 +42,7 @@ internal sealed class VisualBriefingPlanStage(StructuredLlmStageRunner stageRunn
|
||||
await store.SaveBuildAsync(build, token);
|
||||
progressService.Publish(build);
|
||||
|
||||
var run = await stageRunner.RunAsync<VisualBriefingPlanResponse>(provider, profile, BuildSystemContract(), BuildPrompt(manifest, evidence),
|
||||
var run = await stageRunner.RunAsync<VisualBriefingPlanResponse>(provider, profile, manifest.Restrictions, BuildSystemContract(), BuildPrompt(manifest, evidence),
|
||||
[], VisualBriefingBuildStage.PLAN, build.OperationId, build.BuildId, response => VisualBriefingValidation.ValidatePlan(evidence, response), token);
|
||||
|
||||
stage.Attempts = run.Attempts;
|
||||
|
||||
@@ -42,7 +42,7 @@ internal sealed class VisualBriefingPresentationStage(StructuredLlmStageRunner s
|
||||
await store.SaveBuildAsync(build, token);
|
||||
progressService.Publish(build);
|
||||
|
||||
var run = await stageRunner.RunAsync<VisualBriefingDesignResponse>(provider, profile, BuildSystemContract(),
|
||||
var run = await stageRunner.RunAsync<VisualBriefingDesignResponse>(provider, profile, manifest.Restrictions, BuildSystemContract(),
|
||||
BuildPrompt(manifest, plan, parentPresentation), [], VisualBriefingBuildStage.DESIGN, build.OperationId, build.BuildId,
|
||||
response => ValidateDesign(manifest, plan, content, response), token);
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System.Text.Json;
|
||||
|
||||
using AIStudio.Chat;
|
||||
|
||||
namespace AIStudio.Assistants.VisualBriefing;
|
||||
|
||||
public sealed partial class VisualBriefingStore
|
||||
@@ -294,10 +296,14 @@ public sealed partial class VisualBriefingStore
|
||||
/// <summary>
|
||||
/// Defines <c>CreateAsync</c> for the visual briefing feature.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The restrictions are set here only, see VisualBriefingManifest.Restrictions: nothing later on loosens them.
|
||||
/// </remarks>
|
||||
public async Task<VisualBriefingManifest> CreateAsync(
|
||||
string name,
|
||||
string author,
|
||||
VisualBriefingLocalSettings settings,
|
||||
ChatRestrictions restrictions,
|
||||
Guid? briefingId = null,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
@@ -330,6 +336,7 @@ public sealed partial class VisualBriefingStore
|
||||
CreatedAtUtc = now,
|
||||
ModifiedAtUtc = now,
|
||||
Settings = settings,
|
||||
Restrictions = restrictions,
|
||||
};
|
||||
|
||||
await this.StoreManifestAtomicAsync(manifest, token);
|
||||
@@ -455,6 +462,8 @@ public sealed partial class VisualBriefingStore
|
||||
/// </summary>
|
||||
private async Task StoreManifestAtomicAsync(VisualBriefingManifest manifest, CancellationToken token)
|
||||
{
|
||||
// What is written now holds everything of the current version, the restrictions among them, which an older AI Studio cannot read:
|
||||
manifest.ManifestVersion = VisualBriefingVersions.MANIFEST;
|
||||
var json = JsonSerializer.Serialize(manifest, JSON_OPTIONS);
|
||||
await WriteTextAtomicAsync(this.ManifestPath(manifest.BriefingId), json, overwrite: true, token);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
using AIStudio.Chat;
|
||||
|
||||
namespace AIStudio.Assistants.VisualBriefing;
|
||||
|
||||
public sealed partial class VisualBriefingStore
|
||||
@@ -339,6 +341,10 @@ public sealed partial class VisualBriefingStore
|
||||
export.Name,
|
||||
export.Author,
|
||||
SettingsFromExport(export),
|
||||
|
||||
// An export is a file outside AI Studio, which carries neither the instruction nor any restrictions.
|
||||
// A briefing which exists here already keeps its own:
|
||||
ChatRestrictions.NONE,
|
||||
export.BriefingId,
|
||||
token);
|
||||
}
|
||||
@@ -440,6 +446,9 @@ public sealed partial class VisualBriefingStore
|
||||
parts.ExportManifest.Name,
|
||||
parts.ExportManifest.Author,
|
||||
SettingsFromExport(parts.ExportManifest),
|
||||
|
||||
// An export is a file outside AI Studio, which carries neither the instruction nor any restrictions:
|
||||
ChatRestrictions.NONE,
|
||||
copyId,
|
||||
token);
|
||||
|
||||
|
||||
@@ -9,7 +9,11 @@ public static class VisualBriefingVersions
|
||||
public const int ARTIFACT = 1;
|
||||
|
||||
/// <summary>Gets the project manifest contract version.</summary>
|
||||
public const int MANIFEST = 1;
|
||||
/// <remarks>
|
||||
/// Version 2 added the restrictions of a text sent to the briefing. An older version of AI Studio cannot
|
||||
/// read them, so it tells the user that the briefing needs a newer version instead of ignoring them.
|
||||
/// </remarks>
|
||||
public const int MANIFEST = 2;
|
||||
|
||||
/// <summary>Gets the canonical data schema version.</summary>
|
||||
public const int SCHEMA = 2;
|
||||
|
||||
Reference in new issue
Block a user