mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-10 23:47:21 +00:00
Added a way to see the complete system prompt of a chat (#1001)
Co-authored-by: Thorsten Sommer <SommerEngineering@users.noreply.github.com>
This commit is contained in:
1 parent
dce0b534aa
commit
c37393817b
14 files changed
+815
-70
No files matched your search
@@ -0,0 +1,32 @@
|
||||
@inherits MSGComponentBase
|
||||
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||
@T("These are the instructions the AI receives ahead of your conversation, the system prompt of this chat. AI Studio assembles it from the parts below, in the order in which the AI reads them.")
|
||||
</MudJustifiedText>
|
||||
|
||||
@foreach (var section in this.sections)
|
||||
{
|
||||
<MudText Typo="Typo.h6" Class="mt-4 mb-2">@section.Heading</MudText>
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(section.Notice))
|
||||
{
|
||||
<MudAlert Severity="Severity.Info" Dense="@true" Class="mb-2">@section.Notice</MudAlert>
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrEmpty(section.Text))
|
||||
{
|
||||
<TextInfoLines Lines="1" MaxLines="12" Value="@section.Text" ClipboardTooltipSubject="@T("this part")"/>
|
||||
}
|
||||
}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="@this.CopyCompletePrompt" Variant="Variant.Outlined" StartIcon="@Icons.Material.Filled.ContentCopy">
|
||||
@T("Copy the complete prompt")
|
||||
</MudButton>
|
||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled" Color="Color.Primary">
|
||||
@T("Close")
|
||||
</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
@@ -0,0 +1,142 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Components;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Dialogs;
|
||||
|
||||
/// <summary>
|
||||
/// Shows the system prompt a chat sends, part by part, each under the name of where it comes from.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Nobody types this prompt as it is sent. A chat template replaces the default, a profile, the
|
||||
/// retrieved data of a data source, and the policy of the selected tools are added to it, and the
|
||||
/// date goes in front. Each part stands under its own heading, in the order of the prompt, so a
|
||||
/// person does not have to work out from the text alone where a paragraph came from.<br/><br/>
|
||||
///
|
||||
/// The parts are those ChatThread.BuildSystemPrompt recorded, so the dialog decides nothing about
|
||||
/// them. Only the retrieved data needs a word of its own: AI Studio works it out anew whenever a
|
||||
/// message is sent, see AISrcSelWithRetCtxVal.ProcessAsync. What the dialog shows of it was sent with
|
||||
/// the last message, and the next one gets what the search finds then. Before the first search,
|
||||
/// e.g., in a new chat or one just loaded, there is nothing to show, but the chat may still search.
|
||||
/// Two more things stand where they would be: a selected profile which the chat template does not
|
||||
/// allow, and the instruction the tool calling loop adds once the limit of tool calls is reached.
|
||||
/// </remarks>
|
||||
public partial class SystemPromptDialog : MSGComponentBase
|
||||
{
|
||||
[CascadingParameter]
|
||||
private IMudDialogInstance MudDialog { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The system prompt, together with the parts it was assembled from.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public PreparedSystemPrompt Prompt { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The display names of the tools whose usage policy is part of the prompt.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public IReadOnlyList<string> ToolNames { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// How the next message searches the data sources of the chat, or null when it searches none.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public DataSourceRetrievalMode? RetrievalMode { get; set; }
|
||||
|
||||
private IReadOnlyList<Section> sections = [];
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
this.sections = this.BuildSections();
|
||||
base.OnParametersSet();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Lists the parts of the prompt in its order, with what stands where nothing was added.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Retrieved data follows the base prompt or the chat template, and a profile follows the
|
||||
/// retrieved data. So a chat which has not retrieved anything yet, but searches its data sources,
|
||||
/// says so right after the base prompt, and a profile which the chat template switched off is
|
||||
/// named where it would otherwise stand.
|
||||
/// </remarks>
|
||||
private IReadOnlyList<Section> BuildSections()
|
||||
{
|
||||
var result = new List<Section>(this.Prompt.Parts.Count + 2);
|
||||
var hasRetrievedData = this.Prompt.Parts.Any(part => part.Kind is SystemPromptPartKind.RETRIEVED_DATA);
|
||||
foreach (var part in this.Prompt.Parts)
|
||||
{
|
||||
result.Add(this.SectionOf(part));
|
||||
switch (part.Kind)
|
||||
{
|
||||
case SystemPromptPartKind.BASE_PROMPT or SystemPromptPartKind.CHAT_TEMPLATE when !hasRetrievedData:
|
||||
if (this.NoticeWithoutRetrievedData is { Length: > 0 } notice)
|
||||
result.Add(new(T("Data from your data sources"), string.Empty, notice));
|
||||
|
||||
this.AddSwitchedOffProfile(result);
|
||||
break;
|
||||
|
||||
case SystemPromptPartKind.RETRIEVED_DATA:
|
||||
this.AddSwitchedOffProfile(result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Section SectionOf(SystemPromptPart part) => part.Kind switch
|
||||
{
|
||||
SystemPromptPartKind.DATE_TIME => new(T("Current date and time"), part.Text, string.Empty),
|
||||
SystemPromptPartKind.BASE_PROMPT when part.Text == SystemPrompts.DEFAULT => new(T("Default prompt of AI Studio"), part.Text, string.Empty),
|
||||
SystemPromptPartKind.BASE_PROMPT => new(T("System prompt of this chat"), part.Text, string.Empty),
|
||||
SystemPromptPartKind.CHAT_TEMPLATE => new(string.Format(T("Chat template '{0}'"), part.SourceName), part.Text, string.Empty),
|
||||
SystemPromptPartKind.RETRIEVED_DATA => new(T("Data from your data sources"), part.Text, this.NoticeWithRetrievedData),
|
||||
SystemPromptPartKind.PROFILE => new(string.Format(T("Profile '{0}'"), part.SourceName), part.Text, string.Empty),
|
||||
SystemPromptPartKind.TOOL_INSTRUCTIONS => new(string.Format(T("Instructions for your tools: {0}"), string.Join(", ", this.ToolNames)), part.Text, T("When the AI reaches the limit of tool calls for one answer, AI Studio adds one more instruction at the end: to answer with what the tools have returned so far.")),
|
||||
_ => new(string.Empty, part.Text, string.Empty),
|
||||
};
|
||||
|
||||
private void AddSwitchedOffProfile(List<Section> result)
|
||||
{
|
||||
if (this.Prompt.SwitchedOffProfile == Profile.NO_PROFILE)
|
||||
return;
|
||||
|
||||
result.Add(new(string.Format(T("Profile '{0}'"), this.Prompt.SwitchedOffProfile.Name), string.Empty, T("The chat template does not allow profiles, so your selected profile is not part of the prompt.")));
|
||||
}
|
||||
|
||||
private string NoticeWithRetrievedData => this.RetrievalMode is DataSourceRetrievalMode.EVERY_MESSAGE
|
||||
? T("AI Studio retrieved this data for your last message. When you send your next message, AI Studio searches your data sources again and replaces this data with what it finds then.")
|
||||
: T("AI Studio sent this data with your last message. When you send your next message, AI Studio works out this part anew.");
|
||||
|
||||
private string NoticeWithoutRetrievedData => this.RetrievalMode switch
|
||||
{
|
||||
DataSourceRetrievalMode.EVERY_MESSAGE => T("When you send your next message, AI Studio searches your data sources and adds what it finds here."),
|
||||
DataSourceRetrievalMode.SEMANTIC_SEARCH => T("The AI searches your data sources itself whenever a question calls for it. What it finds reaches the AI as the result of that search, not as part of this prompt."),
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
private async Task CopyCompletePrompt() => await this.RustService.CopyText2Clipboard(this.Prompt.Text);
|
||||
|
||||
private void Close() => this.MudDialog.Close();
|
||||
|
||||
/// <summary>
|
||||
/// One part of the prompt as the dialog shows it.
|
||||
/// </summary>
|
||||
/// <param name="Heading">Where the part comes from.</param>
|
||||
/// <param name="Text">The text of the part, or empty when only the notice stands there.</param>
|
||||
/// <param name="Notice">What the user should know about the part, or empty when there is nothing to say.</param>
|
||||
private sealed record Section(string Heading, string Text, string Notice);
|
||||
}
|
||||
Reference in new issue
Block a user