diff --git a/Directory.Build.props b/Directory.Build.props
index dcda08f8..1649ba72 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -28,7 +28,6 @@
true
snupkg
true
-
false
@@ -36,4 +35,10 @@
$(NoWarn);IL2026;IL2046;IL2057;IL2067;IL2070;IL2072;IL2075;IL2087;IL2091
+
+
+ 3.6.0-preview.4036
+ 3.6.0-preview.1297
+
+
\ No newline at end of file
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 9b67d493..bd9a5f9a 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -3,13 +3,6 @@
true
false
-
- 3.7.0-preview.3961
- 3.7.0-preview.1198
- 8.0.22
- 9.0.11
- 10.0.0
-
@@ -47,6 +40,7 @@
+
@@ -56,6 +50,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -106,28 +168,16 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -137,10 +187,10 @@
-
+
@@ -163,9 +213,8 @@
-
-
+
@@ -177,47 +226,4 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Elsa.Extensions.sln b/Elsa.Extensions.sln
index 2e5180f1..08021ec4 100644
--- a/Elsa.Extensions.sln
+++ b/Elsa.Extensions.sln
@@ -277,6 +277,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "data", "data", "{6D2A4421-A
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Data.Csv", "src\modules\data\Elsa.Data.Csv\Elsa.Data.Csv.csproj", "{015646EB-EC33-4ADF-8417-B9D1A6B7CF06}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "agents", "agents", "{60A25F2D-634D-438A-87EA-F204677978BE}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -751,6 +753,7 @@ Global
{BEB35A25-5A80-4B56-A1EC-F005288CD11C} = {AAD61D93-7C78-42C4-9F37-2564D127A668}
{6D2A4421-A388-4BEE-BB11-D0FC32A80A10} = {30CF0330-4B09-4784-B499-46BED303810B}
{015646EB-EC33-4ADF-8417-B9D1A6B7CF06} = {6D2A4421-A388-4BEE-BB11-D0FC32A80A10}
+ {60A25F2D-634D-438A-87EA-F204677978BE} = {3DDE6F89-531C-47F8-9CD7-7A4E6984FA48}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {11A771DA-B728-445E-8A88-AE1C84C3B3A6}
diff --git a/src/modules/agents/Elsa.Agents.Activities/Activities/CodeFirstAgentActivity.cs b/src/modules/agents/Elsa.Agents.Activities/Activities/CodeFirstAgentActivity.cs
new file mode 100644
index 00000000..6915417b
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Activities/Activities/CodeFirstAgentActivity.cs
@@ -0,0 +1,71 @@
+using System.ComponentModel;
+using System.Dynamic;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Elsa.Expressions.Helpers;
+using Elsa.Agents.Activities.ActivityProviders;
+using Elsa.Workflows;
+using Elsa.Workflows.Models;
+using static Elsa.Agents.Activities.Extensions.ResponseHelpers;
+
+namespace Elsa.Agents.Activities;
+
+///
+/// An activity that executes a function of a skilled agent. This is an internal activity that is used by .
+///
+[Browsable(false)]
+public class CodeFirstAgentActivity : CodeActivity
+{
+ private static JsonSerializerOptions? _serializerOptions;
+
+ [JsonIgnore] internal string AgentName { get; set; } = null!;
+
+ ///
+ protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
+ {
+ var cancellationToken = context.CancellationToken;
+ var activityDescriptor = context.ActivityDescriptor;
+ var inputDescriptors = activityDescriptor.Inputs;
+ var functionInput = new Dictionary();
+
+ foreach (var inputDescriptor in inputDescriptors)
+ {
+ var input = (Input?)inputDescriptor.ValueGetter(this);
+ var inputValue = input != null ? context.Get(input.MemoryBlockReference()) : null;
+
+ if (inputValue is ExpandoObject expandoObject)
+ inputValue = expandoObject.ConvertTo();
+
+ functionInput[inputDescriptor.Name] = inputValue;
+ }
+
+ // Resolve the agent via the unified abstraction.
+ var agentResolver = context.GetRequiredService();
+ var agent = await agentResolver.ResolveAsync(AgentName, cancellationToken);
+ var agentType = agent.GetType();
+ var agentPropertyLookup = agentType.GetProperties().ToDictionary(x => x.Name, x => x);
+
+ // Copy activity input descriptor values into the agent public properties:
+ foreach (var inputDescriptor in inputDescriptors)
+ {
+ var input = (Input?)inputDescriptor.ValueGetter(this);
+ var inputValue = input != null ? context.Get(input.MemoryBlockReference()) : null;
+ agentPropertyLookup[inputDescriptor.Name].SetValue(agent, inputValue);
+ }
+
+ var agentExecutionContext = new AgentExecutionContext { CancellationToken = context.CancellationToken };
+ var agentExecutionResponse = await agent.RunAsync(agentExecutionContext);
+ var responseText = StripCodeFences(agentExecutionResponse.Text);
+ var isJsonResponse = IsJsonResponse(responseText);
+ var outputType = context.ActivityDescriptor.Outputs.Single().Type;
+
+ // If the target type is object and the response is in JSON format, we want it to be deserialized into an ExpandoObject for dynamic field access.
+ if (outputType == typeof(object) && isJsonResponse)
+ outputType = typeof(ExpandoObject);
+
+ var outputValue = isJsonResponse ? responseText.ConvertTo(outputType) : responseText;
+ var outputDescriptor = activityDescriptor.Outputs.Single();
+ var output = (Output?)outputDescriptor.ValueGetter(this);
+ context.Set(output, outputValue, "Output");
+ }
+}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Activities/Activities/AgentActivity.cs b/src/modules/agents/Elsa.Agents.Activities/Activities/ConfiguredAgentActivity.cs
similarity index 63%
rename from src/modules/agents/Elsa.Agents.Activities/Activities/AgentActivity.cs
rename to src/modules/agents/Elsa.Agents.Activities/Activities/ConfiguredAgentActivity.cs
index 6fa3213a..49b66dbe 100644
--- a/src/modules/agents/Elsa.Agents.Activities/Activities/AgentActivity.cs
+++ b/src/modules/agents/Elsa.Agents.Activities/Activities/ConfiguredAgentActivity.cs
@@ -4,21 +4,21 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Unicode;
-using Elsa.Agents;
using Elsa.Expressions.Helpers;
using Elsa.Extensions;
using Elsa.Agents.Activities.ActivityProviders;
using Elsa.Workflows;
using Elsa.Workflows.Models;
using Elsa.Workflows.Serialization.Converters;
+using static Elsa.Agents.Activities.Extensions.ResponseHelpers;
namespace Elsa.Agents.Activities;
///
-/// An activity that executes a function of a skilled agent. This is an internal activity that is used by .
+/// An activity that executes a function of a skilled agent. This is an internal activity that is used by .
///
[Browsable(false)]
-public class AgentActivity : CodeActivity
+public class ConfiguredAgentActivity : CodeActivity
{
private static JsonSerializerOptions? _serializerOptions;
@@ -44,30 +44,31 @@ protected override async ValueTask ExecuteAsync(ActivityExecutionContext context
var inputValue = input != null ? context.Get(input.MemoryBlockReference()) : null;
if (inputValue is ExpandoObject expandoObject)
- {
inputValue = expandoObject.ConvertTo();
- }
functionInput[inputDescriptor.Name] = inputValue;
}
- var agentInvoker = context.GetRequiredService();
- var result = await agentInvoker.InvokeAgentAsync(AgentName, functionInput, context.CancellationToken);
- var json = result.ChatMessageContent.Content?.Trim();
-
- if (string.IsNullOrWhiteSpace(json))
- throw new InvalidOperationException("The message content is empty or null.");
-
+ var agentInvoker = context.GetRequiredService();
+ var request = new InvokeAgentRequest
+ {
+ AgentName = AgentName,
+ Input = functionInput,
+ CancellationToken = context.CancellationToken
+ };
+ var agentExecutionResponse = await agentInvoker.InvokeAsync(request);
+ var responseText = StripCodeFences(agentExecutionResponse.ChatMessageContent.Content!);
+ var isJsonResponse = IsJsonResponse(responseText);
var outputType = context.ActivityDescriptor.Outputs.Single().Type;
- // If the target type is object, we want the JSON to be deserialized into an ExpandoObject for dynamic field access.
- if (outputType == typeof(object))
+ // If the target type is object and the response is in JSON format, we want it to be deserialized into an ExpandoObject for dynamic field access.
+ if (outputType == typeof(object) && isJsonResponse)
outputType = typeof(ExpandoObject);
var converterOptions = new ObjectConverterOptions(SerializerOptions);
- var outputValue = json.ConvertTo(outputType, converterOptions);
+ var outputValue = isJsonResponse ? responseText.ConvertTo(outputType, converterOptions) : responseText;
var outputDescriptor = activityDescriptor.Outputs.Single();
- var output = (Output)outputDescriptor.ValueGetter(this);
- context.Set(output, outputValue);
+ var output = (Output?)outputDescriptor.ValueGetter(this);
+ context.Set(output, outputValue, "Output");
}
}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Activities/ActivityProviders/AgentActivityProvider.cs b/src/modules/agents/Elsa.Agents.Activities/ActivityProviders/AgentActivityProvider.cs
deleted file mode 100644
index 764d0be6..00000000
--- a/src/modules/agents/Elsa.Agents.Activities/ActivityProviders/AgentActivityProvider.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-using Elsa.Agents;
-using Elsa.Expressions.Contracts;
-using Elsa.Expressions.Extensions;
-using Elsa.Extensions;
-using Elsa.Workflows;
-using Elsa.Workflows.Models;
-using Humanizer;
-using JetBrains.Annotations;
-
-namespace Elsa.Agents.Activities.ActivityProviders;
-
-///
-/// Provides activities for each function of registered agents.
-///
-[UsedImplicitly]
-public class AgentActivityProvider(
- IKernelConfigProvider kernelConfigProvider,
- IActivityDescriber activityDescriber,
- IWellKnownTypeRegistry wellKnownTypeRegistry
-) : IActivityProvider
-{
- ///
- public async ValueTask> GetDescriptorsAsync(CancellationToken cancellationToken = default)
- {
- var kernelConfig = await kernelConfigProvider.GetKernelConfigAsync(cancellationToken);
- var agents = kernelConfig.Agents;
- var activityDescriptors = new List();
-
- foreach (var kvp in agents)
- {
- var agentConfig = kvp.Value;
- var activityDescriptor = await activityDescriber.DescribeActivityAsync(typeof(AgentActivity), cancellationToken);
- var activityTypeName = $"Elsa.Agents.{agentConfig.Name.Pascalize()}";
- activityDescriptor.Name = agentConfig.Name.Pascalize();
- activityDescriptor.TypeName = activityTypeName;
- activityDescriptor.Description = agentConfig.Description;
- activityDescriptor.DisplayName = agentConfig.Name.Humanize().Transform(To.TitleCase);
- activityDescriptor.IsBrowsable = true;
- activityDescriptor.Category = "Agents";
- activityDescriptor.Kind = ActivityKind.Task;
- activityDescriptor.CustomProperties["RootType"] = nameof(AgentActivity);
-
- activityDescriptor.Constructor = context =>
- {
- var activity = context.CreateActivity();
- activity.Type = activityTypeName;
- activity.AgentName = agentConfig.Name;
- return activity;
- };
-
- activityDescriptors.Add(activityDescriptor);
- activityDescriptor.Inputs.Clear();
-
- foreach (var inputVariable in agentConfig.InputVariables)
- {
- var inputName = inputVariable.Name;
- var inputType = inputVariable.Type == null! ? "object" : inputVariable.Type;
- var nakedInputType = wellKnownTypeRegistry.GetTypeOrDefault(inputType);
- var inputDescriptor = new InputDescriptor
- {
- Name = inputVariable.Name,
- DisplayName = inputVariable.Name.Humanize(),
- Description = inputVariable.Description,
- Type = nakedInputType,
- ValueGetter = activity => activity.SyntheticProperties.GetValueOrDefault(inputName),
- ValueSetter = (activity, value) => activity.SyntheticProperties[inputName] = value!,
- IsSynthetic = true,
- IsWrapped = true,
- UIHint = ActivityDescriber.GetUIHint(nakedInputType)
- };
- activityDescriptor.Inputs.Add(inputDescriptor);
- }
-
- activityDescriptor.Outputs.Clear();
- var outputVariable = agentConfig.OutputVariable;
- var outputType = outputVariable.Type == null! ? "object" : outputVariable.Type;
- var nakedOutputType = wellKnownTypeRegistry.GetTypeOrDefault(outputType);
- var outputName = "Output";
- var outputDescriptor = new OutputDescriptor
- {
- Name = outputName,
- Description = agentConfig.OutputVariable.Description,
- Type = nakedOutputType,
- IsSynthetic = true,
- ValueGetter = activity => activity.SyntheticProperties.GetValueOrDefault(outputName),
- ValueSetter = (activity, value) => activity.SyntheticProperties[outputName] = value!,
- };
- activityDescriptor.Outputs.Add(outputDescriptor);
- }
-
- return activityDescriptors;
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Activities/ActivityProviders/CodeFirstAgentActivityProvider.cs b/src/modules/agents/Elsa.Agents.Activities/ActivityProviders/CodeFirstAgentActivityProvider.cs
new file mode 100644
index 00000000..9a8f1704
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Activities/ActivityProviders/CodeFirstAgentActivityProvider.cs
@@ -0,0 +1,127 @@
+using System.ComponentModel;
+using System.Reflection;
+using Elsa.Expressions.Contracts;
+using Elsa.Expressions.Extensions;
+using Elsa.Extensions;
+using Elsa.Workflows;
+using Elsa.Workflows.Models;
+using Humanizer;
+using JetBrains.Annotations;
+using Microsoft.Extensions.Options;
+
+namespace Elsa.Agents.Activities.ActivityProviders;
+
+///
+/// Provides activities for each code-first agent registered via .
+/// Inputs are derived from public properties on the agent type using simple
+/// reflection rules. Execution is delegated to
+/// via the common abstraction.
+///
+[UsedImplicitly]
+public class CodeFirstAgentActivityProvider(
+ IOptions agentOptions,
+ IActivityDescriber activityDescriber,
+ IWellKnownTypeRegistry wellKnownTypeRegistry) : IActivityProvider
+{
+ public async ValueTask> GetDescriptorsAsync(CancellationToken cancellationToken = default)
+ {
+ var descriptors = new List();
+
+ foreach (var kvp in agentOptions.Value.AgentTypes)
+ {
+ var key = kvp.Key;
+ var type = kvp.Value;
+ var descriptor = await CreateDescriptorForAgentAsync(key, type, cancellationToken);
+ descriptors.Add(descriptor);
+ }
+
+ return descriptors;
+ }
+
+ private async Task CreateDescriptorForAgentAsync(string key, Type agentType, CancellationToken cancellationToken)
+ {
+ var descriptor = await activityDescriber.DescribeActivityAsync(typeof(CodeFirstAgentActivity), cancellationToken);
+ var activityTypeName = $"Elsa.Agents.CodeFirst.{key.Pascalize()}";
+
+ descriptor.Name = key.Pascalize();
+ descriptor.TypeName = activityTypeName;
+ descriptor.DisplayName = key.Humanize().Transform(To.TitleCase);
+ descriptor.Category = "Agents";
+ descriptor.Kind = ActivityKind.Task;
+ descriptor.RunAsynchronously = true;
+ descriptor.IsBrowsable = true;
+ descriptor.ClrType = typeof(CodeFirstAgentActivity);
+
+ descriptor.Constructor = context =>
+ {
+ var activity = context.CreateActivity();
+ activity.Type = activityTypeName;
+ activity.AgentName = key;
+ activity.RunAsynchronously = true;
+ return activity;
+ };
+
+ // Build inputs from public instance properties.
+ descriptor.Inputs.Clear();
+ foreach (var prop in agentType.GetProperties(BindingFlags.Instance | BindingFlags.Public))
+ {
+ if (!IsInputProperty(prop))
+ continue;
+
+ var inputName = prop.Name;
+ var inputType = prop.PropertyType.FullName ?? "object";
+ var nakedInputType = wellKnownTypeRegistry.GetTypeOrDefault(inputType);
+ var description = prop.GetCustomAttribute()?.Description;
+
+ var inputDescriptor = new InputDescriptor
+ {
+ Name = inputName,
+ DisplayName = inputName.Humanize(),
+ Description = description,
+ Type = nakedInputType,
+ ValueGetter = activity => activity.SyntheticProperties.GetValueOrDefault(inputName),
+ ValueSetter = (activity, value) => activity.SyntheticProperties[inputName] = value!,
+ IsSynthetic = true,
+ IsWrapped = true,
+ UIHint = ActivityDescriber.GetUIHint(nakedInputType)
+ };
+
+ descriptor.Inputs.Add(inputDescriptor);
+ }
+
+ // For now, expose a single synthetic Output of type object, mirroring
+ // the existing AgentActivity behavior.
+ descriptor.Outputs.Clear();
+ var outputName = "Output";
+ var outputDescriptor = new OutputDescriptor
+ {
+ Name = outputName,
+ Description = "The agent's output.",
+ Type = typeof(object),
+ IsSynthetic = true,
+ ValueGetter = activity => activity.SyntheticProperties.GetValueOrDefault(outputName),
+ ValueSetter = (activity, value) => activity.SyntheticProperties[outputName] = value!,
+ };
+ descriptor.Outputs.Add(outputDescriptor);
+
+ return descriptor;
+ }
+
+ private static bool IsInputProperty(PropertyInfo prop)
+ {
+ // Simple heuristic for now:
+ // - Must be readable and writable
+ // - Exclude indexers
+ if (!prop.CanRead || !prop.CanWrite)
+ return false;
+
+ if (prop.GetIndexParameters().Length > 0)
+ return false;
+
+ // In the future, you can add a dedicated [AgentInput] attribute and
+ // check for it here. For now, treat all simple public properties as
+ // potential inputs.
+ return true;
+ }
+}
+
diff --git a/src/modules/agents/Elsa.Agents.Activities/ActivityProviders/ConfiguredAgentActivityProvider.cs b/src/modules/agents/Elsa.Agents.Activities/ActivityProviders/ConfiguredAgentActivityProvider.cs
new file mode 100644
index 00000000..d79b9681
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Activities/ActivityProviders/ConfiguredAgentActivityProvider.cs
@@ -0,0 +1,101 @@
+using Elsa.Expressions.Contracts;
+using Elsa.Expressions.Extensions;
+using Elsa.Extensions;
+using Elsa.Workflows;
+using Elsa.Workflows.Models;
+using Humanizer;
+using JetBrains.Annotations;
+
+namespace Elsa.Agents.Activities.ActivityProviders;
+
+///
+/// Provides activities for each registered agent.
+///
+[UsedImplicitly]
+public class ConfigurationAgentActivityProvider(
+ IKernelConfigProvider kernelConfigProvider,
+ IActivityDescriber activityDescriber,
+ IWellKnownTypeRegistry wellKnownTypeRegistry
+) : IActivityProvider
+{
+ ///
+ public async ValueTask> GetDescriptorsAsync(CancellationToken cancellationToken = default)
+ {
+ var kernelConfig = await kernelConfigProvider.GetKernelConfigAsync(cancellationToken);
+ var activityDescriptors = new List();
+
+ // Add descriptors for individual agents
+ foreach (var kvp in kernelConfig.Agents)
+ {
+ var agentConfig = kvp.Value;
+ var descriptor = await CreateAgentActivityDescriptor(agentConfig, cancellationToken);
+ activityDescriptors.Add(descriptor);
+ }
+
+ return activityDescriptors;
+ }
+
+ private async Task CreateAgentActivityDescriptor(AgentConfig agentConfig, CancellationToken cancellationToken)
+ {
+ var activityDescriptor = await activityDescriber.DescribeActivityAsync(typeof(ConfiguredAgentActivity), cancellationToken);
+ var activityTypeName = $"Elsa.Agents.{agentConfig.Name.Pascalize()}";
+ activityDescriptor.Name = agentConfig.Name.Pascalize();
+ activityDescriptor.TypeName = activityTypeName;
+ activityDescriptor.Description = agentConfig.Description;
+ activityDescriptor.DisplayName = agentConfig.Name.Humanize().Transform(To.TitleCase);
+ activityDescriptor.IsBrowsable = true;
+ activityDescriptor.Category = "Agents";
+ activityDescriptor.Kind = ActivityKind.Task;
+ activityDescriptor.RunAsynchronously = true;
+ activityDescriptor.ClrType = typeof(ConfiguredAgentActivity);
+
+ activityDescriptor.Constructor = context =>
+ {
+ var activity = context.CreateActivity();
+ activity.Type = activityTypeName;
+ activity.AgentName = agentConfig.Name;
+ activity.RunAsynchronously = true;
+ return activity;
+ };
+
+ activityDescriptor.Inputs.Clear();
+
+ foreach (var inputVariable in agentConfig.InputVariables)
+ {
+ var inputName = inputVariable.Name;
+ var inputType = inputVariable.Type == null! ? "object" : inputVariable.Type;
+ var nakedInputType = wellKnownTypeRegistry.GetTypeOrDefault(inputType);
+ var inputDescriptor = new InputDescriptor
+ {
+ Name = inputVariable.Name,
+ DisplayName = inputVariable.Name.Humanize(),
+ Description = inputVariable.Description,
+ Type = nakedInputType,
+ ValueGetter = activity => activity.SyntheticProperties.GetValueOrDefault(inputName),
+ ValueSetter = (activity, value) => activity.SyntheticProperties[inputName] = value!,
+ IsSynthetic = true,
+ IsWrapped = true,
+ UIHint = ActivityDescriber.GetUIHint(nakedInputType)
+ };
+ activityDescriptor.Inputs.Add(inputDescriptor);
+ }
+
+ activityDescriptor.Outputs.Clear();
+ var outputVariable = agentConfig.OutputVariable;
+ var outputType = outputVariable.Type == null! ? "object" : outputVariable.Type;
+ var nakedOutputType = wellKnownTypeRegistry.GetTypeOrDefault(outputType);
+ var outputName = "Output";
+ var outputDescriptor = new OutputDescriptor
+ {
+ Name = outputName,
+ Description = agentConfig.OutputVariable.Description,
+ Type = nakedOutputType,
+ IsSynthetic = true,
+ ValueGetter = activity => activity.SyntheticProperties.GetValueOrDefault(outputName),
+ ValueSetter = (activity, value) => activity.SyntheticProperties[outputName] = value!,
+ };
+ activityDescriptor.Outputs.Add(outputDescriptor);
+
+ return activityDescriptor;
+ }
+}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Activities/Extensions/ResponseHelpers.cs b/src/modules/agents/Elsa.Agents.Activities/Extensions/ResponseHelpers.cs
new file mode 100644
index 00000000..19c96765
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Activities/Extensions/ResponseHelpers.cs
@@ -0,0 +1,20 @@
+namespace Elsa.Agents.Activities.Extensions;
+
+public static class ResponseHelpers
+{
+ public static bool IsJsonResponse(string text)
+ {
+ return text.StartsWith("{", StringComparison.OrdinalIgnoreCase) || text.StartsWith("[", StringComparison.OrdinalIgnoreCase);
+ }
+
+ public static string StripCodeFences(string content)
+ {
+ var trimmed = content.Trim();
+
+ if (!trimmed.StartsWith("```", StringComparison.Ordinal))
+ return trimmed;
+
+ var lines = trimmed.Split('\n');
+ return lines.Length < 2 ? trimmed : string.Join('\n', lines.Skip(1).Take(lines.Length - 2)).Trim();
+ }
+}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Activities/Features/AgentActivitiesFeature.cs b/src/modules/agents/Elsa.Agents.Activities/Features/AgentActivitiesFeature.cs
index 059738c6..046e72c4 100644
--- a/src/modules/agents/Elsa.Agents.Activities/Features/AgentActivitiesFeature.cs
+++ b/src/modules/agents/Elsa.Agents.Activities/Features/AgentActivitiesFeature.cs
@@ -14,7 +14,7 @@ namespace Elsa.Agents.Activities.Features;
/// A feature that installs Semantic Kernel functionality.
///
[DependsOn(typeof(WorkflowManagementFeature))]
-[DependsOn(typeof(AgentsFeature))]
+[DependsOn(typeof(AgentsCoreFeature))]
[UsedImplicitly]
public class AgentActivitiesFeature(IModule module) : FeatureBase(module)
{
@@ -22,7 +22,8 @@ public class AgentActivitiesFeature(IModule module) : FeatureBase(module)
public override void Apply()
{
Services
- .AddActivityProvider()
+ .AddActivityProvider()
+ .AddActivityProvider()
.AddNotificationHandler()
;
}
diff --git a/src/modules/agents/Elsa.Agents.Activities/Handlers/RefreshActivityRegistry.cs b/src/modules/agents/Elsa.Agents.Activities/Handlers/RefreshActivityRegistry.cs
index 6cc0bcf2..8db3830f 100644
--- a/src/modules/agents/Elsa.Agents.Activities/Handlers/RefreshActivityRegistry.cs
+++ b/src/modules/agents/Elsa.Agents.Activities/Handlers/RefreshActivityRegistry.cs
@@ -7,7 +7,7 @@
namespace Elsa.Agents.Activities.Handlers;
[UsedImplicitly]
-public class RefreshActivityRegistry(IActivityRegistry activityRegistry, AgentActivityProvider agentActivityProvider) :
+public class RefreshActivityRegistry(IActivityRegistry activityRegistry, ConfigurationAgentActivityProvider configurationAgentActivityProvider) :
INotificationHandler,
INotificationHandler,
INotificationHandler,
@@ -17,5 +17,5 @@ public class RefreshActivityRegistry(IActivityRegistry activityRegistry, AgentAc
public Task HandleAsync(AgentDefinitionUpdated notification, CancellationToken cancellationToken) => RefreshRegistryAsync(cancellationToken);
public Task HandleAsync(AgentDefinitionDeleted notification, CancellationToken cancellationToken) => RefreshRegistryAsync(cancellationToken);
public Task HandleAsync(AgentDefinitionsDeletedInBulk notification, CancellationToken cancellationToken) => RefreshRegistryAsync(cancellationToken);
- private Task RefreshRegistryAsync(CancellationToken cancellationToken) => activityRegistry.RefreshDescriptorsAsync(agentActivityProvider, cancellationToken);
+ private Task RefreshRegistryAsync(CancellationToken cancellationToken) => activityRegistry.RefreshDescriptorsAsync(configurationAgentActivityProvider, cancellationToken);
}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/BulkDelete/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/BulkDelete/Endpoint.cs
index a14a4d25..4c4e52a3 100644
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/BulkDelete/Endpoint.cs
+++ b/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/BulkDelete/Endpoint.cs
@@ -1,5 +1,4 @@
using Elsa.Abstractions;
-using Elsa.Agents;
using Elsa.Agents.Persistence.Contracts;
using Elsa.Agents.Persistence.Filters;
using JetBrains.Annotations;
@@ -28,6 +27,6 @@ public override async Task ExecuteAsync(BulkDeleteRequest re
Ids = ids
};
var count = await agentManager.DeleteManyAsync(filter, ct);
- return new BulkDeleteResponse(count);
+ return new(count);
}
}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/Create/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/Create/Endpoint.cs
index 393e813d..4b6f6613 100644
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/Create/Endpoint.cs
+++ b/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/Create/Endpoint.cs
@@ -1,5 +1,4 @@
using Elsa.Abstractions;
-using Elsa.Agents;
using Elsa.Extensions;
using Elsa.Agents.Persistence.Contracts;
using Elsa.Agents.Persistence.Entities;
@@ -39,7 +38,7 @@ public override async Task ExecuteAsync(AgentInputModel req, Cancell
Id = identityGenerator.GenerateId(),
Name = req.Name.Trim(),
Description = req.Description.Trim(),
- AgentConfig = new AgentConfig
+ AgentConfig = new()
{
Description = req.Description.Trim(),
Name = req.Name.Trim(),
@@ -47,9 +46,7 @@ public override async Task ExecuteAsync(AgentInputModel req, Cancell
ExecutionSettings = req.ExecutionSettings,
InputVariables = req.InputVariables,
OutputVariable = req.OutputVariable,
- Services = req.Services,
- Plugins = req.Plugins,
- FunctionName = req.FunctionName,
+ Skills = req.Skills,
PromptTemplate = req.PromptTemplate
}
};
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/GenerateUniqueName/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/GenerateUniqueName/Endpoint.cs
index 53e4ebf7..74c02b5b 100644
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/GenerateUniqueName/Endpoint.cs
+++ b/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/GenerateUniqueName/Endpoint.cs
@@ -1,5 +1,4 @@
using Elsa.Abstractions;
-using Elsa.Agents;
using Elsa.Agents.Persistence.Contracts;
using JetBrains.Annotations;
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/Get/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/Get/Endpoint.cs
index 3b70756a..568e0a35 100644
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/Get/Endpoint.cs
+++ b/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/Get/Endpoint.cs
@@ -1,5 +1,4 @@
using Elsa.Abstractions;
-using Elsa.Agents;
using Elsa.Extensions;
using Elsa.Agents.Persistence.Contracts;
using JetBrains.Annotations;
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/Invoke/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/Invoke/Endpoint.cs
index 9d9f7e19..cbf22c66 100644
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/Invoke/Endpoint.cs
+++ b/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/Invoke/Endpoint.cs
@@ -1,6 +1,5 @@
using System.Text.Json;
using Elsa.Abstractions;
-using Elsa.Agents;
using JetBrains.Annotations;
namespace Elsa.Agents.Api.Endpoints.Agents.Invoke;
@@ -9,7 +8,7 @@ namespace Elsa.Agents.Api.Endpoints.Agents.Invoke;
/// Invokes an agent.
///
[UsedImplicitly]
-public class Execute(AgentInvoker agentInvoker) : ElsaEndpoint
+public class Execute(IAgentInvoker agentInvoker) : ElsaEndpoint
{
///
public override void Configure()
@@ -21,7 +20,13 @@ public override void Configure()
///
public override async Task ExecuteAsync(Request req, CancellationToken ct)
{
- var result = await agentInvoker.InvokeAgentAsync(req.Agent, req.Inputs, ct).AsJsonElementAsync();
+ var request = new InvokeAgentRequest
+ {
+ AgentName = req.Agent,
+ Input = req.Inputs,
+ CancellationToken = ct
+ };
+ var result = await agentInvoker.InvokeAsync(request).AsJsonElementAsync();
return result;
}
}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/IsUniqueName/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/IsUniqueName/Endpoint.cs
index 4f0a00ac..db5c2e1f 100644
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/IsUniqueName/Endpoint.cs
+++ b/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/IsUniqueName/Endpoint.cs
@@ -1,5 +1,4 @@
using Elsa.Abstractions;
-using Elsa.Agents;
using Elsa.Agents.Persistence.Contracts;
using JetBrains.Annotations;
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/List/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/List/Endpoint.cs
index 19c81b52..effe7ea0 100644
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/List/Endpoint.cs
+++ b/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/List/Endpoint.cs
@@ -1,5 +1,4 @@
using Elsa.Abstractions;
-using Elsa.Agents;
using Elsa.Extensions;
using Elsa.Agents.Persistence.Contracts;
using Elsa.Models;
@@ -25,6 +24,6 @@ public override async Task> ExecuteAsync(CancellationTo
{
var entities = await agentManager.ListAsync(ct);
var models = entities.Select(x => x.ToModel()).ToList();
- return new ListResponse(models);
+ return new(models);
}
}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/Update/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/Update/Endpoint.cs
index 4d2f9ea1..95d3ebaf 100644
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/Update/Endpoint.cs
+++ b/src/modules/agents/Elsa.Agents.Api/Endpoints/Agents/Update/Endpoint.cs
@@ -1,5 +1,4 @@
using Elsa.Abstractions;
-using Elsa.Agents;
using Elsa.Extensions;
using Elsa.Agents.Persistence.Contracts;
using JetBrains.Annotations;
@@ -42,17 +41,15 @@ public override async Task ExecuteAsync(AgentInputModel req, Cancell
entity.Name = req.Name.Trim();
entity.Description = req.Description.Trim();
- entity.AgentConfig = new AgentConfig
+ entity.AgentConfig = new()
{
Name = req.Name.Trim(),
Description = req.Description.Trim(),
- FunctionName = req.FunctionName.Trim(),
- Services = req.Services,
PromptTemplate = req.PromptTemplate.Trim(),
InputVariables = req.InputVariables,
OutputVariable = req.OutputVariable,
ExecutionSettings = req.ExecutionSettings,
- Plugins = req.Plugins,
+ Skills = req.Skills,
Agents = req.Agents
};
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/BulkDelete/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/BulkDelete/Endpoint.cs
deleted file mode 100644
index a6704639..00000000
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/BulkDelete/Endpoint.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Elsa.Abstractions;
-using Elsa.Agents;
-using Elsa.Agents.Persistence.Contracts;
-using Elsa.Agents.Persistence.Filters;
-using JetBrains.Annotations;
-
-namespace Elsa.Agents.Api.Endpoints.ApiKeys.BulkDelete;
-
-///
-/// Deletes an API key.
-///
-[UsedImplicitly]
-public class Endpoint(IApiKeyStore store) : ElsaEndpoint
-{
- ///
- public override void Configure()
- {
- Post("/ai/bulk-actions/api-keys/delete");
- ConfigurePermissions("ai/api-keys:delete");
- }
-
- ///
- public override async Task ExecuteAsync(BulkDeleteRequest req, CancellationToken ct)
- {
- var ids = req.Ids;
- var filter = new ApiKeyDefinitionFilter
- {
- Ids = ids
- };
- var count = await store.DeleteManyAsync(filter, ct);
- return new BulkDeleteResponse(count);
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/Create/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/Create/Endpoint.cs
deleted file mode 100644
index 2e5809a5..00000000
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/Create/Endpoint.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using Elsa.Abstractions;
-using Elsa.Agents;
-using Elsa.Agents.Persistence.Contracts;
-using Elsa.Agents.Persistence.Entities;
-using Elsa.Agents.Persistence.Filters;
-using Elsa.Workflows;
-using JetBrains.Annotations;
-
-namespace Elsa.Agents.Api.Endpoints.ApiKeys.Create;
-
-///
-/// Lists all registered API keys.
-///
-[UsedImplicitly]
-public class Endpoint(IApiKeyStore store, IIdentityGenerator identityGenerator) : ElsaEndpoint
-{
- ///
- public override void Configure()
- {
- Post("/ai/api-keys");
- ConfigurePermissions("ai/api-keys:write");
- }
-
- ///
- public override async Task ExecuteAsync(ApiKeyInputModel req, CancellationToken ct)
- {
- var existingEntityFilter = new ApiKeyDefinitionFilter
- {
- Name = req.Name
- };
- var existingEntity = await store.FindAsync(existingEntityFilter, ct);
-
- if (existingEntity != null)
- {
- AddError("An API key already exists with the specified name");
- await Send.ErrorsAsync(cancellation: ct);
- return existingEntity.ToModel();
- }
-
- var newEntity = new ApiKeyDefinition
- {
- Id = identityGenerator.GenerateId(),
- Name = req.Name.Trim(),
- Value = req.Value.Trim()
- };
-
- await store.AddAsync(newEntity, ct);
- return newEntity.ToModel();
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/Delete/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/Delete/Endpoint.cs
deleted file mode 100644
index 793bc120..00000000
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/Delete/Endpoint.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Elsa.Abstractions;
-using Elsa.Agents.Persistence.Contracts;
-using JetBrains.Annotations;
-
-namespace Elsa.Agents.Api.Endpoints.ApiKeys.Delete;
-
-///
-/// Delete an API key.
-///
-[UsedImplicitly]
-public class Endpoint(IApiKeyStore store) : ElsaEndpoint
-{
- ///
- public override void Configure()
- {
- Delete("/ai/api-keys/{id}");
- ConfigurePermissions("ai/api-keys:delete");
- }
-
- ///
- public override async Task HandleAsync(Request req, CancellationToken ct)
- {
- var entity = await store.GetAsync(req.Id, ct);
-
- if(entity == null)
- {
- await Send.NotFoundAsync(ct);
- return;
- }
-
- await store.DeleteAsync(entity, ct);
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/Delete/Request.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/Delete/Request.cs
deleted file mode 100644
index c9c25040..00000000
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/Delete/Request.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace Elsa.Agents.Api.Endpoints.ApiKeys.Delete;
-
-public class Request
-{
- [Required] public string Id { get; set; } = null!;
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/Get/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/Get/Endpoint.cs
deleted file mode 100644
index b1da20f1..00000000
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/Get/Endpoint.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using Elsa.Abstractions;
-using Elsa.Agents;
-using Elsa.Agents.Persistence.Contracts;
-using JetBrains.Annotations;
-
-namespace Elsa.Agents.Api.Endpoints.ApiKeys.Get;
-
-///
-/// Lists all registered API keys.
-///
-[UsedImplicitly]
-public class Endpoint(IApiKeyStore store) : ElsaEndpoint
-{
- ///
- public override void Configure()
- {
- Get("/ai/api-keys/{id}");
- ConfigurePermissions("ai/api-keys:read");
- }
-
- ///
- public override async Task ExecuteAsync(Request req, CancellationToken ct)
- {
- var entity = await store.GetAsync(req.Id, ct);
-
- if(entity == null)
- {
- await Send.NotFoundAsync(ct);
- return null!;
- }
-
- return entity.ToModel();
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/Get/Request.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/Get/Request.cs
deleted file mode 100644
index d8bf2d69..00000000
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/Get/Request.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace Elsa.Agents.Api.Endpoints.ApiKeys.Get;
-
-public class Request
-{
- [Required] public string Id { get; set; } = null!;
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/List/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/List/Endpoint.cs
deleted file mode 100644
index ef434a33..00000000
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/List/Endpoint.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using Elsa.Abstractions;
-using Elsa.Agents;
-using Elsa.Agents.Persistence.Contracts;
-using Elsa.Models;
-using JetBrains.Annotations;
-
-namespace Elsa.Agents.Api.Endpoints.ApiKeys.List;
-
-///
-/// Lists all registered API keys.
-///
-[UsedImplicitly]
-public class Endpoint(IApiKeyStore store) : ElsaEndpointWithoutRequest>
-{
- ///
- public override void Configure()
- {
- Get("/ai/api-keys");
- ConfigurePermissions("ai/api-keys:read");
- }
-
- ///
- public override async Task> ExecuteAsync(CancellationToken ct)
- {
- var entities = await store.ListAsync(ct);
- var models = entities.Select(x => x.ToModel()).ToList();
- return new ListResponse(models);
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/Update/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/Update/Endpoint.cs
deleted file mode 100644
index 1950c322..00000000
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/ApiKeys/Update/Endpoint.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using Elsa.Abstractions;
-using Elsa.Agents;
-using Elsa.Agents.Persistence.Contracts;
-using Elsa.Agents.Persistence.Entities;
-using Elsa.Agents.Persistence.Filters;
-using JetBrains.Annotations;
-
-namespace Elsa.Agents.Api.Endpoints.ApiKeys.Update;
-
-///
-/// Lists all registered API keys.
-///
-[UsedImplicitly]
-public class Endpoint(IApiKeyStore store) : ElsaEndpoint
-{
- ///
- public override void Configure()
- {
- Post("/ai/api-keys/{id}");
- ConfigurePermissions("ai/api-keys:write");
- }
-
- ///
- public override async Task ExecuteAsync(ApiKeyModel req, CancellationToken ct)
- {
- var entity = await store.GetAsync(req.Id, ct);
-
- if(entity == null)
- {
- await Send.NotFoundAsync(ct);
- return null!;
- }
-
- var isNameDuplicate = await IsNameDuplicateAsync(req.Name, req.Id, ct);
-
- if (isNameDuplicate)
- {
- AddError("Another API key already exists with the specified name");
- await Send.ErrorsAsync(cancellation: ct);
- return entity;
- }
-
- entity.Name = req.Name.Trim();
- entity.Value = req.Value.Trim();
-
- await store.UpdateAsync(entity, ct);
- return entity;
- }
-
- private async Task IsNameDuplicateAsync(string name, string id, CancellationToken cancellationToken)
- {
- var filter = new ApiKeyDefinitionFilter
- {
- Name = name,
- NotId = id
- };
-
- var entity = await store.FindAsync(filter, cancellationToken);
- return entity != null;
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/Plugins/List/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/Plugins/List/Endpoint.cs
deleted file mode 100644
index e2483a14..00000000
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/Plugins/List/Endpoint.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using Elsa.Abstractions;
-using Elsa.Agents;
-using Elsa.Models;
-using JetBrains.Annotations;
-
-namespace Elsa.Agents.Api.Endpoints.Plugins.List;
-
-///
-/// Lists all registered plugins.
-///
-[UsedImplicitly]
-public class Endpoint(IPluginDiscoverer pluginDiscoverer) : ElsaEndpointWithoutRequest>
-{
- ///
- public override void Configure()
- {
- Get("/ai/plugins");
- ConfigurePermissions("ai/plugins:read");
- }
-
- ///
- public override Task> ExecuteAsync(CancellationToken ct)
- {
- var descriptors = pluginDiscoverer.GetPluginDescriptors();
- var models = descriptors.Select(x => x.ToModel()).ToList();
- return Task.FromResult(new ListResponse(models));
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/ServiceProviders/List/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/ServiceProviders/List/Endpoint.cs
deleted file mode 100644
index e81f4424..00000000
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/ServiceProviders/List/Endpoint.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using Elsa.Abstractions;
-using Elsa.Agents;
-using Elsa.Models;
-using JetBrains.Annotations;
-
-namespace Elsa.Agents.Api.Endpoints.ServiceProviders.List;
-
-///
-/// Lists all registered service providers.
-///
-[UsedImplicitly]
-public class Endpoint(IServiceDiscoverer serviceDiscoverer) : ElsaEndpointWithoutRequest>
-{
- ///
- public override void Configure()
- {
- Get("/ai/service-providers");
- ConfigurePermissions("ai/services:read");
- }
-
- ///
- public override Task> ExecuteAsync(CancellationToken ct)
- {
- var providers = serviceDiscoverer.Discover().Select(x => x.Name).ToList();
- return Task.FromResult(new ListResponse(providers));
- }
-}
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/BulkDelete/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/BulkDelete/Endpoint.cs
deleted file mode 100644
index 2b742d2e..00000000
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/BulkDelete/Endpoint.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Elsa.Abstractions;
-using Elsa.Agents;
-using Elsa.Agents.Persistence.Contracts;
-using Elsa.Agents.Persistence.Filters;
-using JetBrains.Annotations;
-
-namespace Elsa.Agents.Api.Endpoints.Services.BulkDelete;
-
-///
-/// Deletes an API key.
-///
-[UsedImplicitly]
-public class Endpoint(IServiceStore store) : ElsaEndpoint
-{
- ///
- public override void Configure()
- {
- Post("/ai/bulk-actions/services/delete");
- ConfigurePermissions("ai/api-keys:delete");
- }
-
- ///
- public override async Task ExecuteAsync(BulkDeleteRequest req, CancellationToken ct)
- {
- var ids = req.Ids;
- var filter = new ServiceDefinitionFilter
- {
- Ids = ids
- };
- var count = await store.DeleteManyAsync(filter, ct);
- return new BulkDeleteResponse(count);
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/Create/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/Create/Endpoint.cs
deleted file mode 100644
index 1574922f..00000000
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/Create/Endpoint.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using Elsa.Abstractions;
-using Elsa.Agents;
-using Elsa.Agents.Persistence.Contracts;
-using Elsa.Agents.Persistence.Entities;
-using Elsa.Agents.Persistence.Filters;
-using Elsa.Workflows;
-using JetBrains.Annotations;
-
-namespace Elsa.Agents.Api.Endpoints.Services.Create;
-
-///
-/// Lists all registered API keys.
-///
-[UsedImplicitly]
-public class Endpoint(IServiceStore store, IIdentityGenerator identityGenerator) : ElsaEndpoint
-{
- ///
- public override void Configure()
- {
- Post("/ai/services");
- ConfigurePermissions("ai/services:write");
- }
-
- ///
- public override async Task ExecuteAsync(ServiceInputModel req, CancellationToken ct)
- {
- var existingEntityFilter = new ServiceDefinitionFilter
- {
- Name = req.Name
- };
- var existingEntity = await store.FindAsync(existingEntityFilter, ct);
-
- if (existingEntity != null)
- {
- AddError("A Service already exists with the specified name");
- await Send.ErrorsAsync(cancellation: ct);
- return existingEntity.ToModel();
- }
-
- var newEntity = new ServiceDefinition
- {
- Id = identityGenerator.GenerateId(),
- Name = req.Name.Trim(),
- Type = req.Type,
- Settings = req.Settings
- };
-
- await store.AddAsync(newEntity, ct);
- return newEntity.ToModel();
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/Delete/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/Delete/Endpoint.cs
deleted file mode 100644
index 66f50fcd..00000000
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/Delete/Endpoint.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Elsa.Abstractions;
-using Elsa.Agents.Persistence.Contracts;
-using JetBrains.Annotations;
-
-namespace Elsa.Agents.Api.Endpoints.Services.Delete;
-
-///
-/// Delete an API key.
-///
-[UsedImplicitly]
-public class Endpoint(IServiceStore store) : ElsaEndpoint
-{
- ///
- public override void Configure()
- {
- Delete("/ai/services/{id}");
- ConfigurePermissions("ai/services:delete");
- }
-
- ///
- public override async Task HandleAsync(Request req, CancellationToken ct)
- {
- var entity = await store.GetAsync(req.Id, ct);
-
- if(entity == null)
- {
- await Send.NotFoundAsync(ct);
- return;
- }
-
- await store.DeleteAsync(entity, ct);
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/Delete/Request.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/Delete/Request.cs
deleted file mode 100644
index 24698c9c..00000000
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/Delete/Request.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace Elsa.Agents.Api.Endpoints.Services.Delete;
-
-public class Request
-{
- [Required] public string Id { get; set; } = null!;
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/Get/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/Get/Endpoint.cs
deleted file mode 100644
index f7c76a27..00000000
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/Get/Endpoint.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using Elsa.Abstractions;
-using Elsa.Agents;
-using Elsa.Agents.Persistence.Contracts;
-using JetBrains.Annotations;
-
-namespace Elsa.Agents.Api.Endpoints.Services.Get;
-
-///
-/// Lists all registered API keys.
-///
-[UsedImplicitly]
-public class Endpoint(IServiceStore store) : ElsaEndpoint
-{
- ///
- public override void Configure()
- {
- Get("/ai/services/{id}");
- ConfigurePermissions("ai/services:read");
- }
-
- ///
- public override async Task ExecuteAsync(Request req, CancellationToken ct)
- {
- var entity = await store.GetAsync(req.Id, ct);
-
- if(entity == null)
- {
- await Send.NotFoundAsync(ct);
- return null!;
- }
-
- return entity.ToModel();
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/Get/Request.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/Get/Request.cs
deleted file mode 100644
index 5d7d0470..00000000
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/Get/Request.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace Elsa.Agents.Api.Endpoints.Services.Get;
-
-public class Request
-{
- [Required] public string Id { get; set; } = null!;
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/List/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/List/Endpoint.cs
deleted file mode 100644
index bfe5107b..00000000
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/List/Endpoint.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using Elsa.Abstractions;
-using Elsa.Agents;
-using Elsa.Agents.Persistence.Contracts;
-using Elsa.Models;
-using JetBrains.Annotations;
-
-namespace Elsa.Agents.Api.Endpoints.Services.List;
-
-///
-/// Lists all registered API keys.
-///
-[UsedImplicitly]
-public class Endpoint(IServiceStore store) : ElsaEndpointWithoutRequest>
-{
- ///
- public override void Configure()
- {
- Get("/ai/services");
- ConfigurePermissions("ai/services:read");
- }
-
- ///
- public override async Task> ExecuteAsync(CancellationToken ct)
- {
- var entities = await store.ListAsync(ct);
- var models = entities.Select(x => x.ToModel()).ToList();
- return new ListResponse(models);
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/Update/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/Update/Endpoint.cs
deleted file mode 100644
index 6684ba75..00000000
--- a/src/modules/agents/Elsa.Agents.Api/Endpoints/Services/Update/Endpoint.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using Elsa.Abstractions;
-using Elsa.Agents;
-using Elsa.Agents.Persistence.Contracts;
-using Elsa.Agents.Persistence.Filters;
-using JetBrains.Annotations;
-
-namespace Elsa.Agents.Api.Endpoints.Services.Update;
-
-///
-/// Lists all registered API keys.
-///
-[UsedImplicitly]
-public class Endpoint(IServiceStore store) : ElsaEndpoint
-{
- ///
- public override void Configure()
- {
- Post("/ai/services/{id}");
- ConfigurePermissions("ai/services:write");
- }
-
- ///
- public override async Task ExecuteAsync(ServiceModel req, CancellationToken ct)
- {
- var entity = await store.GetAsync(req.Id, ct);
-
- if(entity == null)
- {
- await Send.NotFoundAsync(ct);
- return null!;
- }
-
- var isNameDuplicate = await IsNameDuplicateAsync(req.Name, req.Id, ct);
-
- if (isNameDuplicate)
- {
- AddError("Another service already exists with the specified name");
- await Send.ErrorsAsync(cancellation: ct);
- return entity.ToModel();
- }
-
- entity.Name = req.Name.Trim();
- entity.Type = req.Type.Trim();
- entity.Settings = req.Settings;
-
- await store.UpdateAsync(entity, ct);
- return entity.ToModel();
- }
-
- private async Task IsNameDuplicateAsync(string name, string id, CancellationToken cancellationToken)
- {
- var filter = new ServiceDefinitionFilter
- {
- Name = name,
- NotId = id
- };
-
- var entity = await store.FindAsync(filter, cancellationToken);
- return entity != null;
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Endpoints/Skills/List/Endpoint.cs b/src/modules/agents/Elsa.Agents.Api/Endpoints/Skills/List/Endpoint.cs
new file mode 100644
index 00000000..d1946986
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Api/Endpoints/Skills/List/Endpoint.cs
@@ -0,0 +1,27 @@
+using Elsa.Abstractions;
+using Elsa.Models;
+using JetBrains.Annotations;
+
+namespace Elsa.Agents.Api.Endpoints.Skills.List;
+
+///
+/// Lists all registered skills.
+///
+[UsedImplicitly]
+public class Endpoint(ISkillDiscoverer skillDiscoverer) : ElsaEndpointWithoutRequest>
+{
+ ///
+ public override void Configure()
+ {
+ Get("/ai/skills");
+ ConfigurePermissions("ai/skills:read");
+ }
+
+ ///
+ public override Task> ExecuteAsync(CancellationToken ct)
+ {
+ var descriptors = skillDiscoverer.DiscoverSkills();
+ var models = descriptors.Select(x => x.ToModel()).ToList();
+ return Task.FromResult(new ListResponse(models));
+ }
+}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Api/Extensions/AgentDefinitionExtensions.cs b/src/modules/agents/Elsa.Agents.Api/Extensions/AgentDefinitionExtensions.cs
index f631ce23..7232a688 100644
--- a/src/modules/agents/Elsa.Agents.Api/Extensions/AgentDefinitionExtensions.cs
+++ b/src/modules/agents/Elsa.Agents.Api/Extensions/AgentDefinitionExtensions.cs
@@ -8,7 +8,7 @@ public static class AgentDefinitionExtensions
{
public static AgentModel ToModel(this AgentDefinition agentDefinition)
{
- return new AgentModel
+ return new()
{
Id = agentDefinition.Id,
Name = agentDefinition.Name,
@@ -17,9 +17,7 @@ public static AgentModel ToModel(this AgentDefinition agentDefinition)
ExecutionSettings = agentDefinition.AgentConfig.ExecutionSettings,
InputVariables = agentDefinition.AgentConfig.InputVariables,
OutputVariable = agentDefinition.AgentConfig.OutputVariable,
- Services = agentDefinition.AgentConfig.Services,
- Plugins = agentDefinition.AgentConfig.Plugins,
- FunctionName = agentDefinition.AgentConfig.FunctionName,
+ Skills = agentDefinition.AgentConfig.Skills,
PromptTemplate = agentDefinition.AgentConfig.PromptTemplate
};
}
diff --git a/src/modules/agents/Elsa.Agents.AzureOpenAI/AgentsFeatureExtensions.cs b/src/modules/agents/Elsa.Agents.AzureOpenAI/AgentsFeatureExtensions.cs
new file mode 100644
index 00000000..fbabf207
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.AzureOpenAI/AgentsFeatureExtensions.cs
@@ -0,0 +1,23 @@
+using Microsoft.SemanticKernel;
+
+namespace Elsa.Agents.AzureOpenAI;
+
+public static class AgentsFeatureExtensions
+{
+ public static AgentsFeature AddAzureOpenAIChatCompletion(this AgentsFeature feature,
+ string deploymentName,
+ string endpoint,
+ string apiKey,
+ string? serviceId = null,
+ string? modelId = null,
+ string? apiVersion = null,
+ HttpClient? httpClient = null,
+ string name = "Azure OpenAI Chat Completion")
+ {
+ return feature.AddServiceDescriptor(new()
+ {
+ Name = name,
+ ConfigureKernel = kernel => kernel.Services.AddAzureOpenAIChatCompletion(deploymentName, endpoint, apiKey, serviceId, modelId, apiVersion, httpClient)
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.AzureOpenAI/Elsa.Agents.AzureOpenAI.csproj b/src/modules/agents/Elsa.Agents.AzureOpenAI/Elsa.Agents.AzureOpenAI.csproj
new file mode 100644
index 00000000..9424b0f5
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.AzureOpenAI/Elsa.Agents.AzureOpenAI.csproj
@@ -0,0 +1,22 @@
+
+
+
+ Provides Azure OpenAI integration with Elsa Agents
+ elsa extension module agents azure openai
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/agents/Elsa.Agents.AzureOpenAI/FodyWeavers.xml b/src/modules/agents/Elsa.Agents.AzureOpenAI/FodyWeavers.xml
new file mode 100644
index 00000000..00e1d9a1
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.AzureOpenAI/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Abstractions/PluginProvider.cs b/src/modules/agents/Elsa.Agents.Core/Abstractions/PluginProvider.cs
deleted file mode 100644
index c64a1f34..00000000
--- a/src/modules/agents/Elsa.Agents.Core/Abstractions/PluginProvider.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Elsa.Agents;
-
-public abstract class PluginProvider : IPluginProvider
-{
- public virtual IEnumerable GetPlugins() => [];
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Abstractions/SkillsProvider.cs b/src/modules/agents/Elsa.Agents.Core/Abstractions/SkillsProvider.cs
new file mode 100644
index 00000000..eb367140
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Core/Abstractions/SkillsProvider.cs
@@ -0,0 +1,6 @@
+namespace Elsa.Agents;
+
+public abstract class SkillsProvider : ISkillsProvider
+{
+ public virtual IEnumerable GetSkills() => [];
+}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Contracts/IAgent.cs b/src/modules/agents/Elsa.Agents.Core/Contracts/IAgent.cs
new file mode 100644
index 00000000..b09f573c
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Core/Contracts/IAgent.cs
@@ -0,0 +1,14 @@
+using Microsoft.Agents.AI;
+
+namespace Elsa.Agents;
+
+///
+/// Minimal abstraction to represent a code-first agent that can be automatically discovered as an activity.
+///
+public interface IAgent
+{
+ ///
+ /// Executes the agent with the given context and returns the primary text result.
+ ///
+ Task RunAsync(AgentExecutionContext context);
+}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Contracts/IAgentFactory.cs b/src/modules/agents/Elsa.Agents.Core/Contracts/IAgentFactory.cs
new file mode 100644
index 00000000..7a470108
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Core/Contracts/IAgentFactory.cs
@@ -0,0 +1,18 @@
+using Microsoft.SemanticKernel.Agents;
+
+#pragma warning disable SKEXP0001
+#pragma warning disable SKEXP0010
+#pragma warning disable SKEXP0110
+
+namespace Elsa.Agents;
+
+///
+/// Factory for creating agents from agent configurations.
+///
+public interface IAgentFactory
+{
+ ///
+ /// Creates a ChatCompletionAgent from an Elsa agent configuration.
+ ///
+ ChatCompletionAgent CreateAgent(AgentConfig agentConfig);
+}
diff --git a/src/modules/agents/Elsa.Agents.Core/Contracts/IAgentInvoker.cs b/src/modules/agents/Elsa.Agents.Core/Contracts/IAgentInvoker.cs
new file mode 100644
index 00000000..bd9b55a7
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Core/Contracts/IAgentInvoker.cs
@@ -0,0 +1,12 @@
+namespace Elsa.Agents;
+
+///
+/// Invokes an agent using the Microsoft Agent Framework.
+///
+public interface IAgentInvoker
+{
+ ///
+ /// Invokes an agent using the Microsoft Agent Framework.
+ ///
+ Task InvokeAsync(InvokeAgentRequest request);
+}
diff --git a/src/modules/agents/Elsa.Agents.Core/Contracts/IAgentResolver.cs b/src/modules/agents/Elsa.Agents.Core/Contracts/IAgentResolver.cs
new file mode 100644
index 00000000..bd27d0fc
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Core/Contracts/IAgentResolver.cs
@@ -0,0 +1,15 @@
+namespace Elsa.Agents;
+
+///
+/// Defines the contract for resolving agents by their names.
+///
+public interface IAgentResolver
+{
+ ///
+ /// Resolves an agent instance by its name asynchronously.
+ ///
+ /// The name of the agent to resolve.
+ /// A token to monitor for cancellation requests.
+ /// The resolved agent instance.
+ Task ResolveAsync(string agentName, CancellationToken cancellationToken = default);
+}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Contracts/IAgentServiceProvider.cs b/src/modules/agents/Elsa.Agents.Core/Contracts/IAgentServiceProvider.cs
deleted file mode 100644
index 1eb96057..00000000
--- a/src/modules/agents/Elsa.Agents.Core/Contracts/IAgentServiceProvider.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Elsa.Agents;
-
-public interface IAgentServiceProvider
-{
- string Name { get; }
- void ConfigureKernel(KernelBuilderContext context);
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Contracts/IPluginDiscoverer.cs b/src/modules/agents/Elsa.Agents.Core/Contracts/IPluginDiscoverer.cs
deleted file mode 100644
index 5a9fa460..00000000
--- a/src/modules/agents/Elsa.Agents.Core/Contracts/IPluginDiscoverer.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Elsa.Agents;
-
-public interface IPluginDiscoverer
-{
- IEnumerable GetPluginDescriptors();
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Contracts/IServiceDiscoverer.cs b/src/modules/agents/Elsa.Agents.Core/Contracts/IServiceDiscoverer.cs
deleted file mode 100644
index 681e6fd4..00000000
--- a/src/modules/agents/Elsa.Agents.Core/Contracts/IServiceDiscoverer.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Elsa.Agents;
-
-public interface IServiceDiscoverer
-{
- IEnumerable Discover();
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Contracts/ISkillDiscoverer.cs b/src/modules/agents/Elsa.Agents.Core/Contracts/ISkillDiscoverer.cs
new file mode 100644
index 00000000..311469ae
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Core/Contracts/ISkillDiscoverer.cs
@@ -0,0 +1,6 @@
+namespace Elsa.Agents;
+
+public interface ISkillDiscoverer
+{
+ IEnumerable DiscoverSkills();
+}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Contracts/IPluginProvider.cs b/src/modules/agents/Elsa.Agents.Core/Contracts/ISkillsProvider.cs
similarity index 56%
rename from src/modules/agents/Elsa.Agents.Core/Contracts/IPluginProvider.cs
rename to src/modules/agents/Elsa.Agents.Core/Contracts/ISkillsProvider.cs
index 0db6dfd7..163cc7e1 100644
--- a/src/modules/agents/Elsa.Agents.Core/Contracts/IPluginProvider.cs
+++ b/src/modules/agents/Elsa.Agents.Core/Contracts/ISkillsProvider.cs
@@ -1,9 +1,9 @@
namespace Elsa.Agents;
///
-/// Implementations of this interface are responsible for providing plugins.
+/// Implementations of this interface are responsible for providing skills.
///
-public interface IPluginProvider
+public interface ISkillsProvider
{
- IEnumerable GetPlugins();
+ IEnumerable GetSkills();
}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Contracts/IkernelFactory.cs b/src/modules/agents/Elsa.Agents.Core/Contracts/IkernelFactory.cs
deleted file mode 100644
index 7b14d61d..00000000
--- a/src/modules/agents/Elsa.Agents.Core/Contracts/IkernelFactory.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using Microsoft.SemanticKernel;
-namespace Elsa.Agents;
-
-public interface IKernelFactory
-{
- Kernel CreateKernel(KernelConfig kernelConfig, AgentConfig agentConfig);
- Kernel CreateKernel(KernelConfig kernelConfig, string agentName);
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Contracts/ServiceDescriptor.cs b/src/modules/agents/Elsa.Agents.Core/Contracts/ServiceDescriptor.cs
new file mode 100644
index 00000000..d2ae1b91
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Core/Contracts/ServiceDescriptor.cs
@@ -0,0 +1,32 @@
+using Microsoft.SemanticKernel;
+
+namespace Elsa.Agents;
+
+///
+/// Represents a service descriptor used to define and configure services in the context of the Semantic Kernel framework.
+///
+///
+/// This class encapsulates the name of the service and an action to configure the kernel builder with the specific service.
+/// It is typically used to register and customize services for use within agents or features.
+///
+public class ServiceDescriptor
+{
+ ///
+ /// Gets or sets the name of the service.
+ ///
+ ///
+ /// The name is used to identify the service descriptor and can be helpful for distinguishing
+ /// between different services when configuring or resolving dependencies within the Semantic Kernel framework.
+ ///
+ public string Name { get; set; } = null!;
+
+ ///
+ /// Gets or sets the action used to configure the kernel builder for a specific service.
+ ///
+ ///
+ /// This property defines an used to customize the Semantic Kernel's configuration
+ /// by adding or modifying services within the kernel. It provides a mechanism to integrate and set up specific
+ /// functionality in the context of agents or features.
+ ///
+ public Action ConfigureKernel { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Elsa.Agents.Core.csproj b/src/modules/agents/Elsa.Agents.Core/Elsa.Agents.Core.csproj
index df449170..e0300c3e 100644
--- a/src/modules/agents/Elsa.Agents.Core/Elsa.Agents.Core.csproj
+++ b/src/modules/agents/Elsa.Agents.Core/Elsa.Agents.Core.csproj
@@ -7,26 +7,28 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
-
-
+
+
+
diff --git a/src/modules/agents/Elsa.Agents.Core/Extensions/AgentConfigExtensions.cs b/src/modules/agents/Elsa.Agents.Core/Extensions/AgentConfigExtensions.cs
deleted file mode 100644
index 5afbbab3..00000000
--- a/src/modules/agents/Elsa.Agents.Core/Extensions/AgentConfigExtensions.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using Microsoft.SemanticKernel;
-using Microsoft.SemanticKernel.Connectors.OpenAI;
-
-#pragma warning disable SKEXP0001
-#pragma warning disable SKEXP0010
-
-namespace Elsa.Agents;
-
-public static class AgentConfigExtensions
-{
- public static OpenAIPromptExecutionSettings ToOpenAIPromptExecutionSettings(this AgentConfig agentConfig)
- {
- return new OpenAIPromptExecutionSettings
- {
- Temperature = agentConfig.ExecutionSettings.Temperature,
- TopP = agentConfig.ExecutionSettings.TopP,
- MaxTokens = agentConfig.ExecutionSettings.MaxTokens,
- PresencePenalty = agentConfig.ExecutionSettings.PresencePenalty,
- FrequencyPenalty = agentConfig.ExecutionSettings.FrequencyPenalty,
- ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions,
- ResponseFormat = agentConfig.ExecutionSettings.ResponseFormat,
- ChatSystemPrompt = agentConfig.PromptTemplate,
- ServiceId = "default"
- };
- }
-
- public static PromptTemplateConfig ToPromptTemplateConfig(this AgentConfig agentConfig)
- {
- var promptExecutionSettingsDictionary = new Dictionary
- {
- [PromptExecutionSettings.DefaultServiceId] = agentConfig.ToOpenAIPromptExecutionSettings(),
- };
-
- return new PromptTemplateConfig
- {
- Name = agentConfig.FunctionName,
- Description = agentConfig.Description,
- Template = agentConfig.PromptTemplate,
- ExecutionSettings = promptExecutionSettingsDictionary,
- AllowDangerouslySetContent = true,
- InputVariables = agentConfig.InputVariables.Select(x => new InputVariable
- {
- Name = x.Name,
- Description = x.Description,
- IsRequired = true,
- AllowDangerouslySetContent = true
- }).ToList()
- };
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Extensions/ModuleExtensions.cs b/src/modules/agents/Elsa.Agents.Core/Extensions/ModuleExtensions.cs
index bf521d0f..f7464583 100644
--- a/src/modules/agents/Elsa.Agents.Core/Extensions/ModuleExtensions.cs
+++ b/src/modules/agents/Elsa.Agents.Core/Extensions/ModuleExtensions.cs
@@ -12,7 +12,7 @@ public static class ModuleExtensions
///
/// Installs the Semantic Kernel API feature.
///
- public static IModule UseAgents(this IModule module, Action? configure = null)
+ public static IModule UseAgentsCore(this IModule module, Action? configure = null)
{
return module.Use(configure);
}
diff --git a/src/modules/agents/Elsa.Agents.Core/Extensions/ServiceCollectionExtensions.cs b/src/modules/agents/Elsa.Agents.Core/Extensions/ServiceCollectionExtensions.cs
index b33fed1c..baaf3bbf 100644
--- a/src/modules/agents/Elsa.Agents.Core/Extensions/ServiceCollectionExtensions.cs
+++ b/src/modules/agents/Elsa.Agents.Core/Extensions/ServiceCollectionExtensions.cs
@@ -4,13 +4,8 @@ namespace Elsa.Agents;
public static class ServiceCollectionExtensions
{
- public static IServiceCollection AddPluginProvider(this IServiceCollection services) where T: class, IPluginProvider
+ public static IServiceCollection AddSkillsProvider(this IServiceCollection services) where T: class, ISkillsProvider
{
- return services.AddScoped();
- }
-
- public static IServiceCollection AddAgentServiceProvider(this IServiceCollection services) where T: class, IAgentServiceProvider
- {
- return services.AddScoped();
+ return services.AddScoped();
}
}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Features/AgentsCoreFeature.cs b/src/modules/agents/Elsa.Agents.Core/Features/AgentsCoreFeature.cs
new file mode 100644
index 00000000..2237018a
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Core/Features/AgentsCoreFeature.cs
@@ -0,0 +1,39 @@
+using Elsa.Agents.Skills;
+using Elsa.Features.Abstractions;
+using Elsa.Features.Services;
+using JetBrains.Annotations;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Elsa.Agents.Features;
+
+///
+/// A feature that installs API endpoints to interact with skilled agents.
+///
+[UsedImplicitly]
+public class AgentsCoreFeature(IModule module) : FeatureBase(module)
+{
+ private Func _kernelConfigProviderFactory = sp => sp.GetRequiredService();
+
+ public AgentsCoreFeature UseKernelConfigProvider(Func factory)
+ {
+ _kernelConfigProviderFactory = factory;
+ return this;
+ }
+
+ ///
+ public override void Apply()
+ {
+ Services.AddOptions();
+
+ Services
+ .AddScoped()
+ .AddScoped()
+ .AddScoped()
+ .AddScoped(_kernelConfigProviderFactory)
+ .AddScoped()
+ .AddScoped()
+ .AddSkillsProvider()
+ .AddSkillsProvider()
+ ;
+ }
+}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Features/AgentsFeature.cs b/src/modules/agents/Elsa.Agents.Core/Features/AgentsFeature.cs
deleted file mode 100644
index 77c16f83..00000000
--- a/src/modules/agents/Elsa.Agents.Core/Features/AgentsFeature.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using Elsa.Agents.Plugins;
-using Elsa.Features.Abstractions;
-using Elsa.Features.Services;
-using JetBrains.Annotations;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Elsa.Agents.Features;
-
-///
-/// A feature that installs API endpoints to interact with skilled agents.
-///
-[UsedImplicitly]
-public class AgentsFeature(IModule module) : FeatureBase(module)
-{
- private Func _kernelConfigProviderFactory = sp => sp.GetRequiredService();
-
- public AgentsFeature UseKernelConfigProvider(Func factory)
- {
- _kernelConfigProviderFactory = factory;
- return this;
- }
-
- ///
- public override void Apply()
- {
- Services.AddOptions();
-
- Services
- .AddScoped()
- .AddScoped()
- .AddScoped()
- .AddScoped()
- .AddScoped(_kernelConfigProviderFactory)
- .AddScoped()
- .AddPluginProvider()
- .AddPluginProvider()
- .AddAgentServiceProvider()
- .AddAgentServiceProvider()
- .AddAgentServiceProvider()
- ;
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Models/AgentExecutionContext.cs b/src/modules/agents/Elsa.Agents.Core/Models/AgentExecutionContext.cs
new file mode 100644
index 00000000..861604e9
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Core/Models/AgentExecutionContext.cs
@@ -0,0 +1,7 @@
+namespace Elsa.Agents;
+
+public class AgentExecutionContext
+{
+ public string Message { get; set; } = null!;
+ public CancellationToken CancellationToken { get; set; }
+}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Models/InvokeAgentRequest.cs b/src/modules/agents/Elsa.Agents.Core/Models/InvokeAgentRequest.cs
new file mode 100644
index 00000000..2cff2b57
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Core/Models/InvokeAgentRequest.cs
@@ -0,0 +1,29 @@
+using Microsoft.SemanticKernel.ChatCompletion;
+
+namespace Elsa.Agents;
+
+///
+/// Represents a request to invoke an agent.
+///
+public class InvokeAgentRequest
+{
+ ///
+ /// Gets or sets the name of the agent to invoke.
+ ///
+ public required string AgentName { get; set; }
+
+ ///
+ /// Gets or sets the input parameters for the agent.
+ ///
+ public IDictionary Input { get; set; } = new Dictionary();
+
+ ///
+ /// Gets or sets the chat history. If null, a new chat history will be created.
+ ///
+ public ChatHistory? ChatHistory { get; set; }
+
+ ///
+ /// Gets or sets the cancellation token.
+ ///
+ public CancellationToken CancellationToken { get; set; } = CancellationToken.None;
+}
diff --git a/src/modules/agents/Elsa.Agents.Core/Models/KernelBuilderContext.cs b/src/modules/agents/Elsa.Agents.Core/Models/KernelBuilderContext.cs
deleted file mode 100644
index 8fe3dc15..00000000
--- a/src/modules/agents/Elsa.Agents.Core/Models/KernelBuilderContext.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using JetBrains.Annotations;
-using Microsoft.SemanticKernel;
-
-namespace Elsa.Agents;
-
-[UsedImplicitly]
-public record KernelBuilderContext(IKernelBuilder KernelBuilder, KernelConfig KernelConfig, ServiceConfig ServiceConfig)
-{
- public string GetApiKey()
- {
- var settings = ServiceConfig.Settings;
- if (settings.TryGetValue("ApiKey", out var apiKey))
- return (string)apiKey;
-
- if (settings.TryGetValue("ApiKeyRef", out var apiKeyRef))
- return KernelConfig.ApiKeys[(string)apiKeyRef].Value;
-
- throw new KeyNotFoundException($"No api key found for service {ServiceConfig.Type}");
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Models/PluginDescriptor.cs b/src/modules/agents/Elsa.Agents.Core/Models/PluginDescriptor.cs
deleted file mode 100644
index 31b0c8c7..00000000
--- a/src/modules/agents/Elsa.Agents.Core/Models/PluginDescriptor.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System.ComponentModel;
-using System.Reflection;
-
-namespace Elsa.Agents;
-
-///
-/// A descriptor for a plugin.
-///
-public class PluginDescriptor
-{
- public string Name { get; set; }
- public string Description { get; set; }
- public Type PluginType { get; set; }
-
- public PluginDescriptorModel ToModel() => new()
- {
- Name = Name,
- Description = Description,
- PluginType = PluginType.AssemblyQualifiedName!
- };
-
- public static PluginDescriptor From(string? name = null)
- {
- var pluginType = typeof(TPlugin);
- var description = pluginType.GetCustomAttribute()?.Description ?? string.Empty;
- return new PluginDescriptor
- {
- Name = name ?? pluginType.Name.Replace("Plugin", ""),
- Description = description,
- PluginType = pluginType
- };
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Models/SkillDescriptor.cs b/src/modules/agents/Elsa.Agents.Core/Models/SkillDescriptor.cs
new file mode 100644
index 00000000..97c54808
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Core/Models/SkillDescriptor.cs
@@ -0,0 +1,33 @@
+using System.ComponentModel;
+using System.Reflection;
+
+namespace Elsa.Agents;
+
+///
+/// A descriptor for a skill.
+///
+public class SkillDescriptor
+{
+ public string Name { get; set; } = null!;
+ public string Description { get; set; } = null!;
+ public Type ClrType { get; set; } = null!;
+
+ public SkillDescriptorModel ToModel() => new()
+ {
+ Name = Name,
+ Description = Description,
+ ClrTypeName = ClrType.AssemblyQualifiedName!
+ };
+
+ public static SkillDescriptor From(string? name = null)
+ {
+ var clrType = typeof(TSkill);
+ var description = clrType.GetCustomAttribute()?.Description ?? string.Empty;
+ return new()
+ {
+ Name = name ?? clrType.Name.Replace("Skill", ""),
+ Description = description,
+ ClrType = clrType
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Options/AgentOptions.cs b/src/modules/agents/Elsa.Agents.Core/Options/AgentOptions.cs
new file mode 100644
index 00000000..d9796968
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Core/Options/AgentOptions.cs
@@ -0,0 +1,23 @@
+namespace Elsa.Agents;
+
+public class AgentOptions
+{
+ public ICollection Agents { get; set; } = new List();
+ public ICollection ServiceDescriptors { get; set; } = new List();
+
+ ///
+ /// Map from agent key to the implementing type. Keys are case-insensitive.
+ ///
+ public IDictionary AgentTypes { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ ///
+ /// Registers a code-first agent type. If no key is provided, the type name
+ /// is used as the key.
+ ///
+ public AgentOptions AddAgentType(string? key = null) where TAgent : class, IAgent
+ {
+ key ??= typeof(TAgent).Name;
+ AgentTypes[key] = typeof(TAgent);
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Options/AgentsOptions.cs b/src/modules/agents/Elsa.Agents.Core/Options/AgentsOptions.cs
deleted file mode 100644
index a92862a6..00000000
--- a/src/modules/agents/Elsa.Agents.Core/Options/AgentsOptions.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Elsa.Agents;
-
-public class AgentsOptions
-{
- public ICollection ApiKeys { get; set; }
- public ICollection Services { get; set; }
- public ICollection Agents { get; set; }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/ServiceProviders/OpenAIChatCompletionProvider.cs b/src/modules/agents/Elsa.Agents.Core/ServiceProviders/OpenAIChatCompletionProvider.cs
deleted file mode 100644
index 771582f3..00000000
--- a/src/modules/agents/Elsa.Agents.Core/ServiceProviders/OpenAIChatCompletionProvider.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using Microsoft.SemanticKernel;
-
-namespace Elsa.Agents;
-
-public class OpenAIChatCompletionProvider : IAgentServiceProvider
-{
- public string Name => "OpenAIChatCompletion";
-
- public void ConfigureKernel(KernelBuilderContext context)
- {
- var modelId = (string)context.ServiceConfig.Settings["ModelId"];
- var apiKey = context.GetApiKey();
- context.KernelBuilder.AddOpenAIChatCompletion(modelId, apiKey);
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/ServiceProviders/OpenAIEmbeddingGenerator.cs b/src/modules/agents/Elsa.Agents.Core/ServiceProviders/OpenAIEmbeddingGenerator.cs
deleted file mode 100644
index accfde30..00000000
--- a/src/modules/agents/Elsa.Agents.Core/ServiceProviders/OpenAIEmbeddingGenerator.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-using Elsa.Extensions;
-using Microsoft.SemanticKernel;
-using Microsoft.SemanticKernel.Connectors.InMemory;
-using Microsoft.SemanticKernel.Memory;
-using Microsoft.SemanticKernel.Connectors.OpenAI;
-
-namespace Elsa.Agents;
-
-public class OpenAIEmbeddingGenerator : IAgentServiceProvider
-{
- public string Name => "OpenAIEmbeddingGenerator";
-
- [Experimental("SKEXP0010")]
- public void ConfigureKernel(KernelBuilderContext context)
- {
- var modelId = (string)context.ServiceConfig.Settings["ModelId"];
- var apiKey = context.GetApiKey();
-
- context.KernelBuilder.Services.AddInMemoryVectorStore();
- context.KernelBuilder.AddOpenAIEmbeddingGenerator(modelId, apiKey);
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/ServiceProviders/OpenAITextToImageProvider.cs b/src/modules/agents/Elsa.Agents.Core/ServiceProviders/OpenAITextToImageProvider.cs
deleted file mode 100644
index b20aba85..00000000
--- a/src/modules/agents/Elsa.Agents.Core/ServiceProviders/OpenAITextToImageProvider.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using Microsoft.SemanticKernel;
-#pragma warning disable SKEXP0010
-
-namespace Elsa.Agents;
-
-public class OpenAITextToImageProvider : IAgentServiceProvider
-{
- public string Name => "OpenAITextToImage";
-
- public void ConfigureKernel(KernelBuilderContext context)
- {
- var modelId = (string)context.ServiceConfig.Settings["ModelId"];
- var apiKey = context.GetApiKey();
- context.KernelBuilder.AddOpenAITextToImage(apiKey, modelId: modelId);
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Services/AgentFactory.cs b/src/modules/agents/Elsa.Agents.Core/Services/AgentFactory.cs
new file mode 100644
index 00000000..f6d718af
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Core/Services/AgentFactory.cs
@@ -0,0 +1,76 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Microsoft.SemanticKernel;
+using Microsoft.SemanticKernel.Agents;
+
+#pragma warning disable SKEXP0001
+#pragma warning disable SKEXP0010
+#pragma warning disable SKEXP0110
+
+namespace Elsa.Agents;
+
+///
+public class AgentFactory(
+ ISkillDiscoverer skillDiscoverer,
+ IServiceProvider serviceProvider,
+ IOptions options,
+ ILogger logger) : IAgentFactory
+{
+ ///
+ public ChatCompletionAgent CreateAgent(AgentConfig agentConfig)
+ {
+ var kernel = CreateKernel(agentConfig);
+
+ return new()
+ {
+ Name = agentConfig.Name,
+ Description = agentConfig.Description,
+ Instructions = agentConfig.PromptTemplate,
+ Kernel = kernel
+ };
+ }
+
+ ///
+ /// Creates a Kernel configured for the specified agent.
+ ///
+ private Kernel CreateKernel(AgentConfig agentConfig)
+ {
+ var builder = Kernel.CreateBuilder();
+ builder.Services.AddLogging(services => services.AddConsole().SetMinimumLevel(LogLevel.Trace));
+ builder.Services.AddSingleton(agentConfig);
+
+ ApplyAgentConfig(builder, agentConfig);
+ ApplyServiceDescriptors(builder, options.Value.ServiceDescriptors);
+
+ return builder.Build();
+ }
+
+ private void ApplyServiceDescriptors(IKernelBuilder builder, ICollection serviceDescriptors)
+ {
+ foreach (var descriptor in serviceDescriptors)
+ descriptor.ConfigureKernel(builder);
+ }
+
+ private void ApplyAgentConfig(IKernelBuilder builder, AgentConfig agentConfig)
+ {
+ AddSkills(builder, agentConfig);
+ }
+
+ private void AddSkills(IKernelBuilder builder, AgentConfig agent)
+ {
+ var skills = skillDiscoverer.DiscoverSkills().ToDictionary(x => x.Name);
+ foreach (var skillName in agent.Skills)
+ {
+ if (!skills.TryGetValue(skillName, out var skillDescriptor))
+ {
+ logger.LogWarning($"Skill {skillName} not found");
+ continue;
+ }
+
+ var clrType = skillDescriptor.ClrType;
+ var skillInstance = ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider, clrType);
+ builder.Plugins.AddFromObject(skillInstance, skillName);
+ }
+ }
+}
diff --git a/src/modules/agents/Elsa.Agents.Core/Services/AgentInvoker.cs b/src/modules/agents/Elsa.Agents.Core/Services/AgentInvoker.cs
index 907caae4..781475ce 100644
--- a/src/modules/agents/Elsa.Agents.Core/Services/AgentInvoker.cs
+++ b/src/modules/agents/Elsa.Agents.Core/Services/AgentInvoker.cs
@@ -5,16 +5,37 @@
#pragma warning disable SKEXP0010
#pragma warning disable SKEXP0001
+#pragma warning disable SKEXP0110
namespace Elsa.Agents;
-public class AgentInvoker(IKernelFactory kernelFactory, IKernelConfigProvider kernelConfigProvider)
+public class AgentInvoker(IKernelConfigProvider kernelConfigProvider, IAgentFactory agentFactory) : IAgentInvoker
{
- public async Task InvokeAgentAsync(string agentName, IDictionary input, CancellationToken cancellationToken = default)
+ ///
+ /// Invokes an agent using the Microsoft Agent Framework (new approach).
+ ///
+ public async Task InvokeAsync(InvokeAgentRequest request)
{
- var kernelConfig = await kernelConfigProvider.GetKernelConfigAsync(cancellationToken);
- var kernel = kernelFactory.CreateKernel(kernelConfig, agentName);
- var agentConfig = kernelConfig.Agents[agentName];
+ var kernelConfig = await kernelConfigProvider.GetKernelConfigAsync(request.CancellationToken);
+ var agentConfig = kernelConfig.Agents[request.AgentName];
+
+ // Create agent using Agent Framework
+ var agent = agentFactory.CreateAgent(agentConfig);
+
+ // Use provided chat history or create new one
+ ChatHistory chatHistory = request.ChatHistory ?? [];
+
+ // Format and add user input
+ var promptTemplateConfig = new PromptTemplateConfig
+ {
+ Template = agentConfig.PromptTemplate,
+ TemplateFormat = "handlebars",
+ Name = "Run",
+ AllowDangerouslySetContent = true,
+ };
+
+ var templateFactory = new HandlebarsPromptTemplateFactory();
+ var promptTemplate = templateFactory.Create(promptTemplateConfig);
var executionSettings = agentConfig.ExecutionSettings;
var promptExecutionSettings = new OpenAIPromptExecutionSettings
{
@@ -33,50 +54,33 @@ public async Task InvokeAgentAsync(string agentName, IDiction
[PromptExecutionSettings.DefaultServiceId] = promptExecutionSettings,
};
- var promptTemplateConfig = new PromptTemplateConfig
- {
- Name = agentConfig.FunctionName,
- Description = agentConfig.Description,
- Template = agentConfig.PromptTemplate,
- ExecutionSettings = promptExecutionSettingsDictionary,
- AllowDangerouslySetContent = true,
- InputVariables = agentConfig.InputVariables.Select(x => new InputVariable
- {
- Name = x.Name,
- Description = x.Description,
- IsRequired = true,
- AllowDangerouslySetContent = true
- }).ToList()
- };
+ var kernelArguments = new KernelArguments(request.Input, promptExecutionSettingsDictionary);
+ var renderedPrompt = await promptTemplate.RenderAsync(agent.Kernel, kernelArguments, request.CancellationToken);
- var templateFactory = new HandlebarsPromptTemplateFactory();
+ chatHistory.AddUserMessage(renderedPrompt);
- var promptConfig = new PromptTemplateConfig
+ if (executionSettings.ResponseFormat == "json_object")
{
- Template = agentConfig.PromptTemplate,
- TemplateFormat = "handlebars",
- Name = agentConfig.FunctionName
- };
+ chatHistory.AddSystemMessage(
+ """"
+ You are a function that returns *only* JSON.
- var promptTemplate = templateFactory.Create(promptConfig);
+ Rules:
+ - Return a single valid JSON object.
+ - Do not add explanations.
+ - Do not add code fences.
+ - Do not prefix with ```json or any other markers.
+ - Output must start with { and end with }.
+
+ If there's a problem with the JSON input, include the exact JSON input in your response for troubleshooting.
+ """");
+ }
- var kernelArguments = new KernelArguments(input);
- string renderedPrompt = await promptTemplate.RenderAsync(kernel, kernelArguments);
-
- ChatHistory chatHistory = [];
- chatHistory.AddUserMessage(renderedPrompt);
-
- IChatCompletionService chatCompletion = kernel.GetRequiredService();
-
- OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new()
- {
- FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
- };
+ // Get response from agent
+ var response = await agent.InvokeAsync(chatHistory, cancellationToken: request.CancellationToken).LastOrDefaultAsync(request.CancellationToken);
- var response = await chatCompletion.GetChatMessageContentAsync(
- chatHistory,
- executionSettings: openAIPromptExecutionSettings,
- kernel: kernel);
+ if (response == null)
+ throw new InvalidOperationException("Agent did not produce a response");
return new(agentConfig, response);
}
diff --git a/src/modules/agents/Elsa.Agents.Core/Services/AgentResolver.cs b/src/modules/agents/Elsa.Agents.Core/Services/AgentResolver.cs
new file mode 100644
index 00000000..db53d9a6
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Core/Services/AgentResolver.cs
@@ -0,0 +1,14 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+
+namespace Elsa.Agents;
+
+public class AgentResolver(IServiceProvider serviceProvider, IOptions options) : IAgentResolver
+{
+ public Task ResolveAsync(string agentName, CancellationToken cancellationToken = default)
+ {
+ var agentType = options.Value.AgentTypes[agentName] ?? throw new InvalidOperationException($"No agent with name '{agentName}' was found.");
+ var agent = (IAgent)ActivatorUtilities.CreateInstance(serviceProvider, agentType)!;
+ return Task.FromResult(agent);
+ }
+}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Services/ConfigurationKernelConfigProvider.cs b/src/modules/agents/Elsa.Agents.Core/Services/ConfigurationKernelConfigProvider.cs
index e4320df2..c2bb6947 100644
--- a/src/modules/agents/Elsa.Agents.Core/Services/ConfigurationKernelConfigProvider.cs
+++ b/src/modules/agents/Elsa.Agents.Core/Services/ConfigurationKernelConfigProvider.cs
@@ -3,15 +3,22 @@
namespace Elsa.Agents;
+///
+/// Provides kernel configuration from configuration.
+///
[UsedImplicitly]
-public class ConfigurationKernelConfigProvider(IOptions options) : IKernelConfigProvider
+public class ConfigurationKernelConfigProvider(IOptions options) : IKernelConfigProvider
{
public Task GetKernelConfigAsync(CancellationToken cancellationToken = default)
{
var kernelConfig = new KernelConfig();
- foreach (var apiKey in options.Value.ApiKeys) kernelConfig.ApiKeys[apiKey.Name] = apiKey;
- foreach (var service in options.Value.Services) kernelConfig.Services[service.Name] = service;
- foreach (var agent in options.Value.Agents) kernelConfig.Agents[agent.Name] = agent;
+
+ if (options.Value.Agents != null!)
+ {
+ foreach (var agent in options.Value.Agents)
+ kernelConfig.Agents[agent.Name] = agent;
+ }
+
return Task.FromResult(kernelConfig);
}
}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Services/KernelFactory.cs b/src/modules/agents/Elsa.Agents.Core/Services/KernelFactory.cs
deleted file mode 100644
index 9b8dfaf8..00000000
--- a/src/modules/agents/Elsa.Agents.Core/Services/KernelFactory.cs
+++ /dev/null
@@ -1,113 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.SemanticKernel;
-
-#pragma warning disable SKEXP0001
-#pragma warning disable SKEXP0010
-
-namespace Elsa.Agents;
-
-public class KernelFactory(IPluginDiscoverer pluginDiscoverer, IServiceDiscoverer serviceDiscoverer, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, ILogger logger) : IKernelFactory
-{
- public Kernel CreateKernel(KernelConfig kernelConfig, string agentName)
- {
- var agent = kernelConfig.Agents[agentName];
- return CreateKernel(kernelConfig, agent);
- }
-
- public Kernel CreateKernel(KernelConfig kernelConfig, AgentConfig agentConfig)
- {
- var builder = Kernel.CreateBuilder();
- builder.Services.AddLogging(services => services.AddConsole().SetMinimumLevel(LogLevel.Trace));
- builder.Services.AddSingleton(agentConfig);
-
- ApplyAgentConfig(builder, kernelConfig, agentConfig);
-
- return builder.Build();
- }
-
- private void ApplyAgentConfig(IKernelBuilder builder, KernelConfig kernelConfig, AgentConfig agentConfig)
- {
- var services = serviceDiscoverer.Discover().ToDictionary(x => x.Name);
-
- foreach (string serviceName in agentConfig.Services)
- {
- if (!kernelConfig.Services.TryGetValue(serviceName, out var serviceConfig))
- {
- logger.LogWarning($"Service {serviceName} not found");
- continue;
- }
-
- AddService(builder, kernelConfig, serviceConfig, services);
- }
-
- AddPlugins(builder, agentConfig);
- AddAgents(builder, kernelConfig, agentConfig);
- }
-
- private void AddService(IKernelBuilder builder, KernelConfig kernelConfig, ServiceConfig serviceConfig, Dictionary services)
- {
- if (!services.TryGetValue(serviceConfig.Type, out var serviceProvider))
- {
- logger.LogWarning($"Service provider {serviceConfig.Type} not found");
- return;
- }
-
- var context = new KernelBuilderContext(builder, kernelConfig, serviceConfig);
- serviceProvider.ConfigureKernel(context);
- }
-
- private void AddPlugins(IKernelBuilder builder, AgentConfig agent)
- {
- var plugins = pluginDiscoverer.GetPluginDescriptors().ToDictionary(x => x.Name);
- foreach (var pluginName in agent.Plugins)
- {
- if (!plugins.TryGetValue(pluginName, out var pluginDescriptor))
- {
- logger.LogWarning($"Plugin {pluginName} not found");
- continue;
- }
-
- var pluginType = pluginDescriptor.PluginType;
- var pluginInstance = ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider, pluginType);
- builder.Plugins.AddFromObject(pluginInstance, pluginName);
- }
- }
-
- private void AddAgents(IKernelBuilder builder, KernelConfig kernelConfig, AgentConfig agent)
- {
- foreach (var agentName in agent.Agents)
- {
- if (!kernelConfig.Agents.TryGetValue(agentName, out var subAgent))
- {
- logger.LogWarning($"Agent {agentName} not found");
- continue;
- }
-
- var promptExecutionSettings = subAgent.ToOpenAIPromptExecutionSettings();
- var promptExecutionSettingsDictionary = new Dictionary
- {
- [PromptExecutionSettings.DefaultServiceId] = promptExecutionSettings,
- };
- var promptTemplateConfig = new PromptTemplateConfig
- {
- Name = subAgent.FunctionName,
- Description = subAgent.Description,
- Template = subAgent.PromptTemplate,
- ExecutionSettings = promptExecutionSettingsDictionary,
- AllowDangerouslySetContent = true,
- InputVariables = subAgent.InputVariables.Select(x => new InputVariable
- {
- Name = x.Name,
- Description = x.Description,
- IsRequired = true,
- AllowDangerouslySetContent = true
- }).ToList()
- };
-
- var subAgentFunction = KernelFunctionFactory.CreateFromPrompt(promptTemplateConfig, loggerFactory: loggerFactory);
- var agentPlugin = KernelPluginFactory.CreateFromFunctions(subAgent.Name, subAgent.Description, [subAgentFunction]);
- builder.Plugins.Add(agentPlugin);
- }
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Services/PluginDiscoverer.cs b/src/modules/agents/Elsa.Agents.Core/Services/PluginDiscoverer.cs
deleted file mode 100644
index fa645195..00000000
--- a/src/modules/agents/Elsa.Agents.Core/Services/PluginDiscoverer.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Elsa.Agents;
-
-public class PluginDiscoverer(IEnumerable providers) : IPluginDiscoverer
-{
- public IEnumerable GetPluginDescriptors()
- {
- return providers.SelectMany(x => x.GetPlugins());
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Services/ServiceDiscoverer.cs b/src/modules/agents/Elsa.Agents.Core/Services/ServiceDiscoverer.cs
deleted file mode 100644
index 46f18c6e..00000000
--- a/src/modules/agents/Elsa.Agents.Core/Services/ServiceDiscoverer.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Elsa.Agents;
-
-public class ServiceDiscoverer(IEnumerable providers) : IServiceDiscoverer
-{
- public IEnumerable Discover()
- {
- return providers;
- }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Services/SkillDiscoverer.cs b/src/modules/agents/Elsa.Agents.Core/Services/SkillDiscoverer.cs
new file mode 100644
index 00000000..88ff8b73
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Core/Services/SkillDiscoverer.cs
@@ -0,0 +1,9 @@
+namespace Elsa.Agents;
+
+public class SkillDiscoverer(IEnumerable providers) : ISkillDiscoverer
+{
+ public IEnumerable DiscoverSkills()
+ {
+ return providers.SelectMany(x => x.GetSkills());
+ }
+}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Core/Plugins/DocumentQueryPlugin.cs b/src/modules/agents/Elsa.Agents.Core/Skills/DocumentQuerySkill.cs
similarity index 88%
rename from src/modules/agents/Elsa.Agents.Core/Plugins/DocumentQueryPlugin.cs
rename to src/modules/agents/Elsa.Agents.Core/Skills/DocumentQuerySkill.cs
index 811ecf38..fb2c8348 100644
--- a/src/modules/agents/Elsa.Agents.Core/Plugins/DocumentQueryPlugin.cs
+++ b/src/modules/agents/Elsa.Agents.Core/Skills/DocumentQuerySkill.cs
@@ -6,9 +6,9 @@
using Microsoft.SemanticKernel.Connectors.InMemory;
using Microsoft.SemanticKernel.Data;
-namespace Elsa.Agents.Plugins;
+namespace Elsa.Agents.Skills;
-public class DocumentQueryPlugin
+public class DocumentQuerySkill
{
[Experimental("SKEXP0001")]
[KernelFunction("query_document")]
@@ -39,11 +39,11 @@ public async Task QueryDocumentAsync(
}
}
-public class DocumentQueryPluginProvider : PluginProvider
+public class DocumentQuerySkillsProvider : SkillsProvider
{
- public override IEnumerable GetPlugins()
+ public override IEnumerable GetSkills()
{
- yield return PluginDescriptor.From();
+ yield return SkillDescriptor.From();
}
}
diff --git a/src/modules/agents/Elsa.Agents.Core/Plugins/ImageGeneratorPlugin.cs b/src/modules/agents/Elsa.Agents.Core/Skills/ImageGeneratorSkill.cs
similarity index 80%
rename from src/modules/agents/Elsa.Agents.Core/Plugins/ImageGeneratorPlugin.cs
rename to src/modules/agents/Elsa.Agents.Core/Skills/ImageGeneratorSkill.cs
index a4d62975..8689ca2f 100644
--- a/src/modules/agents/Elsa.Agents.Core/Plugins/ImageGeneratorPlugin.cs
+++ b/src/modules/agents/Elsa.Agents.Core/Skills/ImageGeneratorSkill.cs
@@ -5,11 +5,11 @@
#pragma warning disable SKEXP0001
-namespace Elsa.Agents.Plugins;
+namespace Elsa.Agents.Skills;
[Description("Generates an image from text")]
[UsedImplicitly]
-public class ImageGeneratorPlugin
+public class ImageGeneratorSkill
{
[KernelFunction("generate_image_from_text")]
[Description("Generates an image from text")]
@@ -30,10 +30,10 @@ public async Task GenerateImage(
}
[UsedImplicitly]
-public class ImageGeneratorPluginProvider : PluginProvider
+public class ImageGeneratorSkillsProvider : SkillsProvider
{
- public override IEnumerable GetPlugins()
+ public override IEnumerable GetSkills()
{
- yield return PluginDescriptor.From();
+ yield return SkillDescriptor.From();
}
}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Models/Agents/AgentInputModel.cs b/src/modules/agents/Elsa.Agents.Models/Agents/AgentInputModel.cs
index b5c003ed..2208dc63 100644
--- a/src/modules/agents/Elsa.Agents.Models/Agents/AgentInputModel.cs
+++ b/src/modules/agents/Elsa.Agents.Models/Agents/AgentInputModel.cs
@@ -6,12 +6,10 @@ public class AgentInputModel
{
[Required] public string Name { get; set; } = "";
[Required] public string Description { get; set; } = "";
- public ICollection Services { get; set; } = [];
- [Required] public string FunctionName { get; set; } = "";
[Required] public string PromptTemplate { get; set; } = "";
public ICollection InputVariables { get; set; } = [];
[Required] public OutputVariableConfig OutputVariable { get; set; } = new();
public ExecutionSettingsConfig ExecutionSettings { get; set; } = new();
- public ICollection Plugins { get; set; } = [];
+ public ICollection Skills { get; set; } = [];
public ICollection Agents { get; set; } = [];
}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Models/Configs/AgentConfig.cs b/src/modules/agents/Elsa.Agents.Models/Configs/AgentConfig.cs
index 6257ae12..9dc3c6a3 100644
--- a/src/modules/agents/Elsa.Agents.Models/Configs/AgentConfig.cs
+++ b/src/modules/agents/Elsa.Agents.Models/Configs/AgentConfig.cs
@@ -4,13 +4,11 @@ public class AgentConfig
{
public string Name { get; set; } = "";
public string Description { get; set; } = "";
- public ICollection Services { get; set; } = [];
- public string FunctionName { get; set; } = null!;
public string PromptTemplate { get; set; } = null!;
public ICollection InputVariables { get; set; } = [];
public OutputVariableConfig OutputVariable { get; set; } = new();
public ExecutionSettingsConfig ExecutionSettings { get; set; } = new();
- public ICollection Plugins { get; set; } = [];
+ public ICollection Skills { get; set; } = [];
public ICollection Agents { get; set; } = [];
diff --git a/src/modules/agents/Elsa.Agents.Models/Configs/ApiKeyConfig.cs b/src/modules/agents/Elsa.Agents.Models/Configs/ApiKeyConfig.cs
deleted file mode 100644
index 88b4e733..00000000
--- a/src/modules/agents/Elsa.Agents.Models/Configs/ApiKeyConfig.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using JetBrains.Annotations;
-
-namespace Elsa.Agents;
-
-[UsedImplicitly]
-public class ApiKeyConfig
-{
- public string Name { get; set; }
- public string Value { get; set; }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Models/Configs/FunctionConfig.cs b/src/modules/agents/Elsa.Agents.Models/Configs/FunctionConfig.cs
deleted file mode 100644
index a4b3d05b..00000000
--- a/src/modules/agents/Elsa.Agents.Models/Configs/FunctionConfig.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Elsa.Agents;
-
-public class FunctionConfig
-{
-
-
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Models/Configs/KernelConfig.cs b/src/modules/agents/Elsa.Agents.Models/Configs/KernelConfig.cs
index 75c28527..427325c0 100644
--- a/src/modules/agents/Elsa.Agents.Models/Configs/KernelConfig.cs
+++ b/src/modules/agents/Elsa.Agents.Models/Configs/KernelConfig.cs
@@ -2,7 +2,5 @@ namespace Elsa.Agents;
public class KernelConfig
{
- public IDictionary ApiKeys { get; set; } = new Dictionary();
- public IDictionary Services { get; } = new Dictionary();
public IDictionary Agents { get; } = new Dictionary();
}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Models/Configs/ServiceConfig.cs b/src/modules/agents/Elsa.Agents.Models/Configs/ServiceConfig.cs
deleted file mode 100644
index 4ad83b92..00000000
--- a/src/modules/agents/Elsa.Agents.Models/Configs/ServiceConfig.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Elsa.Agents;
-
-public class ServiceConfig
-{
- public string Name { get; set; }
- public string Type { get; set; }
- public IDictionary Settings { get; set; } = new Dictionary();
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Models/Plugins/PluginDescriptor.cs b/src/modules/agents/Elsa.Agents.Models/Plugins/PluginDescriptor.cs
deleted file mode 100644
index c8325fdf..00000000
--- a/src/modules/agents/Elsa.Agents.Models/Plugins/PluginDescriptor.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace Elsa.Agents;
-
-///
-/// A descriptor for a plugin.
-///
-public class PluginDescriptorModel
-{
- public string Name { get; set; }
- public string Description { get; set; }
- public string PluginType { get; set; }
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Models/Plugins/SkillDescriptorModel.cs b/src/modules/agents/Elsa.Agents.Models/Plugins/SkillDescriptorModel.cs
new file mode 100644
index 00000000..1a4f7e43
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Models/Plugins/SkillDescriptorModel.cs
@@ -0,0 +1,11 @@
+namespace Elsa.Agents;
+
+///
+/// A descriptor of a skill.
+///
+public class SkillDescriptorModel
+{
+ public string Name { get; set; } = null!;
+ public string Description { get; set; } = null!;
+ public string ClrTypeName { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Models/Services/ServiceInputModel.cs b/src/modules/agents/Elsa.Agents.Models/Services/ServiceInputModel.cs
deleted file mode 100644
index 2ffa016a..00000000
--- a/src/modules/agents/Elsa.Agents.Models/Services/ServiceInputModel.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace Elsa.Agents;
-
-public class ServiceInputModel
-{
- [Required] public string Name { get; set; } = null!;
- [Required] public string Type { get; set; } = null!;
- public IDictionary Settings { get; set; } = new Dictionary();
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Models/Services/ServiceModel.cs b/src/modules/agents/Elsa.Agents.Models/Services/ServiceModel.cs
deleted file mode 100644
index 845c835f..00000000
--- a/src/modules/agents/Elsa.Agents.Models/Services/ServiceModel.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace Elsa.Agents;
-
-public class ServiceModel : ServiceInputModel
-{
- [Required] public string Id { get; set; } = null!;
-}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.OpenAI/AgentsFeatureExtensions.cs b/src/modules/agents/Elsa.Agents.OpenAI/AgentsFeatureExtensions.cs
new file mode 100644
index 00000000..a82896f0
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.OpenAI/AgentsFeatureExtensions.cs
@@ -0,0 +1,20 @@
+using Microsoft.SemanticKernel;
+
+namespace Elsa.Agents.OpenAI;
+
+public static class AgentsFeatureExtensions
+{
+ public static AgentsFeature AddOpenAIChatCompletion(this AgentsFeature feature,
+ string modelId,
+ string apiKey,
+ string? orgId = null,
+ string? serviceId = null,
+ string name = "OpenAI Chat Completion")
+ {
+ return feature.AddServiceDescriptor(new()
+ {
+ Name = name,
+ ConfigureKernel = kernel => kernel.Services.AddOpenAIChatCompletion(modelId, apiKey, orgId, serviceId)
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.OpenAI/Elsa.Agents.OpenAI.csproj b/src/modules/agents/Elsa.Agents.OpenAI/Elsa.Agents.OpenAI.csproj
new file mode 100644
index 00000000..6ab1a67d
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.OpenAI/Elsa.Agents.OpenAI.csproj
@@ -0,0 +1,22 @@
+
+
+
+ Provides OpenAI integration with Elsa Agents
+ elsa extension module agents openai
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/agents/Elsa.Agents.OpenAI/FodyWeavers.xml b/src/modules/agents/Elsa.Agents.OpenAI/FodyWeavers.xml
new file mode 100644
index 00000000..00e1d9a1
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.OpenAI/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Persistence.EFCore.MySql/Migrations/20251213194317_V3_6.Designer.cs b/src/modules/agents/Elsa.Agents.Persistence.EFCore.MySql/Migrations/20251213194317_V3_6.Designer.cs
new file mode 100644
index 00000000..8b14cbee
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Persistence.EFCore.MySql/Migrations/20251213194317_V3_6.Designer.cs
@@ -0,0 +1,61 @@
+//
+using Elsa.Agents.Persistence.EFCore;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace Elsa.Agents.Persistence.EFCore.MySql.Migrations
+{
+ [DbContext(typeof(AgentsDbContext))]
+ [Migration("20251213194317_V3_6")]
+ partial class V3_6
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasDefaultSchema("Elsa")
+ .HasAnnotation("ProductVersion", "9.0.11")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
+
+ modelBuilder.Entity("Elsa.Agents.Persistence.Entities.AgentDefinition", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("varchar(255)");
+
+ b.Property("AgentConfig")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("varchar(255)");
+
+ b.Property("TenantId")
+ .HasColumnType("varchar(255)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .HasDatabaseName("IX_AgentDefinition_Name");
+
+ b.HasIndex("TenantId")
+ .HasDatabaseName("IX_AgentDefinition_TenantId");
+
+ b.ToTable("AgentDefinitions", "Elsa");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/src/modules/agents/Elsa.Agents.Persistence.EFCore.MySql/Migrations/20251213194317_V3_6.cs b/src/modules/agents/Elsa.Agents.Persistence.EFCore.MySql/Migrations/20251213194317_V3_6.cs
new file mode 100644
index 00000000..454b9936
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Persistence.EFCore.MySql/Migrations/20251213194317_V3_6.cs
@@ -0,0 +1,100 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Elsa.Agents.Persistence.EFCore.MySql.Migrations
+{
+ ///
+ public partial class V3_6 : Migration
+ {
+ private readonly Elsa.Persistence.EFCore.IElsaDbContextSchema _schema;
+
+ ///
+ public V3_6(Elsa.Persistence.EFCore.IElsaDbContextSchema schema)
+ {
+ _schema = schema;
+ }
+
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "ApiKeysDefinitions",
+ schema: _schema.Schema);
+
+ migrationBuilder.DropTable(
+ name: "ServicesDefinitions",
+ schema: _schema.Schema);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable(
+ name: "ApiKeysDefinitions",
+ schema: _schema.Schema,
+ columns: table => new
+ {
+ Id = table.Column(type: "varchar(255)", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ Name = table.Column(type: "varchar(255)", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ TenantId = table.Column(type: "varchar(255)", nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ Value = table.Column(type: "longtext", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4")
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_ApiKeysDefinitions", x => x.Id);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "ServicesDefinitions",
+ schema: _schema.Schema,
+ columns: table => new
+ {
+ Id = table.Column(type: "varchar(255)", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ Name = table.Column(type: "varchar(255)", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ Settings = table.Column(type: "longtext", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ TenantId = table.Column(type: "varchar(255)", nullable: true)
+ .Annotation("MySql:CharSet", "utf8mb4"),
+ Type = table.Column(type: "longtext", nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4")
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_ServicesDefinitions", x => x.Id);
+ })
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_ApiKeyDefinition_Name",
+ schema: _schema.Schema,
+ table: "ApiKeysDefinitions",
+ column: "Name");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_ApiKeyDefinition_TenantId",
+ schema: _schema.Schema,
+ table: "ApiKeysDefinitions",
+ column: "TenantId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_ServiceDefinition_Name",
+ schema: _schema.Schema,
+ table: "ServicesDefinitions",
+ column: "Name");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_ServiceDefinition_TenantId",
+ schema: _schema.Schema,
+ table: "ServicesDefinitions",
+ column: "TenantId");
+ }
+ }
+}
diff --git a/src/modules/agents/Elsa.Agents.Persistence.EFCore.MySql/Migrations/AgentsDbContextModelSnapshot.cs b/src/modules/agents/Elsa.Agents.Persistence.EFCore.MySql/Migrations/AgentsDbContextModelSnapshot.cs
index 3f3f3b03..a38076e9 100644
--- a/src/modules/agents/Elsa.Agents.Persistence.EFCore.MySql/Migrations/AgentsDbContextModelSnapshot.cs
+++ b/src/modules/agents/Elsa.Agents.Persistence.EFCore.MySql/Migrations/AgentsDbContextModelSnapshot.cs
@@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("Elsa")
- .HasAnnotation("ProductVersion", "8.0.13")
+ .HasAnnotation("ProductVersion", "9.0.11")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
@@ -52,64 +52,6 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.ToTable("AgentDefinitions", "Elsa");
});
-
- modelBuilder.Entity("Elsa.Agents.Persistence.Entities.ApiKeyDefinition", b =>
- {
- b.Property("Id")
- .HasColumnType("varchar(255)");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("varchar(255)");
-
- b.Property("TenantId")
- .HasColumnType("varchar(255)");
-
- b.Property("Value")
- .IsRequired()
- .HasColumnType("longtext");
-
- b.HasKey("Id");
-
- b.HasIndex("Name")
- .HasDatabaseName("IX_ApiKeyDefinition_Name");
-
- b.HasIndex("TenantId")
- .HasDatabaseName("IX_ApiKeyDefinition_TenantId");
-
- b.ToTable("ApiKeysDefinitions", "Elsa");
- });
-
- modelBuilder.Entity("Elsa.Agents.Persistence.Entities.ServiceDefinition", b =>
- {
- b.Property("Id")
- .HasColumnType("varchar(255)");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("varchar(255)");
-
- b.Property("Settings")
- .IsRequired()
- .HasColumnType("longtext");
-
- b.Property("TenantId")
- .HasColumnType("varchar(255)");
-
- b.Property("Type")
- .IsRequired()
- .HasColumnType("longtext");
-
- b.HasKey("Id");
-
- b.HasIndex("Name")
- .HasDatabaseName("IX_ServiceDefinition_Name");
-
- b.HasIndex("TenantId")
- .HasDatabaseName("IX_ServiceDefinition_TenantId");
-
- b.ToTable("ServicesDefinitions", "Elsa");
- });
#pragma warning restore 612, 618
}
}
diff --git a/src/modules/agents/Elsa.Agents.Persistence.EFCore.MySql/efcore-3.6.sh b/src/modules/agents/Elsa.Agents.Persistence.EFCore.MySql/efcore-3.6.sh
new file mode 100644
index 00000000..16d773fe
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Persistence.EFCore.MySql/efcore-3.6.sh
@@ -0,0 +1 @@
+ef-migration-runtime-schema --interface Elsa.Persistence.EFCore.IElsaDbContextSchema --efOptions "migrations add V3_6 -c AgentsDbContext -o Migrations"
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Persistence.EFCore.PostgreSql/Migrations/20251213195607_V3_6.Designer.cs b/src/modules/agents/Elsa.Agents.Persistence.EFCore.PostgreSql/Migrations/20251213195607_V3_6.Designer.cs
new file mode 100644
index 00000000..a646c5b9
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Persistence.EFCore.PostgreSql/Migrations/20251213195607_V3_6.Designer.cs
@@ -0,0 +1,61 @@
+//
+using Elsa.Agents.Persistence.EFCore;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace Elsa.Agents.Persistence.EFCore.PostgreSql.Migrations
+{
+ [DbContext(typeof(AgentsDbContext))]
+ [Migration("20251213195607_V3_6")]
+ partial class V3_6
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasDefaultSchema("Elsa")
+ .HasAnnotation("ProductVersion", "9.0.11")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("Elsa.Agents.Persistence.Entities.AgentDefinition", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("AgentConfig")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("TenantId")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .HasDatabaseName("IX_AgentDefinition_Name");
+
+ b.HasIndex("TenantId")
+ .HasDatabaseName("IX_AgentDefinition_TenantId");
+
+ b.ToTable("AgentDefinitions", "Elsa");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/src/modules/agents/Elsa.Agents.Persistence.EFCore.PostgreSql/Migrations/20251213195607_V3_6.cs b/src/modules/agents/Elsa.Agents.Persistence.EFCore.PostgreSql/Migrations/20251213195607_V3_6.cs
new file mode 100644
index 00000000..4fc4b8ad
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Persistence.EFCore.PostgreSql/Migrations/20251213195607_V3_6.cs
@@ -0,0 +1,89 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Elsa.Agents.Persistence.EFCore.PostgreSql.Migrations
+{
+ ///
+ public partial class V3_6 : Migration
+ {
+ private readonly Elsa.Persistence.EFCore.IElsaDbContextSchema _schema;
+
+ ///
+ public V3_6(Elsa.Persistence.EFCore.IElsaDbContextSchema schema)
+ {
+ _schema = schema;
+ }
+
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "ApiKeysDefinitions",
+ schema: _schema.Schema);
+
+ migrationBuilder.DropTable(
+ name: "ServicesDefinitions",
+ schema: _schema.Schema);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable(
+ name: "ApiKeysDefinitions",
+ schema: _schema.Schema,
+ columns: table => new
+ {
+ Id = table.Column(type: "text", nullable: false),
+ Name = table.Column(type: "text", nullable: false),
+ TenantId = table.Column(type: "text", nullable: true),
+ Value = table.Column(type: "text", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_ApiKeysDefinitions", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "ServicesDefinitions",
+ schema: _schema.Schema,
+ columns: table => new
+ {
+ Id = table.Column(type: "text", nullable: false),
+ Name = table.Column(type: "text", nullable: false),
+ Settings = table.Column(type: "text", nullable: false),
+ TenantId = table.Column(type: "text", nullable: true),
+ Type = table.Column(type: "text", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_ServicesDefinitions", x => x.Id);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_ApiKeyDefinition_Name",
+ schema: _schema.Schema,
+ table: "ApiKeysDefinitions",
+ column: "Name");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_ApiKeyDefinition_TenantId",
+ schema: _schema.Schema,
+ table: "ApiKeysDefinitions",
+ column: "TenantId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_ServiceDefinition_Name",
+ schema: _schema.Schema,
+ table: "ServicesDefinitions",
+ column: "Name");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_ServiceDefinition_TenantId",
+ schema: _schema.Schema,
+ table: "ServicesDefinitions",
+ column: "TenantId");
+ }
+ }
+}
diff --git a/src/modules/agents/Elsa.Agents.Persistence.EFCore.PostgreSql/Migrations/AgentsDbContextModelSnapshot.cs b/src/modules/agents/Elsa.Agents.Persistence.EFCore.PostgreSql/Migrations/AgentsDbContextModelSnapshot.cs
index e905428b..4f944deb 100644
--- a/src/modules/agents/Elsa.Agents.Persistence.EFCore.PostgreSql/Migrations/AgentsDbContextModelSnapshot.cs
+++ b/src/modules/agents/Elsa.Agents.Persistence.EFCore.PostgreSql/Migrations/AgentsDbContextModelSnapshot.cs
@@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("Elsa")
- .HasAnnotation("ProductVersion", "8.0.13")
+ .HasAnnotation("ProductVersion", "9.0.11")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
@@ -52,64 +52,6 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.ToTable("AgentDefinitions", "Elsa");
});
-
- modelBuilder.Entity("Elsa.Agents.Persistence.Entities.ApiKeyDefinition", b =>
- {
- b.Property("Id")
- .HasColumnType("text");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("text");
-
- b.Property("TenantId")
- .HasColumnType("text");
-
- b.Property("Value")
- .IsRequired()
- .HasColumnType("text");
-
- b.HasKey("Id");
-
- b.HasIndex("Name")
- .HasDatabaseName("IX_ApiKeyDefinition_Name");
-
- b.HasIndex("TenantId")
- .HasDatabaseName("IX_ApiKeyDefinition_TenantId");
-
- b.ToTable("ApiKeysDefinitions", "Elsa");
- });
-
- modelBuilder.Entity("Elsa.Agents.Persistence.Entities.ServiceDefinition", b =>
- {
- b.Property("Id")
- .HasColumnType("text");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("text");
-
- b.Property("Settings")
- .IsRequired()
- .HasColumnType("text");
-
- b.Property("TenantId")
- .HasColumnType("text");
-
- b.Property("Type")
- .IsRequired()
- .HasColumnType("text");
-
- b.HasKey("Id");
-
- b.HasIndex("Name")
- .HasDatabaseName("IX_ServiceDefinition_Name");
-
- b.HasIndex("TenantId")
- .HasDatabaseName("IX_ServiceDefinition_TenantId");
-
- b.ToTable("ServicesDefinitions", "Elsa");
- });
#pragma warning restore 612, 618
}
}
diff --git a/src/modules/agents/Elsa.Agents.Persistence.EFCore.PostgreSql/efcore-3.6.sh b/src/modules/agents/Elsa.Agents.Persistence.EFCore.PostgreSql/efcore-3.6.sh
new file mode 100644
index 00000000..16d773fe
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Persistence.EFCore.PostgreSql/efcore-3.6.sh
@@ -0,0 +1 @@
+ef-migration-runtime-schema --interface Elsa.Persistence.EFCore.IElsaDbContextSchema --efOptions "migrations add V3_6 -c AgentsDbContext -o Migrations"
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Persistence.EFCore.SqlServer/Migrations/20251213200214_V3_6.Designer.cs b/src/modules/agents/Elsa.Agents.Persistence.EFCore.SqlServer/Migrations/20251213200214_V3_6.Designer.cs
new file mode 100644
index 00000000..30646b43
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Persistence.EFCore.SqlServer/Migrations/20251213200214_V3_6.Designer.cs
@@ -0,0 +1,61 @@
+//
+using Elsa.Agents.Persistence.EFCore;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace Elsa.Agents.Persistence.EFCore.SqlServer.Migrations
+{
+ [DbContext(typeof(AgentsDbContext))]
+ [Migration("20251213200214_V3_6")]
+ partial class V3_6
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasDefaultSchema("Elsa")
+ .HasAnnotation("ProductVersion", "9.0.11")
+ .HasAnnotation("Relational:MaxIdentifierLength", 128);
+
+ SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
+
+ modelBuilder.Entity("Elsa.Agents.Persistence.Entities.AgentDefinition", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("AgentConfig")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("TenantId")
+ .HasColumnType("nvarchar(450)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .HasDatabaseName("IX_AgentDefinition_Name");
+
+ b.HasIndex("TenantId")
+ .HasDatabaseName("IX_AgentDefinition_TenantId");
+
+ b.ToTable("AgentDefinitions", "Elsa");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/src/modules/agents/Elsa.Agents.Persistence.EFCore.SqlServer/Migrations/20251213200214_V3_6.cs b/src/modules/agents/Elsa.Agents.Persistence.EFCore.SqlServer/Migrations/20251213200214_V3_6.cs
new file mode 100644
index 00000000..cb921966
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Persistence.EFCore.SqlServer/Migrations/20251213200214_V3_6.cs
@@ -0,0 +1,89 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Elsa.Agents.Persistence.EFCore.SqlServer.Migrations
+{
+ ///
+ public partial class V3_6 : Migration
+ {
+ private readonly Elsa.Persistence.EFCore.IElsaDbContextSchema _schema;
+
+ ///
+ public V3_6(Elsa.Persistence.EFCore.IElsaDbContextSchema schema)
+ {
+ _schema = schema;
+ }
+
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "ApiKeysDefinitions",
+ schema: _schema.Schema);
+
+ migrationBuilder.DropTable(
+ name: "ServicesDefinitions",
+ schema: _schema.Schema);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable(
+ name: "ApiKeysDefinitions",
+ schema: _schema.Schema,
+ columns: table => new
+ {
+ Id = table.Column(type: "nvarchar(450)", nullable: false),
+ Name = table.Column(type: "nvarchar(450)", nullable: false),
+ TenantId = table.Column(type: "nvarchar(450)", nullable: true),
+ Value = table.Column(type: "nvarchar(max)", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_ApiKeysDefinitions", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "ServicesDefinitions",
+ schema: _schema.Schema,
+ columns: table => new
+ {
+ Id = table.Column(type: "nvarchar(450)", nullable: false),
+ Name = table.Column(type: "nvarchar(450)", nullable: false),
+ Settings = table.Column(type: "nvarchar(max)", nullable: false),
+ TenantId = table.Column(type: "nvarchar(450)", nullable: true),
+ Type = table.Column(type: "nvarchar(max)", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_ServicesDefinitions", x => x.Id);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_ApiKeyDefinition_Name",
+ schema: _schema.Schema,
+ table: "ApiKeysDefinitions",
+ column: "Name");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_ApiKeyDefinition_TenantId",
+ schema: _schema.Schema,
+ table: "ApiKeysDefinitions",
+ column: "TenantId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_ServiceDefinition_Name",
+ schema: _schema.Schema,
+ table: "ServicesDefinitions",
+ column: "Name");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_ServiceDefinition_TenantId",
+ schema: _schema.Schema,
+ table: "ServicesDefinitions",
+ column: "TenantId");
+ }
+ }
+}
diff --git a/src/modules/agents/Elsa.Agents.Persistence.EFCore.SqlServer/Migrations/AgentsDbContextModelSnapshot.cs b/src/modules/agents/Elsa.Agents.Persistence.EFCore.SqlServer/Migrations/AgentsDbContextModelSnapshot.cs
index cd735a6e..74560fad 100644
--- a/src/modules/agents/Elsa.Agents.Persistence.EFCore.SqlServer/Migrations/AgentsDbContextModelSnapshot.cs
+++ b/src/modules/agents/Elsa.Agents.Persistence.EFCore.SqlServer/Migrations/AgentsDbContextModelSnapshot.cs
@@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("Elsa")
- .HasAnnotation("ProductVersion", "8.0.13")
+ .HasAnnotation("ProductVersion", "9.0.11")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
@@ -52,64 +52,6 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.ToTable("AgentDefinitions", "Elsa");
});
-
- modelBuilder.Entity("Elsa.Agents.Persistence.Entities.ApiKeyDefinition", b =>
- {
- b.Property("Id")
- .HasColumnType("nvarchar(450)");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("nvarchar(450)");
-
- b.Property("TenantId")
- .HasColumnType("nvarchar(450)");
-
- b.Property("Value")
- .IsRequired()
- .HasColumnType("nvarchar(max)");
-
- b.HasKey("Id");
-
- b.HasIndex("Name")
- .HasDatabaseName("IX_ApiKeyDefinition_Name");
-
- b.HasIndex("TenantId")
- .HasDatabaseName("IX_ApiKeyDefinition_TenantId");
-
- b.ToTable("ApiKeysDefinitions", "Elsa");
- });
-
- modelBuilder.Entity("Elsa.Agents.Persistence.Entities.ServiceDefinition", b =>
- {
- b.Property("Id")
- .HasColumnType("nvarchar(450)");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("nvarchar(450)");
-
- b.Property("Settings")
- .IsRequired()
- .HasColumnType("nvarchar(max)");
-
- b.Property("TenantId")
- .HasColumnType("nvarchar(450)");
-
- b.Property("Type")
- .IsRequired()
- .HasColumnType("nvarchar(max)");
-
- b.HasKey("Id");
-
- b.HasIndex("Name")
- .HasDatabaseName("IX_ServiceDefinition_Name");
-
- b.HasIndex("TenantId")
- .HasDatabaseName("IX_ServiceDefinition_TenantId");
-
- b.ToTable("ServicesDefinitions", "Elsa");
- });
#pragma warning restore 612, 618
}
}
diff --git a/src/modules/agents/Elsa.Agents.Persistence.EFCore.SqlServer/efcore-3.6.sh b/src/modules/agents/Elsa.Agents.Persistence.EFCore.SqlServer/efcore-3.6.sh
new file mode 100644
index 00000000..16d773fe
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Persistence.EFCore.SqlServer/efcore-3.6.sh
@@ -0,0 +1 @@
+ef-migration-runtime-schema --interface Elsa.Persistence.EFCore.IElsaDbContextSchema --efOptions "migrations add V3_6 -c AgentsDbContext -o Migrations"
\ No newline at end of file
diff --git a/src/modules/agents/Elsa.Agents.Persistence.EFCore.Sqlite/Migrations/20251213193144_V3_6.Designer.cs b/src/modules/agents/Elsa.Agents.Persistence.EFCore.Sqlite/Migrations/20251213193144_V3_6.Designer.cs
new file mode 100644
index 00000000..4632bad6
--- /dev/null
+++ b/src/modules/agents/Elsa.Agents.Persistence.EFCore.Sqlite/Migrations/20251213193144_V3_6.Designer.cs
@@ -0,0 +1,57 @@
+//
+using Elsa.Agents.Persistence.EFCore;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace Elsa.Agents.Persistence.EFCore.Sqlite.Migrations
+{
+ [DbContext(typeof(AgentsDbContext))]
+ [Migration("20251213193144_V3_6")]
+ partial class V3_6
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasDefaultSchema("Elsa")
+ .HasAnnotation("ProductVersion", "9.0.11");
+
+ modelBuilder.Entity("Elsa.Agents.Persistence.Entities.AgentDefinition", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("TEXT");
+
+ b.Property