forked from microsoft/kernel-memory
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNodeConfig.cs
More file actions
152 lines (136 loc) · 5.1 KB
/
NodeConfig.cs
File metadata and controls
152 lines (136 loc) · 5.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
using KernelMemory.Core.Config.ContentIndex;
using KernelMemory.Core.Config.Embeddings;
using KernelMemory.Core.Config.Enums;
using KernelMemory.Core.Config.SearchIndex;
using KernelMemory.Core.Config.Storage;
using KernelMemory.Core.Config.Validation;
namespace KernelMemory.Core.Config;
/// <summary>
/// Configuration for a single memory node
/// </summary>
public sealed class NodeConfig : IValidatable
{
/// <summary>
/// Unique identifier for this node
/// </summary>
[JsonPropertyName("id")]
public string Id { get; set; } = string.Empty;
/// <summary>
/// Access level for this node
/// </summary>
[JsonPropertyName("access")]
public NodeAccessLevels Access { get; set; } = NodeAccessLevels.Full;
/// <summary>
/// Weight for relevance scoring when searching across multiple nodes.
/// Higher weight = results from this node ranked higher.
/// Default: 1.0 (neutral weight).
/// Range: 0.0 (exclude) to any positive value.
/// </summary>
[JsonPropertyName("weight")]
public float Weight { get; set; } = 1.0f;
/// <summary>
/// Content index (source of truth) - REQUIRED
/// Stores metadata, cached content, and ingestion state
/// </summary>
[JsonPropertyName("contentIndex")]
public ContentIndexConfig ContentIndex { get; set; } = null!;
/// <summary>
/// Optional file storage for binary files
/// </summary>
[JsonPropertyName("fileStorage")]
public StorageConfig? FileStorage { get; set; }
/// <summary>
/// Optional repository storage for git repositories
/// </summary>
[JsonPropertyName("repoStorage")]
public StorageConfig? RepoStorage { get; set; }
/// <summary>
/// Search indexes for content retrieval
/// Multiple indexes can be used for different search strategies
/// </summary>
[JsonPropertyName("searchIndexes")]
public List<SearchIndexConfig> SearchIndexes { get; set; } = new();
/// <summary>
/// Validates the node configuration
/// </summary>
/// <param name="path"></param>
public void Validate(string path)
{
if (string.IsNullOrWhiteSpace(this.Id))
{
throw new ConfigException(path, "Node ID is required");
}
if (this.Weight < 0.0f)
{
throw new ConfigException($"{path}.Weight", "Weight must be non-negative (0.0 or higher)");
}
if (this.ContentIndex == null)
{
throw new ConfigException($"{path}.ContentIndex", "ContentIndex is required");
}
this.ContentIndex.Validate($"{path}.ContentIndex");
this.FileStorage?.Validate($"{path}.FileStorage");
this.RepoStorage?.Validate($"{path}.RepoStorage");
// Validate search indexes
for (int i = 0; i < this.SearchIndexes.Count; i++)
{
this.SearchIndexes[i].Validate($"{path}.SearchIndexes[{i}]");
}
// Ensure all search index IDs are unique within this node
var duplicateIds = this.SearchIndexes
.GroupBy(idx => idx.Id)
.Where(g => g.Count() > 1)
.Select(g => g.Key)
.ToList();
if (duplicateIds.Count > 0)
{
throw new ConfigException($"{path}.SearchIndexes",
$"Duplicate search index IDs found: {string.Join(", ", duplicateIds)}. Each search index must have a unique ID within a node.");
}
}
/// <summary>
/// Creates a default "personal" node configuration with FTS and vector search.
/// Uses Ollama with qwen3-embedding model (1024 dimensions) for local, offline-capable vector search.
/// </summary>
/// <param name="nodeDir"></param>
internal static NodeConfig CreateDefaultPersonalNode(string nodeDir)
{
return new NodeConfig
{
Id = "personal",
Access = NodeAccessLevels.Full,
ContentIndex = new SqliteContentIndexConfig
{
Path = Path.Combine(nodeDir, "content.db")
},
FileStorage = null,
RepoStorage = null,
SearchIndexes = new List<SearchIndexConfig>
{
new FtsSearchIndexConfig
{
Id = "sqlite-fts",
Type = SearchIndexTypes.SqliteFTS,
Path = Path.Combine(nodeDir, "fts.db"),
EnableStemming = true,
Required = true
},
new VectorSearchIndexConfig
{
Id = "sqlite-vector",
Type = SearchIndexTypes.SqliteVector,
Path = Path.Combine(nodeDir, "vector.db"),
Dimensions = 1024,
UseSqliteVec = false,
Embeddings = new OllamaEmbeddingsConfig
{
Model = Constants.EmbeddingDefaults.DefaultOllamaModel,
BaseUrl = Constants.EmbeddingDefaults.DefaultOllamaBaseUrl
}
}
}
};
}
}