diff --git a/dotnet/.editorconfig b/dotnet/.editorconfig
index 13fe5da6a..9668b0676 100644
--- a/dotnet/.editorconfig
+++ b/dotnet/.editorconfig
@@ -65,7 +65,7 @@ dotnet_naming_symbols.constant_fields.required_modifiers = const
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
# static fields should have s_ prefix
-dotnet_naming_rule.static_fields_should_have_prefix.severity = warning
+dotnet_naming_rule.static_fields_should_have_prefix.severity = silent
dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields
dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style
dotnet_naming_symbols.static_fields.applicable_kinds = field
@@ -97,6 +97,27 @@ csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
+# IDE0290: Use primary constructor
+dotnet_diagnostic.IDE0290.severity = suggestion
+
+# IDE1591 Missing XML comment for publicly visible type or member
+dotnet_diagnostic.CS1591.severity = none
+
+# CA1848: Use the LoggerMessage delegates
+dotnet_diagnostic.CA1848.severity = suggestion
+
+# CA1002: Do not expose generic lists
+dotnet_diagnostic.CA1002.severity = none
+
+# CA2227: Collection properties should be read only
+dotnet_diagnostic.CA2227.severity = silent
+
+# CA1031: Do not catch general exception types
+dotnet_diagnostic.CA1031.severity = silent
+
+# CA1056: URI-like properties should not be strings
+dotnet_diagnostic.CA1056.severity = silent
+
[*.{xml,config,*proj,nuspec,props,resx,targets,yml,tasks}]
indent_size = 2
@@ -129,7 +150,7 @@ dotnet_diagnostic.CA1018.severity = warning
dotnet_diagnostic.CA1047.severity = warning
# CA1305: Specify IFormatProvider
-dotnet_diagnostic.CA1305.severity = warning
+dotnet_diagnostic.CA1305.severity = suggestion
# CA1507: Use nameof to express symbol names
dotnet_diagnostic.CA1507.severity = warning
@@ -162,7 +183,7 @@ dotnet_diagnostic.CA1810.severity = warning
dotnet_diagnostic.CA1821.severity = warning
# CA1822: Make member static
-dotnet_diagnostic.CA1822.severity = warning
+dotnet_diagnostic.CA1822.severity = suggestion
dotnet_code_quality.CA1822.api_surface = private, internal
# CA1823: Avoid unused private fields
@@ -256,7 +277,7 @@ dotnet_diagnostic.CA1857.severity = warning
dotnet_diagnostic.CA1858.severity = warning
# CA2007: Consider calling ConfigureAwait on the awaited task
-dotnet_diagnostic.CA2007.severity = warning
+dotnet_diagnostic.CA2007.severity = suggestion
# CA2008: Do not create tasks without passing a TaskScheduler
dotnet_diagnostic.CA2008.severity = warning
@@ -332,7 +353,7 @@ dotnet_diagnostic.IDE0043.severity = warning
dotnet_diagnostic.IDE0044.severity = warning
# IDE0051: Remove unused private members
-dotnet_diagnostic.IDE0051.severity = warning
+dotnet_diagnostic.IDE0051.severity = suggestion
# IDE0055: All formatting rules
dotnet_diagnostic.IDE0055.severity = suggestion
@@ -489,3 +510,9 @@ dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion
dotnet_style_namespace_match_folder = true:suggestion
+
+[**/*.g.cs]
+generated_code = true
+
+# IDE1591 Missing XML comment for publicly visible type or member
+dotnet_diagnostic.CS1591.severity = none
diff --git a/dotnet/Directory.Build.props b/dotnet/Directory.Build.props
index 658f80bc3..b42ed82d3 100644
--- a/dotnet/Directory.Build.props
+++ b/dotnet/Directory.Build.props
@@ -24,4 +24,8 @@
$([MSBuild]::NormalizeDirectory('$(ArtifactsLogDir)', 'TestLogs'))
+
+ $(NoWarn);CS1591;SKEXP0001;SKEXP0010;SKEXP0020
+
+
diff --git a/dotnet/global.json b/dotnet/global.json
new file mode 100644
index 000000000..df63f66f7
--- /dev/null
+++ b/dotnet/global.json
@@ -0,0 +1,6 @@
+{
+ "sdk": {
+ "rollForward": "major",
+ "version": "8.0.302"
+ }
+}
diff --git a/dotnet/samples/WorkflowsApp/Activities/ActivityProviders/SemanticKernelSkillActivityProvider.cs b/dotnet/samples/WorkflowsApp/Activities/ActivityProviders/SemanticKernelSkillActivityProvider.cs
index 4c8256994..66b0c808a 100644
--- a/dotnet/samples/WorkflowsApp/Activities/ActivityProviders/SemanticKernelSkillActivityProvider.cs
+++ b/dotnet/samples/WorkflowsApp/Activities/ActivityProviders/SemanticKernelSkillActivityProvider.cs
@@ -26,33 +26,33 @@ public class SemanticKernelActivityProvider : IActivityProvider
_activityFactory = activityFactory;
_activityDescriber = activityDescriber;
}
- public async ValueTask> GetDescriptorsAsync(CancellationToken cancellationToken = default)
+
+ public ValueTask> GetDescriptorsAsync(CancellationToken cancellationToken = default)
{
// get the kernel
var kernel = KernelBuilder();
// get a list of skills in the assembly
- var skills = LoadSkillsFromAssemblyAsync("skills", kernel);
+ _ = LoadSkillsFromAssemblyAsync("skills", kernel);
var functionsAvailable = kernel.Plugins.GetFunctionsMetadata();
-
- // create activity descriptors for each skilland function
+
+ // create activity descriptors for each skill and function
var activities = new List();
foreach (var function in functionsAvailable)
{
Console.WriteLine($"Creating Activities for Plugin: {function.PluginName}");
- activities.Add(CreateActivityDescriptorFromSkillAndFunction(function, cancellationToken));
+ activities.Add(CreateActivityDescriptorFromSkillAndFunction(function));
}
- return activities;
+ return new(activities);
}
///
/// Creates an activity descriptor from a skill and function.
///
/// The semantic kernel function
- /// An optional cancellation token.
/// An activity descriptor.
- private ActivityDescriptor CreateActivityDescriptorFromSkillAndFunction(KernelFunctionMetadata function, CancellationToken cancellationToken = default)
+ private ActivityDescriptor CreateActivityDescriptorFromSkillAndFunction(KernelFunctionMetadata function)
{
// Create a fully qualified type name for the activity
var thisNamespace = GetType().Namespace;
@@ -62,7 +62,7 @@ public class SemanticKernelActivityProvider : IActivityProvider
// create inputs from the function parameters - the SemanticKernelSkill activity will be the base for each activity
var inputs = new List();
foreach (var p in function.Parameters) { inputs.Add(CreateInputDescriptorFromSKParameter(p)); }
- inputs.Add(CreateInputDescriptor(typeof(string), "SkillName", function.PluginName, "The name of the skill to use (generated, do not change)"));
+ inputs.Add(CreateInputDescriptor(typeof(string), "SkillName", function.PluginName!, "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"));
@@ -76,7 +76,7 @@ public class SemanticKernelActivityProvider : IActivityProvider
Namespace = $"{thisNamespace}.{function.PluginName}",
DisplayName = $"{function.PluginName}.{function.Name}",
Inputs = inputs,
- Outputs = new[] {new OutputDescriptor()},
+ Outputs = new[] { new OutputDescriptor() },
Constructor = context =>
{
// The constructor is called when an activity instance of this type is requested.
@@ -88,8 +88,8 @@ public class SemanticKernelActivityProvider : IActivityProvider
activityInstance.Type = fullTypeName;
// Configure the activity's URL and method properties.
- activityInstance.SkillName = new Input(function.PluginName);
- activityInstance.FunctionName = new Input(function.Name);
+ activityInstance.SkillName = new Input(function.PluginName!);
+ activityInstance.FunctionName = new Input(function.Name);
return activityInstance;
}
@@ -99,9 +99,11 @@ public class SemanticKernelActivityProvider : IActivityProvider
///
/// Creates an input descriptor for a single line string
///
+ /// The input type.
/// The name of the input field
+ /// The default value.
/// The description of the input field
- private InputDescriptor CreateInputDescriptor(Type inputType, string name, Object defaultValue, string description)
+ private static InputDescriptor CreateInputDescriptor(Type inputType, string name, object defaultValue, string description)
{
var inputDescriptor = new InputDescriptor
{
@@ -124,12 +126,12 @@ public class SemanticKernelActivityProvider : IActivityProvider
///
/// The function parameter.
/// An input descriptor.
- private InputDescriptor CreateInputDescriptorFromSKParameter(KernelParameterMetadata parameter)
+ private static InputDescriptor CreateInputDescriptorFromSKParameter(KernelParameterMetadata parameter)
{
var inputDescriptor = new InputDescriptor
{
Description = string.IsNullOrEmpty(parameter.Description) ? parameter.Name : parameter.Description,
- DefaultValue = parameter.DefaultValue == null ? string.Empty : parameter.DefaultValue,
+ DefaultValue = parameter.DefaultValue ?? string.Empty,
Type = typeof(string),
Name = parameter.Name,
DisplayName = parameter.Name,
@@ -146,14 +148,14 @@ public class SemanticKernelActivityProvider : IActivityProvider
///
/// Gets a list of the skills in the assembly
///
- private IEnumerable LoadSkillsFromAssemblyAsync(string assemblyName, Kernel kernel)
+ private static IEnumerable LoadSkillsFromAssemblyAsync(string assemblyName, Kernel kernel)
{
var skills = new List();
var assembly = Assembly.Load(assemblyName);
Type[] skillTypes = assembly.GetTypes().ToArray();
foreach (Type skillType in skillTypes)
{
- if (skillType.Namespace.Equals("Microsoft.AI.DevTeam"))
+ if (string.Equals("Microsoft.AI.DevTeam", skillType.Namespace))
{
skills.Add(skillType.Name);
var functions = skillType.GetFields();
@@ -178,7 +180,7 @@ public class SemanticKernelActivityProvider : IActivityProvider
/// Gets a semantic kernel instance
///
/// Microsoft.SemanticKernel.IKernel
- private Kernel KernelBuilder()
+ private static Kernel KernelBuilder()
{
var kernelSettings = KernelSettings.LoadSettings();
@@ -186,11 +188,12 @@ public class SemanticKernelActivityProvider : IActivityProvider
clientOptions.Retry.NetworkTimeout = TimeSpan.FromMinutes(5);
var openAIClient = new OpenAIClient(new Uri(kernelSettings.Endpoint), new AzureKeyCredential(kernelSettings.ApiKey), clientOptions);
var builder = Kernel.CreateBuilder();
- builder.Services.AddLogging( c=> c.AddConsole().AddDebug().SetMinimumLevel(LogLevel.Debug));
+ builder.Services.AddLogging(c => c.AddConsole().AddDebug().SetMinimumLevel(LogLevel.Debug));
builder.Services.AddAzureOpenAIChatCompletion(kernelSettings.DeploymentOrModelId, openAIClient);
- builder.Services.ConfigureHttpClientDefaults(c=>
+ builder.Services.ConfigureHttpClientDefaults(c =>
{
- c.AddStandardResilienceHandler().Configure( o=> {
+ c.AddStandardResilienceHandler().Configure(o =>
+ {
o.Retry.MaxRetryAttempts = 5;
o.Retry.BackoffType = Polly.DelayBackoffType.Exponential;
});
diff --git a/dotnet/samples/WorkflowsApp/Activities/SemanticKernel.cs b/dotnet/samples/WorkflowsApp/Activities/SemanticKernel.cs
index 85ad789eb..09794a32d 100644
--- a/dotnet/samples/WorkflowsApp/Activities/SemanticKernel.cs
+++ b/dotnet/samples/WorkflowsApp/Activities/SemanticKernel.cs
@@ -12,7 +12,7 @@ using Microsoft.SemanticKernel;
using Azure.AI.OpenAI;
using Azure;
using Microsoft.Extensions.Http.Resilience;
-
+using System.Globalization;
namespace Elsa.SemanticKernel;
@@ -33,25 +33,19 @@ public class SemanticKernelSkill : CodeActivity
Description = "User Input Prompt",
UIHint = InputUIHints.MultiLine,
DefaultValue = PromptDefaults.UserPrompt)]
- public Input Prompt { get; set; }
-
- [Input(
- Description = "Max retries",
- UIHint = InputUIHints.SingleLine,
- DefaultValue = KernelSettings.DefaultMaxRetries)]
- public Input MaxRetries { get; set; }
+ public Input Prompt { get; set; } = default!;
[Input(
Description = "The skill to invoke from the semantic kernel",
UIHint = InputUIHints.SingleLine,
DefaultValue = "Chat")]
- public Input SkillName { get; set; }
+ public Input SkillName { get; set; } = default!;
[Input(
Description = "The function to invoke from the skill",
UIHint = InputUIHints.SingleLine,
DefaultValue = "ChatCompletion")]
- public Input FunctionName { get; set; }
+ public Input FunctionName { get; set; } = default!;
/* [Input(
Description = "Mockup - don't actually call the AI, just output the prompts",
@@ -62,11 +56,8 @@ public class SemanticKernelSkill : CodeActivity
///
protected override async ValueTask ExecuteAsync(ActivityExecutionContext workflowContext)
{
- 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);
//var mockup = Mockup.Get(workflowContext);
var mockup = false;
@@ -88,7 +79,8 @@ public class SemanticKernelSkill : CodeActivity
var function = kernel.CreateFunctionFromPrompt(promptTemplate.PromptTemplate, new OpenAIPromptExecutionSettings { MaxTokens = 8000, Temperature = 0.4, TopP = 1 });
// set the context (our prompt)
- var arguments = new KernelArguments{
+ var arguments = new KernelArguments
+ {
["input"] = prompt
};
@@ -102,36 +94,35 @@ public class SemanticKernelSkill : CodeActivity
///
private string ListSkillsInKernel(Kernel kernel)
{
-
- var theSkills = LoadSkillsFromAssemblyAsync("skills", kernel);
+ _ = LoadSkillsFromAssemblyAsync("skills", kernel);
var functionsAvailable = kernel.Plugins.GetFunctionsMetadata();
var list = new StringBuilder();
foreach (var function in functionsAvailable)
{
Console.WriteLine($"Skill: {function.PluginName}");
-
- // Function description
- if (function.Description != null)
- {
- list.AppendLine($"// {function.Description}");
- }
- else
- {
- Console.WriteLine("{0}.{1} is missing a description", function.PluginName, function.Name);
- list.AppendLine($"// Function {function.PluginName}.{function.Name}.");
- }
- // Function name
- list.AppendLine($"{function.PluginName}.{function.Name}");
+ // Function description
+ if (function.Description != null)
+ {
+ list.AppendLine($"// {function.Description}");
+ }
+ else
+ {
+ Console.WriteLine("{0}.{1} is missing a description", function.PluginName, function.Name);
+ list.AppendLine($"// Function {function.PluginName}.{function.Name}.");
+ }
- // Function parameters
- foreach (var p in function.Parameters)
- {
- var description = string.IsNullOrEmpty(p.Description) ? p.Name : p.Description;
- var defaultValueString = p.DefaultValue == null ? string.Empty : $" (default value: {p.DefaultValue})";
- list.AppendLine($"Parameter \"{p.Name}\": {description} {defaultValueString}");
- }
+ // Function name
+ list.AppendLine($"{function.PluginName}.{function.Name}");
+
+ // Function parameters
+ foreach (var p in function.Parameters)
+ {
+ var description = string.IsNullOrEmpty(p.Description) ? p.Name : p.Description;
+ var defaultValueString = p.DefaultValue == null ? string.Empty : $" (default value: {p.DefaultValue})";
+ list.AppendLine(CultureInfo.InvariantCulture, $"Parameter \"{p.Name}\": {description} {defaultValueString}");
+ }
}
Console.WriteLine($"List of all skills ----- {list.ToString()}");
@@ -142,7 +133,7 @@ public class SemanticKernelSkill : CodeActivity
/// Gets a semantic kernel instance
///
/// Microsoft.SemanticKernel.IKernel
- private Kernel KernelBuilder()
+ private static Kernel KernelBuilder()
{
var kernelSettings = KernelSettings.LoadSettings();
@@ -150,11 +141,12 @@ public class SemanticKernelSkill : CodeActivity
clientOptions.Retry.NetworkTimeout = TimeSpan.FromMinutes(5);
var openAIClient = new OpenAIClient(new Uri(kernelSettings.Endpoint), new AzureKeyCredential(kernelSettings.ApiKey), clientOptions);
var builder = Kernel.CreateBuilder();
- builder.Services.AddLogging( c=> c.AddConsole().AddDebug().SetMinimumLevel(LogLevel.Debug));
+ builder.Services.AddLogging(c => c.AddConsole().AddDebug().SetMinimumLevel(LogLevel.Debug));
builder.Services.AddAzureOpenAIChatCompletion(kernelSettings.DeploymentOrModelId, openAIClient);
- builder.Services.ConfigureHttpClientDefaults(c=>
+ builder.Services.ConfigureHttpClientDefaults(c =>
{
- c.AddStandardResilienceHandler().Configure( o=> {
+ c.AddStandardResilienceHandler().Configure(o =>
+ {
o.Retry.MaxRetryAttempts = 5;
o.Retry.BackoffType = Polly.DelayBackoffType.Exponential;
});
@@ -165,14 +157,14 @@ public class SemanticKernelSkill : CodeActivity
///
/// Gets a list of the skills in the assembly
///
- private IEnumerable LoadSkillsFromAssemblyAsync(string assemblyName, Kernel kernel)
+ private static IEnumerable LoadSkillsFromAssemblyAsync(string assemblyName, Kernel kernel)
{
var skills = new List();
var assembly = Assembly.Load(assemblyName);
Type[] skillTypes = assembly.GetTypes().ToArray();
foreach (Type skillType in skillTypes)
{
- if (skillType.Namespace.Equals("Microsoft.SKDevTeam"))
+ if (string.Equals("Microsoft.SKDevTeam", skillType.Namespace))
{
skills.Add(skillType.Name);
var functions = skillType.GetFields();
@@ -192,4 +184,4 @@ public class SemanticKernelSkill : CodeActivity
}
return skills;
}
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/WorkflowsApp/Config/KernelSettings.cs b/dotnet/samples/WorkflowsApp/Config/KernelSettings.cs
index 7e549c9af..caba637ab 100644
--- a/dotnet/samples/WorkflowsApp/Config/KernelSettings.cs
+++ b/dotnet/samples/WorkflowsApp/Config/KernelSettings.cs
@@ -1,10 +1,6 @@
using System.Text.Json.Serialization;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Logging;
-using System.IO;
-using System;
-internal class KernelSettings
+internal sealed class KernelSettings
{
public const string DefaultConfigFile = "config/appsettings.json";
public const string OpenAI = "OPENAI";
@@ -19,7 +15,7 @@ internal class KernelSettings
[JsonPropertyName("deploymentOrModelId")]
public string DeploymentOrModelId { get; set; } = string.Empty;
- [JsonPropertyName("embeddingDeploymentOrModelId")]
+ [JsonPropertyName("embeddingDeploymentOrModelId")]
public string EmbeddingDeploymentOrModelId { get; set; } = string.Empty;
[JsonPropertyName("endpoint")]
diff --git a/dotnet/samples/WorkflowsApp/Config/PromptDefaults.cs b/dotnet/samples/WorkflowsApp/Config/PromptDefaults.cs
index 6f5a16c75..8264fbab9 100644
--- a/dotnet/samples/WorkflowsApp/Config/PromptDefaults.cs
+++ b/dotnet/samples/WorkflowsApp/Config/PromptDefaults.cs
@@ -1,7 +1,8 @@
-internal static class PromptDefaults {
+internal static class PromptDefaults
+{
public const string SystemPrompt = @"You are fulfilling roles on a software development team.
Provide a response to the following prompt, do not provide any additional output.";
public const string UserPrompt = @"Let's build a ToDoList Application!";
-
- }
\ No newline at end of file
+
+}
\ No newline at end of file
diff --git a/dotnet/samples/WorkflowsApp/NuGet.Config b/dotnet/samples/WorkflowsApp/NuGet.Config
deleted file mode 100644
index a89a1e8b5..000000000
--- a/dotnet/samples/WorkflowsApp/NuGet.Config
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/dotnet/samples/WorkflowsApp/Program.cs b/dotnet/samples/WorkflowsApp/Program.cs
index 463bfda62..bc3e8f0bd 100644
--- a/dotnet/samples/WorkflowsApp/Program.cs
+++ b/dotnet/samples/WorkflowsApp/Program.cs
@@ -12,21 +12,21 @@ builder.Services.AddElsa(elsa =>
// Configure management feature to use EF Core.
elsa.UseWorkflowManagement(management => management.UseEntityFrameworkCore(ef => ef.UseSqlite()));
- elsa.UseWorkflowRuntime(runtime =>runtime.UseEntityFrameworkCore());
-
+ elsa.UseWorkflowRuntime(runtime => runtime.UseEntityFrameworkCore());
+
// Expose API endpoints.
elsa.UseWorkflowsApi();
// Add services for HTTP activities and workflow middleware.
elsa.UseHttp();
-
+
// Configure identity so that we can create a default admin user.
elsa.UseIdentity(identity =>
{
identity.UseAdminUserProvider();
identity.TokenOptions = options => options.SigningKey = "secret-token-signing-key";
});
-
+
// Use default authentication (JWT + API Key).
elsa.UseDefaultAuthentication(auth => auth.UseAdminApiKey());
diff --git a/dotnet/samples/WorkflowsApp/Skills/DevLeadPrompts.cs b/dotnet/samples/WorkflowsApp/Skills/DevLeadPrompts.cs
index 4072a8a3f..2760c47d2 100644
--- a/dotnet/samples/WorkflowsApp/Skills/DevLeadPrompts.cs
+++ b/dotnet/samples/WorkflowsApp/Skills/DevLeadPrompts.cs
@@ -1,5 +1,6 @@
namespace Microsoft.SKDevTeam;
-public static class DevLead {
+public static class DevLead
+{
public static SemanticFunctionConfig Plan = new SemanticFunctionConfig
{
PromptTemplate = """
diff --git a/dotnet/samples/WorkflowsApp/Skills/Developer.cs b/dotnet/samples/WorkflowsApp/Skills/Developer.cs
index ed4ed5781..b8f16a111 100644
--- a/dotnet/samples/WorkflowsApp/Skills/Developer.cs
+++ b/dotnet/samples/WorkflowsApp/Skills/Developer.cs
@@ -1,5 +1,6 @@
namespace Microsoft.SKDevTeam;
-public static class Developer {
+public static class Developer
+{
public static SemanticFunctionConfig Implement = new SemanticFunctionConfig
{
PromptTemplate = """
diff --git a/dotnet/samples/WorkflowsApp/Skills/PMPrompts.cs b/dotnet/samples/WorkflowsApp/Skills/PMPrompts.cs
index c8fb11883..59d529a9d 100644
--- a/dotnet/samples/WorkflowsApp/Skills/PMPrompts.cs
+++ b/dotnet/samples/WorkflowsApp/Skills/PMPrompts.cs
@@ -1,9 +1,9 @@
namespace Microsoft.SKDevTeam;
public static class PM
{
- public static SemanticFunctionConfig BootstrapProject = new SemanticFunctionConfig
- {
- PromptTemplate = """
+ public static SemanticFunctionConfig BootstrapProject = new SemanticFunctionConfig
+ {
+ PromptTemplate = """
Please write a bash script with the commands that would be required to generate applications as described in the following input.
You may add comments to the script and the generated output but do not add any other text except the bash script.
You may include commands to build the applications but do not run them.
@@ -11,18 +11,18 @@ public static class PM
Input: {{$input}}
{{$wafContext}}
""",
- Name = nameof(BootstrapProject),
- SkillName = nameof(PM),
- Description = "Bootstrap a new project",
+ Name = nameof(BootstrapProject),
+ SkillName = nameof(PM),
+ Description = "Bootstrap a new project",
MaxTokens = 6500,
Temperature = 0.0,
TopP = 0.0,
PPenalty = 0.0,
FPenalty = 0.0
- };
- public static SemanticFunctionConfig Readme = new SemanticFunctionConfig
- {
- PromptTemplate = """
+ };
+ public static SemanticFunctionConfig Readme = new SemanticFunctionConfig
+ {
+ PromptTemplate = """
You are a program manager on a software development team. You are working on an app described below.
Based on the input below, and any dialog or other context, please output a raw README.MD markdown file documenting the main features of the app and the architecture or code organization.
Do not describe how to create the application.
@@ -30,13 +30,13 @@ public static class PM
Input: {{$input}}
{{$wafContext}}
""",
- Name = nameof(Readme),
- SkillName = nameof(PM),
- Description = "From a simple description output a README.md file for a GitHub repository.",
+ Name = nameof(Readme),
+ SkillName = nameof(PM),
+ Description = "From a simple description output a README.md file for a GitHub repository.",
MaxTokens = 6500,
Temperature = 0.0,
TopP = 0.0,
PPenalty = 0.0,
FPenalty = 0.0
- };
+ };
}
\ No newline at end of file
diff --git a/dotnet/samples/WorkflowsApp/Skills/SemanticFunctionConfig.cs b/dotnet/samples/WorkflowsApp/Skills/SemanticFunctionConfig.cs
index 1569962e2..4156501de 100644
--- a/dotnet/samples/WorkflowsApp/Skills/SemanticFunctionConfig.cs
+++ b/dotnet/samples/WorkflowsApp/Skills/SemanticFunctionConfig.cs
@@ -2,16 +2,16 @@ namespace Microsoft.SKDevTeam;
public class SemanticFunctionConfig
{
- public string PromptTemplate { get; set; }
- public string Name { get; set; }
- public string SkillName { get; set; }
- public string Description { get; set; }
+ public required string PromptTemplate { get; set; }
+ public required string Name { get; set; }
+ public required string SkillName { get; set; }
+ public required string Description { get; set; }
public int MaxTokens { get; set; }
public double Temperature { get; set; }
public double TopP { get; set; }
public double PPenalty { get; set; }
public double FPenalty { get; set; }
- public static SemanticFunctionConfig ForSkillAndFunction(string skillName, string functionName) =>
+ public static SemanticFunctionConfig ForSkillAndFunction(string skillName, string functionName) =>
(skillName, functionName) switch
{
(nameof(PM), nameof(PM.Readme)) => PM.Readme,
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Architect/Architect.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Architect/Architect.cs
index 66812798a..69eba6a77 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Architect/Architect.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Architect/Architect.cs
@@ -7,9 +7,8 @@ using Microsoft.SemanticKernel.Memory;
namespace Microsoft.AI.DevTeam.Dapr;
-
// The architect has Org+Repo scope and is holding the knowledge of the high level architecture of the project
-public class Architect : AiAgent,IDaprAgent
+public class Architect : AiAgent, IDaprAgent
{
public Architect(ActorHost host, DaprClient client, ISemanticTextMemory memory, Kernel kernel)
: base(host, client, memory, kernel)
@@ -24,6 +23,6 @@ public class Architect : AiAgent,IDaprAgent
public class ArchitectState
{
- public string FilesTree { get; set; }
- public string HighLevelArchitecture { get; set; }
-}
\ No newline at end of file
+ public string? FilesTree { get; set; }
+ public string? HighLevelArchitecture { get; set; }
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/AzureGenie.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/AzureGenie.cs
index 15305a5e0..7417f022c 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/AzureGenie.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/AzureGenie.cs
@@ -1,4 +1,3 @@
-using Dapr.Actors;
using Dapr.Actors.Runtime;
using Dapr.Client;
using Microsoft.AI.Agents.Abstractions;
@@ -11,41 +10,42 @@ public class AzureGenie : Agent, IDaprAgent
{
private readonly IManageAzure _azureService;
- public AzureGenie(ActorHost host,DaprClient client, IManageAzure azureService) : base(host, client)
+ public AzureGenie(ActorHost host, DaprClient client, IManageAzure azureService) : base(host, client)
{
_azureService = azureService;
}
public override async Task HandleEvent(Event item)
{
+ ArgumentNullException.ThrowIfNull(item);
switch (item.Type)
{
case nameof(GithubFlowEventType.ReadmeCreated):
- {
- var context = item.ToGithubContext();
- await Store(context.Org,context.Repo, context.ParentNumber.Value, context.IssueNumber, "readme", "md", "output", item.Data["readme"]);
- await PublishEvent(Consts.PubSub, Consts.MainTopic, new Event
{
- Type = nameof(GithubFlowEventType.ReadmeStored),
- Subject = context.Subject,
- Data = context.ToData()
- });
- }
-
+ var context = item.ToGithubContext();
+ await Store(context.Org, context.Repo, context.ParentNumber ?? 0, context.IssueNumber, "readme", "md", "output", item.Data["readme"]);
+ await PublishEvent(Consts.PubSub, Consts.MainTopic, new Event
+ {
+ Type = nameof(GithubFlowEventType.ReadmeStored),
+ Subject = context.Subject,
+ Data = context.ToData()
+ });
+ }
+
break;
case nameof(GithubFlowEventType.CodeCreated):
- {
- var context = item.ToGithubContext();
- await Store(context.Org,context.Repo, context.ParentNumber.Value, context.IssueNumber, "run", "sh", "output", item.Data["code"]);
- await RunInSandbox(context.Org,context.Repo, context.ParentNumber.Value, context.IssueNumber);
- await PublishEvent(Consts.PubSub, Consts.MainTopic, new Event
{
- Type = nameof(GithubFlowEventType.SandboxRunCreated),
- Subject = context.Subject,
- Data = context.ToData()
- });
- }
-
+ var context = item.ToGithubContext();
+ await Store(context.Org, context.Repo, context.ParentNumber ?? 0, context.IssueNumber, "run", "sh", "output", item.Data["code"]);
+ await RunInSandbox(context.Org, context.Repo, context.ParentNumber ?? 0, context.IssueNumber);
+ await PublishEvent(Consts.PubSub, Consts.MainTopic, new Event
+ {
+ Type = nameof(GithubFlowEventType.SandboxRunCreated),
+ Subject = context.Subject,
+ Data = context.ToData()
+ });
+ }
+
break;
default:
break;
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Developer/Developer.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Developer/Developer.cs
index 8648cb602..3f22c00ee 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Developer/Developer.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Developer/Developer.cs
@@ -1,4 +1,3 @@
-using Dapr.Actors;
using Dapr.Actors.Runtime;
using Dapr.Client;
using Microsoft.AI.Agents.Abstractions;
@@ -9,20 +8,21 @@ using Microsoft.SemanticKernel.Memory;
namespace Microsoft.AI.DevTeam.Dapr;
public class Dev : AiAgent, IDaprAgent
{
-
private readonly ILogger _logger;
- public Dev(ActorHost host, DaprClient client, Kernel kernel, ISemanticTextMemory memory, ILogger logger)
+ public Dev(ActorHost host, DaprClient client, Kernel kernel, ISemanticTextMemory memory, ILogger logger)
: base(host, client, memory, kernel)
{
_logger = logger;
}
- public async override Task HandleEvent(Event item)
+
+ public override async Task HandleEvent(Event item)
{
+ ArgumentNullException.ThrowIfNull(item);
switch (item.Type)
{
case nameof(GithubFlowEventType.CodeGenerationRequested):
- {
+ {
var context = item.ToGithubContext();
var code = await GenerateCode(item.Data["input"]);
var data = context.ToData();
@@ -59,26 +59,26 @@ public class Dev : AiAgent, IDaprAgent
try
{
// TODO: ask the architect for the high level architecture as well as the files structure of the project
- var context = new KernelArguments { ["input"] = AppendChatHistory(ask)};
+ var context = new KernelArguments { ["input"] = AppendChatHistory(ask) };
var instruction = "Consider the following architectural guidelines:!waf!";
- var enhancedContext = await AddKnowledge(instruction, "waf",context);
+ var enhancedContext = await AddKnowledge(instruction, "waf", context);
return await CallFunction(DeveloperSkills.Implement, enhancedContext);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error generating code");
- return default;
+ return "";
}
}
}
public class DeveloperState
{
- public string Understanding { get; set; }
+ public string? Understanding { get; set; }
}
public class UnderstandingResult
{
- public string NewUnderstanding { get; set; }
- public string Explanation { get; set; }
-}
\ No newline at end of file
+ public required string NewUnderstanding { get; set; }
+ public required string Explanation { get; set; }
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Developer/DeveloperPrompts.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Developer/DeveloperPrompts.cs
index 1e876ad9e..cc97db029 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Developer/DeveloperPrompts.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Developer/DeveloperPrompts.cs
@@ -1,7 +1,8 @@
namespace Microsoft.AI.DevTeam.Dapr;
-public static class DeveloperSkills {
- public static string Implement = """
+public static class DeveloperSkills
+{
+ public const string Implement = """
You are a Developer for an application.
Please output the code required to accomplish the task assigned to you below and wrap it in a bash script that creates the files.
Do not use any IDE commands and do not build and run the code.
@@ -11,7 +12,7 @@ public static class DeveloperSkills {
{{$waf}}
""";
- public static string Improve = """
+ public const string Improve = """
You are a Developer for an application. Your job is to imrove the code that you are given in the input below.
Please output a new version of code that fixes any problems with this version.
If there is an error message in the input you should fix that error in the code.
@@ -23,7 +24,7 @@ public static class DeveloperSkills {
{{$waf}}
""";
- public static string Explain = """
+ public const string Explain = """
You are an experienced software developer, with strong experience in Azure and Microsoft technologies.
Extract the key features and capabilities of the code file below, with the intent to build an understanding of an entire code repository.
You can include references or documentation links in your explanation. Also where appropriate please output a list of keywords to describe the code or its capabilities.
@@ -39,7 +40,7 @@ public static class DeveloperSkills {
Error: The model could not determine the purpose of the code.
""";
- public static string ConsolidateUnderstanding = """
+ public const string ConsolidateUnderstanding = """
You are an experienced software developer, with strong experience in Azure and Microsoft technologies.
You are trying to build an understanding of the codebase from code files. This is the current understanding of the project:
===current-understanding===
@@ -53,4 +54,4 @@ public static class DeveloperSkills {
Only include the points in a bullet point format and DON'T add anything outside of the bulleted list.
Be short and concise.
""";
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/DeveloperLead/DevLeadPrompts.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/DeveloperLead/DevLeadPrompts.cs
index e5cdba5da..806be7ad8 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/DeveloperLead/DevLeadPrompts.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/DeveloperLead/DevLeadPrompts.cs
@@ -1,6 +1,7 @@
namespace Microsoft.AI.DevTeam.Dapr;
-public static class DevLeadSkills {
- public static string Plan = """
+public static class DevLeadSkills
+{
+ public const string Plan = """
You are a Dev Lead for an application team, building the application described below.
Please break down the steps and modules required to develop the complete application, describe each step in detail.
Make prescriptive architecture, language, and frameowrk choices, do not provide a range of choices.
@@ -35,7 +36,7 @@ public static class DevLeadSkills {
{{$waf}}
""";
- public static string Explain = """
+ public const string Explain = """
You are a Dev Lead.
Please explain the code that is in the input below. You can include references or documentation links in your explanation.
Also where appropriate please output a list of keywords to describe the code or its capabilities.
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/DeveloperLead/DeveloperLead.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/DeveloperLead/DeveloperLead.cs
index bae105962..899ce6911 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/DeveloperLead/DeveloperLead.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/DeveloperLead/DeveloperLead.cs
@@ -1,4 +1,3 @@
-using Dapr.Actors;
using Dapr.Actors.Runtime;
using Dapr.Client;
using Microsoft.AI.Agents.Abstractions;
@@ -18,17 +17,18 @@ public class DeveloperLead : AiAgent, IDaprAgent
_logger = logger;
}
- public async override Task HandleEvent(Event item)
+ public override async Task HandleEvent(Event item)
{
+ ArgumentNullException.ThrowIfNull(item);
switch (item.Type)
{
case nameof(GithubFlowEventType.DevPlanRequested):
{
- var context = item.ToGithubContext();
+ var context = item.ToGithubContext();
var plan = await CreatePlan(item.Data["input"]);
var data = context.ToData();
data["result"] = plan;
- await PublishEvent(Consts.PubSub,Consts.MainTopic, new Event
+ await PublishEvent(Consts.PubSub, Consts.MainTopic, new Event
{
Type = nameof(GithubFlowEventType.DevPlanGenerated),
Subject = context.Subject,
@@ -37,12 +37,12 @@ public class DeveloperLead : AiAgent, IDaprAgent
}
break;
case nameof(GithubFlowEventType.DevPlanChainClosed):
- {
+ {
var context = item.ToGithubContext();
var latestPlan = state.History.Last().Message;
var data = context.ToData();
data["plan"] = latestPlan;
- await PublishEvent(Consts.PubSub,Consts.MainTopic, new Event
+ await PublishEvent(Consts.PubSub, Consts.MainTopic, new Event
{
Type = nameof(GithubFlowEventType.DevPlanCreated),
Subject = context.Subject,
@@ -68,30 +68,30 @@ public class DeveloperLead : AiAgent, IDaprAgent
catch (Exception ex)
{
_logger.LogError(ex, "Error creating development plan");
- return default;
+ return "";
}
}
}
public class DevLeadPlanResponse
{
- public List steps { get; set; }
+ public required List Steps { get; set; }
}
-public class Step
+public class StepDescription
{
- public string description { get; set; }
- public string step { get; set; }
- public List subtasks { get; set; }
+ public required string Description { get; set; }
+ public required string Step { get; set; }
+ public required List subtasks { get; set; }
}
-public class Subtask
+public class SubtaskDescription
{
- public string subtask { get; set; }
- public string prompt { get; set; }
+ public required string Subtask { get; set; }
+ public required string Prompt { get; set; }
}
public class DeveloperLeadState
{
- public string Plan { get; set; }
-}
\ No newline at end of file
+ public string? Plan { get; set; }
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Hubber.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Hubber.cs
index 902ab210c..03ce61c12 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Hubber.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Hubber.cs
@@ -1,5 +1,4 @@
-using System.Text.Json;
-using Dapr.Actors;
+using System.Text.Json;
using Dapr.Actors.Runtime;
using Dapr.Client;
using Microsoft.AI.Agents.Abstractions;
@@ -12,22 +11,23 @@ public class Hubber : Agent, IDaprAgent
{
private readonly IManageGithub _ghService;
- public Hubber(ActorHost host, DaprClient client, IManageGithub ghService) :base(host, client)
+ public Hubber(ActorHost host, DaprClient client, IManageGithub ghService) : base(host, client)
{
_ghService = ghService;
}
public override async Task HandleEvent(Event item)
{
+ ArgumentNullException.ThrowIfNull(item);
switch (item.Type)
{
case nameof(GithubFlowEventType.NewAsk):
{
var context = item.ToGithubContext();
- var pmIssue = await CreateIssue(context.Org, context.Repo , item.Data["input"], "PM.Readme", context.IssueNumber);
- var devLeadIssue = await CreateIssue(context.Org, context.Repo , item.Data["input"], "DevLead.Plan", context.IssueNumber);
+ var pmIssue = await CreateIssue(context.Org, context.Repo, item.Data["input"], "PM.Readme", context.IssueNumber);
+ var devLeadIssue = await CreateIssue(context.Org, context.Repo, item.Data["input"], "DevLead.Plan", context.IssueNumber);
await PostComment(context.Org, context.Repo, context.IssueNumber, $" - #{pmIssue} - tracks PM.Readme");
- await PostComment(context.Org, context.Repo, context.IssueNumber, $" - #{devLeadIssue} - tracks DevLead.Plan");
+ await PostComment(context.Org, context.Repo, context.IssueNumber, $" - #{devLeadIssue} - tracks DevLead.Plan");
await CreateBranch(context.Org, context.Repo, $"sk-{context.IssueNumber}");
}
break;
@@ -37,23 +37,23 @@ public class Hubber : Agent, IDaprAgent
{
var context = item.ToGithubContext();
var result = item.Data["result"];
- var contents = string.IsNullOrEmpty(result)? "Sorry, I got tired, can you try again please? ": result;
- await PostComment(context.Org,context.Repo, context.IssueNumber, contents);
+ var contents = string.IsNullOrEmpty(result) ? "Sorry, I got tired, can you try again please? " : result;
+ await PostComment(context.Org, context.Repo, context.IssueNumber, contents);
}
-
+
break;
case nameof(GithubFlowEventType.DevPlanCreated):
{
var context = item.ToGithubContext();
var plan = JsonSerializer.Deserialize(item.Data["plan"]);
- var prompts = plan.steps.SelectMany(s => s.subtasks.Select(st => st.prompt));
-
+ var prompts = plan!.Steps.SelectMany(s => s.subtasks.Select(st => st.Prompt));
+
foreach (var prompt in prompts)
{
var functionName = "Developer.Implement";
- var issue = await CreateIssue(context.Org, context.Repo, prompt, functionName, context.ParentNumber.Value);
+ var issue = await CreateIssue(context.Org, context.Repo, prompt, functionName, context.ParentNumber ?? 0);
var commentBody = $" - #{issue} - tracks {functionName}";
- await PostComment(context.Org, context.Repo, context.ParentNumber.Value, commentBody);
+ await PostComment(context.Org, context.Repo, context.ParentNumber ?? 0 , commentBody);
}
}
break;
@@ -61,15 +61,15 @@ public class Hubber : Agent, IDaprAgent
{
var context = item.ToGithubContext();
var branch = $"sk-{context.ParentNumber}";
- await CommitToBranch(context.Org, context.Repo, context.ParentNumber.Value, context.IssueNumber, "output", branch);
- await CreatePullRequest(context.Org, context.Repo, context.ParentNumber.Value, branch);
+ await CommitToBranch(context.Org, context.Repo, context.ParentNumber ?? 0, context.IssueNumber, "output", branch);
+ await CreatePullRequest(context.Org, context.Repo, context.ParentNumber ?? 0, branch);
}
break;
case nameof(GithubFlowEventType.SandboxRunFinished):
{
var context = item.ToGithubContext();
var branch = $"sk-{context.ParentNumber}";
- await CommitToBranch(context.Org, context.Repo, context.ParentNumber.Value, context.IssueNumber, "output", branch);
+ await CommitToBranch(context.Org, context.Repo, context.ParentNumber ?? 0, context.IssueNumber, "output", branch);
}
break;
default:
@@ -97,4 +97,4 @@ public class Hubber : Agent, IDaprAgent
{
await _ghService.CommitToBranch(org, repo, parentNumber, issueNumber, rootDir, branch);
}
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/ProductManager/PMPrompts.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/ProductManager/PMPrompts.cs
index a6b727a1b..e272632ff 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/ProductManager/PMPrompts.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/ProductManager/PMPrompts.cs
@@ -1,7 +1,7 @@
namespace Microsoft.AI.DevTeam.Dapr;
public static class PMSkills
{
-public static string BootstrapProject = """
+ public const string BootstrapProject = """
Please write a bash script with the commands that would be required to generate applications as described in the following input.
You may add comments to the script and the generated output but do not add any other text except the bash script.
You may include commands to build the applications but do not run them.
@@ -9,7 +9,7 @@ public static string BootstrapProject = """
Input: {{$input}}
{{$waf}}
""";
- public static string Readme = """
+ public const string Readme = """
You are a program manager on a software development team. You are working on an app described below.
Based on the input below, and any dialog or other context, please output a raw README.MD markdown file documenting the main features of the app and the architecture or code organization.
Do not describe how to create the application.
@@ -18,7 +18,7 @@ public static string BootstrapProject = """
{{$waf}}
""";
- public static string Explain = """
+ public const string Explain = """
You are a Product Manager.
Please explain the code that is in the input below. You can include references or documentation links in your explanation.
Also where appropriate please output a list of keywords to describe the code or its capabilities.
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/ProductManager/ProductManager.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/ProductManager/ProductManager.cs
index ce08c1d1a..3e8e62977 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/ProductManager/ProductManager.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/ProductManager/ProductManager.cs
@@ -1,4 +1,3 @@
-using Dapr.Actors;
using Dapr.Actors.Runtime;
using Dapr.Client;
using Microsoft.AI.Agents.Abstractions;
@@ -19,8 +18,9 @@ public class ProductManager : AiAgent, IDaprAgent
_logger = logger;
}
- public async override Task HandleEvent(Event item)
+ public override async Task HandleEvent(Event item)
{
+ ArgumentNullException.ThrowIfNull(item);
switch (item.Type)
{
case nameof(GithubFlowEventType.ReadmeRequested):
@@ -28,8 +28,9 @@ public class ProductManager : AiAgent, IDaprAgent
var context = item.ToGithubContext();
var readme = await CreateReadme(item.Data["input"]);
var data = context.ToData();
- data["result"]=readme;
- await PublishEvent(Consts.PubSub,Consts.MainTopic, new Event {
+ data["result"] = readme;
+ await PublishEvent(Consts.PubSub, Consts.MainTopic, new Event
+ {
Type = nameof(GithubFlowEventType.ReadmeGenerated),
Subject = context.Subject,
Data = data
@@ -39,14 +40,15 @@ public class ProductManager : AiAgent, IDaprAgent
case nameof(GithubFlowEventType.ReadmeChainClosed):
{
var context = item.ToGithubContext();
- var lastReadme = state.History.Last().Message;
- var data = context.ToData();
- data["readme"] = lastReadme;
- await PublishEvent(Consts.PubSub,Consts.MainTopic, new Event {
- Type = nameof(GithubFlowEventType.ReadmeCreated),
- Subject = context.Subject,
- Data = data
- });
+ var lastReadme = state.History.Last().Message;
+ var data = context.ToData();
+ data["readme"] = lastReadme;
+ await PublishEvent(Consts.PubSub, Consts.MainTopic, new Event
+ {
+ Type = nameof(GithubFlowEventType.ReadmeCreated),
+ Subject = context.Subject,
+ Data = data
+ });
}
break;
@@ -67,12 +69,12 @@ public class ProductManager : AiAgent, IDaprAgent
catch (Exception ex)
{
_logger.LogError(ex, "Error creating readme");
- return default;
+ return "";
}
}
}
public class ProductManagerState
{
- public string Capabilities { get; set; }
-}
\ No newline at end of file
+ public string? Capabilities { get; set; }
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Sandbox.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Sandbox.cs
index e5b364978..a3e61863c 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Sandbox.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Agents/Sandbox.cs
@@ -1,4 +1,4 @@
-using Dapr.Actors.Runtime;
+using Dapr.Actors.Runtime;
using Dapr.Client;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Dapr;
@@ -8,7 +8,7 @@ namespace Microsoft.AI.DevTeam.Dapr;
public class Sandbox : Agent, IDaprAgent, IRemindable
{
private const string ReminderName = "SandboxRunReminder";
- public string StateStore = "agents-statestore";
+ public const string StateStore = "agents-statestore";
private readonly IManageAzure _azService;
public Sandbox(ActorHost host, DaprClient client, IManageAzure azService) : base(host, client)
@@ -18,33 +18,36 @@ public class Sandbox : Agent, IDaprAgent, IRemindable
public override async Task HandleEvent(Event item)
{
- switch(item.Type)
- {
- case nameof(GithubFlowEventType.SandboxRunCreated):
- {
- var context = item.ToGithubContext();
- await ScheduleCommitSandboxRun(context.Org, context.Repo, context.ParentNumber.Value, context.IssueNumber);
- }
- break;
-
- default:
- break;
- }
+ ArgumentNullException.ThrowIfNull(item);
+
+ switch (item.Type)
+ {
+ case nameof(GithubFlowEventType.SandboxRunCreated):
+ {
+ var context = item.ToGithubContext();
+ await ScheduleCommitSandboxRun(context.Org, context.Repo, context.ParentNumber ?? 0, context.IssueNumber);
+ }
+ break;
+
+ default:
+ break;
+ }
}
public async Task ScheduleCommitSandboxRun(string org, string repo, long parentIssueNumber, long issueNumber)
{
await StoreState(org, repo, parentIssueNumber, issueNumber);
await this.RegisterReminderAsync(
- ReminderName,
+ ReminderName,
null,
- TimeSpan.Zero,
+ TimeSpan.Zero,
TimeSpan.FromMinutes(1));
}
private async Task StoreState(string org, string repo, long parentIssueNumber, long issueNumber)
{
- var state = new SandboxMetadata {
+ var state = new SandboxMetadata
+ {
Org = org,
Repo = repo,
IssueNumber = issueNumber,
@@ -70,7 +73,7 @@ public class Sandbox : Agent, IDaprAgent, IRemindable
var agentState = await StateManager.GetStateAsync(StateStore);
if (!agentState.IsCompleted)
{
- var sandboxId = $"sk-sandbox-{agentState.Org}-{agentState.Repo}-{agentState.ParentIssueNumber}-{agentState.IssueNumber}";
+ var sandboxId = $"sk-sandbox-{agentState.Org}-{agentState.Repo}-{agentState.ParentIssueNumber}-{agentState.IssueNumber}";
if (await _azService.IsSandboxCompleted(sandboxId))
{
await _azService.DeleteSandbox(sandboxId);
@@ -81,9 +84,10 @@ public class Sandbox : Agent, IDaprAgent, IRemindable
{ "parentNumber", agentState.ParentIssueNumber.ToString() }
};
var subject = $"{agentState.Org}-{agentState.Repo}-{agentState.IssueNumber}";
- await PublishEvent(Consts.PubSub,Consts.MainTopic, new Event {
- Type = nameof(GithubFlowEventType.SandboxRunFinished),
- Subject = subject,
+ await PublishEvent(Consts.PubSub, Consts.MainTopic, new Event
+ {
+ Type = nameof(GithubFlowEventType.SandboxRunFinished),
+ Subject = subject,
Data = data
});
await Cleanup();
@@ -98,9 +102,9 @@ public class Sandbox : Agent, IDaprAgent, IRemindable
public class SandboxMetadata
{
- public string Org { get; set; }
- public string Repo { get; set; }
+ public required string Org { get; set; }
+ public required string Repo { get; set; }
public long ParentIssueNumber { get; set; }
public long IssueNumber { get; set; }
public bool IsCompleted { get; set; }
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Events/GithubFlowEventType.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Events/GithubFlowEventType.cs
index 24b165711..432e007c9 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Events/GithubFlowEventType.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Events/GithubFlowEventType.cs
@@ -1,69 +1,70 @@
+using System.Globalization;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using Microsoft.AI.Agents.Abstractions;
-namespace Microsoft.AI.DevTeam.Dapr.Events
+namespace Microsoft.AI.DevTeam.Dapr.Events;
+
+public enum GithubFlowEventType
{
- public enum GithubFlowEventType
- {
- NewAsk,
- ReadmeChainClosed,
- CodeChainClosed,
- CodeGenerationRequested,
- DevPlanRequested,
- ReadmeGenerated,
- DevPlanGenerated,
- CodeGenerated,
- DevPlanChainClosed,
- ReadmeRequested,
- ReadmeStored,
- SandboxRunFinished,
- ReadmeCreated,
- CodeCreated,
- DevPlanCreated,
- SandboxRunCreated
- }
+ NewAsk,
+ ReadmeChainClosed,
+ CodeChainClosed,
+ CodeGenerationRequested,
+ DevPlanRequested,
+ ReadmeGenerated,
+ DevPlanGenerated,
+ CodeGenerated,
+ DevPlanChainClosed,
+ ReadmeRequested,
+ ReadmeStored,
+ SandboxRunFinished,
+ ReadmeCreated,
+ CodeCreated,
+ DevPlanCreated,
+ SandboxRunCreated
+}
- public static class EventExtensions
+public static class EventExtensions
+{
+ public static GithubContext ToGithubContext(this Event evt)
{
- public static GithubContext ToGithubContext(this Event evt)
+ ArgumentNullException.ThrowIfNull(evt);
+ return new GithubContext
{
- return new GithubContext
- {
- Org = evt.Data["org"],
- Repo = evt.Data["repo"],
- IssueNumber = long.Parse(evt.Data["issueNumber"]),
- ParentNumber = string.IsNullOrEmpty(evt.Data["parentNumber"]) ? default : long.Parse(evt.Data["parentNumber"])
- };
- }
-
- public static Dictionary ToData(this GithubContext context)
- {
- return new Dictionary {
- { "org", context.Org },
- { "repo", context.Repo },
- { "issueNumber", $"{context.IssueNumber}" },
- { "parentNumber", context.ParentNumber.HasValue? context.ParentNumber.ToString(): default }
- };
- }
-
-
+ Org = evt.Data["org"],
+ Repo = evt.Data["repo"],
+ IssueNumber = long.Parse(evt.Data["issueNumber"]),
+ ParentNumber = string.IsNullOrEmpty(evt.Data["parentNumber"]) ? default : long.Parse(evt.Data["parentNumber"])
+ };
}
- public class GithubContext
+ public static Dictionary ToData(this GithubContext context)
{
- public string Org { get; set; }
- public string Repo { get; set; }
- public long IssueNumber { get; set; }
- public long? ParentNumber { get; set; }
-
- public string Subject => $"{Org}-{Repo}-{IssueNumber}";
+ ArgumentNullException.ThrowIfNull(context);
+ return new Dictionary {
+ { "org", context.Org },
+ { "repo", context.Repo },
+ { "issueNumber", $"{context.IssueNumber}" },
+ { "parentNumber", context.ParentNumber?.ToString(CultureInfo.InvariantCulture) ?? ""}
+ };
}
- [DataContract]
- public class EventEnvelope
- {
- [JsonPropertyName("data")]
- public Event Data { get; set; }
- }
-}
\ No newline at end of file
+}
+
+public class GithubContext
+{
+ public required string Org { get; set; }
+ public required string Repo { get; set; }
+ public long IssueNumber { get; set; }
+ public long? ParentNumber { get; set; }
+
+ public string Subject => $"{Org}-{Repo}-{IssueNumber}";
+}
+
+[DataContract]
+public class EventEnvelope
+{
+ [JsonPropertyName("data")]
+ public required Event Data { get; set; }
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/AzureOptions.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/AzureOptions.cs
index 2fcbcab78..463b090ee 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/AzureOptions.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/AzureOptions.cs
@@ -4,19 +4,17 @@ namespace Microsoft.AI.DevTeam.Dapr;
public class AzureOptions
{
[Required]
- public string SubscriptionId { get; set; }
+ public required string SubscriptionId { get; set; }
[Required]
- public string Location { get; set; }
+ public required string Location { get; set; }
[Required]
- public string ContainerInstancesResourceGroup { get; set; }
+ public required string ContainerInstancesResourceGroup { get; set; }
[Required]
- public string FilesShareName { get; set; }
+ public required string FilesShareName { get; set; }
[Required]
- public string FilesAccountName { get; set; }
+ public required string FilesAccountName { get; set; }
[Required]
- public string FilesAccountKey { get; set; }
+ public required string FilesAccountKey { get; set; }
[Required]
- public string SandboxImage { get; set; }
- public string ManagedIdentity { get; set; }
- public string CosmosConnectionString { get; set; }
-}
\ No newline at end of file
+ public required string SandboxImage { get; set; }
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/GithubOptions.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/GithubOptions.cs
index a88480538..1f6384ac3 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/GithubOptions.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/GithubOptions.cs
@@ -4,11 +4,11 @@ namespace Microsoft.AI.DevTeam.Dapr;
public class GithubOptions
{
[Required]
- public string AppKey { get; set; }
+ public required string AppKey { get; set; }
[Required]
public int AppId { get; set; }
[Required]
public long InstallationId { get; set; }
[Required]
- public string WebhookSecret { get; set; }
-}
\ No newline at end of file
+ public required string WebhookSecret { get; set; }
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/OpenAIOptions.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/OpenAIOptions.cs
index e69ebf59f..804019fbb 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/OpenAIOptions.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/OpenAIOptions.cs
@@ -4,15 +4,15 @@ namespace Microsoft.AI.DevTeam.Dapr;
public class OpenAIOptions
{
[Required]
- public string ServiceType { get; set; }
+ public required string ServiceType { get; set; }
[Required]
- public string ServiceId { get; set; }
+ public required string ServiceId { get; set; }
[Required]
- public string DeploymentOrModelId { get; set; }
+ public required string DeploymentOrModelId { get; set; }
[Required]
- public string EmbeddingDeploymentOrModelId { get; set; }
+ public required string EmbeddingDeploymentOrModelId { get; set; }
[Required]
- public string Endpoint { get; set; }
+ public required string Endpoint { get; set; }
[Required]
- public string ApiKey { get; set; }
+ public required string ApiKey { get; set; }
}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/QdrantOptions.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/QdrantOptions.cs
index ea79a9557..a18a744f3 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/QdrantOptions.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/QdrantOptions.cs
@@ -4,7 +4,7 @@ namespace Microsoft.AI.DevTeam.Dapr;
public class QdrantOptions
{
[Required]
- public string Endpoint { get; set; }
+ public required string Endpoint { get; set; }
[Required]
public int VectorSize { get; set; }
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/ServiceOptions.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/ServiceOptions.cs
index 8c3b09c76..7df93c80a 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/ServiceOptions.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Options/ServiceOptions.cs
@@ -3,8 +3,6 @@ using System.ComponentModel.DataAnnotations;
namespace Microsoft.AI.DevTeam.Dapr;
public class ServiceOptions
{
- private string _ingesterUrl;
-
[Required]
- public string IngesterUrl { get; set; }
+ public required Uri IngesterUrl { get; set; }
}
\ No newline at end of file
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Program.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Program.cs
index 14a610c7c..fb1c85510 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Program.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Program.cs
@@ -25,14 +25,13 @@ builder.Services.AddHttpClient();
builder.Services.AddSingleton(s =>
{
- var ghOptions = s.GetService>();
- var logger = s.GetService>();
+ var ghOptions = s.GetRequiredService>();
+ var logger = s.GetRequiredService>();
var ghService = new GithubAuthService(ghOptions, logger);
var client = ghService.GetGitHubClient();
return client;
});
-
builder.Services.AddAzureClients(clientBuilder =>
{
clientBuilder.UseCredential(new DefaultAzureCredential());
@@ -46,7 +45,8 @@ builder.Services.AddActors(
{
options.UseJsonSerialization = true;
options.JsonSerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web);
- options.ReentrancyConfig = new ActorReentrancyConfig {
+ options.ReentrancyConfig = new ActorReentrancyConfig
+ {
Enabled = true
};
options.Actors.RegisterActor();
@@ -104,7 +104,6 @@ builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddSingleton();
-
builder.Services.Configure(options =>
{
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
@@ -114,7 +113,7 @@ var app = builder.Build();
app.UseRouting()
.UseEndpoints(endpoints =>
{
- var ghOptions = app.Services.GetService>().Value;
+ var ghOptions = app.Services.GetRequiredService>().Value;
endpoints.MapGitHubWebhooks(secret: ghOptions.WebhookSecret);
endpoints.MapActorsHandlers();
endpoints.MapSubscribeHandler();
@@ -122,52 +121,45 @@ app.UseRouting()
app.UseCloudEvents();
-app.MapPost("/developers", [Topic(Consts.PubSub, Consts.MainTopic,
- $"(event.type ==\"{nameof(GithubFlowEventType.CodeGenerationRequested)}\") || (event.type ==\"{nameof(GithubFlowEventType.CodeGenerationRequested)}\")", 1)]
- async (IActorProxyFactory proxyFactory, EventEnvelope evt) => await HandleEvent(proxyFactory,nameof(Dev), nameof(Dev.HandleEvent), evt));
+app.MapPost("/developers", [Topic(Consts.PubSub, Consts.MainTopic,
+ $"(event.type ==\"{nameof(GithubFlowEventType.CodeGenerationRequested)}\") || (event.type ==\"{nameof(GithubFlowEventType.CodeGenerationRequested)}\")", 1)]
+async (IActorProxyFactory proxyFactory, EventEnvelope evt) => await HandleEvent(proxyFactory, nameof(Dev), nameof(Dev.HandleEvent), evt));
app.MapPost("/devleads", [Topic(Consts.PubSub, Consts.MainTopic,
-$"(event.type ==\"{nameof(GithubFlowEventType.DevPlanRequested)}\") || (event.type ==\"{nameof(GithubFlowEventType.DevPlanChainClosed)}\")", 2)]
+$"(event.type ==\"{nameof(GithubFlowEventType.DevPlanRequested)}\") || (event.type ==\"{nameof(GithubFlowEventType.DevPlanChainClosed)}\")", 2)]
async (IActorProxyFactory proxyFactory, EventEnvelope evt) => await HandleEvent(proxyFactory, nameof(DeveloperLead), nameof(DeveloperLead.HandleEvent), evt));
-app.MapPost("/productmanagers", [Topic(Consts.PubSub, Consts.MainTopic,
+app.MapPost("/productmanagers", [Topic(Consts.PubSub, Consts.MainTopic,
$"(event.type ==\"{nameof(GithubFlowEventType.ReadmeRequested)}\") || (event.type ==\"{nameof(GithubFlowEventType.ReadmeChainClosed)}\")", 3)]
-async (IActorProxyFactory proxyFactory, EventEnvelope evt) => await HandleEvent(proxyFactory, nameof(ProductManager), nameof(ProductManager.HandleEvent), evt));
+async (IActorProxyFactory proxyFactory, EventEnvelope evt) => await HandleEvent(proxyFactory, nameof(ProductManager), nameof(ProductManager.HandleEvent), evt));
app.MapPost("/hubbers", [Topic(Consts.PubSub, Consts.MainTopic,
$"(event.type ==\"{nameof(GithubFlowEventType.NewAsk)}\") || (event.type ==\"{nameof(GithubFlowEventType.ReadmeGenerated)}\") || (event.type ==\"{nameof(GithubFlowEventType.DevPlanGenerated)}\") || (event.type ==\"{nameof(GithubFlowEventType.CodeGenerated)}\") || (event.type ==\"{nameof(GithubFlowEventType.DevPlanCreated)}\") || (event.type ==\"{nameof(GithubFlowEventType.ReadmeStored)}\") || (event.type ==\"{nameof(GithubFlowEventType.SandboxRunFinished)}\")", 4)]
- async (IActorProxyFactory proxyFactory, EventEnvelope evt) => await HandleEvent(proxyFactory, nameof(Hubber), nameof(Hubber.HandleEvent), evt));
+async (IActorProxyFactory proxyFactory, EventEnvelope evt) => await HandleEvent(proxyFactory, nameof(Hubber), nameof(Hubber.HandleEvent), evt));
-app.MapPost("/azuregenies", [Topic(Consts.PubSub, Consts.MainTopic,
+app.MapPost("/azuregenies", [Topic(Consts.PubSub, Consts.MainTopic,
$"(event.type ==\"{nameof(GithubFlowEventType.ReadmeCreated)}\") || (event.type ==\"{nameof(GithubFlowEventType.CodeCreated)}\")", 5)]
async (IActorProxyFactory proxyFactory, EventEnvelope evt) => await HandleEvent(proxyFactory, nameof(AzureGenie), nameof(AzureGenie.HandleEvent), evt));
-app.MapPost("/sandboxes", [Topic(Consts.PubSub, Consts.MainTopic,$"(event.type ==\"{nameof(GithubFlowEventType.SandboxRunCreated)}\")", 6)]
+app.MapPost("/sandboxes", [Topic(Consts.PubSub, Consts.MainTopic, $"(event.type ==\"{nameof(GithubFlowEventType.SandboxRunCreated)}\")", 6)]
async (IActorProxyFactory proxyFactory, EventEnvelope evt) => await HandleEvent(proxyFactory, nameof(Sandbox), nameof(Sandbox.HandleEvent), evt));
app.Run();
static async Task HandleEvent(IActorProxyFactory proxyFactory, string type, string method, EventEnvelope evt)
{
- try
+ var proxyOptions = new ActorProxyOptions
{
- var proxyOptions = new ActorProxyOptions
- {
- RequestTimeout = Timeout.InfiniteTimeSpan
- };
- var proxy = proxyFactory.Create(new ActorId(evt.Data.Subject), type, proxyOptions);
- await proxy.InvokeMethodAsync(method, evt.Data);
- }
- catch (Exception ex)
- {
- throw;
- }
+ RequestTimeout = Timeout.InfiniteTimeSpan
+ };
+ var proxy = proxyFactory.Create(new ActorId(evt.Data.Subject), type, proxyOptions);
+ await proxy.InvokeMethodAsync(method, evt.Data);
}
static ISemanticTextMemory CreateMemory(IServiceProvider provider)
{
- var openAiConfig = provider.GetService>().Value;
- var qdrantConfig = provider.GetService>().Value;
+ var openAiConfig = provider.GetRequiredService>().Value;
+ var qdrantConfig = provider.GetRequiredService>().Value;
var loggerFactory = LoggerFactory.Create(builder =>
{
@@ -186,7 +178,7 @@ static ISemanticTextMemory CreateMemory(IServiceProvider provider)
static Kernel CreateKernel(IServiceProvider provider)
{
- var openAiConfig = provider.GetService>().Value;
+ var openAiConfig = provider.GetRequiredService>().Value;
var clientOptions = new OpenAIClientOptions();
clientOptions.Retry.NetworkTimeout = TimeSpan.FromMinutes(5);
var openAIClient = new OpenAIClient(new Uri(openAiConfig.Endpoint), new AzureKeyCredential(openAiConfig.ApiKey), clientOptions);
@@ -202,4 +194,4 @@ static Kernel CreateKernel(IServiceProvider provider)
});
});
return builder.Build();
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/AzureService.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/AzureService.cs
index 5eef10dc2..626fc6600 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/AzureService.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/AzureService.cs
@@ -1,4 +1,4 @@
-using System.Text;
+using System.Text;
using Azure;
using Azure.Core;
using Azure.ResourceManager;
@@ -8,7 +8,6 @@ using Azure.ResourceManager.Resources;
using Azure.Storage.Files.Shares;
using Microsoft.Extensions.Options;
-
namespace Microsoft.AI.DevTeam.Dapr;
public class AzureService : IManageAzure
@@ -19,6 +18,9 @@ public class AzureService : IManageAzure
public AzureService(IOptions azOptions, ILogger logger, ArmClient client)
{
+ ArgumentNullException.ThrowIfNull(azOptions);
+ ArgumentNullException.ThrowIfNull(logger);
+ ArgumentNullException.ThrowIfNull(client);
_azSettings = azOptions.Value;
_logger = logger;
_client = client;
@@ -58,7 +60,7 @@ public class AzureService : IManageAzure
catch (Exception ex)
{
_logger.LogError(ex, "Error checking sandbox status");
- throw;
+ throw;
}
}
@@ -104,13 +106,14 @@ public class AzureService : IManageAzure
catch (Exception ex)
{
_logger.LogError(ex, "Error running sandbox");
- throw;
+ throw;
}
-
+
}
public async Task Store(string org, string repo, long parentIssueNumber, long issueNumber, string filename, string extension, string dir, string output)
{
+ ArgumentNullException.ThrowIfNull(output);
try
{
var connectionString = $"DefaultEndpointsProtocol=https;AccountName={_azSettings.FilesAccountName};AccountKey={_azSettings.FilesAccountKey};EndpointSuffix=core.windows.net";
@@ -134,7 +137,7 @@ public class AzureService : IManageAzure
var file = directory.GetFileClient(fileName);
// hack to enable script to save files in the same directory
var cwdHack = "#!/bin/bash\n cd $(dirname $0)";
- var contents = extension == "sh" ? output.Replace("#!/bin/bash", cwdHack) : output;
+ var contents = extension == "sh" ? output.Replace("#!/bin/bash", cwdHack, StringComparison.InvariantCulture) : output;
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents)))
{
await file.CreateAsync(stream.Length);
@@ -146,7 +149,7 @@ public class AzureService : IManageAzure
catch (Exception ex)
{
_logger.LogError(ex, "Error storing output");
- throw;
+ throw;
}
}
}
@@ -157,4 +160,4 @@ public interface IManageAzure
Task RunInSandbox(string org, string repo, long parentIssueNumber, long issueNumber);
Task IsSandboxCompleted(string sandboxId);
Task DeleteSandbox(string sandboxId);
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/CodeAnalyzer.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/CodeAnalyzer.cs
index 392d44137..399c3b390 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/CodeAnalyzer.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/CodeAnalyzer.cs
@@ -1,12 +1,11 @@
-using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Options;
namespace Microsoft.AI.DevTeam.Dapr;
-public interface IAnalyzeCode
+public interface IAnalyzeCode
{
- Task> Analyze(string content);
+ Task> Analyze(string content);
}
public class CodeAnalyzer : IAnalyzeCode
{
@@ -16,38 +15,41 @@ public class CodeAnalyzer : IAnalyzeCode
public CodeAnalyzer(IOptions serviceOptions, HttpClient httpClient, ILogger logger)
{
+ ArgumentNullException.ThrowIfNull(serviceOptions);
+ ArgumentNullException.ThrowIfNull(httpClient);
+ ArgumentNullException.ThrowIfNull(logger);
+
_serviceOptions = serviceOptions.Value;
_httpClient = httpClient;
_logger = logger;
- _httpClient.BaseAddress = new Uri(_serviceOptions.IngesterUrl);
-
+ _httpClient.BaseAddress = _serviceOptions.IngesterUrl;
+
}
- public async Task> Analyze(string content)
+ public async Task> Analyze(string content)
{
try
{
- var request = new CodeAnalysisRequest { Content = content };
- var body = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json");
- var response = await _httpClient.PostAsync("api/AnalyzeCode", body);
+ var request = new CodeAnalysisRequest { Content = content };
+ var response = await _httpClient.PostAsJsonAsync("api/AnalyzeCode", request);
var stringResult = await response.Content.ReadAsStringAsync();
- var result = JsonSerializer.Deserialize>(stringResult);
- return result;
+ var result = JsonSerializer.Deserialize>(stringResult);
+ return result ?? [];
}
catch (Exception ex)
{
_logger.LogError(ex, "Error analyzing code");
- return Enumerable.Empty();
+ return [];
}
}
}
public class CodeAnalysisRequest
{
- public string Content { get; set; }
+ public required string Content { get; set; }
}
-public class CodeAnalysis
+public class CodeAnalysisResult
{
- public string Meaning { get; set; }
- public string CodeBlock { get; set; }
+ public required string Meaning { get; set; }
+ public required string CodeBlock { get; set; }
}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/GithubAuthService.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/GithubAuthService.cs
index 12168ace2..ea2fe77e3 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/GithubAuthService.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/GithubAuthService.cs
@@ -13,6 +13,8 @@ public class GithubAuthService
public GithubAuthService(IOptions ghOptions, ILogger logger)
{
+ ArgumentNullException.ThrowIfNull(ghOptions);
+ ArgumentNullException.ThrowIfNull(logger);
_githubSettings = ghOptions.Value;
_logger = logger;
}
@@ -43,7 +45,7 @@ public class GithubAuthService
return new JwtSecurityTokenHandler().WriteToken(token);
}
-
+
public GitHubClient GetGitHubClient()
{
try
@@ -62,7 +64,7 @@ public class GithubAuthService
catch (Exception ex)
{
_logger.LogError(ex, "Error getting GitHub client");
- throw;
+ throw;
}
}
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/GithubService.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/GithubService.cs
index 70853fe25..5d85ec83c 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/GithubService.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/GithubService.cs
@@ -4,7 +4,6 @@ using Microsoft.Extensions.Options;
using Octokit;
using Octokit.Helpers;
-
namespace Microsoft.AI.DevTeam.Dapr;
public class GithubService : IManageGithub
@@ -16,6 +15,10 @@ public class GithubService : IManageGithub
public GithubService(IOptions azOptions, GitHubClient ghClient, ILogger logger, HttpClient httpClient)
{
+ ArgumentNullException.ThrowIfNull(azOptions);
+ ArgumentNullException.ThrowIfNull(ghClient);
+ ArgumentNullException.ThrowIfNull(logger);
+ ArgumentNullException.ThrowIfNull(httpClient);
_ghClient = ghClient;
_azSettings = azOptions.Value;
_logger = logger;
@@ -44,12 +47,12 @@ public class GithubService : IManageGithub
try
{
var file = dir.GetFileClient(item.Name);
- var filePath = file.Path.Replace($"{_azSettings.FilesShareName}/", "")
- .Replace($"{dirName}/", "");
+ var filePath = file.Path.Replace($"{_azSettings.FilesShareName}/", "", StringComparison.OrdinalIgnoreCase)
+ .Replace($"{dirName}/", "", StringComparison.OrdinalIgnoreCase);
var fileStream = await file.OpenReadAsync();
using (var reader = new StreamReader(fileStream, Encoding.UTF8))
{
- var value = reader.ReadToEnd();
+ var value = await reader.ReadToEndAsync();
await _ghClient.Repository.Content.CreateFile(
org, repo, filePath,
@@ -58,7 +61,7 @@ public class GithubService : IManageGithub
}
catch (Exception ex)
{
- _logger.LogError(ex, $"Error while uploading file {item.Name}");
+ _logger.LogError(ex, "Error while uploading file '{FileName}'.", item.Name);
}
}
else if (item.IsDirectory)
@@ -164,6 +167,8 @@ public class GithubService : IManageGithub
public async Task> GetFiles(string org, string repo, string branch, Func filter)
{
+ ArgumentNullException.ThrowIfNull(filter);
+
try
{
var items = await _ghClient.Repository.Content.GetAllContentsByRef(org, repo, branch);
@@ -185,7 +190,7 @@ public class GithubService : IManageGithub
{
if (item.Type == ContentType.File && filter(item))
{
- var content = await _httpClient.GetStringAsync(item.DownloadUrl);
+ var content = await _httpClient.GetStringAsync(new Uri(item.DownloadUrl));
result.Add(new FileResponse
{
Name = item.Name,
@@ -210,13 +215,13 @@ public class GithubService : IManageGithub
public class FileResponse
{
- public string Name { get; set; }
- public string Content { get; set; }
+ public required string Name { get; set; }
+ public required string Content { get; set; }
}
public interface IManageGithub
{
- Task CreateIssue(string org, string repo, string input, string function, long parentNumber);
+ Task CreateIssue(string org, string repo, string input, string functionName, long parentNumber);
Task CreatePR(string org, string repo, long number, string branch);
Task CreateBranch(string org, string repo, string branch);
Task CommitToBranch(string org, string repo, long parentNumber, long issueNumber, string rootDir, string branch);
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/GithubWebHookProcessor.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/GithubWebHookProcessor.cs
index 0bff68a51..91edb7709 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/GithubWebHookProcessor.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam.Dapr/Services/GithubWebHookProcessor.cs
@@ -1,3 +1,4 @@
+using System.Globalization;
using Dapr.Client;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.DevTeam.Dapr.Events;
@@ -20,10 +21,13 @@ public sealed class GithubWebHookProcessor : WebhookEventProcessor
}
protected override async Task ProcessIssuesWebhookAsync(WebhookHeaders headers, IssuesEvent issuesEvent, IssuesAction action)
{
+ ArgumentNullException.ThrowIfNull(headers);
+ ArgumentNullException.ThrowIfNull(issuesEvent);
+ ArgumentNullException.ThrowIfNull(action);
try
{
_logger.LogInformation("Processing issue event");
- var org = issuesEvent.Repository.Owner.Login;
+ var org = issuesEvent.Repository!.Owner.Login;
var repo = issuesEvent.Repository.Name;
var issueNumber = issuesEvent.Issue.Number;
var input = issuesEvent.Issue.Body;
@@ -33,14 +37,14 @@ public sealed class GithubWebHookProcessor : WebhookEventProcessor
.Select(l => l.Name.Split('.'))
.Where(parts => parts.Length == 2)
.ToDictionary(parts => parts[0], parts => parts[1]);
- var skillName = labels.Keys.Where(k => k != "Parent").FirstOrDefault();
- long? parentNumber = labels.ContainsKey("Parent") ? long.Parse(labels["Parent"]) : null;
+ var skillName = labels.Keys.First(k => k != "Parent");
+ long? parentNumber = labels.TryGetValue("Parent", out var value) ? long.Parse(value, CultureInfo.InvariantCulture) : null;
var suffix = $"{org}-{repo}";
if (issuesEvent.Action == IssuesAction.Opened)
{
_logger.LogInformation("Processing HandleNewAsk");
- await HandleNewAsk(issueNumber, parentNumber, skillName, labels[skillName], input, org, repo);
+ await HandleNewAsk(issueNumber, parentNumber, skillName, labels[skillName], input!, org, repo);
}
else if (issuesEvent.Action == IssuesAction.Closed && issuesEvent.Issue.User.Type.Value == UserType.Bot)
{
@@ -60,10 +64,13 @@ public sealed class GithubWebHookProcessor : WebhookEventProcessor
IssueCommentEvent issueCommentEvent,
IssueCommentAction action)
{
+ ArgumentNullException.ThrowIfNull(headers);
+ ArgumentNullException.ThrowIfNull(issueCommentEvent);
+ ArgumentNullException.ThrowIfNull(action);
try
{
_logger.LogInformation("Processing issue comment event");
- var org = issueCommentEvent.Repository.Owner.Login;
+ var org = issueCommentEvent.Repository!.Owner.Login;
var repo = issueCommentEvent.Repository.Name;
var issueNumber = issueCommentEvent.Issue.Number;
var input = issueCommentEvent.Comment.Body;
@@ -72,11 +79,12 @@ public sealed class GithubWebHookProcessor : WebhookEventProcessor
.Select(l => l.Name.Split('.'))
.Where(parts => parts.Length == 2)
.ToDictionary(parts => parts[0], parts => parts[1]);
- var skillName = labels.Keys.Where(k => k != "Parent").FirstOrDefault();
- long? parentNumber = labels.ContainsKey("Parent") ? long.Parse(labels["Parent"]) : null;
+ var skillName = labels.Keys.First(k => k != "Parent");
+ long? parentNumber = labels.TryGetValue("Parent", out var value) ? long.Parse(value, CultureInfo.InvariantCulture) : null;
var suffix = $"{org}-{repo}";
+
// we only respond to non-bot comments
- if (issueCommentEvent.Sender.Type.Value != UserType.Bot)
+ if (issueCommentEvent.Sender!.Type.Value != UserType.Bot)
{
await HandleNewAsk(issueNumber, parentNumber, skillName, labels[skillName], input, org, repo);
}
@@ -104,7 +112,7 @@ public sealed class GithubWebHookProcessor : WebhookEventProcessor
{ "org", org },
{ "repo", repo },
{ "issueNumber", issueNumber.ToString() },
- { "parentNumber", parentNumber?.ToString()}
+ { "parentNumber", parentNumber?.ToString(CultureInfo.InvariantCulture) ?? "" }
};
var evt = new Event
@@ -135,7 +143,7 @@ public sealed class GithubWebHookProcessor : WebhookEventProcessor
{ "org", org },
{ "repo", repo },
{ "issueNumber", issueNumber.ToString() },
- { "parentNumber", parentNumber?.ToString()},
+ { "parentNumber", parentNumber ?.ToString(CultureInfo.InvariantCulture) ?? ""},
{ "input" , input}
};
var evt = new Event
@@ -157,7 +165,7 @@ public sealed class GithubWebHookProcessor : WebhookEventProcessor
{
var metadata = new Dictionary() {
{ "cloudevent.Type", evt.Type },
- { "cloudevent.Subject", evt.Subject },
+ { "cloudevent.Subject", evt.Subject ?? "" },
{ "cloudevent.id", Guid.NewGuid().ToString()}
};
@@ -165,5 +173,3 @@ public sealed class GithubWebHookProcessor : WebhookEventProcessor
}
}
-
-
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Architect/Architect.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Architect/Architect.cs
index 5fa20dc5c..ef20de8cb 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Architect/Architect.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Architect/Architect.cs
@@ -6,20 +6,19 @@ using Orleans.Runtime;
namespace Microsoft.AI.DevTeam;
-
// The architect has Org+Repo scope and is holding the knowledge of the high level architecture of the project
[ImplicitStreamSubscription(Consts.MainNamespace)]
public class Architect : AiAgent
{
protected override string Namespace => Consts.MainNamespace;
- public Architect([PersistentState("state", "messages")] IPersistentState> state, ISemanticTextMemory memory, Kernel kernel)
+ public Architect([PersistentState("state", "messages")] IPersistentState> state, ISemanticTextMemory memory, Kernel kernel)
: base(state, memory, kernel)
{
}
public override Task HandleEvent(Event item)
{
- return Task.CompletedTask;
+ return Task.CompletedTask;
}
}
@@ -27,7 +26,8 @@ public class Architect : AiAgent
public class ArchitectState
{
[Id(0)]
- public string FilesTree { get; set; }
+ public string FilesTree { get; set; } = "";
+
[Id(1)]
- public string HighLevelArchitecture { get; set; }
+ public string HighLevelArchitecture { get; set; } = "";
}
\ No newline at end of file
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/AzureGenie.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/AzureGenie.cs
index 4becca3e8..5590af7fd 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/AzureGenie.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/AzureGenie.cs
@@ -1,7 +1,6 @@
-using Microsoft.AI.Agents.Abstractions;
+using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Orleans;
using Microsoft.AI.DevTeam.Events;
-using Microsoft.AI.DevTeam.Extensions;
namespace Microsoft.AI.DevTeam;
@@ -26,32 +25,31 @@ public class AzureGenie : Agent
switch (item.Type)
{
case nameof(GithubFlowEventType.ReadmeCreated):
- {
- var context = item.ToGithubContext();
- await Store(context.Org,context.Repo, context.ParentNumber.Value, context.IssueNumber, "readme", "md", "output", item.Data["readme"]);
- await PublishEvent(Consts.MainNamespace, this.GetPrimaryKeyString(), new Event
{
- Type = nameof(GithubFlowEventType.ReadmeStored),
- Subject = context.Subject,
- Data = context.ToData()
- });
- break;
- }
-
-
+ var context = item.ToGithubContext();
+ await Store(context.Org, context.Repo, context.ParentNumber ?? 0, context.IssueNumber, "readme", "md", "output", item.Data["readme"]);
+ await PublishEvent(Consts.MainNamespace, this.GetPrimaryKeyString(), new Event
+ {
+ Type = nameof(GithubFlowEventType.ReadmeStored),
+ Subject = context.Subject,
+ Data = context.ToData()
+ });
+ break;
+ }
+
case nameof(GithubFlowEventType.CodeCreated):
- {
- var context = item.ToGithubContext();
- await Store(context.Org,context.Repo, context.ParentNumber.Value, context.IssueNumber, "run", "sh", "output", item.Data["code"]);
- await RunInSandbox(context.Org,context.Repo, context.ParentNumber.Value, context.IssueNumber);
- await PublishEvent(Consts.MainNamespace, this.GetPrimaryKeyString(), new Event
{
- Type = nameof(GithubFlowEventType.SandboxRunCreated),
- Subject = context.Subject,
- Data = context.ToData()
- });
- break;
- }
+ var context = item.ToGithubContext();
+ await Store(context.Org, context.Repo, context.ParentNumber ?? 0, context.IssueNumber, "run", "sh", "output", item.Data["code"]);
+ await RunInSandbox(context.Org, context.Repo, context.ParentNumber ?? 0, context.IssueNumber);
+ await PublishEvent(Consts.MainNamespace, this.GetPrimaryKeyString(), new Event
+ {
+ Type = nameof(GithubFlowEventType.SandboxRunCreated),
+ Subject = context.Subject,
+ Data = context.ToData()
+ });
+ break;
+ }
default:
break;
@@ -67,4 +65,4 @@ public class AzureGenie : Agent
{
await _azureService.RunInSandbox(org, repo, parentIssueNumber, issueNumber);
}
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Developer/Developer.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Developer/Developer.cs
index 40254e054..86d63560d 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Developer/Developer.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Developer/Developer.cs
@@ -20,8 +20,9 @@ public class Dev : AiAgent, IDevelopApps
_logger = logger;
}
- public async override Task HandleEvent(Event item)
+ public override async Task HandleEvent(Event item)
{
+ ArgumentNullException.ThrowIfNull(item);
switch (item.Type)
{
case nameof(GithubFlowEventType.CodeGenerationRequested):
@@ -72,7 +73,7 @@ public class Dev : AiAgent, IDevelopApps
catch (Exception ex)
{
_logger.LogError(ex, "Error generating code");
- return default;
+ return "";
}
}
}
@@ -81,7 +82,7 @@ public class Dev : AiAgent, IDevelopApps
public class DeveloperState
{
[Id(0)]
- public string Understanding { get; set; }
+ public string? Understanding { get; set; }
}
public interface IDevelopApps
@@ -93,7 +94,7 @@ public interface IDevelopApps
public class UnderstandingResult
{
[Id(0)]
- public string NewUnderstanding { get; set; }
+ public required string NewUnderstanding { get; set; }
[Id(1)]
- public string Explanation { get; set; }
-}
\ No newline at end of file
+ public required string Explanation { get; set; }
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Developer/DeveloperPrompts.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Developer/DeveloperPrompts.cs
index d9d666020..8e23f0fd8 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Developer/DeveloperPrompts.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Developer/DeveloperPrompts.cs
@@ -1,7 +1,8 @@
namespace Microsoft.AI.DevTeam;
-public static class DeveloperSkills {
- public static string Implement = """
+public static class DeveloperSkills
+{
+ public const string Implement = """
You are a Developer for an application.
Please output the code required to accomplish the task assigned to you below and wrap it in a bash script that creates the files.
Do not use any IDE commands and do not build and run the code.
@@ -11,7 +12,7 @@ public static class DeveloperSkills {
{{$waf}}
""";
- public static string Improve = """
+ public const string Improve = """
You are a Developer for an application. Your job is to imrove the code that you are given in the input below.
Please output a new version of code that fixes any problems with this version.
If there is an error message in the input you should fix that error in the code.
@@ -23,7 +24,7 @@ public static class DeveloperSkills {
{{$waf}}
""";
- public static string Explain = """
+ public const string Explain = """
You are an experienced software developer, with strong experience in Azure and Microsoft technologies.
Extract the key features and capabilities of the code file below, with the intent to build an understanding of an entire code repository.
You can include references or documentation links in your explanation. Also where appropriate please output a list of keywords to describe the code or its capabilities.
@@ -39,7 +40,7 @@ public static class DeveloperSkills {
Error: The model could not determine the purpose of the code.
""";
- public static string ConsolidateUnderstanding = """
+ public const string ConsolidateUnderstanding = """
You are an experienced software developer, with strong experience in Azure and Microsoft technologies.
You are trying to build an understanding of the codebase from code files. This is the current understanding of the project:
===current-understanding===
@@ -53,4 +54,4 @@ public static class DeveloperSkills {
Only include the points in a bullet point format and DON'T add anything outside of the bulleted list.
Be short and concise.
""";
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/DeveloperLead/DevLeadPrompts.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/DeveloperLead/DevLeadPrompts.cs
index 2c20812a5..aff57b4ac 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/DeveloperLead/DevLeadPrompts.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/DeveloperLead/DevLeadPrompts.cs
@@ -1,11 +1,12 @@
namespace Microsoft.AI.DevTeam;
-public static class DevLeadSkills {
- public static string Plan = """
+public static class DevLeadSkills
+{
+ public const string Plan = """
You are a Dev Lead for an application team, building the application described below.
Please break down the steps and modules required to develop the complete application, describe each step in detail.
- Make prescriptive architecture, language, and frameowrk choices, do not provide a range of choices.
+ Make prescriptive architecture, language, and framework choices, do not provide a range of choices.
For each step or module then break down the steps or subtasks required to complete that step or module.
- For each subtask write an LLM prompt that would be used to tell a model to write the coee that will accomplish that subtask. If the subtask involves taking action/running commands tell the model to write the script that will run those commands.
+ For each subtask write an LLM prompt that would be used to tell a model to write the code that will accomplish that subtask. If the subtask involves taking action/running commands tell the model to write the script that will run those commands.
In each LLM prompt restrict the model from outputting other text that is not in the form of code or code comments.
Please output a JSON array data structure, in the precise schema shown below, with a list of steps and a description of each step, and the steps or subtasks that each requires, and the LLM prompts for each subtask.
Example:
@@ -35,7 +36,7 @@ public static class DevLeadSkills {
{{$waf}}
""";
- public static string Explain = """
+ public const string Explain = """
You are a Dev Lead.
Please explain the code that is in the input below. You can include references or documentation links in your explanation.
Also where appropriate please output a list of keywords to describe the code or its capabilities.
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/DeveloperLead/DeveloperLead.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/DeveloperLead/DeveloperLead.cs
index bb87db69c..c70737d4f 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/DeveloperLead/DeveloperLead.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/DeveloperLead/DeveloperLead.cs
@@ -19,8 +19,10 @@ public class DeveloperLead : AiAgent, ILeadDevelopers
_logger = logger;
}
- public async override Task HandleEvent(Event item)
+ public override async Task HandleEvent(Event item)
{
+ ArgumentNullException.ThrowIfNull(item);
+
switch (item.Type)
{
case nameof(GithubFlowEventType.DevPlanRequested):
@@ -37,7 +39,7 @@ public class DeveloperLead : AiAgent, ILeadDevelopers
});
}
- break;
+ break;
case nameof(GithubFlowEventType.DevPlanChainClosed):
{
var context = item.ToGithubContext();
@@ -66,18 +68,19 @@ public class DeveloperLead : AiAgent, ILeadDevelopers
var context = new KernelArguments { ["input"] = AppendChatHistory(ask) };
var instruction = "Consider the following architectural guidelines:!waf!";
var enhancedContext = await AddKnowledge(instruction, "waf", context);
- var settings = new OpenAIPromptExecutionSettings{
- ResponseFormat = "json_object",
- MaxTokens = 4096,
- Temperature = 0.8,
- TopP = 1
+ var settings = new OpenAIPromptExecutionSettings
+ {
+ ResponseFormat = "json_object",
+ MaxTokens = 4096,
+ Temperature = 0.8,
+ TopP = 1
};
return await CallFunction(DevLeadSkills.Plan, enhancedContext, settings);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating development plan");
- return default;
+ return "";
}
}
}
@@ -91,30 +94,31 @@ public interface ILeadDevelopers
public class DevLeadPlanResponse
{
[Id(0)]
- public List steps { get; set; }
+ public required List Steps { get; set; }
}
[GenerateSerializer]
-public class Step
+public class StepDescription
{
[Id(0)]
- public string description { get; set; }
+ public string? Description { get; set; }
[Id(1)]
- public string step { get; set; }
+ public string? Step { get; set; }
[Id(2)]
- public List subtasks { get; set; }
+ public List? Subtasks { get; set; }
}
[GenerateSerializer]
-public class Subtask
+public class SubtaskDescription
{
[Id(0)]
- public string subtask { get; set; }
+ public string? Subtask { get; set; }
+
[Id(1)]
- public string prompt { get; set; }
+ public string? Prompt { get; set; }
}
public class DeveloperLeadState
{
- public string Plan { get; set; }
-}
\ No newline at end of file
+ public string? Plan { get; set; }
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Hubber.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Hubber.cs
index 4737c8aee..b71c5e30e 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Hubber.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Hubber.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Text.Json;
+using System.Text.Json;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Orleans;
using Microsoft.AI.DevTeam.Events;
@@ -9,7 +8,7 @@ namespace Microsoft.AI.DevTeam;
[ImplicitStreamSubscription(Consts.MainNamespace)]
public class Hubber : Agent
{
- protected override string Namespace => Consts.MainNamespace;
+ protected override string Namespace => Consts.MainNamespace;
private readonly IManageGithub _ghService;
public Hubber(IManageGithub ghService)
@@ -27,33 +26,33 @@ public class Hubber : Agent
case nameof(GithubFlowEventType.NewAsk):
{
var context = item.ToGithubContext();
- var pmIssue = await CreateIssue(context.Org, context.Repo , item.Data["input"], "PM.Readme", context.IssueNumber);
- var devLeadIssue = await CreateIssue(context.Org, context.Repo , item.Data["input"], "DevLead.Plan", context.IssueNumber);
+ var pmIssue = await CreateIssue(context.Org, context.Repo, item.Data["input"], "PM.Readme", context.IssueNumber);
+ var devLeadIssue = await CreateIssue(context.Org, context.Repo, item.Data["input"], "DevLead.Plan", context.IssueNumber);
await PostComment(context.Org, context.Repo, context.IssueNumber, $" - #{pmIssue} - tracks PM.Readme");
- await PostComment(context.Org, context.Repo, context.IssueNumber, $" - #{devLeadIssue} - tracks DevLead.Plan");
+ await PostComment(context.Org, context.Repo, context.IssueNumber, $" - #{devLeadIssue} - tracks DevLead.Plan");
await CreateBranch(context.Org, context.Repo, $"sk-{context.IssueNumber}");
}
break;
case nameof(GithubFlowEventType.ReadmeGenerated):
case nameof(GithubFlowEventType.DevPlanGenerated):
case nameof(GithubFlowEventType.CodeGenerated):
- {
- var context = item.ToGithubContext();
- var result = item.Data["result"];
- var contents = string.IsNullOrEmpty(result)? "Sorry, I got tired, can you try again please? ": result;
- await PostComment(context.Org,context.Repo, context.IssueNumber, contents);
- }
+ {
+ var context = item.ToGithubContext();
+ var result = item.Data["result"];
+ var contents = string.IsNullOrEmpty(result) ? "Sorry, I got tired, can you try again please? " : result;
+ await PostComment(context.Org, context.Repo, context.IssueNumber, contents);
+ }
break;
case nameof(GithubFlowEventType.DevPlanCreated):
{
var context = item.ToGithubContext();
var plan = JsonSerializer.Deserialize(item.Data["plan"]);
- var prompts = plan.steps.SelectMany(s => s.subtasks.Select(st => st.prompt));
-
+ var prompts = plan!.Steps.SelectMany(s => s.Subtasks!.Select(st => st.Prompt));
+
foreach (var prompt in prompts)
{
var functionName = "Developer.Implement";
- var issue = await CreateIssue(context.Org, context.Repo, prompt, functionName, context.ParentNumber.Value);
+ var issue = await CreateIssue(context.Org, context.Repo, prompt!, functionName, context.ParentNumber!.Value);
var commentBody = $" - #{issue} - tracks {functionName}";
await PostComment(context.Org, context.Repo, context.ParentNumber.Value, commentBody);
}
@@ -63,7 +62,7 @@ public class Hubber : Agent
{
var context = item.ToGithubContext();
var branch = $"sk-{context.ParentNumber}";
- await CommitToBranch(context.Org, context.Repo, context.ParentNumber.Value, context.IssueNumber, "output", branch);
+ await CommitToBranch(context.Org, context.Repo, context.ParentNumber!.Value, context.IssueNumber, "output", branch);
await CreatePullRequest(context.Org, context.Repo, context.ParentNumber.Value, branch);
}
break;
@@ -71,7 +70,7 @@ public class Hubber : Agent
{
var context = item.ToGithubContext();
var branch = $"sk-{context.ParentNumber}";
- await CommitToBranch(context.Org, context.Repo, context.ParentNumber.Value, context.IssueNumber, "output", branch);
+ await CommitToBranch(context.Org, context.Repo, context.ParentNumber!.Value, context.IssueNumber, "output", branch);
}
break;
default:
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/ProductManager/PMPrompts.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/ProductManager/PMPrompts.cs
index 7683010e4..e575a0b07 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/ProductManager/PMPrompts.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/ProductManager/PMPrompts.cs
@@ -1,7 +1,7 @@
namespace Microsoft.AI.DevTeam;
public static class PMSkills
{
-public static string BootstrapProject = """
+ public const string BootstrapProject = """
Please write a bash script with the commands that would be required to generate applications as described in the following input.
You may add comments to the script and the generated output but do not add any other text except the bash script.
You may include commands to build the applications but do not run them.
@@ -9,7 +9,7 @@ public static string BootstrapProject = """
Input: {{$input}}
{{$waf}}
""";
- public static string Readme = """
+ public const string Readme = """
You are a program manager on a software development team. You are working on an app described below.
Based on the input below, and any dialog or other context, please output a raw README.MD markdown file documenting the main features of the app and the architecture or code organization.
Do not describe how to create the application.
@@ -18,7 +18,7 @@ public static string BootstrapProject = """
{{$waf}}
""";
- public static string Explain = """
+ public const string Explain = """
You are a Product Manager.
Please explain the code that is in the input below. You can include references or documentation links in your explanation.
Also where appropriate please output a list of keywords to describe the code or its capabilities.
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/ProductManager/ProductManager.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/ProductManager/ProductManager.cs
index 3ffbc8a59..54df8699a 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/ProductManager/ProductManager.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/ProductManager/ProductManager.cs
@@ -13,43 +13,46 @@ public class ProductManager : AiAgent, IManageProducts
protected override string Namespace => Consts.MainNamespace;
private readonly ILogger _logger;
- public ProductManager([PersistentState("state", "messages")] IPersistentState> state, Kernel kernel, ISemanticTextMemory memory, ILogger logger)
+ public ProductManager([PersistentState("state", "messages")] IPersistentState> state, Kernel kernel, ISemanticTextMemory memory, ILogger logger)
: base(state, memory, kernel)
{
_logger = logger;
}
- public async override Task HandleEvent(Event item)
+ public override async Task HandleEvent(Event item)
{
+ ArgumentNullException.ThrowIfNull(item);
switch (item.Type)
{
case nameof(GithubFlowEventType.ReadmeRequested):
- {
- var context = item.ToGithubContext();
- var readme = await CreateReadme(item.Data["input"]);
- var data = context.ToData();
- data["result"]=readme;
- await PublishEvent(Consts.MainNamespace, this.GetPrimaryKeyString(), new Event {
- Type = nameof(GithubFlowEventType.ReadmeGenerated),
- Subject = context.Subject,
- Data = data
- });
- }
-
+ {
+ var context = item.ToGithubContext();
+ var readme = await CreateReadme(item.Data["input"]);
+ var data = context.ToData();
+ data["result"] = readme;
+ await PublishEvent(Consts.MainNamespace, this.GetPrimaryKeyString(), new Event
+ {
+ Type = nameof(GithubFlowEventType.ReadmeGenerated),
+ Subject = context.Subject,
+ Data = data
+ });
+ }
+
break;
case nameof(GithubFlowEventType.ReadmeChainClosed):
- {
- var context = item.ToGithubContext();
- var lastReadme = _state.State.History.Last().Message;
- var data = context.ToData();
- data["readme"] = lastReadme;
- await PublishEvent(Consts.MainNamespace, this.GetPrimaryKeyString(), new Event {
- Type = nameof(GithubFlowEventType.ReadmeCreated),
- Subject = context.Subject,
- Data = data
- });
- }
-
+ {
+ var context = item.ToGithubContext();
+ var lastReadme = _state.State.History.Last().Message;
+ var data = context.ToData();
+ data["readme"] = lastReadme;
+ await PublishEvent(Consts.MainNamespace, this.GetPrimaryKeyString(), new Event
+ {
+ Type = nameof(GithubFlowEventType.ReadmeCreated),
+ Subject = context.Subject,
+ Data = data
+ });
+ }
+
break;
default:
break;
@@ -60,15 +63,15 @@ public class ProductManager : AiAgent, IManageProducts
{
try
{
- var context = new KernelArguments { ["input"] = AppendChatHistory(ask)};
+ var context = new KernelArguments { ["input"] = AppendChatHistory(ask) };
var instruction = "Consider the following architectural guidelines:!waf!";
- var enhancedContext = await AddKnowledge(instruction, "waf",context);
+ var enhancedContext = await AddKnowledge(instruction, "waf", context);
return await CallFunction(PMSkills.Readme, enhancedContext);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating readme");
- return default;
+ return "";
}
}
}
@@ -82,5 +85,5 @@ public interface IManageProducts
public class ProductManagerState
{
[Id(0)]
- public string Capabilities { get; set; }
-}
\ No newline at end of file
+ public string? Capabilities { get; set; }
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Sandbox.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Sandbox.cs
index 4802c3baf..d9fefaeb4 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Sandbox.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Agents/Sandbox.cs
@@ -1,4 +1,4 @@
-using Microsoft.AI.Agents.Abstractions;
+using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Orleans;
using Microsoft.AI.DevTeam.Events;
using Orleans.Runtime;
@@ -6,16 +6,15 @@ using Orleans.Timers;
namespace Microsoft.AI.DevTeam;
[ImplicitStreamSubscription(Consts.MainNamespace)]
-public class Sandbox : Agent, IRemindable
+public sealed class Sandbox : Agent, IRemindable
{
protected override string Namespace => Consts.MainNamespace;
private const string ReminderName = "SandboxRunReminder";
private readonly IManageAzure _azService;
private readonly IReminderRegistry _reminderRegistry;
+ private readonly IPersistentState _state;
private IGrainReminder? _reminder;
- protected readonly IPersistentState _state;
-
public Sandbox([PersistentState("state", "messages")] IPersistentState state,
IReminderRegistry reminderRegistry, IManageAzure azService)
{
@@ -25,12 +24,14 @@ public class Sandbox : Agent, IRemindable
}
public override async Task HandleEvent(Event item)
{
+ ArgumentNullException.ThrowIfNull(item);
+
switch (item.Type)
{
case nameof(GithubFlowEventType.SandboxRunCreated):
{
var context = item.ToGithubContext();
- await ScheduleCommitSandboxRun(context.Org, context.Repo, context.ParentNumber.Value, context.IssueNumber);
+ await ScheduleCommitSandboxRun(context.Org, context.Repo, context.ParentNumber!.Value, context.IssueNumber);
break;
}
@@ -52,7 +53,7 @@ public class Sandbox : Agent, IRemindable
{
if (!_state.State.IsCompleted)
{
- var sandboxId = $"sk-sandbox-{_state.State.Org}-{_state.State.Repo}-{_state.State.ParentIssueNumber}-{_state.State.IssueNumber}".ToLower();
+ var sandboxId = $"sk-sandbox-{_state.State.Org}-{_state.State.Repo}-{_state.State.ParentIssueNumber}-{_state.State.IssueNumber}".ToUpperInvariant();
if (await _azService.IsSandboxCompleted(sandboxId))
{
@@ -94,15 +95,13 @@ public class Sandbox : Agent, IRemindable
await _state.WriteStateAsync();
}
-
}
-
public class SandboxMetadata
{
- public string Org { get; set; }
- public string Repo { get; set; }
+ public string Org { get; set; } = default!;
+ public string Repo { get; set; } = default!;
public long ParentIssueNumber { get; set; }
public long IssueNumber { get; set; }
public bool IsCompleted { get; set; }
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Events/GithubFlowEventType.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Events/GithubFlowEventType.cs
index 3aec523bc..de3d2746a 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Events/GithubFlowEventType.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Events/GithubFlowEventType.cs
@@ -2,65 +2,63 @@ using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.DevTeam.Extensions;
using System.Globalization;
-namespace Microsoft.AI.DevTeam.Events
+namespace Microsoft.AI.DevTeam.Events;
+
+public enum GithubFlowEventType
{
- public enum GithubFlowEventType
+ NewAsk,
+ ReadmeChainClosed,
+ CodeChainClosed,
+ CodeGenerationRequested,
+ DevPlanRequested,
+ ReadmeGenerated,
+ DevPlanGenerated,
+ CodeGenerated,
+ DevPlanChainClosed,
+ ReadmeRequested,
+ ReadmeStored,
+ SandboxRunFinished,
+ ReadmeCreated,
+ CodeCreated,
+ DevPlanCreated,
+ SandboxRunCreated
+}
+
+public static class EventExtensions
+{
+ public static GithubContext ToGithubContext(this Event evt)
{
- NewAsk,
- ReadmeChainClosed,
- CodeChainClosed,
- CodeGenerationRequested,
- DevPlanRequested,
- ReadmeGenerated,
- DevPlanGenerated,
- CodeGenerated,
- DevPlanChainClosed,
- ReadmeRequested,
- ReadmeStored,
- SandboxRunFinished,
- ReadmeCreated,
- CodeCreated,
- DevPlanCreated,
- SandboxRunCreated
+ ArgumentNullException.ThrowIfNull(evt);
+
+ return new GithubContext
+ {
+ Org = evt.Data["org"],
+ Repo = evt.Data["repo"],
+ IssueNumber = evt.Data.TryParseLong("issueNumber"),
+ ParentNumber = evt.Data.TryParseLong("parentNumber")
+ };
}
- public static class EventExtensions
+ public static Dictionary ToData(this GithubContext context)
{
- public static GithubContext ToGithubContext(this Event evt)
- {
- ArgumentNullException.ThrowIfNull(evt);
+ ArgumentNullException.ThrowIfNull(context);
- return new GithubContext
- {
- Org = evt.Data["org"],
- Repo = evt.Data["repo"],
- IssueNumber = evt.Data.TryParseLong("issueNumber"),
- ParentNumber = evt.Data.TryParseLong("parentNumber")
- };
- }
-
- public static Dictionary ToData(this GithubContext context)
- {
- ArgumentNullException.ThrowIfNull(context);
-
- return new Dictionary {
- { "org", context.Org },
- { "repo", context.Repo },
- { "issueNumber", $"{context.IssueNumber}" },
- { "parentNumber", context.ParentNumber.HasValue ? Convert.ToString(context.ParentNumber, CultureInfo.InvariantCulture) : string.Empty }
- };
- }
- }
-
- public class GithubContext
- {
- public string Org { get; set; }
- public string Repo { get; set; }
- public long IssueNumber { get; set; }
- public long? ParentNumber { get; set; }
-
- public string Subject => $"{Org}/{Repo}/{IssueNumber}";
+ return new Dictionary {
+ { "org", context.Org },
+ { "repo", context.Repo },
+ { "issueNumber", $"{context.IssueNumber}" },
+ { "parentNumber", context.ParentNumber?.ToString(CultureInfo.InvariantCulture) ?? "" }
+ };
}
}
+public class GithubContext
+{
+ public required string Org { get; set; }
+ public required string Repo { get; set; }
+ public long IssueNumber { get; set; }
+ public long? ParentNumber { get; set; }
+
+ public string Subject => $"{Org}/{Repo}/{IssueNumber}";
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Extensions/ParseExtensions.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Extensions/ParseExtensions.cs
index 8c618c1a4..af35ecb2f 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Extensions/ParseExtensions.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Extensions/ParseExtensions.cs
@@ -1,16 +1,15 @@
-namespace Microsoft.AI.DevTeam.Extensions
-{
- public static class ParseExtensions
- {
- public static long TryParseLong(this Dictionary data, string key)
- {
- ArgumentNullException.ThrowIfNull(data);
+namespace Microsoft.AI.DevTeam.Extensions;
- if (data.TryGetValue(key, out string? value) && !string.IsNullOrEmpty(value) && long.TryParse(value, out var result))
- {
- return result;
- }
- return default;
+public static class ParseExtensions
+{
+ public static long TryParseLong(this Dictionary data, string key)
+ {
+ ArgumentNullException.ThrowIfNull(data);
+
+ if (data.TryGetValue(key, out string? value) && !string.IsNullOrEmpty(value) && long.TryParse(value, out var result))
+ {
+ return result;
}
+ return default;
}
}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/AzureOptions.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/AzureOptions.cs
index 4f29ed8b7..45343507c 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/AzureOptions.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/AzureOptions.cs
@@ -5,19 +5,17 @@ namespace Microsoft.AI.DevTeam;
public class AzureOptions
{
[Required]
- public string SubscriptionId { get; set; }
+ public required string SubscriptionId { get; set; }
[Required]
- public string Location { get; set; }
+ public required string Location { get; set; }
[Required]
- public string ContainerInstancesResourceGroup { get; set; }
+ public required string ContainerInstancesResourceGroup { get; set; }
[Required]
- public string FilesShareName { get; set; }
+ public required string FilesShareName { get; set; }
[Required]
- public string FilesAccountName { get; set; }
+ public required string FilesAccountName { get; set; }
[Required]
- public string FilesAccountKey { get; set; }
+ public required string FilesAccountKey { get; set; }
[Required]
- public string SandboxImage { get; set; }
- public string ManagedIdentity { get; set; }
- public string CosmosConnectionString { get; set; }
-}
\ No newline at end of file
+ public required string SandboxImage { get; set; }
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/GithubOptions.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/GithubOptions.cs
index c13fc1e67..ddfd18d4f 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/GithubOptions.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/GithubOptions.cs
@@ -4,11 +4,11 @@ namespace Microsoft.AI.DevTeam;
public class GithubOptions
{
[Required]
- public string AppKey { get; set; }
+ public required string AppKey { get; set; }
[Required]
public int AppId { get; set; }
[Required]
public long InstallationId { get; set; }
[Required]
- public string WebhookSecret { get; set; }
-}
\ No newline at end of file
+ public required string WebhookSecret { get; set; }
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/OpenAIOptions.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/OpenAIOptions.cs
index 1b251c1fb..d7eb01913 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/OpenAIOptions.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/OpenAIOptions.cs
@@ -4,15 +4,15 @@ namespace Microsoft.AI.DevTeam;
public class OpenAIOptions
{
[Required]
- public string ServiceType { get; set; }
+ public required string ServiceType { get; set; }
[Required]
- public string ServiceId { get; set; }
+ public required string ServiceId { get; set; }
[Required]
- public string DeploymentOrModelId { get; set; }
+ public required string DeploymentOrModelId { get; set; }
[Required]
- public string EmbeddingDeploymentOrModelId { get; set; }
+ public required string EmbeddingDeploymentOrModelId { get; set; }
[Required]
- public string Endpoint { get; set; }
+ public required string Endpoint { get; set; }
[Required]
- public string ApiKey { get; set; }
+ public required string ApiKey { get; set; }
}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/QdrantOptions.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/QdrantOptions.cs
index da292ce62..ada9397e8 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/QdrantOptions.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/QdrantOptions.cs
@@ -4,7 +4,7 @@ namespace Microsoft.AI.DevTeam;
public class QdrantOptions
{
[Required]
- public string Endpoint { get; set; }
+ public required string Endpoint { get; set; }
[Required]
public int VectorSize { get; set; }
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/ServiceOptions.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/ServiceOptions.cs
index 33228424e..f49df7cbf 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/ServiceOptions.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Options/ServiceOptions.cs
@@ -3,8 +3,6 @@ using System.ComponentModel.DataAnnotations;
namespace Microsoft.AI.DevTeam;
public class ServiceOptions
{
- private string _ingesterUrl;
-
[Required]
- public string IngesterUrl { get; set; }
+ public required Uri IngesterUrl { get; set; }
}
\ No newline at end of file
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Program.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Program.cs
index 00e9ae586..445339052 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Program.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Program.cs
@@ -21,11 +21,10 @@ builder.Services.AddTransient(CreateKernel);
builder.Services.AddTransient(CreateMemory);
builder.Services.AddHttpClient();
-
builder.Services.AddTransient(s =>
{
- var ghOptions = s.GetService>();
- var logger = s.GetService>();
+ var ghOptions = s.GetRequiredService>();
+ var logger = s.GetRequiredService>();
var ghService = new GithubAuthService(ghOptions, logger);
var client = ghService.GetGitHubClient();
return client;
@@ -94,14 +93,19 @@ builder.Host.UseOrleans(siloBuilder =>
.AddMemoryGrainStorage("PubSubStore")
.AddMemoryGrainStorage("messages");
- siloBuilder.UseInMemoryReminderService();
- siloBuilder.UseDashboard(x => x.HostSelf = true);
+ siloBuilder.UseInMemoryReminderService();
+ siloBuilder.UseDashboard(x => x.HostSelf = true);
siloBuilder.UseInMemoryReminderService();
}
else
{
var cosmosDbconnectionString = builder.Configuration.GetValue("AzureOptions:CosmosConnectionString");
+ if (cosmosDbconnectionString is null)
+ {
+ throw new ArgumentException($"Set AzureOptions:CosmosConnectionString in configuration.");
+ }
+
siloBuilder.Configure(options =>
{
options.ClusterId = "ai-dev-cluster";
@@ -140,11 +144,11 @@ builder.Host.UseOrleans(siloBuilder =>
o.DatabaseName = "persistence";
o.IsResourceCreationEnabled = true;
});
- //TODO: replace with EventHub
- siloBuilder
- .AddMemoryStreams("StreamProvider")
- .AddMemoryGrainStorage("PubSubStore");
- }
+ //TODO: replace with EventHub
+ siloBuilder
+ .AddMemoryStreams("StreamProvider")
+ .AddMemoryGrainStorage("PubSubStore");
+ }
});
builder.Services.Configure(options =>
@@ -156,7 +160,7 @@ var app = builder.Build();
app.UseRouting()
.UseEndpoints(endpoints =>
{
- var ghOptions = app.Services.GetService>().Value;
+ var ghOptions = app.Services.GetRequiredService>().Value;
endpoints.MapGitHubWebhooks(secret: ghOptions.WebhookSecret);
});
@@ -166,8 +170,8 @@ app.Run();
static ISemanticTextMemory CreateMemory(IServiceProvider provider)
{
- var openAiConfig = provider.GetService>().Value;
- var qdrantConfig = provider.GetService>().Value;
+ var openAiConfig = provider.GetRequiredService>().Value;
+ var qdrantConfig = provider.GetRequiredService>().Value;
var loggerFactory = LoggerFactory.Create(builder =>
{
@@ -186,7 +190,7 @@ static ISemanticTextMemory CreateMemory(IServiceProvider provider)
static Kernel CreateKernel(IServiceProvider provider)
{
- var openAiConfig = provider.GetService>().Value;
+ var openAiConfig = provider.GetRequiredService>().Value;
var clientOptions = new OpenAIClientOptions();
clientOptions.Retry.NetworkTimeout = TimeSpan.FromMinutes(5);
var openAIClient = new OpenAIClient(new Uri(openAiConfig.Endpoint), new AzureKeyCredential(openAiConfig.ApiKey), clientOptions);
@@ -202,4 +206,4 @@ static Kernel CreateKernel(IServiceProvider provider)
});
});
return builder.Build();
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/AzureService.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/AzureService.cs
index 4ebead697..011d2495f 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/AzureService.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/AzureService.cs
@@ -1,4 +1,4 @@
-using System.Text;
+using System.Text;
using Azure;
using Azure.Core;
using Azure.ResourceManager;
@@ -8,7 +8,6 @@ using Azure.ResourceManager.Resources;
using Azure.Storage.Files.Shares;
using Microsoft.Extensions.Options;
-
namespace Microsoft.AI.DevTeam;
public class AzureService : IManageAzure
@@ -19,6 +18,9 @@ public class AzureService : IManageAzure
public AzureService(IOptions azOptions, ILogger logger, ArmClient client)
{
+ ArgumentNullException.ThrowIfNull(azOptions);
+ ArgumentNullException.ThrowIfNull(logger);
+ ArgumentNullException.ThrowIfNull(client);
_azSettings = azOptions.Value;
_logger = logger;
_client = client;
@@ -58,7 +60,7 @@ public class AzureService : IManageAzure
catch (Exception ex)
{
_logger.LogError(ex, "Error checking sandbox status");
- throw;
+ throw;
}
}
@@ -66,14 +68,14 @@ public class AzureService : IManageAzure
{
try
{
- var runId = $"sk-sandbox-{org}-{repo}-{parentIssueNumber}-{issueNumber}".ToLower();
+ var runId = $"sk-sandbox-{org}-{repo}-{parentIssueNumber}-{issueNumber}".ToUpperInvariant();
var resourceGroupResourceId = ResourceGroupResource.CreateResourceIdentifier(_azSettings.SubscriptionId, _azSettings.ContainerInstancesResourceGroup);
var resourceGroupResource = _client.GetResourceGroupResource(resourceGroupResourceId);
var scriptPath = $"/azfiles/output/{org}-{repo}/{parentIssueNumber}/{issueNumber}/run.sh";
var collection = resourceGroupResource.GetContainerGroups();
var data = new ContainerGroupData(new AzureLocation(_azSettings.Location), new ContainerInstanceContainer[]
{
- new ContainerInstanceContainer(runId,_azSettings.SandboxImage,new ContainerResourceRequirements(new ContainerResourceRequestsContent(1.5,1)))
+ new ContainerInstanceContainer(runId, _azSettings.SandboxImage,new ContainerResourceRequirements(new ContainerResourceRequestsContent(1.5,1)))
{
Command = { "/bin/bash", $"{scriptPath}" },
VolumeMounts =
@@ -104,13 +106,15 @@ public class AzureService : IManageAzure
catch (Exception ex)
{
_logger.LogError(ex, "Error running sandbox");
- throw;
+ throw;
}
-
+
}
public async Task Store(string org, string repo, long parentIssueNumber, long issueNumber, string filename, string extension, string dir, string output)
{
+ ArgumentNullException.ThrowIfNull(output);
+
try
{
var connectionString = $"DefaultEndpointsProtocol=https;AccountName={_azSettings.FilesAccountName};AccountKey={_azSettings.FilesAccountKey};EndpointSuffix=core.windows.net";
@@ -134,7 +138,7 @@ public class AzureService : IManageAzure
var file = directory.GetFileClient(fileName);
// hack to enable script to save files in the same directory
var cwdHack = "#!/bin/bash\n cd $(dirname $0)";
- var contents = extension == "sh" ? output.Replace("#!/bin/bash", cwdHack) : output;
+ var contents = extension == "sh" ? output.Replace("#!/bin/bash", cwdHack, StringComparison.Ordinal) : output;
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents)))
{
await file.CreateAsync(stream.Length);
@@ -146,7 +150,7 @@ public class AzureService : IManageAzure
catch (Exception ex)
{
_logger.LogError(ex, "Error storing output");
- throw;
+ throw;
}
}
}
@@ -157,4 +161,4 @@ public interface IManageAzure
Task RunInSandbox(string org, string repo, long parentIssueNumber, long issueNumber);
Task IsSandboxCompleted(string sandboxId);
Task DeleteSandbox(string sandboxId);
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/CodeAnalyzer.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/CodeAnalyzer.cs
index a56e9f5e5..03205ffaa 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/CodeAnalyzer.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/CodeAnalyzer.cs
@@ -1,12 +1,11 @@
-using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Options;
namespace Microsoft.AI.DevTeam;
-public interface IAnalyzeCode
+public interface IAnalyzeCode
{
- Task> Analyze(string content);
+ Task> Analyze(string content);
}
public class CodeAnalyzer : IAnalyzeCode
{
@@ -16,38 +15,40 @@ public class CodeAnalyzer : IAnalyzeCode
public CodeAnalyzer(IOptions serviceOptions, HttpClient httpClient, ILogger logger)
{
+ ArgumentNullException.ThrowIfNull(serviceOptions);
+ ArgumentNullException.ThrowIfNull(httpClient);
+ ArgumentNullException.ThrowIfNull(logger);
_serviceOptions = serviceOptions.Value;
_httpClient = httpClient;
_logger = logger;
- _httpClient.BaseAddress = new Uri(_serviceOptions.IngesterUrl);
-
+ _httpClient.BaseAddress = _serviceOptions.IngesterUrl;
+
}
- public async Task> Analyze(string content)
+ public async Task> Analyze(string content)
{
try
{
- var request = new CodeAnalysisRequest { Content = content };
- var body = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json");
- var response = await _httpClient.PostAsync("api/AnalyzeCode", body);
+ var request = new CodeAnalysisRequest { Content = content };
+ var response = await _httpClient.PatchAsJsonAsync("api/AnalyzeCode", request);
var stringResult = await response.Content.ReadAsStringAsync();
- var result = JsonSerializer.Deserialize>(stringResult);
- return result;
+ var result = JsonSerializer.Deserialize>(stringResult);
+ return result ?? [];
}
catch (Exception ex)
{
_logger.LogError(ex, "Error analyzing code");
- return Enumerable.Empty();
+ return [];
}
}
}
public class CodeAnalysisRequest
{
- public string Content { get; set; }
+ public required string Content { get; set; }
}
-public class CodeAnalysis
+public class CodeAnalysisResult
{
- public string Meaning { get; set; }
- public string CodeBlock { get; set; }
+ public required string Meaning { get; set; }
+ public required string CodeBlock { get; set; }
}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/GithubAuthService.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/GithubAuthService.cs
index 0c964d9db..756197563 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/GithubAuthService.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/GithubAuthService.cs
@@ -13,6 +13,8 @@ public class GithubAuthService
public GithubAuthService(IOptions ghOptions, ILogger logger)
{
+ ArgumentNullException.ThrowIfNull(ghOptions);
+ ArgumentNullException.ThrowIfNull(logger);
_githubSettings = ghOptions.Value;
_logger = logger;
}
@@ -43,7 +45,7 @@ public class GithubAuthService
return new JwtSecurityTokenHandler().WriteToken(token);
}
-
+
public GitHubClient GetGitHubClient()
{
try
@@ -62,7 +64,7 @@ public class GithubAuthService
catch (Exception ex)
{
_logger.LogError(ex, "Error getting GitHub client");
- throw;
+ throw;
}
}
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/GithubService.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/GithubService.cs
index 7fba64809..07d50e22b 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/GithubService.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/GithubService.cs
@@ -4,7 +4,6 @@ using Microsoft.Extensions.Options;
using Octokit;
using Octokit.Helpers;
-
namespace Microsoft.AI.DevTeam;
public class GithubService : IManageGithub
@@ -16,6 +15,11 @@ public class GithubService : IManageGithub
public GithubService(IOptions azOptions, GitHubClient ghClient, ILogger logger, HttpClient httpClient)
{
+ ArgumentNullException.ThrowIfNull(azOptions);
+ ArgumentNullException.ThrowIfNull(ghClient);
+ ArgumentNullException.ThrowIfNull(logger);
+ ArgumentNullException.ThrowIfNull(httpClient);
+
_ghClient = ghClient;
_azSettings = azOptions.Value;
_logger = logger;
@@ -44,18 +48,18 @@ public class GithubService : IManageGithub
try
{
var file = dir.GetFileClient(item.Name);
- var filePath = file.Path.Replace($"{_azSettings.FilesShareName}/", "")
- .Replace($"{dirName}/", "");
+ var filePath = file.Path.Replace($"{_azSettings.FilesShareName}/", "", StringComparison.OrdinalIgnoreCase)
+ .Replace($"{dirName}/", "", StringComparison.OrdinalIgnoreCase);
var fileStream = await file.OpenReadAsync();
using (var reader = new StreamReader(fileStream, Encoding.UTF8))
{
- var value = reader.ReadToEnd();
+ var value = await reader.ReadToEndAsync();
try
{
// Check if the file exists
var existingFiles = await _ghClient.Repository.Content.GetAllContentsByRef(org, repo, filePath, branch);
- var existingFile = existingFiles.First();
+ var existingFile = existingFiles[0];
// If the file exists, update it
var updateChangeSet = await _ghClient.Repository.Content.UpdateFile(
org, repo, filePath,
@@ -70,13 +74,13 @@ public class GithubService : IManageGithub
}
catch (Exception ex)
{
- _logger.LogError(ex, $"Error while uploading file {item.Name}");
+ _logger.LogError(ex, "Error while uploading file '{FileName}'.", item.Name);
}
}
}
catch (Exception ex)
{
- _logger.LogError(ex, $"Error while uploading file {item.Name}");
+ _logger.LogError(ex, "Error while uploading file '{FileName}'.", item.Name);
}
}
else if (item.IsDirectory)
@@ -182,6 +186,8 @@ public class GithubService : IManageGithub
public async Task> GetFiles(string org, string repo, string branch, Func filter)
{
+ ArgumentNullException.ThrowIfNull(filter);
+
try
{
var items = await _ghClient.Repository.Content.GetAllContentsByRef(org, repo, branch);
@@ -203,7 +209,7 @@ public class GithubService : IManageGithub
{
if (item.Type == ContentType.File && filter(item))
{
- var content = await _httpClient.GetStringAsync(item.DownloadUrl);
+ var content = await _httpClient.GetStringAsync(new Uri(item.DownloadUrl));
result.Add(new FileResponse
{
Name = item.Name,
@@ -228,13 +234,13 @@ public class GithubService : IManageGithub
public class FileResponse
{
- public string Name { get; set; }
- public string Content { get; set; }
+ public required string Name { get; set; }
+ public required string Content { get; set; }
}
public interface IManageGithub
{
- Task CreateIssue(string org, string repo, string input, string function, long parentNumber);
+ Task CreateIssue(string org, string repo, string input, string functionName, long parentNumber);
Task CreatePR(string org, string repo, long number, string branch);
Task CreateBranch(string org, string repo, string branch);
Task CommitToBranch(string org, string repo, long parentNumber, long issueNumber, string rootDir, string branch);
diff --git a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/GithubWebHookProcessor.cs b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/GithubWebHookProcessor.cs
index 4cda39249..20e544089 100644
--- a/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/GithubWebHookProcessor.cs
+++ b/dotnet/samples/gh-flow/src/Microsoft.AI.DevTeam/Services/GithubWebHookProcessor.cs
@@ -1,3 +1,4 @@
+using System.Globalization;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.DevTeam.Events;
using Octokit.Webhooks;
@@ -78,10 +79,14 @@ public sealed class GithubWebHookProcessor : WebhookEventProcessor
IssueCommentEvent issueCommentEvent,
IssueCommentAction action)
{
+ ArgumentNullException.ThrowIfNull(headers);
+ ArgumentNullException.ThrowIfNull(issueCommentEvent);
+ ArgumentNullException.ThrowIfNull(action);
+
try
{
_logger.LogInformation("Processing issue comment event");
- var org = issueCommentEvent.Repository.Owner.Login;
+ var org = issueCommentEvent.Repository!.Owner.Login;
var repo = issueCommentEvent.Repository.Name;
var issueNumber = issueCommentEvent.Issue.Number;
var input = issueCommentEvent.Comment.Body;
@@ -90,11 +95,12 @@ public sealed class GithubWebHookProcessor : WebhookEventProcessor
.Select(l => l.Name.Split('.'))
.Where(parts => parts.Length == 2)
.ToDictionary(parts => parts[0], parts => parts[1]);
- var skillName = labels.Keys.Where(k => k != "Parent").FirstOrDefault();
- long? parentNumber = labels.ContainsKey("Parent") ? long.Parse(labels["Parent"]) : null;
+ var skillName = labels.Keys.First(k => k != "Parent");
+ long? parentNumber = labels.TryGetValue("Parent", out var value) ? long.Parse(value, CultureInfo.InvariantCulture) : null;
var suffix = $"{org}-{repo}";
+
// we only respond to non-bot comments
- if (issueCommentEvent.Sender.Type.Value != UserType.Bot)
+ if (issueCommentEvent.Sender!.Type.Value != UserType.Bot)
{
await HandleNewAsk(issueNumber, parentNumber, skillName, labels[skillName], suffix, input, org, repo);
}
@@ -109,7 +115,7 @@ public sealed class GithubWebHookProcessor : WebhookEventProcessor
private async Task HandleClosingIssue(long issueNumber, long? parentNumber, string skillName, string functionName, string suffix, string org, string repo)
{
- var subject = suffix+issueNumber.ToString();
+ var subject = suffix + issueNumber.ToString();
var streamProvider = _client.GetStreamProvider("StreamProvider");
var streamId = StreamId.Create(Consts.MainNamespace, subject);
var stream = streamProvider.GetStream(streamId);
@@ -125,7 +131,7 @@ public sealed class GithubWebHookProcessor : WebhookEventProcessor
{ "org", org },
{ "repo", repo },
{ "issueNumber", issueNumber.ToString() },
- { "parentNumber", parentNumber?.ToString()}
+ { "parentNumber", (parentNumber ?? 0).ToString()}
};
await stream.OnNextAsync(new Event
@@ -141,7 +147,7 @@ public sealed class GithubWebHookProcessor : WebhookEventProcessor
try
{
_logger.LogInformation("Handling new ask");
- var subject = suffix+issueNumber.ToString();
+ var subject = suffix + issueNumber.ToString();
var streamProvider = _client.GetStreamProvider("StreamProvider");
var streamId = StreamId.Create(Consts.MainNamespace, subject);
var stream = streamProvider.GetStream(streamId);
@@ -159,7 +165,7 @@ public sealed class GithubWebHookProcessor : WebhookEventProcessor
{ "org", org },
{ "repo", repo },
{ "issueNumber", issueNumber.ToString() },
- { "parentNumber", parentNumber?.ToString()},
+ { "parentNumber", (parentNumber ?? 0).ToString()},
{ "input", input}
};
diff --git a/dotnet/samples/gh-flow/src/seed-memory/Program.cs b/dotnet/samples/gh-flow/src/seed-memory/Program.cs
index 1a557aaaf..8db131bd2 100644
--- a/dotnet/samples/gh-flow/src/seed-memory/Program.cs
+++ b/dotnet/samples/gh-flow/src/seed-memory/Program.cs
@@ -6,12 +6,12 @@ using Microsoft.SemanticKernel.Memory;
using UglyToad.PdfPig;
using UglyToad.PdfPig.DocumentLayoutAnalysis.TextExtractor;
-class Program
+public sealed class Program
{
- static string WafFileName = "azure-well-architected.pdf";
- static async Task Main(string[] args)
+ private const string WafFileName = "azure-well-architected.pdf";
+ static async Task Main()
{
- var kernelSettings = KernelSettings.LoadSettings();
+ var kernelSettings = KernelSettings.LoadSettings();
using ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
{
@@ -20,38 +20,44 @@ class Program
.AddConsole()
.AddDebug();
});
-
+
var memoryBuilder = new MemoryBuilder();
var memory = memoryBuilder.WithLoggerFactory(loggerFactory)
.WithQdrantMemoryStore(kernelSettings.QdrantEndpoint, 1536)
- .WithAzureOpenAITextEmbeddingGeneration(kernelSettings.EmbeddingDeploymentOrModelId,kernelSettings.Endpoint, kernelSettings.ApiKey)
+ .WithAzureOpenAITextEmbeddingGeneration(kernelSettings.EmbeddingDeploymentOrModelId, kernelSettings.Endpoint, kernelSettings.ApiKey)
.Build();
- await ImportDocumentAsync(memory, WafFileName);
+ await ImportDocumentAsync(memory, WafFileName).ConfigureAwait(false);
}
public static async Task ImportDocumentAsync(ISemanticTextMemory memory, string filename)
+ {
+ var asm = Assembly.GetExecutingAssembly();
+ var currentDirectory = Path.GetDirectoryName(asm.Location);
+ if (currentDirectory is null)
{
- var currentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
- var filePath = Path.Combine(currentDirectory, filename);
- using var pdfDocument = PdfDocument.Open(File.OpenRead(filePath));
- var pages = pdfDocument.GetPages();
- foreach (var page in pages)
+ throw new DirectoryNotFoundException($"Could not find directory for assembly '{asm}'.");
+ }
+
+ var filePath = Path.Combine(currentDirectory, filename);
+ using var pdfDocument = PdfDocument.Open(File.OpenRead(filePath));
+ var pages = pdfDocument.GetPages();
+ foreach (var page in pages)
+ {
+ try
{
- try
- {
- var text = ContentOrderTextExtractor.GetText(page);
- var descr = text.Take(100);
- await memory.SaveInformationAsync(
- collection: "waf",
- text: text,
- id: $"{Guid.NewGuid()}",
- description: $"Document: {descr}");
- }
- catch(Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
+ var text = ContentOrderTextExtractor.GetText(page);
+ var descr = text.Take(100);
+ await memory.SaveInformationAsync(
+ collection: "waf",
+ text: text,
+ id: $"{Guid.NewGuid()}",
+ description: $"Document: {descr}").ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex.Message);
}
}
+ }
}
\ No newline at end of file
diff --git a/dotnet/samples/gh-flow/src/seed-memory/config/KernelSettings.cs b/dotnet/samples/gh-flow/src/seed-memory/config/KernelSettings.cs
index 18f956f50..0fcdd20d9 100644
--- a/dotnet/samples/gh-flow/src/seed-memory/config/KernelSettings.cs
+++ b/dotnet/samples/gh-flow/src/seed-memory/config/KernelSettings.cs
@@ -1,8 +1,8 @@
-using System.Text.Json.Serialization;
+using System.Text.Json.Serialization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
-internal class KernelSettings
+internal sealed class KernelSettings
{
public const string DefaultConfigFile = "config/appsettings.json";
public const string OpenAI = "OPENAI";
diff --git a/dotnet/samples/marketing/src/backend/Agents/CommunityManager/CommunityManager.cs b/dotnet/samples/marketing/src/backend/Agents/CommunityManager/CommunityManager.cs
index cd2416bf1..583ed330a 100644
--- a/dotnet/samples/marketing/src/backend/Agents/CommunityManager/CommunityManager.cs
+++ b/dotnet/samples/marketing/src/backend/Agents/CommunityManager/CommunityManager.cs
@@ -15,19 +15,24 @@ public class CommunityManager : AiAgent
private readonly ILogger _logger;
- public CommunityManager([PersistentState("state", "messages")] IPersistentState> state, Kernel kernel, ISemanticTextMemory memory, ILogger logger)
+ public CommunityManager([PersistentState("state", "messages")] IPersistentState> state, Kernel kernel, ISemanticTextMemory memory, ILogger logger)
: base(state, memory, kernel)
{
_logger = logger;
}
- public async override Task HandleEvent(Event item)
+ public override async Task HandleEvent(Event item)
{
+ if (item?.Type is null)
+ {
+ throw new ArgumentNullException(nameof(item));
+ }
+
switch (item.Type)
{
case nameof(EventTypes.UserConnected):
// The user reconnected, let's send the last message if we have one
- string lastMessage = _state.State.History.LastOrDefault()?.Message;
+ var lastMessage = _state.State.History.LastOrDefault()?.Message;
if (lastMessage == null)
{
return;
@@ -36,18 +41,18 @@ public class CommunityManager : AiAgent
await SendDesignedCreatedEvent(lastMessage, item.Data["UserId"]);
break;
- case nameof(EventTypes.ArticleCreated):
- {
- var article = item.Data["article"];
+ case nameof(EventTypes.ArticleCreated):
+ {
+ var article = item.Data["article"];
- _logger.LogInformation($"[{nameof(GraphicDesigner)}] Event {nameof(EventTypes.ArticleCreated)}. Article: {article}");
-
- var context = new KernelArguments { ["input"] = AppendChatHistory(article) };
- string socialMediaPost = await CallFunction(CommunityManagerPrompts.WritePost, context);
- _state.State.Data.WrittenSocialMediaPost = socialMediaPost;
- await SendDesignedCreatedEvent(socialMediaPost, item.Data["UserId"]);
- break;
- }
+ _logger.LogInformation($"[{nameof(GraphicDesigner)}] Event {nameof(EventTypes.ArticleCreated)}. Article: {{Article}}", article);
+
+ var context = new KernelArguments { ["input"] = AppendChatHistory(article) };
+ string socialMediaPost = await CallFunction(CommunityManagerPrompts.WritePost, context);
+ _state.State.Data.WrittenSocialMediaPost = socialMediaPost;
+ await SendDesignedCreatedEvent(socialMediaPost, item.Data["UserId"]);
+ break;
+ }
default:
break;
}
@@ -65,7 +70,7 @@ public class CommunityManager : AiAgent
});
}
- public Task GetArticle()
+ public Task GetArticle()
{
return Task.FromResult(_state.State.Data.WrittenSocialMediaPost);
}
diff --git a/dotnet/samples/marketing/src/backend/Agents/CommunityManager/CommunityManagerPrompts.cs b/dotnet/samples/marketing/src/backend/Agents/CommunityManager/CommunityManagerPrompts.cs
index 74365103a..1101dc739 100644
--- a/dotnet/samples/marketing/src/backend/Agents/CommunityManager/CommunityManagerPrompts.cs
+++ b/dotnet/samples/marketing/src/backend/Agents/CommunityManager/CommunityManagerPrompts.cs
@@ -2,10 +2,10 @@ namespace Marketing.Agents;
public static class CommunityManagerPrompts
{
- public static string WritePost = """
+ public const string WritePost = """
You are a Marketing community manager writer.
Write a tweet to promote what it is described bellow.
The tweet cannot be longer than 280 characters
Input: {{$input}}
""";
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/marketing/src/backend/Agents/CommunityManager/CommunityManagerState.cs b/dotnet/samples/marketing/src/backend/Agents/CommunityManager/CommunityManagerState.cs
index baed2a973..4eeb7ff9d 100644
--- a/dotnet/samples/marketing/src/backend/Agents/CommunityManager/CommunityManagerState.cs
+++ b/dotnet/samples/marketing/src/backend/Agents/CommunityManager/CommunityManagerState.cs
@@ -4,5 +4,5 @@
public class CommunityManagerState
{
[Id(0)]
- public string WrittenSocialMediaPost { get; set; }
+ public string WrittenSocialMediaPost { get; set; } = "";
}
\ No newline at end of file
diff --git a/dotnet/samples/marketing/src/backend/Agents/GraphicDesigner/GraphicDesignedState.cs b/dotnet/samples/marketing/src/backend/Agents/GraphicDesigner/GraphicDesignedState.cs
index 3add06873..63894c51e 100644
--- a/dotnet/samples/marketing/src/backend/Agents/GraphicDesigner/GraphicDesignedState.cs
+++ b/dotnet/samples/marketing/src/backend/Agents/GraphicDesigner/GraphicDesignedState.cs
@@ -4,5 +4,5 @@
public class GraphicDesignerState
{
[Id(0)]
- public string imageUrl { get; set; }
+ public string ImageUrl { get; set; } = "";
}
\ No newline at end of file
diff --git a/dotnet/samples/marketing/src/backend/Agents/GraphicDesigner/GraphicDesigner.cs b/dotnet/samples/marketing/src/backend/Agents/GraphicDesigner/GraphicDesigner.cs
index 8830f6d26..21e3b6235 100644
--- a/dotnet/samples/marketing/src/backend/Agents/GraphicDesigner/GraphicDesigner.cs
+++ b/dotnet/samples/marketing/src/backend/Agents/GraphicDesigner/GraphicDesigner.cs
@@ -24,15 +24,15 @@ public class GraphicDesigner : AiAgent
_configuration = configuration;
}
- public async override Task HandleEvent(Event item)
+ public override async Task HandleEvent(Event item)
{
- string lastMessage;
+ ArgumentNullException.ThrowIfNull(item);
switch (item.Type)
{
case nameof(EventTypes.UserConnected):
// The user reconnected, let's send the last message if we have one
- lastMessage = _state.State.History.LastOrDefault()?.Message;
+ var lastMessage = _state.State.History.LastOrDefault()?.Message;
if (lastMessage == null)
{
return;
@@ -48,7 +48,7 @@ public class GraphicDesigner : AiAgent
var dallEService = _kernel.GetRequiredService();
var imageUri = await dallEService.GenerateImageAsync(article, 1024, 1024);
- _state.State.Data.imageUrl = imageUri;
+ _state.State.Data.ImageUrl = imageUri;
await SendDesignedCreatedEvent(imageUri, item.Data["UserId"]);
@@ -70,4 +70,4 @@ public class GraphicDesigner : AiAgent
}
});
}
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/marketing/src/backend/Agents/GraphicDesigner/GraphicDesignerPrompts.cs b/dotnet/samples/marketing/src/backend/Agents/GraphicDesigner/GraphicDesignerPrompts.cs
index d17cf5f72..89cca6ac3 100644
--- a/dotnet/samples/marketing/src/backend/Agents/GraphicDesigner/GraphicDesignerPrompts.cs
+++ b/dotnet/samples/marketing/src/backend/Agents/GraphicDesigner/GraphicDesignerPrompts.cs
@@ -2,10 +2,10 @@
namespace Marketing.Agents;
public static class GraphicDesignerPrompts
{
- public static string GenerateImage = """
+ public const string GenerateImage = """
You are a Marketing community manager graphic designer.
- Bellow is a campaing that you need to create a image for.
- Create an image of maximum 500x500 pixels that could be use in social medias as a marketing iamge.
+ Bellow is a campaign that you need to create a image for.
+ Create an image of maximum 500x500 pixels that could be use in social medias as a marketing image.
Input: {{$input}}
""";
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/marketing/src/backend/Agents/SignalR/SignalR.cs b/dotnet/samples/marketing/src/backend/Agents/SignalR/SignalRAgent.cs
similarity index 68%
rename from dotnet/samples/marketing/src/backend/Agents/SignalR/SignalR.cs
rename to dotnet/samples/marketing/src/backend/Agents/SignalR/SignalRAgent.cs
index 8d4ee4ea9..e75fc2af0 100644
--- a/dotnet/samples/marketing/src/backend/Agents/SignalR/SignalR.cs
+++ b/dotnet/samples/marketing/src/backend/Agents/SignalR/SignalRAgent.cs
@@ -3,41 +3,41 @@ using Marketing.Options;
using Marketing.SignalRHub;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Orleans;
-using System;
-using System.Security.Policy;
namespace Marketing.Agents;
[ImplicitStreamSubscription(Consts.OrleansNamespace)]
-public class SignalR : Agent
+public class SignalRAgent : Agent
{
protected override string Namespace => Consts.OrleansNamespace;
-
- private readonly ILogger _logger;
+
+ private readonly ILogger _logger;
private readonly ISignalRService _signalRClient;
- public SignalR(ILogger logger, ISignalRService signalRClient)
+ public SignalRAgent(ILogger logger, ISignalRService signalRClient)
{
_logger = logger;
_signalRClient = signalRClient;
}
- public async override Task HandleEvent(Event item)
+ public override async Task HandleEvent(Event item)
{
+ ArgumentNullException.ThrowIfNull(item);
+
switch (item.Type)
{
case nameof(EventTypes.ArticleCreated):
- var writenArticle = item.Data["article"];
- await _signalRClient.SendMessageToSpecificClient(item.Data["UserId"], writenArticle, AgentTypes.Chat);
+ var writtenArticle = item.Data["article"];
+ await _signalRClient.SendMessageToSpecificClient(item.Data["UserId"], writtenArticle, AgentTypes.Chat);
break;
case nameof(EventTypes.GraphicDesignCreated):
- var imageUrl = item.Data["imageUri"];
+ var imageUrl = item.Data["imageUri"];
await _signalRClient.SendMessageToSpecificClient(item.Data["UserId"], imageUrl, AgentTypes.GraphicDesigner);
break;
case nameof(EventTypes.SocialMediaPostCreated):
- var post = item.Data["socialMediaPost"];
+ var post = item.Data["socialMediaPost"];
await _signalRClient.SendMessageToSpecificClient(item.Data["UserId"], post, AgentTypes.CommunityManager);
break;
diff --git a/dotnet/samples/marketing/src/backend/Agents/Writer/IWritter.cs b/dotnet/samples/marketing/src/backend/Agents/Writer/IWritter.cs
index 63174839a..38d1b72fb 100644
--- a/dotnet/samples/marketing/src/backend/Agents/Writer/IWritter.cs
+++ b/dotnet/samples/marketing/src/backend/Agents/Writer/IWritter.cs
@@ -1,5 +1,5 @@
namespace Marketing.Agents;
public interface IWriter : IGrainWithStringKey
{
- Task GetArticle();
+ Task GetArticle();
}
\ No newline at end of file
diff --git a/dotnet/samples/marketing/src/backend/Agents/Writer/Writer.cs b/dotnet/samples/marketing/src/backend/Agents/Writer/Writer.cs
index ba13b1515..844261326 100644
--- a/dotnet/samples/marketing/src/backend/Agents/Writer/Writer.cs
+++ b/dotnet/samples/marketing/src/backend/Agents/Writer/Writer.cs
@@ -12,22 +12,23 @@ namespace Marketing.Agents;
public class Writer : AiAgent, IWriter
{
protected override string Namespace => Consts.OrleansNamespace;
-
+
private readonly ILogger _logger;
- public Writer([PersistentState("state", "messages")] IPersistentState> state, Kernel kernel, ISemanticTextMemory memory, ILogger logger)
+ public Writer([PersistentState("state", "messages")] IPersistentState> state, Kernel kernel, ISemanticTextMemory memory, ILogger logger)
: base(state, memory, kernel)
{
_logger = logger;
}
- public async override Task HandleEvent(Event item)
+ public override async Task HandleEvent(Event item)
{
+ ArgumentNullException.ThrowIfNull(item);
switch (item.Type)
{
case nameof(EventTypes.UserConnected):
// The user reconnected, let's send the last message if we have one
- string lastMessage = _state.State.History.LastOrDefault()?.Message;
+ var lastMessage = _state.State.History.LastOrDefault()?.Message;
if (lastMessage == null)
{
return;
@@ -37,18 +38,18 @@ public class Writer : AiAgent, IWriter
break;
- case nameof(EventTypes.UserChatInput):
+ case nameof(EventTypes.UserChatInput):
{
- var userMessage = item.Data["userMessage"];
- _logger.LogInformation($"[{nameof(GraphicDesigner)}] Event {nameof(EventTypes.UserChatInput)}. UserMessage: {userMessage}");
-
+ var userMessage = item.Data["userMessage"];
+ _logger.LogInformation($"[{nameof(GraphicDesigner)}] Event {nameof(EventTypes.UserChatInput)}. UserMessage: {{UserMessage}}", userMessage);
+
var context = new KernelArguments { ["input"] = AppendChatHistory(userMessage) };
string newArticle = await CallFunction(WriterPrompts.Write, context);
await SendDesignedCreatedEvent(newArticle, item.Data["UserId"]);
- break;
+ break;
}
-
+
default:
break;
}
@@ -66,9 +67,8 @@ public class Writer : AiAgent, IWriter
});
}
-
- public Task GetArticle()
+ public Task GetArticle()
{
- return Task.FromResult(_state.State.Data.WrittenArticle);
+ return Task.FromResult(_state.State.Data.WrittenArticle!);
}
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/marketing/src/backend/Agents/Writer/WriterPrompts.cs b/dotnet/samples/marketing/src/backend/Agents/Writer/WriterPrompts.cs
index a3c54c936..30250c15e 100644
--- a/dotnet/samples/marketing/src/backend/Agents/Writer/WriterPrompts.cs
+++ b/dotnet/samples/marketing/src/backend/Agents/Writer/WriterPrompts.cs
@@ -2,11 +2,11 @@
namespace Marketing.Agents;
public static class WriterPrompts
{
- public static string Write = """
+ public const string Write = """
You are a Marketing writer.
- Write up to three paragraphs for a campain to promote what it is described bellow.
- Bellow are a series of inputs from the user that you can use to create the campain.
+ Write up to three paragraphs for a campaign to promote what it is described bellow.
+ Bellow are a series of inputs from the user that you can use to create the campaign.
If the input talks about twitter or images, dismiss it and return the same as before.
Input: {{$input}}
""";
-}
\ No newline at end of file
+}
diff --git a/dotnet/samples/marketing/src/backend/Agents/Writer/WriterState.cs b/dotnet/samples/marketing/src/backend/Agents/Writer/WriterState.cs
index 33f77d53b..fc0a81865 100644
--- a/dotnet/samples/marketing/src/backend/Agents/Writer/WriterState.cs
+++ b/dotnet/samples/marketing/src/backend/Agents/Writer/WriterState.cs
@@ -1,8 +1,8 @@
-namespace Marketing.Agents;
+namespace Marketing.Agents;
[GenerateSerializer]
public class WriterState
{
[Id(0)]
- public string WrittenArticle { get; set; }
-}
\ No newline at end of file
+ public string? WrittenArticle { get; set; }
+}
diff --git a/dotnet/samples/marketing/src/backend/Controller/Articles.cs b/dotnet/samples/marketing/src/backend/Controller/Articles.cs
index 78cd09fc3..4c4e8664c 100644
--- a/dotnet/samples/marketing/src/backend/Controller/Articles.cs
+++ b/dotnet/samples/marketing/src/backend/Controller/Articles.cs
@@ -7,57 +7,56 @@ using Orleans.Runtime;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
-namespace Marketing.Controller
-{
+namespace Marketing.Controller;
- [GenerateSerializer]
- public class Asd
+[GenerateSerializer]
+public class Asd
+{
+ [Id(0)]
+ public required string Name { get; set; }
+}
+
+[Route("api/[controller]")]
+[ApiController]
+public class Articles : ControllerBase
+{
+ private readonly IClusterClient _client;
+
+ public Articles(IClusterClient client)
{
- [Id(0)]
- public string Name { get; set; }
+ _client = client;
}
- [Route("api/[controller]")]
- [ApiController]
- public class Articles : ControllerBase
+ // GET api//5
+ [HttpGet("{id}")]
+ public async Task Get(string id)
{
- private readonly IClusterClient _client;
+ var grain = _client.GetGrain(id);
+ string article = await grain.GetArticle();
+ return article;
+ }
- public Articles(IClusterClient client)
+ // PUT api//5
+ [HttpPut("{UserId}")]
+ public async Task Put(string UserId, [FromBody] string userMessage)
+ {
+ ArgumentNullException.ThrowIfNull(UserId);
+ var streamProvider = _client.GetStreamProvider("StreamProvider");
+ var streamId = StreamId.Create(Consts.OrleansNamespace, UserId);
+ var stream = streamProvider.GetStream(streamId);
+
+ var data = new Dictionary
{
- _client = client;
- }
+ { nameof(UserId), UserId.ToString() },
+ { nameof(userMessage), userMessage },
+ };
- // GET api//5
- [HttpGet("{id}")]
- public async Task Get(string id)
+ await stream.OnNextAsync(new Event
{
- var grain = _client.GetGrain(id);
- string article = await grain.GetArticle();
- return article;
- }
+ Type = nameof(EventTypes.UserChatInput),
+ Data = data
+ });
- // PUT api//5
- [HttpPut("{UserId}")]
- public async Task Put(string UserId, [FromBody] string userMessage)
- {
- var streamProvider = _client.GetStreamProvider("StreamProvider");
- var streamId = StreamId.Create(Consts.OrleansNamespace, UserId);
- var stream = streamProvider.GetStream(streamId);
-
- var data = new Dictionary
- {
- { nameof(UserId), UserId.ToString() },
- { nameof(userMessage), userMessage },
- };
-
- await stream.OnNextAsync(new Event
- {
- Type = nameof(EventTypes.UserChatInput),
- Data = data
- });
-
- return $"Task {UserId} accepted";
- }
+ return $"Task {UserId} accepted";
}
}
diff --git a/dotnet/samples/marketing/src/backend/Options/OpenAIOptions.cs b/dotnet/samples/marketing/src/backend/Options/OpenAIOptions.cs
index 1c336855e..a88f03277 100644
--- a/dotnet/samples/marketing/src/backend/Options/OpenAIOptions.cs
+++ b/dotnet/samples/marketing/src/backend/Options/OpenAIOptions.cs
@@ -6,25 +6,25 @@ public class OpenAIOptions
{
// Embeddings
[Required]
- public string EmbeddingsEndpoint { get; set; }
+ public required string EmbeddingsEndpoint { get; set; }
[Required]
- public string EmbeddingsApiKey { get; set; }
+ public required string EmbeddingsApiKey { get; set; }
[Required]
- public string EmbeddingsDeploymentOrModelId { get; set; }
+ public required string EmbeddingsDeploymentOrModelId { get; set; }
// Chat
[Required]
- public string ChatEndpoint { get; set; }
+ public required string ChatEndpoint { get; set; }
[Required]
- public string ChatApiKey { get; set; }
+ public required string ChatApiKey { get; set; }
[Required]
- public string ChatDeploymentOrModelId { get; set; }
+ public required string ChatDeploymentOrModelId { get; set; }
// TextToImage
[Required]
- public string ImageEndpoint { get; set; }
+ public required string ImageEndpoint { get; set; }
[Required]
- public string ImageApiKey { get; set; }
+ public required string ImageApiKey { get; set; }
// When using OpenAI, this is not required.
- public string ImageDeploymentOrModelId { get; set; }
+ public required string ImageDeploymentOrModelId { get; set; }
}
\ No newline at end of file
diff --git a/dotnet/samples/marketing/src/backend/Options/QdrantOptions.cs b/dotnet/samples/marketing/src/backend/Options/QdrantOptions.cs
index ac1549875..73d94bc9d 100644
--- a/dotnet/samples/marketing/src/backend/Options/QdrantOptions.cs
+++ b/dotnet/samples/marketing/src/backend/Options/QdrantOptions.cs
@@ -4,7 +4,7 @@ namespace Marketing.Options;
public class QdrantOptions
{
[Required]
- public string Endpoint { get; set; }
+ public required string Endpoint { get; set; }
[Required]
- public int VectorSize { get; set; }
-}
\ No newline at end of file
+ public required int VectorSize { get; set; }
+}
diff --git a/dotnet/samples/marketing/src/backend/Options/Throw.cs b/dotnet/samples/marketing/src/backend/Options/Throw.cs
deleted file mode 100644
index ff6d672c8..000000000
--- a/dotnet/samples/marketing/src/backend/Options/Throw.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace Marketing
-{
- public static class Throw
- {
- public static void IfNullOrEmpty(string paramName, string value)
- {
- if (string.IsNullOrEmpty(value))
- {
- throw new ArgumentNullException(paramName);
- }
- }
- }
-}
diff --git a/dotnet/samples/marketing/src/backend/Program.cs b/dotnet/samples/marketing/src/backend/Program.cs
index 2bba0d21a..ff9060d31 100644
--- a/dotnet/samples/marketing/src/backend/Program.cs
+++ b/dotnet/samples/marketing/src/backend/Program.cs
@@ -9,7 +9,6 @@ using Microsoft.SemanticKernel.Connectors.Qdrant;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Marketing.SignalRHub;
using Marketing.Options;
-using Marketing;
using Orleans.Serialization;
var builder = WebApplication.CreateBuilder(args);
@@ -22,20 +21,20 @@ builder.Services.AddSwaggerGen();
builder.Services.AddSignalR();
builder.Services.AddSingleton();
-
// Allow any CORS origin if in DEV
const string AllowDebugOriginPolicy = "AllowDebugOrigin";
if (builder.Environment.IsDevelopment())
{
builder.Services.AddCors(options =>
{
- options.AddPolicy(AllowDebugOriginPolicy, builder => {
- builder
- .WithOrigins("http://localhost:3000") // client url
- .AllowAnyHeader()
- .AllowAnyMethod()
- .AllowCredentials();
- });
+ options.AddPolicy(AllowDebugOriginPolicy, builder =>
+ {
+ builder
+ .WithOrigins("http://localhost:3000") // client url
+ .AllowAnyHeader()
+ .AllowAnyMethod()
+ .AllowCredentials();
+ });
});
}
@@ -88,8 +87,8 @@ app.Run();
static ISemanticTextMemory CreateMemory(IServiceProvider provider)
{
- OpenAIOptions openAiConfig = provider.GetService>().Value;
- QdrantOptions qdrantConfig = provider.GetService>().Value;
+ OpenAIOptions openAiConfig = provider.GetRequiredService>().Value;
+ QdrantOptions qdrantConfig = provider.GetRequiredService>().Value;
var loggerFactory = LoggerFactory.Create(builder =>
{
@@ -108,7 +107,7 @@ static ISemanticTextMemory CreateMemory(IServiceProvider provider)
static Kernel CreateKernel(IServiceProvider provider)
{
- OpenAIOptions openAiConfig = provider.GetService>().Value;
+ OpenAIOptions openAiConfig = provider.GetRequiredService>().Value;
var clientOptions = new OpenAIClientOptions();
clientOptions.Retry.NetworkTimeout = TimeSpan.FromMinutes(5);
var builder = Kernel.CreateBuilder();
@@ -130,7 +129,7 @@ static Kernel CreateKernel(IServiceProvider provider)
openAIClient = new OpenAIClient(new Uri(openAiConfig.ImageEndpoint), new AzureKeyCredential(openAiConfig.ImageApiKey), clientOptions);
if (openAiConfig.ImageEndpoint.Contains(".azure", StringComparison.OrdinalIgnoreCase))
{
- Throw.IfNullOrEmpty(nameof(openAiConfig.ImageDeploymentOrModelId), openAiConfig.ImageDeploymentOrModelId);
+ ArgumentException.ThrowIfNullOrEmpty(nameof(openAiConfig.ImageDeploymentOrModelId), openAiConfig.ImageDeploymentOrModelId);
builder.Services.AddAzureOpenAITextToImage(openAiConfig.ImageDeploymentOrModelId, openAIClient);
}
else
@@ -141,11 +140,11 @@ static Kernel CreateKernel(IServiceProvider provider)
// Embeddings
openAIClient = new OpenAIClient(new Uri(openAiConfig.EmbeddingsEndpoint), new AzureKeyCredential(openAiConfig.EmbeddingsApiKey), clientOptions);
if (openAiConfig.EmbeddingsEndpoint.Contains(".azure", StringComparison.OrdinalIgnoreCase))
- {
+ {
builder.Services.AddAzureOpenAITextEmbeddingGeneration(openAiConfig.EmbeddingsDeploymentOrModelId, openAIClient);
}
else
- {
+ {
builder.Services.AddOpenAITextEmbeddingGeneration(openAiConfig.EmbeddingsDeploymentOrModelId, openAIClient);
}
diff --git a/dotnet/samples/marketing/src/backend/SignalRHub/ArticleHub.cs b/dotnet/samples/marketing/src/backend/SignalRHub/ArticleHub.cs
index f60876a2a..579d0bc5c 100644
--- a/dotnet/samples/marketing/src/backend/SignalRHub/ArticleHub.cs
+++ b/dotnet/samples/marketing/src/backend/SignalRHub/ArticleHub.cs
@@ -13,9 +13,8 @@ public class ArticleHub : Hub
await base.OnConnectedAsync();
}
- public override async Task OnDisconnectedAsync(Exception exception)
+ public override async Task OnDisconnectedAsync(Exception? exception)
{
- string removedUserId;
SignalRConnectionsDB.ConnectionIdByUser.TryRemove(Context.ConnectionId, out _);
await base.OnDisconnectedAsync(exception);
}
@@ -28,6 +27,8 @@ public class ArticleHub : Hub
///
public async Task ProcessMessage(FrontEndMessage frontEndMessage, IClusterClient clusterClient)
{
+ ArgumentNullException.ThrowIfNull(frontEndMessage);
+
var streamProvider = clusterClient.GetStreamProvider("StreamProvider");
var streamId = StreamId.Create(Consts.OrleansNamespace, frontEndMessage.UserId);
var stream = streamProvider.GetStream(streamId);
diff --git a/dotnet/samples/marketing/src/backend/SignalRHub/FrontEndMessage.cs b/dotnet/samples/marketing/src/backend/SignalRHub/FrontEndMessage.cs
index 7e1debaa7..f63963a86 100644
--- a/dotnet/samples/marketing/src/backend/SignalRHub/FrontEndMessage.cs
+++ b/dotnet/samples/marketing/src/backend/SignalRHub/FrontEndMessage.cs
@@ -2,7 +2,7 @@
public class FrontEndMessage
{
- public string UserId { get; set; }
- public string Message { get; set; }
- public string Agent { get; set; }
+ public required string UserId { get; set; }
+ public required string Message { get; set; }
+ public required string Agent { get; set; }
}
diff --git a/dotnet/samples/marketing/src/backend/SignalRHub/SignalRService.cs b/dotnet/samples/marketing/src/backend/SignalRHub/SignalRService.cs
index b1b771afc..ea6aa6e7a 100644
--- a/dotnet/samples/marketing/src/backend/SignalRHub/SignalRService.cs
+++ b/dotnet/samples/marketing/src/backend/SignalRHub/SignalRService.cs
@@ -1,5 +1,4 @@
using Microsoft.AspNetCore.SignalR;
-using System.Collections.Concurrent;
namespace Marketing.SignalRHub;
diff --git a/dotnet/src/Microsoft.AI.Agents.Dapr/Agent.cs b/dotnet/src/Microsoft.AI.Agents.Dapr/Agent.cs
index 8d6d26f28..10e4c95fa 100644
--- a/dotnet/src/Microsoft.AI.Agents.Dapr/Agent.cs
+++ b/dotnet/src/Microsoft.AI.Agents.Dapr/Agent.cs
@@ -6,11 +6,11 @@ namespace Microsoft.AI.Agents.Dapr;
public abstract class Agent : Actor, IAgent
{
- private readonly DaprClient daprClient;
+ private readonly DaprClient _daprClient;
protected Agent(ActorHost host, DaprClient daprClient) : base(host)
{
- this.daprClient = daprClient;
+ this._daprClient = daprClient;
}
public abstract Task HandleEvent(Event item);
@@ -21,7 +21,7 @@ public abstract class Agent : Actor, IAgent
{ "cloudevent.Subject", item.Subject },
{ "cloudevent.id", Guid.NewGuid().ToString()}
};
-
- await daprClient.PublishEventAsync(ns, id, item, metadata);
+
+ await _daprClient.PublishEventAsync(ns, id, item, metadata).ConfigureAwait(false);
}
}
diff --git a/dotnet/src/Microsoft.AI.Agents.Dapr/AiAgent.cs b/dotnet/src/Microsoft.AI.Agents.Dapr/AiAgent.cs
index 1537defb7..0e29f7b68 100644
--- a/dotnet/src/Microsoft.AI.Agents.Dapr/AiAgent.cs
+++ b/dotnet/src/Microsoft.AI.Agents.Dapr/AiAgent.cs
@@ -1,5 +1,5 @@
+using System.Globalization;
using System.Text;
-using Dapr.Actors;
using Dapr.Actors.Runtime;
using Dapr.Client;
using Microsoft.AI.Agents.Abstractions;
@@ -9,29 +9,33 @@ using Microsoft.SemanticKernel.Memory;
namespace Microsoft.AI.Agents.Dapr;
-public abstract class AiAgent : Agent, IAiAgent where T: class, new()
+public abstract class AiAgent : Agent, IAiAgent where T : class, new()
{
public string StateStore = "agents-statestore";
- public AiAgent(ActorHost host, DaprClient client,ISemanticTextMemory memory, Kernel kernel)
+ public AiAgent(ActorHost host, DaprClient client, ISemanticTextMemory memory, Kernel kernel)
: base(host, client)
{
_memory = memory;
_kernel = kernel;
}
+
private readonly ISemanticTextMemory _memory;
private readonly Kernel _kernel;
- protected AgentState state;
+ protected AgentState state = default!;
-
protected override async Task OnActivateAsync()
{
state = await StateManager.GetOrAddStateAsync(StateStore, new AgentState());
- }
+ }
public void AddToHistory(string message, ChatUserType userType)
{
- if (state.History == null) state.History = new List();
+ if (state.History == null)
+ {
+ state.History = new List();
+ }
+
state.History.Add(new ChatHistoryItem
{
Message = message,
@@ -48,8 +52,7 @@ public abstract class AiAgent : Agent, IAiAgent where T: class, new()
public virtual async Task CallFunction(string template, KernelArguments arguments, OpenAIPromptExecutionSettings? settings = null)
{
- var propmptSettings = (settings == null) ? new OpenAIPromptExecutionSettings { MaxTokens = 18000, Temperature = 0.8, TopP = 1 }
- : settings;
+ var propmptSettings = settings ?? new OpenAIPromptExecutionSettings { MaxTokens = 18000, Temperature = 0.8, TopP = 1 };
var function = _kernel.CreateFunctionFromPrompt(template, propmptSettings);
var result = (await _kernel.InvokeAsync(function, arguments)).ToString();
AddToHistory(result, ChatUserType.Agent);
@@ -68,11 +71,11 @@ public abstract class AiAgent : Agent, IAiAgent where T: class, new()
///
public async Task AddKnowledge(string instruction, string index, KernelArguments arguments)
{
- var documents = _memory.SearchAsync(index, arguments["input"].ToString(), 5);
+ var documents = _memory.SearchAsync(index, arguments["input"]?.ToString()!, 5);
var kbStringBuilder = new StringBuilder();
await foreach (var doc in documents)
{
- kbStringBuilder.AppendLine($"{doc.Metadata.Text}");
+ kbStringBuilder.AppendLine(CultureInfo.InvariantCulture, $"{doc.Metadata.Text}");
}
arguments[index] = instruction.Replace($"!{index}!", $"{kbStringBuilder}");
return arguments;
diff --git a/dotnet/src/Microsoft.AI.Agents.Orleans/Agent.cs b/dotnet/src/Microsoft.AI.Agents.Orleans/Agent.cs
index 48fd318ff..0e803067b 100644
--- a/dotnet/src/Microsoft.AI.Agents.Orleans/Agent.cs
+++ b/dotnet/src/Microsoft.AI.Agents.Orleans/Agent.cs
@@ -6,27 +6,27 @@ namespace Microsoft.AI.Agents.Orleans;
public abstract class Agent : Grain, IGrainWithStringKey, IAgent
{
- protected virtual string Namespace { get;set;}
- public abstract Task HandleEvent(Event item);
+ protected abstract string Namespace { get; }
+ public abstract Task HandleEvent(Event item);
private async Task HandleEvent(Event item, StreamSequenceToken? token)
{
- await HandleEvent(item);
+ await HandleEvent(item).ConfigureAwait(true);
}
-
+
public async Task PublishEvent(string ns, string id, Event item)
{
var streamProvider = this.GetStreamProvider("StreamProvider");
var streamId = StreamId.Create(ns, id);
var stream = streamProvider.GetStream(streamId);
- await stream.OnNextAsync(item);
+ await stream.OnNextAsync(item).ConfigureAwait(true);
}
- public async override Task OnActivateAsync(CancellationToken cancellationToken)
+ public override async Task OnActivateAsync(CancellationToken cancellationToken)
{
var streamProvider = this.GetStreamProvider("StreamProvider");
var streamId = StreamId.Create(Namespace, this.GetPrimaryKeyString());
var stream = streamProvider.GetStream(streamId);
- await stream.SubscribeAsync(HandleEvent);
+ await stream.SubscribeAsync(HandleEvent).ConfigureAwait(true);
}
}
diff --git a/dotnet/src/Microsoft.AI.Agents.Orleans/AiAgent.cs b/dotnet/src/Microsoft.AI.Agents.Orleans/AiAgent.cs
index 7128d85a1..a4f9507d8 100644
--- a/dotnet/src/Microsoft.AI.Agents.Orleans/AiAgent.cs
+++ b/dotnet/src/Microsoft.AI.Agents.Orleans/AiAgent.cs
@@ -13,11 +13,10 @@ public abstract class AiAgent : Agent, IAiAgent where T : class, new()
protected IPersistentState> _state;
protected Kernel _kernel;
private readonly ISemanticTextMemory _memory;
-
- public AiAgent([PersistentState("state", "messages")] IPersistentState> state, ISemanticTextMemory memory, Kernel kernel)
+ public AiAgent([PersistentState("state", "messages")] IPersistentState> state, ISemanticTextMemory memory, Kernel kernel)
{
- _state = state;
+ _state = state;
_memory = memory;
_kernel = kernel;
}
@@ -25,8 +24,15 @@ public abstract class AiAgent : Agent, IAiAgent where T : class, new()
public override Task OnActivateAsync(CancellationToken cancellationToken)
{
// Initialize the Agent state
- if (_state.State.History == null) _state.State.History = new List();
- if (_state.State.Data == null) _state.State.Data = new T();
+ if (_state.State.History == null)
+ {
+ _state.State.History = new List();
+ }
+
+ if (_state.State.Data == null)
+ {
+ _state.State.Data = new T();
+ }
return base.OnActivateAsync(cancellationToken);
}
diff --git a/dotnet/src/Microsoft.AI.Agents.Orleans/EventSurrogate.cs b/dotnet/src/Microsoft.AI.Agents.Orleans/EventSurrogate.cs
index 50ef08a44..9e550f056 100644
--- a/dotnet/src/Microsoft.AI.Agents.Orleans/EventSurrogate.cs
+++ b/dotnet/src/Microsoft.AI.Agents.Orleans/EventSurrogate.cs
@@ -19,7 +19,7 @@ internal sealed class EventSurrogateConverter :
{
public Event ConvertFromSurrogate(
in EventSurrogate surrogate) =>
- new Event { Data = surrogate.Data, Subject = surrogate.Subject, Type = surrogate.Type};
+ new Event { Data = surrogate.Data, Subject = surrogate.Subject, Type = surrogate.Type };
public EventSurrogate ConvertToSurrogate(
in Event value) =>
diff --git a/dotnet/src/Microsoft.AI.Agents/Abstractions/AgentState.cs b/dotnet/src/Microsoft.AI.Agents/Abstractions/AgentState.cs
index 10bc212f9..97666e2d9 100644
--- a/dotnet/src/Microsoft.AI.Agents/Abstractions/AgentState.cs
+++ b/dotnet/src/Microsoft.AI.Agents/Abstractions/AgentState.cs
@@ -1,7 +1,7 @@
namespace Microsoft.AI.Agents.Abstractions;
-public class AgentState where T: class, new()
+public class AgentState where T : class, new()
{
- public List History { get; set; }
- public T Data { get; set; }
+ public List History { get; set; } = new();
+ public T Data { get; set; } = new();
}
diff --git a/dotnet/src/Microsoft.AI.Agents/Abstractions/ChatHistoryItem.cs b/dotnet/src/Microsoft.AI.Agents/Abstractions/ChatHistoryItem.cs
index 30d915254..e8e27833b 100644
--- a/dotnet/src/Microsoft.AI.Agents/Abstractions/ChatHistoryItem.cs
+++ b/dotnet/src/Microsoft.AI.Agents/Abstractions/ChatHistoryItem.cs
@@ -3,7 +3,7 @@ namespace Microsoft.AI.Agents.Abstractions;
[Serializable]
public class ChatHistoryItem
{
- public string Message { get; set; }
+ public required string Message { get; set; }
public ChatUserType UserType { get; set; }
public int Order { get; set; }
diff --git a/dotnet/src/Microsoft.AI.Agents/Abstractions/Event.cs b/dotnet/src/Microsoft.AI.Agents/Abstractions/Event.cs
index 18e5e5535..5924ca307 100644
--- a/dotnet/src/Microsoft.AI.Agents/Abstractions/Event.cs
+++ b/dotnet/src/Microsoft.AI.Agents/Abstractions/Event.cs
@@ -5,7 +5,7 @@ namespace Microsoft.AI.Agents.Abstractions;
[DataContract]
public class Event
{
- public Dictionary Data { get; set; }
- public string Type { get; set; }
- public string Subject { get; set; }
-}
\ No newline at end of file
+ public required Dictionary Data { get; set; }
+ public required string Type { get; set; }
+ public string Subject { get; set; } = "";
+}