Skip to content

Commit

Permalink
Use separate AssemblyLoadContext for every agent plugin (#56)
Browse files Browse the repository at this point in the history
  • Loading branch information
daxian-dbw authored Jan 23, 2024
1 parent 2efc3d3 commit 249faf0
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 7 deletions.
4 changes: 3 additions & 1 deletion shell/ShellCopilot.Kernel/LLMAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ namespace ShellCopilot.Kernel;
internal class LLMAgent
{
internal ILLMAgent Impl { get; }
internal AgentAssemblyLoadContext LoadContext { get; }
internal bool OrchestratorRoleDisabled { set; get; }
internal bool AnalyzerRoleDisabled { set; get; }

internal LLMAgent(ILLMAgent agent)
internal LLMAgent(ILLMAgent agent, AgentAssemblyLoadContext loadContext)
{
Impl = agent;
LoadContext = loadContext;

OrchestratorRoleDisabled = false;
AnalyzerRoleDisabled = false;
Expand Down
10 changes: 6 additions & 4 deletions shell/ShellCopilot.Kernel/Shell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,11 @@ internal List<string> GetCodeBlockFromLastResponse()
/// <summary>
/// Load a plugin assembly file and process the agents defined in it.
/// </summary>
internal void ProcessAgentPlugin(string pluginFile)
internal void ProcessAgentPlugin(string pluginName, string pluginDir, string pluginFile)
{
Assembly plugin = Assembly.LoadFrom(pluginFile);
AgentAssemblyLoadContext context = new(pluginName, pluginDir);
Assembly plugin = context.LoadFromAssemblyPath(pluginFile);

foreach (Type type in plugin.ExportedTypes)
{
if (!typeof(ILLMAgent).IsAssignableFrom(type))
Expand All @@ -121,7 +123,7 @@ internal void ProcessAgentPlugin(string pluginFile)
};

agent.Initialize(config);
_agents.Add(new LLMAgent(agent));
_agents.Add(new LLMAgent(agent, context));
}
}

Expand All @@ -147,7 +149,7 @@ private void LoadAvailableAgents()

try
{
ProcessAgentPlugin(file);
ProcessAgentPlugin(name, dir, file);
}
catch (Exception ex)
{
Expand Down
3 changes: 1 addition & 2 deletions shell/ShellCopilot.Kernel/Utility/Clipboard.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Diagnostics;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

Expand Down
36 changes: 36 additions & 0 deletions shell/ShellCopilot.Kernel/Utility/LoadContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.Loader;

namespace ShellCopilot.Kernel;

internal class AgentAssemblyLoadContext : AssemblyLoadContext
{
private readonly string _dependencyDir;

internal AgentAssemblyLoadContext(string name, string dependencyDir)
: base($"{name.Replace(' ', '.')}-ALC", isCollectible: false)
{
if (!Directory.Exists(dependencyDir))
{
throw new ArgumentException($"The agent home directory '{dependencyDir}' doesn't exist.", nameof(dependencyDir));
}

// Save the full path to the dependencies directory when creating the context.
_dependencyDir = dependencyDir;
}

protected override Assembly Load(AssemblyName assemblyName)
{
// Create a path to the assembly in the dependencies directory.
string path = Path.Combine(_dependencyDir, $"{assemblyName.Name}.dll");

if (File.Exists(path))
{
// If the assembly exists in our dependency directory, then load it into this load context.
return LoadFromAssemblyPath(path);
}

// Otherwise we will depend on the default load context to resolve the request.
return null;
}
}

0 comments on commit 249faf0

Please sign in to comment.