2023-07-10 15:37:36 -07:00
|
|
|
using Elsa.Extensions;
|
|
|
|
using Elsa.Workflows.Core;
|
|
|
|
using Elsa.Workflows.Core.Attributes;
|
|
|
|
using Elsa.Workflows.Core.Models;
|
|
|
|
using JetBrains.Annotations;
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
using Microsoft.SemanticKernel;
|
|
|
|
using Microsoft.SemanticKernel.Connectors.Memory.Qdrant;
|
|
|
|
using Microsoft.SemanticKernel.Connectors.AI.OpenAI.TextEmbedding;
|
|
|
|
using Microsoft.SemanticKernel.Memory;
|
|
|
|
using Microsoft.SemanticKernel.Orchestration;
|
|
|
|
using Microsoft.SemanticKernel.Reliability;
|
2023-07-14 13:57:07 -07:00
|
|
|
using Microsoft.SemanticKernel.SkillDefinition;
|
|
|
|
using Microsoft.SKDevTeam;
|
|
|
|
|
2023-07-10 15:37:36 -07:00
|
|
|
using System;
|
2023-07-14 13:57:07 -07:00
|
|
|
using System.Text;
|
|
|
|
using System.Collections;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Reflection;
|
|
|
|
using System.Threading;
|
2023-07-10 15:37:36 -07:00
|
|
|
using System.Threading.Tasks;
|
2023-07-14 13:57:07 -07:00
|
|
|
|
|
|
|
|
|
|
|
namespace Elsa.SemanticKernel;
|
2023-07-10 15:37:36 -07:00
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Invoke a Semantic Kernel skill.
|
|
|
|
/// </summary>
|
2023-07-10 18:34:46 -07:00
|
|
|
[Activity("Elsa", "AI Chat", "Invoke a Semantic Kernel skill. ", DisplayName = "Semantic Kernel Skill", Kind = ActivityKind.Task)]
|
2023-07-10 15:37:36 -07:00
|
|
|
[PublicAPI]
|
|
|
|
public class SemanticKernelSkill : CodeActivity<string>
|
|
|
|
{
|
|
|
|
[Input(
|
2023-07-10 18:34:46 -07:00
|
|
|
Description = "System Prompt",
|
|
|
|
UIHint = InputUIHints.MultiLine,
|
|
|
|
DefaultValue = PromptDefaults.SystemPrompt)]
|
|
|
|
public Input<string> SysPrompt { get; set; } = default!;
|
2023-07-10 15:37:36 -07:00
|
|
|
|
|
|
|
[Input(
|
2023-07-10 18:34:46 -07:00
|
|
|
Description = "User Input Prompt",
|
|
|
|
UIHint = InputUIHints.MultiLine,
|
|
|
|
DefaultValue = PromptDefaults.UserPrompt)]
|
2023-07-10 15:37:36 -07:00
|
|
|
public Input<string> Prompt { get; set; }
|
|
|
|
|
|
|
|
[Input(
|
|
|
|
Description = "Max retries",
|
|
|
|
UIHint = InputUIHints.SingleLine,
|
|
|
|
DefaultValue = 9)]
|
|
|
|
public Input<int> MaxRetries { get; set; }
|
|
|
|
|
|
|
|
[Input(
|
|
|
|
Description = "The skill to invoke from the semantic kernel",
|
|
|
|
UIHint = InputUIHints.SingleLine,
|
2023-07-10 18:34:46 -07:00
|
|
|
DefaultValue = "Chat")]
|
2023-07-10 15:37:36 -07:00
|
|
|
public Input<string> SkillName { get; set; }
|
|
|
|
|
|
|
|
[Input(
|
|
|
|
Description = "The function to invoke from the skill",
|
|
|
|
UIHint = InputUIHints.SingleLine,
|
2023-07-10 18:34:46 -07:00
|
|
|
DefaultValue = "ChatCompletion")]
|
2023-07-10 15:37:36 -07:00
|
|
|
public Input<string> FunctionName { get; set; }
|
|
|
|
|
2023-07-14 13:57:07 -07:00
|
|
|
/* [Input(
|
|
|
|
Description = "Mockup - don't actually call the AI, just output the prompts",
|
|
|
|
UIHint = InputUIHints.Checkbox,
|
|
|
|
DefaultValue = false)]
|
|
|
|
public Input<bool> Mockup { get; set; } */
|
2023-07-12 20:39:21 -07:00
|
|
|
|
2023-07-10 15:37:36 -07:00
|
|
|
/// <inheritdoc />
|
2023-07-10 22:52:23 -07:00
|
|
|
protected override async ValueTask ExecuteAsync(ActivityExecutionContext workflowContext)
|
2023-07-10 15:37:36 -07:00
|
|
|
{
|
2023-07-10 22:52:23 -07:00
|
|
|
var test = SkillName.Get(workflowContext);
|
|
|
|
var skillName = SkillName.Get(workflowContext);
|
|
|
|
var functionName = FunctionName.Get(workflowContext);
|
|
|
|
var systemPrompt = SysPrompt.Get(workflowContext);
|
|
|
|
var maxRetries = MaxRetries.Get(workflowContext);
|
|
|
|
var prompt = Prompt.Get(workflowContext);
|
2023-07-12 20:39:21 -07:00
|
|
|
//var mockup = Mockup.Get(workflowContext);
|
|
|
|
var mockup = false;
|
2023-07-10 15:37:36 -07:00
|
|
|
|
2023-07-12 20:39:21 -07:00
|
|
|
string info = ($"#################\nSkill: {skillName}\nFunction: {functionName}\nPrompt: {prompt}\n#################\n\n");
|
|
|
|
|
|
|
|
if (mockup)
|
2023-07-10 15:37:36 -07:00
|
|
|
{
|
2023-07-12 20:39:21 -07:00
|
|
|
workflowContext.SetResult(info);
|
|
|
|
}
|
|
|
|
else
|
2023-07-10 15:37:36 -07:00
|
|
|
{
|
2023-07-12 20:39:21 -07:00
|
|
|
var kernelSettings = KernelSettings.LoadSettings();
|
|
|
|
var kernelConfig = new KernelConfig();
|
|
|
|
|
|
|
|
using ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
|
|
|
|
{
|
|
|
|
builder
|
|
|
|
.SetMinimumLevel(kernelSettings.LogLevel ?? LogLevel.Warning);
|
|
|
|
});
|
|
|
|
/* var memoryStore = new QdrantMemoryStore(new QdrantVectorDbClient("http://qdrant", 1536, port: 6333));
|
|
|
|
var embedingGeneration = new AzureTextEmbeddingGeneration(kernelSettings.EmbeddingDeploymentOrModelId, kernelSettings.Endpoint, kernelSettings.ApiKey);
|
|
|
|
var semanticTextMemory = new SemanticTextMemory(memoryStore, embedingGeneration);
|
|
|
|
*/
|
|
|
|
var kernel = new KernelBuilder()
|
|
|
|
.WithLogger(loggerFactory.CreateLogger<IKernel>())
|
|
|
|
.WithAzureChatCompletionService(kernelSettings.DeploymentOrModelId, kernelSettings.Endpoint, kernelSettings.ApiKey, true, kernelSettings.ServiceId, true)
|
|
|
|
//.WithMemory(semanticTextMemory)
|
|
|
|
.WithConfiguration(kernelConfig)
|
|
|
|
.Configure(c => c.SetDefaultHttpRetryConfig(new HttpRetryConfig
|
|
|
|
{
|
|
|
|
MaxRetryCount = maxRetries,
|
|
|
|
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();
|
|
|
|
|
|
|
|
/* var interestingMemories = kernel.Memory.SearchAsync("ImportedMemories", prompt, 2);
|
|
|
|
var wafContext = "Consider the following contextual snippets:";
|
|
|
|
await foreach (var memory in interestingMemories)
|
|
|
|
{
|
|
|
|
wafContext += $"\n {memory.Metadata.Text}";
|
|
|
|
} */
|
|
|
|
|
|
|
|
var skillConfig = SemanticFunctionConfig.ForSkillAndFunction(skillName, functionName);
|
2023-07-10 15:37:36 -07:00
|
|
|
|
|
|
|
|
2023-07-14 13:57:07 -07:00
|
|
|
/* 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}.");
|
|
|
|
}
|
2023-07-10 15:37:36 -07:00
|
|
|
|
2023-07-14 13:57:07 -07:00
|
|
|
// Function name
|
|
|
|
list.AppendLine($"{func.SkillName}.{func.Name}");
|
2023-07-12 09:48:51 -07:00
|
|
|
|
2023-07-14 13:57:07 -07:00
|
|
|
// 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, function).ConfigureAwait(false);
|
|
|
|
string result = answer.Result;
|
|
|
|
|
2023-07-12 20:39:21 -07:00
|
|
|
workflowContext.SetResult(result);
|
|
|
|
}
|
2023-07-10 15:37:36 -07:00
|
|
|
}
|
2023-07-14 13:57:07 -07:00
|
|
|
///<summary>
|
|
|
|
/// Gets a list of the skills in the assembly
|
|
|
|
///</summary>
|
|
|
|
private IEnumerable<string> LoadSkillsFromAssemblyAsync(string assemblyName, IKernel kernel)
|
|
|
|
{
|
|
|
|
var skills = new List<string>();
|
|
|
|
var assembly = Assembly.Load(assemblyName);
|
|
|
|
Type[] skillTypes = assembly.GetTypes().ToArray();
|
|
|
|
foreach (Type skillType in skillTypes)
|
|
|
|
{
|
|
|
|
if (skillType.Namespace.Equals("Microsoft.SKDevTeam"))
|
|
|
|
{
|
|
|
|
skills.Add(skillType.Name);
|
|
|
|
var functions = skillType.GetFields();
|
|
|
|
foreach (var function in functions)
|
|
|
|
{
|
|
|
|
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($"SK Added function: {skfunc.SkillName}.{skfunc.Name}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return skills;
|
|
|
|
}
|
2023-07-10 15:37:36 -07:00
|
|
|
}
|