Skip to content

Commit df03deb

Browse files
authored
Add service config check (#971)
Add basic configuration check to detect some warnings and errors. The feature can be extended in future to check more services and more details, e.g. availability and permissions. How to use: ``` cd service/Service dotnet run check ```
1 parent 385d8c4 commit df03deb

File tree

15 files changed

+518
-17
lines changed

15 files changed

+518
-17
lines changed

extensions/AzureQueues/AzureQueuesPipeline.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ private sealed class MessageEventArgs : EventArgs
4141
// Queue client builder, requiring the queue name in input
4242
private readonly Func<string, QueueClient> _clientBuilder;
4343

44-
// Queue confirguration
44+
// Queue configuration
4545
private readonly AzureQueuesConfig _config;
4646

4747
// Queue client, once connected

extensions/KM/KernelMemory/Internals/KernelMemoryComposer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public void ConfigureBuilder()
110110

111111
private void ConfigureQueueDependency()
112112
{
113-
if (string.Equals(this._memoryConfiguration.DataIngestion.OrchestrationType, "Distributed", StringComparison.OrdinalIgnoreCase))
113+
if (string.Equals(this._memoryConfiguration.DataIngestion.OrchestrationType, KernelMemoryConfig.OrchestrationTypeDistributed, StringComparison.OrdinalIgnoreCase))
114114
{
115115
switch (this._memoryConfiguration.DataIngestion.DistributedOrchestration.QueueType)
116116
{

service/Core/Configuration/KernelMemoryConfig.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ namespace Microsoft.KernelMemory;
1212

1313
public class KernelMemoryConfig
1414
{
15+
public const string OrchestrationTypeInProcess = "InProcess";
16+
public const string OrchestrationTypeDistributed = "Distributed";
17+
1518
/// <summary>
1619
/// Settings for the upload of documents and memory creation/update.
1720
/// </summary>

service/Service/Program.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,17 @@ public static void Main(string[] args)
5858
// *************************** CONFIG WIZARD ***************************
5959

6060
// Run `dotnet run setup` to run this code and set up the service
61-
if (new[] { "setup", "-setup", "config" }.Contains(args.FirstOrDefault(), StringComparer.OrdinalIgnoreCase))
61+
if (new[] { "setup", "--setup", "config" }.Contains(args.FirstOrDefault(), StringComparer.OrdinalIgnoreCase))
6262
{
6363
InteractiveSetup.Program.Main(args.Skip(1).ToArray());
6464
}
6565

66+
// Run `dotnet run check` to run this code and analyze the service configuration
67+
if (new[] { "check", "--check" }.Contains(args.FirstOrDefault(), StringComparer.OrdinalIgnoreCase))
68+
{
69+
InteractiveSetup.Program.Main(["--check"]);
70+
}
71+
6672
// *************************** APP BUILD *******************************
6773

6874
int asyncHandlersCount = 0;
@@ -239,7 +245,10 @@ private static int AddHandlersAsHostedServices(
239245
IKernelMemoryBuilder memoryBuilder,
240246
WebApplicationBuilder appBuilder)
241247
{
242-
if (config.DataIngestion.OrchestrationType != "Distributed") { return 0; }
248+
if (!string.Equals(config.DataIngestion.OrchestrationType, KernelMemoryConfig.OrchestrationTypeDistributed, StringComparison.OrdinalIgnoreCase))
249+
{
250+
return 0;
251+
}
243252

244253
if (!config.Service.RunHandlers) { return 0; }
245254

service/Service/check.cmd

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@echo off
2+
3+
dotnet clean
4+
dotnet build -c Debug -p "SolutionName=KernelMemory"
5+
cmd /C "set ASPNETCORE_ENVIRONMENT=Development && dotnet run check --no-build --no-restore"

service/Service/check.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# This script is used also in the Docker image
2+
3+
if [ -f "Microsoft.KernelMemory.ServiceAssembly.dll" ]; then
4+
dotnet Microsoft.KernelMemory.ServiceAssembly.dll check
5+
else
6+
dotnet clean
7+
dotnet build -c Debug -p "SolutionName=KernelMemory"
8+
ASPNETCORE_ENVIRONMENT=Development dotnet run check --no-build --no-restore
9+
fi

tools/InteractiveSetup/AppSettings.cs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,53 @@ public static void GlobalChange(Action<JObject> configChanges)
5555
File.WriteAllText(DevelopmentSettingsFile, json);
5656
}
5757

58+
/// <summary>
59+
/// Read the configuration, if available
60+
/// </summary>
61+
public static KernelMemoryConfig? ReadConfig()
62+
{
63+
JObject? devConf = null;
64+
JObject? defaultConf = null;
65+
66+
if (File.Exists(DevelopmentSettingsFile))
67+
{
68+
devConf = JsonConvert.DeserializeObject<JObject>(File.ReadAllText(DevelopmentSettingsFile));
69+
}
70+
71+
if (File.Exists(DefaultSettingsFile))
72+
{
73+
defaultConf = JsonConvert.DeserializeObject<JObject>(File.ReadAllText(DefaultSettingsFile));
74+
}
75+
76+
if (devConf == null)
77+
{
78+
if (defaultConf == null) { return null; }
79+
80+
return JsonConvert.DeserializeObject<KernelMemoryConfig>(JsonConvert.SerializeObject(defaultConf["KernelMemory"]));
81+
}
82+
83+
if (defaultConf == null)
84+
{
85+
return JsonConvert.DeserializeObject<KernelMemoryConfig>(JsonConvert.SerializeObject(devConf["KernelMemory"]));
86+
}
87+
88+
defaultConf.Merge(devConf, new JsonMergeSettings
89+
{
90+
MergeArrayHandling = MergeArrayHandling.Replace,
91+
PropertyNameComparison = StringComparison.OrdinalIgnoreCase,
92+
});
93+
94+
return JsonConvert.DeserializeObject<KernelMemoryConfig>(JsonConvert.SerializeObject(defaultConf["KernelMemory"]));
95+
}
96+
5897
/// <summary>
5998
/// Load current configuration from current folder, merging appsettings.json (if present) with appsettings.development.json
6099
/// Note: the code reads from the current folder, which is usually service/Service. Using ConfigurationBuilder would read from
61100
/// bin/Debug/net7.0/, causing problems because GetGlobalConfig doesn't.
62101
/// </summary>
63102
public static KernelMemoryConfig GetCurrentConfig()
64103
{
65-
JObject data = GetGlobalConfig(true);
104+
JObject data = GetGlobalConfig(includeDefaults: true);
66105
if (data["KernelMemory"] == null)
67106
{
68107
Console.WriteLine("KernelMemory property missing, using an empty configuration.");
@@ -74,7 +113,7 @@ public static KernelMemoryConfig GetCurrentConfig()
74113
.SerializeObject(data["KernelMemory"]));
75114
if (config == null)
76115
{
77-
throw new SetupException("Unable to parse file");
116+
throw new SetupException("Unable to parse configuration file");
78117
}
79118

80119
return config;
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
7+
namespace Microsoft.KernelMemory.InteractiveSetup.Doctor;
8+
9+
public static class Check
10+
{
11+
public static void Run()
12+
{
13+
KernelMemoryConfig? config = AppSettings.ReadConfig();
14+
15+
if (config == null)
16+
{
17+
Console.WriteLine("No configuration found.");
18+
return;
19+
}
20+
21+
var stats = new List<Tuple<string, string>>();
22+
var services = new HashSet<string>();
23+
var warnings = new List<Tuple<string, string>>();
24+
var errors = new List<Tuple<string, string>>();
25+
26+
Orchestration.Run(config, stats, services, warnings, errors);
27+
stats.AddSeparator();
28+
EmbeddingGeneration.Run(config, stats, services, warnings, errors);
29+
Storage.Run(config, stats, services, warnings, errors);
30+
stats.AddSeparator();
31+
32+
// Partitioning
33+
stats.Add("Text partitioning", $"Line:{config.DataIngestion.TextPartitioning.MaxTokensPerLine}; Paragraph:{config.DataIngestion.TextPartitioning.MaxTokensPerParagraph}; Overlapping:{config.DataIngestion.TextPartitioning.OverlappingTokens}");
34+
35+
// Image OCR
36+
stats.Add("Image OCR", string.IsNullOrWhiteSpace(config.DataIngestion.ImageOcrType) ? "Disabled" : config.DataIngestion.ImageOcrType);
37+
38+
// Text Generation
39+
if (string.IsNullOrWhiteSpace(config.TextGeneratorType))
40+
{
41+
errors.Add("Text Generation", "No text generation service configured");
42+
}
43+
else
44+
{
45+
stats.Add("Text Generation", config.TextGeneratorType);
46+
}
47+
48+
// Moderation
49+
stats.Add("Moderation", string.IsNullOrWhiteSpace(config.ContentModerationType) ? "Disabled" : config.ContentModerationType);
50+
if (string.IsNullOrWhiteSpace(config.ContentModerationType))
51+
{
52+
warnings.Add("Moderation", "No moderation service configured");
53+
}
54+
55+
ShowStats("Service configuration", stats);
56+
Services.CheckAndShow(config, services, warnings, errors);
57+
ShowStats("Warnings", warnings);
58+
ShowStats("Errors", errors);
59+
Environment.Exit(0);
60+
}
61+
62+
private static void ShowStats(string title, List<Tuple<string, string>> stats)
63+
{
64+
var columnWidth = 1 + stats.Select(stat => stat.Item1.Length).Prepend(0).Max();
65+
66+
Console.WriteLine($"\n\u001b[1;37m### {title}\u001b[0m\n");
67+
var count = 0;
68+
foreach (var kv in stats)
69+
{
70+
if (string.IsNullOrWhiteSpace(kv.Item1))
71+
{
72+
Console.WriteLine();
73+
continue;
74+
}
75+
76+
Console.WriteLine($"{kv.Item1.PadRight(columnWidth)}: {kv.Item2}");
77+
count++;
78+
}
79+
80+
if (count == 0)
81+
{
82+
Console.WriteLine("None.");
83+
}
84+
}
85+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
using System;
4+
using System.Collections.Generic;
5+
6+
namespace Microsoft.KernelMemory.InteractiveSetup.Doctor;
7+
8+
public static class EmbeddingGeneration
9+
{
10+
public static void Run(KernelMemoryConfig config, List<Tuple<string, string>> stats, HashSet<string> services, List<Tuple<string, string>> warnings, List<Tuple<string, string>> errors)
11+
{
12+
if (config.DataIngestion.EmbeddingGenerationEnabled)
13+
{
14+
stats.Add("Embedding Generation", "Enabled")
15+
.Add("Embedding Generators", config.DataIngestion.EmbeddingGeneratorTypes.Count == 0
16+
? "ERROR: no embedding generators configured"
17+
: string.Join(", ", config.DataIngestion.EmbeddingGeneratorTypes));
18+
19+
foreach (var t in config.DataIngestion.EmbeddingGeneratorTypes)
20+
{
21+
services.Add(t);
22+
}
23+
}
24+
else
25+
{
26+
stats.Add("Embedding Generation", "Disabled (this means your pipelines don't need vectorization, or your memory storage uses internal vectorization)");
27+
if (config.DataIngestion.EmbeddingGeneratorTypes.Count > 0)
28+
{
29+
stats.Add("Embedding Generators", "WARNING: some embedding generators are configured but Embedding Generation is disabled");
30+
}
31+
}
32+
}
33+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
7+
namespace Microsoft.KernelMemory.InteractiveSetup.Doctor;
8+
9+
internal static class ListTupleExtensions
10+
{
11+
public static string Get(this List<Tuple<string, string>> list, string key)
12+
{
13+
foreach (var kv in list.Where(kv => kv.Item1 == key))
14+
{
15+
return kv.Item2;
16+
}
17+
18+
return string.Empty;
19+
}
20+
21+
public static List<Tuple<string, string>> Add(this List<Tuple<string, string>> list, string key, string value)
22+
{
23+
list.Add(new Tuple<string, string>(key, value));
24+
return list;
25+
}
26+
27+
public static List<Tuple<string, string>> AddSeparator(this List<Tuple<string, string>> list)
28+
{
29+
return Add(list, "", "");
30+
}
31+
}

0 commit comments

Comments
 (0)