mirror of
https://github.com/microsoft/autogen.git
synced 2026-02-17 05:23:12 -05:00
builds but doesn't dynamically create the activities - why?
This commit is contained in:
@@ -25,6 +25,7 @@ using Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextEmbedding;
|
||||
using Microsoft.SemanticKernel.Memory;
|
||||
using Microsoft.SemanticKernel.Orchestration;
|
||||
using Microsoft.SemanticKernel.Reliability;
|
||||
using Microsoft.SemanticKernel.SkillDefinition;
|
||||
using Microsoft.SKDevTeam;
|
||||
|
||||
namespace Elsa.SemanticKernel;
|
||||
@@ -44,47 +45,196 @@ public class SemanticKernelActivityProvider : IActivityProvider
|
||||
}
|
||||
public async ValueTask<IEnumerable<ActivityDescriptor>> GetDescriptorsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
//get a list of skills in the assembly
|
||||
var skills = await LoadSkillsFromAssemblyAsync("skills");
|
||||
var descriptors = new List<ActivityDescriptor>();
|
||||
foreach (var skill in skills)
|
||||
// get the kernel
|
||||
var kernel = KernelBuilder();
|
||||
|
||||
// get a list of skills in the assembly
|
||||
var skills = LoadSkillsFromAssemblyAsync("skills", kernel);
|
||||
SKContext context = new SKContext();
|
||||
var functionsAvailable = context.Skills.GetFunctionsView();
|
||||
|
||||
// create activity descriptors for each skilland function
|
||||
var activities = new List<ActivityDescriptor>();
|
||||
foreach (KeyValuePair<string, List<FunctionView>> skill in functionsAvailable.SemanticFunctions)
|
||||
{
|
||||
//var descriptor = await CreateActivityDescriptors(skill, cancellationToken);
|
||||
// descriptors.Add(descriptor);
|
||||
Console.WriteLine($"Creating Activities for Skill: {skill.Key}");
|
||||
foreach (FunctionView func in skill.Value)
|
||||
{
|
||||
activities.Add(CreateActivityDescriptorFromSkillAndFunction(func, cancellationToken));
|
||||
}
|
||||
}
|
||||
return descriptors;
|
||||
|
||||
return activities;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an activity descriptor from a skill and function.
|
||||
/// </summary>
|
||||
/// <param name="function">The semantic kernel function</param>
|
||||
/// <param name="cancellationToken">An optional cancellation token.</param>
|
||||
/// <returns>An activity descriptor.</returns>
|
||||
private ActivityDescriptor CreateActivityDescriptorFromSkillAndFunction(FunctionView function, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Create a fully qualified type name for the activity
|
||||
var thisNamespace = GetType().Namespace;
|
||||
var fullTypeName = $"{thisNamespace}.{function.SkillName}.{function.Name}";
|
||||
Console.WriteLine($"Creating Activity: {fullTypeName}");
|
||||
|
||||
// create inputs from the function parameters - the SemanticKernelSkill activity will be the base for each activity
|
||||
var inputs = new List<InputDescriptor>();
|
||||
foreach (var p in function.Parameters) { inputs.Add(CreateInputDescriptorFromSKParameter(p)); }
|
||||
inputs.Add(CreateInputDescriptor(typeof(string), "SkillName", function.SkillName, "The name of the skill to use (generated, do not change)"));
|
||||
inputs.Add(CreateInputDescriptor(typeof(string), "FunctionName", function.Name, "The name of the function to use (generated, do not change)"));
|
||||
inputs.Add(CreateInputDescriptor(typeof(int), "MaxRetries", KernelSettings.DefaultMaxRetries, "Max Retries to contact AI Service"));
|
||||
|
||||
return new ActivityDescriptor
|
||||
{
|
||||
Kind = ActivityKind.Task,
|
||||
Category = "Semantic Kernel",
|
||||
Description = function.Description,
|
||||
Name = function.Name,
|
||||
TypeName = fullTypeName,
|
||||
Namespace = $"{thisNamespace}.{function.SkillName}",
|
||||
DisplayName = $"{function.SkillName}.{function.Name}",
|
||||
Inputs = inputs,
|
||||
Outputs = new[] {new OutputDescriptor()},
|
||||
Constructor = context =>
|
||||
{
|
||||
// The constructor is called when an activity instance of this type is requested.
|
||||
|
||||
// Create the activity instance.
|
||||
var activityInstance = _activityFactory.Create<SemanticKernelSkill>(context);
|
||||
|
||||
// Customize the activity type name.
|
||||
activityInstance.Type = fullTypeName;
|
||||
|
||||
// Configure the activity's URL and method properties.
|
||||
activityInstance.SkillName = new Input<string?>(function.SkillName);
|
||||
activityInstance.FunctionName = new Input<string?>(function.Name);
|
||||
|
||||
return activityInstance;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates an input descriptor for a single line string
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the input field</param>
|
||||
/// <param name="description">The description of the input field</param>
|
||||
private InputDescriptor CreateInputDescriptor(Type inputType, string name, Object defaultValue, string description)
|
||||
{
|
||||
var inputDescriptor = new InputDescriptor
|
||||
{
|
||||
Description = description,
|
||||
DefaultValue = defaultValue,
|
||||
Type = inputType,
|
||||
Name = name,
|
||||
DisplayName = name,
|
||||
IsSynthetic = true, // This is a synthetic property, i.e. it is not part of the activity's .NET type.
|
||||
IsWrapped = true, // This property is wrapped within an Input<T> object.
|
||||
UIHint = InputUIHints.SingleLine,
|
||||
ValueGetter = activity => activity.SyntheticProperties.GetValueOrDefault(name),
|
||||
ValueSetter = (activity, value) => activity.SyntheticProperties[name] = value!,
|
||||
};
|
||||
return inputDescriptor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an input descriptor from an sk funciton parameter definition.
|
||||
/// </summary>
|
||||
/// <param name="parameter">The function parameter.</param>
|
||||
/// <returns>An input descriptor.</returns>
|
||||
private InputDescriptor CreateInputDescriptorFromSKParameter(ParameterView parameter)
|
||||
{
|
||||
var inputDescriptor = new InputDescriptor
|
||||
{
|
||||
Description = string.IsNullOrEmpty(parameter.Description) ? parameter.Name : parameter.Description,
|
||||
DefaultValue = string.IsNullOrEmpty(parameter.DefaultValue) ? string.Empty : parameter.DefaultValue,
|
||||
Type = typeof(string),
|
||||
Name = parameter.Name,
|
||||
DisplayName = parameter.Name,
|
||||
IsSynthetic = true, // This is a synthetic property, i.e. it is not part of the activity's .NET type.
|
||||
IsWrapped = true, // This property is wrapped within an Input<T> object.
|
||||
UIHint = InputUIHints.MultiLine,
|
||||
ValueGetter = activity => activity.SyntheticProperties.GetValueOrDefault(parameter.Name),
|
||||
ValueSetter = (activity, value) => activity.SyntheticProperties[parameter.Name] = value!,
|
||||
|
||||
};
|
||||
return inputDescriptor;
|
||||
}
|
||||
|
||||
///<summary>
|
||||
/// Gets a list of the skills in the assembly
|
||||
///</summary>
|
||||
private async Task<IEnumerable<string>> LoadSkillsFromAssemblyAsync(string assemblyName)
|
||||
private IEnumerable<string> LoadSkillsFromAssemblyAsync(string assemblyName, IKernel kernel)
|
||||
{
|
||||
var skills = new List<string>();
|
||||
var assembly = Assembly.Load(assemblyName);
|
||||
//IEnumerable<Type> skillTypes = GetTypesInNamespace(assembly, "skills");
|
||||
Type[] skillTypes = assembly.GetTypes().ToArray();
|
||||
foreach(Type skillType in skillTypes)
|
||||
Type[] skillTypes = assembly.GetTypes().ToArray();
|
||||
foreach (Type skillType in skillTypes)
|
||||
{
|
||||
if (skillType.Namespace.Equals("Microsoft.SKDevTeam"))
|
||||
{
|
||||
Console.WriteLine($"Found type: {assembly.FullName}.{skillType.Namespace}.{skillType.Name}");
|
||||
if(skillType.Namespace.Equals("Microsoft.SKDevTeam"))
|
||||
skills.Add(skillType.Name);
|
||||
var functions = skillType.GetFields();
|
||||
foreach (var function in functions)
|
||||
{
|
||||
skills.Add(skillType.Name);
|
||||
string field = function.FieldType.ToString();
|
||||
if (field.Equals("Microsoft.SKDevTeam.SemanticFunctionConfig"))
|
||||
{
|
||||
var skillConfig = SemanticFunctionConfig.ForSkillAndFunction(skillType.Name, function.Name);
|
||||
var skfunc = kernel.CreateSemanticFunction(
|
||||
skillConfig.PromptTemplate,
|
||||
skillConfig.Name,
|
||||
skillConfig.SkillName,
|
||||
skillConfig.Description,
|
||||
skillConfig.MaxTokens,
|
||||
skillConfig.Temperature,
|
||||
skillConfig.TopP,
|
||||
skillConfig.PPenalty,
|
||||
skillConfig.FPenalty);
|
||||
|
||||
Console.WriteLine($"Added skill: {skillType.Name}");
|
||||
Console.WriteLine($"SK Added function: {skfunc.SkillName}.{skfunc.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return skills;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private IEnumerable<Type> GetTypesInNamespace(Assembly assembly, string nameSpace)
|
||||
/// <summary>
|
||||
/// Gets a semantic kernel instance
|
||||
/// </summary>
|
||||
/// <returns>Microsoft.SemanticKernel.IKernel</returns>
|
||||
private IKernel KernelBuilder()
|
||||
{
|
||||
return
|
||||
assembly.GetTypes()
|
||||
.Where(t => String.Equals(t.Namespace, nameSpace, StringComparison.Ordinal));
|
||||
var kernelSettings = KernelSettings.LoadSettings();
|
||||
var kernelConfig = new KernelConfig();
|
||||
|
||||
using ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
|
||||
{
|
||||
builder.SetMinimumLevel(kernelSettings.LogLevel ?? LogLevel.Warning);
|
||||
});
|
||||
|
||||
var kernel = new KernelBuilder()
|
||||
.WithLogger(loggerFactory.CreateLogger<IKernel>())
|
||||
.WithAzureChatCompletionService(kernelSettings.DeploymentOrModelId, kernelSettings.Endpoint, kernelSettings.ApiKey, true, kernelSettings.ServiceId, true)
|
||||
.WithConfiguration(kernelConfig)
|
||||
.Configure(c => c.SetDefaultHttpRetryConfig(new HttpRetryConfig
|
||||
{
|
||||
MaxRetryCount = KernelSettings.DefaultMaxRetries,
|
||||
UseExponentialBackoff = true,
|
||||
// MinRetryDelay = TimeSpan.FromSeconds(2),
|
||||
// MaxRetryDelay = TimeSpan.FromSeconds(8),
|
||||
MaxTotalRetryTime = TimeSpan.FromSeconds(300),
|
||||
// RetryableStatusCodes = new[] { HttpStatusCode.TooManyRequests, HttpStatusCode.RequestTimeout },
|
||||
// RetryableExceptions = new[] { typeof(HttpRequestException) }
|
||||
}))
|
||||
.Build();
|
||||
|
||||
return kernel;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -33,12 +33,6 @@ namespace Elsa.SemanticKernel;
|
||||
[PublicAPI]
|
||||
public class SemanticKernelSkill : CodeActivity<string>
|
||||
{
|
||||
//constructor - called by the workflow engine
|
||||
public SemanticKernelSkill(string? source = default, int? line = default) : base(source, line)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[Input(
|
||||
Description = "System Prompt",
|
||||
UIHint = InputUIHints.MultiLine,
|
||||
@@ -54,7 +48,7 @@ public class SemanticKernelSkill : CodeActivity<string>
|
||||
[Input(
|
||||
Description = "Max retries",
|
||||
UIHint = InputUIHints.SingleLine,
|
||||
DefaultValue = 9)]
|
||||
DefaultValue = KernelSettings.DefaultMaxRetries)]
|
||||
public Input<int> MaxRetries { get; set; }
|
||||
|
||||
[Input(
|
||||
@@ -98,6 +92,17 @@ public class SemanticKernelSkill : CodeActivity<string>
|
||||
// get the kernel
|
||||
var kernel = KernelBuilder();
|
||||
|
||||
// load the skill
|
||||
var skillConfig = SemanticFunctionConfig.ForSkillAndFunction(skillName, functionName);
|
||||
|
||||
var function = kernel.CreateSemanticFunction(skillConfig.PromptTemplate, skillConfig.Name, skillConfig.SkillName,
|
||||
skillConfig.Description, skillConfig.MaxTokens, skillConfig.Temperature,
|
||||
skillConfig.TopP, skillConfig.PPenalty, skillConfig.FPenalty);
|
||||
|
||||
// set the context (our prompt)
|
||||
var contextVars = new ContextVariables();
|
||||
contextVars.Set("input", prompt);
|
||||
|
||||
/* var interestingMemories = kernel.Memory.SearchAsync("ImportedMemories", prompt, 2);
|
||||
var wafContext = "Consider the following contextual snippets:";
|
||||
await foreach (var memory in interestingMemories)
|
||||
@@ -105,61 +110,60 @@ public class SemanticKernelSkill : CodeActivity<string>
|
||||
wafContext += $"\n {memory.Metadata.Text}";
|
||||
} */
|
||||
|
||||
var skillConfig = SemanticFunctionConfig.ForSkillAndFunction(skillName, functionName);
|
||||
|
||||
|
||||
/* var function = kernel.CreateSemanticFunction(skillConfig.PromptTemplate, skillConfig.Name, skillConfig.SkillName,
|
||||
skillConfig.Description, skillConfig.MaxTokens, skillConfig.Temperature,
|
||||
skillConfig.TopP, skillConfig.PPenalty, skillConfig.FPenalty); */
|
||||
|
||||
var contextVars = new ContextVariables();
|
||||
contextVars.Set("input", prompt);
|
||||
SKContext context = kernel.CreateNewContext();
|
||||
|
||||
var theSkills = LoadSkillsFromAssemblyAsync("skills", kernel);
|
||||
var functionsAvailable = context.Skills.GetFunctionsView();
|
||||
|
||||
var list = new StringBuilder();
|
||||
foreach (KeyValuePair<string, List<FunctionView>> skill in functionsAvailable.SemanticFunctions)
|
||||
{
|
||||
Console.WriteLine($"Skill: {skill.Key}");
|
||||
foreach (FunctionView func in skill.Value)
|
||||
{
|
||||
// Function description
|
||||
if (func.Description != null)
|
||||
{
|
||||
list.AppendLine($"// {func.Description}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("{0}.{1} is missing a description", func.SkillName, func.Name);
|
||||
list.AppendLine($"// Function {func.SkillName}.{func.Name}.");
|
||||
}
|
||||
|
||||
// Function name
|
||||
list.AppendLine($"{func.SkillName}.{func.Name}");
|
||||
|
||||
// Function parameters
|
||||
foreach (var p in func.Parameters)
|
||||
{
|
||||
var description = string.IsNullOrEmpty(p.Description) ? p.Name : p.Description;
|
||||
var defaultValueString = string.IsNullOrEmpty(p.DefaultValue) ? string.Empty : $" (default value: {p.DefaultValue})";
|
||||
list.AppendLine($"Parameter \"{p.Name}\": {description} {defaultValueString}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"List of all skills ----- {list.ToString()}");
|
||||
|
||||
//context.Set("wafContext", wafContext);
|
||||
|
||||
SKContext answer = await kernel.RunAsync(contextVars, functionName).ConfigureAwait(false);
|
||||
SKContext answer = await kernel.RunAsync(contextVars, function).ConfigureAwait(false);
|
||||
string result = answer.Result;
|
||||
|
||||
workflowContext.SetResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the skills into the kernel
|
||||
/// </summary>
|
||||
private string ListSkillsInKernel(IKernel kernel)
|
||||
{
|
||||
|
||||
var theSkills = LoadSkillsFromAssemblyAsync("skills", kernel);
|
||||
SKContext context = kernel.CreateNewContext();
|
||||
var functionsAvailable = context.Skills.GetFunctionsView();
|
||||
|
||||
var list = new StringBuilder();
|
||||
foreach (KeyValuePair<string, List<FunctionView>> skill in functionsAvailable.SemanticFunctions)
|
||||
{
|
||||
Console.WriteLine($"Skill: {skill.Key}");
|
||||
foreach (FunctionView func in skill.Value)
|
||||
{
|
||||
// Function description
|
||||
if (func.Description != null)
|
||||
{
|
||||
list.AppendLine($"// {func.Description}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("{0}.{1} is missing a description", func.SkillName, func.Name);
|
||||
list.AppendLine($"// Function {func.SkillName}.{func.Name}.");
|
||||
}
|
||||
|
||||
// Function name
|
||||
list.AppendLine($"{func.SkillName}.{func.Name}");
|
||||
|
||||
// Function parameters
|
||||
foreach (var p in func.Parameters)
|
||||
{
|
||||
var description = string.IsNullOrEmpty(p.Description) ? p.Name : p.Description;
|
||||
var defaultValueString = string.IsNullOrEmpty(p.DefaultValue) ? string.Empty : $" (default value: {p.DefaultValue})";
|
||||
list.AppendLine($"Parameter \"{p.Name}\": {description} {defaultValueString}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"List of all skills ----- {list.ToString()}");
|
||||
return list.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a semantic kernel instance
|
||||
/// </summary>
|
||||
@@ -187,7 +191,7 @@ public class SemanticKernelSkill : CodeActivity<string>
|
||||
.WithConfiguration(kernelConfig)
|
||||
.Configure(c => c.SetDefaultHttpRetryConfig(new HttpRetryConfig
|
||||
{
|
||||
MaxRetryCount = maxRetries,
|
||||
MaxRetryCount = KernelSettings.DefaultMaxRetries,
|
||||
UseExponentialBackoff = true,
|
||||
// MinRetryDelay = TimeSpan.FromSeconds(2),
|
||||
// MaxRetryDelay = TimeSpan.FromSeconds(8),
|
||||
|
||||
@@ -9,6 +9,7 @@ internal class KernelSettings
|
||||
public const string DefaultConfigFile = "config/appsettings.json";
|
||||
public const string OpenAI = "OPENAI";
|
||||
public const string AzureOpenAI = "AZUREOPENAI";
|
||||
public const int DefaultMaxRetries = 9;
|
||||
|
||||
[JsonPropertyName("serviceType")]
|
||||
public string ServiceType { get; set; } = string.Empty;
|
||||
|
||||
Reference in New Issue
Block a user