mirror of
				https://github.com/MindWorkAI/AI-Studio.git
				synced 2025-11-04 02:00:20 +00:00 
			
		
		
		
	Added plugin hot reloading (#377)
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				Build and Release / Read metadata (push) Has been cancelled
				
			
		
			
				
	
				Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg updater) (push) Has been cancelled
				
			
		
			
				
	
				Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis updater) (push) Has been cancelled
				
			
		
			
				
	
				Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg updater) (push) Has been cancelled
				
			
		
			
				
	
				Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis updater) (push) Has been cancelled
				
			
		
			
				
	
				Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage deb updater) (push) Has been cancelled
				
			
		
			
				
	
				Build and Release / Build app (linux-arm64) (push) Has been cancelled
				
			
		
			
				
	
				Build and Release / Prepare & create release (push) Has been cancelled
				
			
		
			
				
	
				Build and Release / Publish release (push) Has been cancelled
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	Build and Release / Read metadata (push) Has been cancelled
				
			Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg updater) (push) Has been cancelled
				
			Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis updater) (push) Has been cancelled
				
			Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg updater) (push) Has been cancelled
				
			Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis updater) (push) Has been cancelled
				
			Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage deb updater) (push) Has been cancelled
				
			Build and Release / Build app (linux-arm64) (push) Has been cancelled
				
			Build and Release / Prepare & create release (push) Has been cancelled
				
			Build and Release / Publish release (push) Has been cancelled
				
			This commit is contained in:
		
							parent
							
								
									b632854cd4
								
							
						
					
					
						commit
						d0074a6fc7
					
				@ -94,6 +94,9 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
 | 
			
		||||
            // Load (but not start) all plugins, without waiting for them:
 | 
			
		||||
            var pluginLoadingTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
 | 
			
		||||
            _ = PluginFactory.LoadAll(pluginLoadingTimeout.Token);
 | 
			
		||||
            
 | 
			
		||||
            // Set up hot reloading for plugins:
 | 
			
		||||
            PluginFactory.SetUpHotReloading();
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Register this component with the message bus:
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Components;
 | 
			
		||||
 | 
			
		||||
namespace AIStudio.Pages;
 | 
			
		||||
 | 
			
		||||
public partial class Plugins : ComponentBase
 | 
			
		||||
public partial class Plugins : ComponentBase, IMessageBusReceiver
 | 
			
		||||
{
 | 
			
		||||
    private const string GROUP_ENABLED = "Enabled";
 | 
			
		||||
    private const string GROUP_DISABLED = "Disabled";
 | 
			
		||||
@ -23,6 +23,9 @@ public partial class Plugins : ComponentBase
 | 
			
		||||
 | 
			
		||||
    protected override async Task OnInitializedAsync()
 | 
			
		||||
    {
 | 
			
		||||
        this.MessageBus.RegisterComponent(this);
 | 
			
		||||
        this.MessageBus.ApplyFilters(this, [], [ Event.PLUGINS_RELOADED ]);
 | 
			
		||||
        
 | 
			
		||||
        this.groupConfig = new TableGroupDefinition<IPluginMetadata>
 | 
			
		||||
        {
 | 
			
		||||
            Expandable = true,
 | 
			
		||||
@ -53,4 +56,27 @@ public partial class Plugins : ComponentBase
 | 
			
		||||
        await this.SettingsManager.StoreSettings();
 | 
			
		||||
        await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    #region Implementation of IMessageBusReceiver
 | 
			
		||||
 | 
			
		||||
    public string ComponentName => nameof(Plugins);
 | 
			
		||||
    
 | 
			
		||||
    public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
 | 
			
		||||
    {
 | 
			
		||||
        switch (triggeredEvent)
 | 
			
		||||
        {
 | 
			
		||||
            case Event.PLUGINS_RELOADED:
 | 
			
		||||
                this.InvokeAsync(this.StateHasChanged);
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return Task.CompletedTask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.FromResult<TResult?>(default);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion
 | 
			
		||||
}
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
using AIStudio.Agents;
 | 
			
		||||
using AIStudio.Settings;
 | 
			
		||||
using AIStudio.Tools.PluginSystem;
 | 
			
		||||
using AIStudio.Tools.Services;
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
 | 
			
		||||
@ -209,6 +210,7 @@ internal sealed class Program
 | 
			
		||||
        await serverTask;
 | 
			
		||||
        
 | 
			
		||||
        RUST_SERVICE.Dispose();
 | 
			
		||||
        PluginFactory.Dispose();
 | 
			
		||||
        programLogger.LogInformation("The AI Studio server was stopped.");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -8,6 +8,7 @@ public enum Event
 | 
			
		||||
    STATE_HAS_CHANGED,
 | 
			
		||||
    CONFIGURATION_CHANGED,
 | 
			
		||||
    COLOR_THEME_CHANGED,
 | 
			
		||||
    PLUGINS_RELOADED,
 | 
			
		||||
    
 | 
			
		||||
    // Update events:
 | 
			
		||||
    USER_SEARCH_FOR_UPDATE,
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,33 @@
 | 
			
		||||
namespace AIStudio.Tools.PluginSystem;
 | 
			
		||||
 | 
			
		||||
public static partial class PluginFactory
 | 
			
		||||
{
 | 
			
		||||
    public static void SetUpHotReloading()
 | 
			
		||||
    {
 | 
			
		||||
        LOG.LogInformation($"Start hot reloading plugins for path '{HOT_RELOAD_WATCHER.Path}'.");
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var messageBus = Program.SERVICE_PROVIDER.GetRequiredService<MessageBus>();
 | 
			
		||||
        
 | 
			
		||||
            HOT_RELOAD_WATCHER.IncludeSubdirectories = true;
 | 
			
		||||
            HOT_RELOAD_WATCHER.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName;
 | 
			
		||||
            HOT_RELOAD_WATCHER.Filter = "*.lua";
 | 
			
		||||
            HOT_RELOAD_WATCHER.Changed += async (_, args) =>
 | 
			
		||||
            {
 | 
			
		||||
                LOG.LogInformation($"File changed: {args.FullPath}");
 | 
			
		||||
                await LoadAll();
 | 
			
		||||
                await messageBus.SendMessage<bool>(null, Event.PLUGINS_RELOADED);
 | 
			
		||||
            };
 | 
			
		||||
            
 | 
			
		||||
            HOT_RELOAD_WATCHER.EnableRaisingEvents = true;
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception e)
 | 
			
		||||
        {
 | 
			
		||||
            LOG.LogError(e, "Error while setting up hot reloading.");
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
            LOG.LogInformation("Hot reloading plugins set up.");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,5 +1,3 @@
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
 | 
			
		||||
using Microsoft.Extensions.FileProviders;
 | 
			
		||||
 | 
			
		||||
namespace AIStudio.Tools.PluginSystem;
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,8 @@ public static partial class PluginFactory
 | 
			
		||||
    
 | 
			
		||||
    private static readonly string INTERNAL_PLUGINS_ROOT = Path.Join(PLUGINS_ROOT, ".internal");
 | 
			
		||||
    
 | 
			
		||||
    private static readonly FileSystemWatcher HOT_RELOAD_WATCHER = new(PLUGINS_ROOT);
 | 
			
		||||
 | 
			
		||||
    private static readonly List<IPluginMetadata> AVAILABLE_PLUGINS = [];
 | 
			
		||||
    
 | 
			
		||||
    /// <summary>
 | 
			
		||||
@ -45,6 +47,8 @@ public static partial class PluginFactory
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        AVAILABLE_PLUGINS.Clear();
 | 
			
		||||
        
 | 
			
		||||
        //
 | 
			
		||||
        // The easiest way to load all plugins is to find all `plugin.lua` files and load them.
 | 
			
		||||
        // By convention, each plugin is enforced to have a `plugin.lua` file.
 | 
			
		||||
@ -136,4 +140,9 @@ public static partial class PluginFactory
 | 
			
		||||
            _ => new NoPlugin("This plugin type is not supported yet. Please try again with a future version of AI Studio.")
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public static void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
        HOT_RELOAD_WATCHER.Dispose();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -2,5 +2,6 @@
 | 
			
		||||
- Added a feature flag for the plugin system. This flag is disabled by default and can be enabled inside the app settings. Please note that this feature is still in development; there are no plugins available yet.
 | 
			
		||||
- Added the Lua library we use for the plugin system to the about page.
 | 
			
		||||
- Added the plugin overview page. This page shows all installed plugins and allows you to enable or disable them. It is only available when the plugin preview feature is enabled.
 | 
			
		||||
- Added hot reloading for plugins. When any plugin is changed, the app will automatically reload the plugin without needing to restart the app.
 | 
			
		||||
- Fixed the preview tooltip component not showing the correct position when used inside a scrollable container.
 | 
			
		||||
- Upgraded to Rust 1.85.1
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user