2026-06-23 09:37:40 +00:00
using System.Text ;
using AIStudio.Dialogs.Settings ;
2026-06-22 18:54:16 +00:00
namespace AIStudio.Assistants.Meta ;
public partial class AssistantMetaAssistant : AssistantBaseCore < NoSettingsPanel >
{
2026-06-23 09:37:40 +00:00
private static readonly ILogger LOGGER = Program . LOGGER_FACTORY . CreateLogger ( nameof ( AssistantMetaAssistant ) ) ;
2026-06-22 18:54:16 +00:00
protected override Tools . Components Component = > Tools . Components . META_ASSISTANT ;
protected override string Title = > T ( "Assistant Builder" ) ;
protected override string Description = > string . Empty ;
protected override string SystemPrompt = > this . AssembleSystemPrompt ( ) ;
protected override string SubmitText = > T ( "Build your assistant" ) ;
protected override Func < Task > SubmitAction = > this . GenerateLuaAssistant ;
protected override bool SubmitDisabled = > this . isAgentRunning ;
protected override bool ShowResult { get ; }
protected override bool AllowProfiles { get ; }
protected override bool ShowProfileSelection { get ; }
protected override IReadOnlyList < IButtonData > FooterButtons = > [ ] ;
protected override bool HasSettingsPanel { get ; }
private bool isAgentRunning ;
2026-06-23 09:37:40 +00:00
private AssistantCategory selectedCategory ;
private string customCategory = string . Empty ;
2026-06-22 18:54:16 +00:00
private static readonly AssistantContextFile [ ] ASSISTANT_CONTEXT_FILES =
[
new ( "Assistant plugin schema" , "Plugins/assistants/README.md" , IsRequired : true ) ,
new ( "Lua manifest template" , "Plugins/assistants/plugin.lua" , IsRequired : true ) ,
new ( "Translation example" , "Plugins/assistants/examples/translation/plugin.lua" , IsRequired : false ) ,
] ;
private readonly record struct AssistantContextFile (
string Title ,
string RelativePath ,
bool IsRequired ) ;
2026-06-23 09:37:40 +00:00
2026-06-22 18:54:16 +00:00
#region Overrides of ComponentBase
2026-06-23 09:37:40 +00:00
2026-06-22 18:54:16 +00:00
protected override async Task OnInitializedAsync ( )
{
await base . OnInitializedAsync ( ) ;
}
2026-06-23 09:37:40 +00:00
2026-06-22 18:54:16 +00:00
#endregion
2026-06-23 09:37:40 +00:00
2026-06-22 18:54:16 +00:00
protected override void ResetForm ( )
{
2026-06-23 09:37:40 +00:00
this . selectedCategory = AssistantCategory . AS_IS ;
this . customCategory = string . Empty ;
2026-06-22 18:54:16 +00:00
}
protected override bool MightPreselectValues ( )
{
return false ;
}
2026-06-23 09:37:40 +00:00
2026-06-22 18:54:16 +00:00
private string AssembleSystemPrompt ( ) = > string . Empty ;
2026-06-23 09:37:40 +00:00
private string? ValidatingCategory ( AssistantCategory category )
{
if ( category is AssistantCategory . AS_IS )
return T ( "Please select an assistant category." ) ;
return null ;
}
private string? ValidateCustomCategory ( string category )
{
if ( this . selectedCategory is AssistantCategory . OTHER & & string . IsNullOrWhiteSpace ( category ) )
return T ( "Please provide a custom category." ) ;
return null ;
}
2026-06-22 18:54:16 +00:00
private async Task GenerateLuaAssistant ( )
{
2026-06-23 09:37:40 +00:00
await this . Form ! . Validate ( ) ;
if ( ! this . InputIsValid )
return ;
2026-06-22 18:54:16 +00:00
this . CreateChatThread ( ) ;
var time = this . AddUserRequest (
$"" "
2026-06-23 09:37:40 +00:00
Assistant category : { this . GetSelectedCategoryName ( ) }
2026-06-22 18:54:16 +00:00
Remind me to replace this placeholder with the real lua plugin context
"" ");
2026-06-23 09:37:40 +00:00
2026-06-22 18:54:16 +00:00
await this . AddAIResponseAsync ( time ) ;
}
2026-06-23 09:37:40 +00:00
private string GetSelectedCategoryName ( ) = > this . selectedCategory is AssistantCategory . OTHER
? this . customCategory
: this . selectedCategory . Name ( ) ;
2026-06-22 18:54:16 +00:00
private static async Task < string > ReadAppResourceTextAsync ( string relativePath )
{
relativePath = relativePath . Replace ( '\\' , '/' ) ;
#if DEBUG
var filePath = Path . Join ( Environment . CurrentDirectory , relativePath ) ;
return File . Exists ( filePath )
? await File . ReadAllTextAsync ( filePath )
: string . Empty ;
#else
var provider = new ManifestEmbeddedFileProvider ( Assembly . GetAssembly ( type : typeof ( Program ) ) ! ) ;
var file = provider . GetFileInfo ( relativePath ) ;
if ( ! file . Exists )
return string . Empty ;
await using var stream = file . CreateReadStream ( ) ;
using var reader = new StreamReader ( stream , Encoding . UTF8 ) ;
return await reader . ReadToEndAsync ( ) ;
#endif
}
2026-06-23 09:37:40 +00:00
private async Task < string > LoadAssistantBuilderContextAsync ( )
{
var builder = new StringBuilder ( ) ;
foreach ( var contextFile in ASSISTANT_CONTEXT_FILES )
{
var content = await ReadAppResourceTextAsync ( contextFile . RelativePath ) ;
if ( string . IsNullOrWhiteSpace ( content ) )
{
LOGGER . LogError ( $"The context for \" { contextFile . Title } \ " could not be read from the assembly. Path: {contextFile.RelativePath}" ) ;
if ( contextFile . IsRequired )
{
await MessageBus . INSTANCE . SendError ( new ( Icons . Material . Filled . SettingsSuggest , string . Format ( T ( "The Assistant-Builder was not able to read the plugin manifest and therefore cannot safely generate your assistant right now." ) ) ) ) ;
this . isAgentRunning = true ;
return string . Empty ;
}
continue ;
}
builder . AppendLine ( $"# {contextFile.Title}" ) ;
builder . AppendLine ( $"Source: {contextFile.RelativePath}" ) ;
builder . AppendLine ( "<context>" ) ;
builder . AppendLine ( content . Trim ( ) ) ;
builder . AppendLine ( "</context>" ) ;
builder . AppendLine ( ) ;
}
return builder . ToString ( ) . Trim ( ) ;
}
2026-06-22 18:54:16 +00:00
}