Add Knowledge Hub books FTS and remove training UI (0.16.0).
Index Assistent/books search.jsonl, expose knowledge hops/catalog in chat, and drop QLoRA/HF training stack. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+134
-40
@@ -239,6 +239,134 @@ public sealed class AssistentConfig
|
||||
return map;
|
||||
}
|
||||
|
||||
static List<string> ParseYamlBracketList(string value)
|
||||
{
|
||||
value = (value ?? "").Trim();
|
||||
if (value.StartsWith('[') && value.EndsWith(']'))
|
||||
{
|
||||
value = value[1..^1];
|
||||
}
|
||||
return value.Split(',')
|
||||
.Select(s => s.Trim().Trim('"', '\''))
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public string TryFindPackManifestPath(string personaId)
|
||||
{
|
||||
string shelf = PackShelfRoot(personaId);
|
||||
if (string.IsNullOrWhiteSpace(shelf))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
string dir = shelf;
|
||||
for (int i = 0; i < 5 && !string.IsNullOrWhiteSpace(dir); i++)
|
||||
{
|
||||
string yaml = Path.Combine(dir, "assistent-pack.yaml");
|
||||
if (File.Exists(yaml))
|
||||
{
|
||||
return yaml;
|
||||
}
|
||||
dir = Directory.GetParent(dir)?.FullName;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Pack manifest knowledge.attach list for a pack persona.</summary>
|
||||
public List<string> TryParsePackKnowledgeAttach(string personaId)
|
||||
{
|
||||
string path = TryFindPackManifestPath(personaId);
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
List<string> items = [];
|
||||
try
|
||||
{
|
||||
foreach (string rawLine in File.ReadAllLines(path, Encoding.UTF8))
|
||||
{
|
||||
string line = rawLine.Trim();
|
||||
if (string.IsNullOrWhiteSpace(line) || line.StartsWith('#'))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (line.StartsWith("knowledge.attach:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string val = line["knowledge.attach:".Length..].Trim();
|
||||
items.AddRange(ParseYamlBracketList(val));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logs.Debug($"AssistentConfig pack knowledge.attach {path}: {ex.Message}");
|
||||
}
|
||||
return items
|
||||
.Select(SafeId)
|
||||
.Where(id => id is not null)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public JObject LoadKnowledgeAttachOverlay(string personaId)
|
||||
{
|
||||
string id = SafeId(personaId);
|
||||
if (id is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
string path = Path.Combine(_overlayRoot, "personas", id, "knowledge.json");
|
||||
return File.Exists(path) ? TryReadJson(path) : null;
|
||||
}
|
||||
|
||||
public void SaveKnowledgeAttachOverlay(string personaId, JArray attach)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
string id = SafeId(personaId) ?? throw new InvalidOperationException("invalid persona");
|
||||
string dir = Path.Combine(_overlayRoot, "personas", id);
|
||||
Directory.CreateDirectory(dir);
|
||||
string path = Path.Combine(dir, "knowledge.json");
|
||||
JObject doc = new()
|
||||
{
|
||||
["attach"] = attach ?? new JArray(),
|
||||
};
|
||||
File.WriteAllText(path, doc.ToString(Newtonsoft.Json.Formatting.Indented) + "\n", Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
|
||||
static readonly string[] DefaultBundledKnowledgeAttach = ["civitai-krea2", "ru-fictext-rplus"];
|
||||
|
||||
static readonly HashSet<string> BundledKnowledgePersonas = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"neutral", "aggressive", "dreamer",
|
||||
};
|
||||
|
||||
/// <summary>Effective book attach list for a persona (overlay → pack → bundled defaults).</summary>
|
||||
public List<string> ResolveKnowledgeAttach(string personaId)
|
||||
{
|
||||
string pid = SafeId(personaId) ?? DefaultPersonaId();
|
||||
JObject overlay = LoadKnowledgeAttachOverlay(pid);
|
||||
if (overlay?["attach"] is JArray custom && custom.Count > 0)
|
||||
{
|
||||
return custom.Select(t => SafeId(t?.ToString()))
|
||||
.Where(id => id is not null)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
}
|
||||
List<string> fromPack = TryParsePackKnowledgeAttach(pid);
|
||||
if (fromPack.Count > 0)
|
||||
{
|
||||
return fromPack;
|
||||
}
|
||||
if (IsBundledPersona(pid) && BundledKnowledgePersonas.Contains(pid))
|
||||
{
|
||||
return DefaultBundledKnowledgeAttach.ToList();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/// <summary>Discover persona packs under Assistent/extensions/*/.</summary>
|
||||
public List<PackPersona> DiscoverPackPersonas()
|
||||
{
|
||||
@@ -477,7 +605,7 @@ public sealed class AssistentConfig
|
||||
|
||||
static readonly HashSet<string> ReservedConfigFiles = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"exact.json", "controls.json", "skills.json", "ui.json", "assistant.json", "training-qlora.json",
|
||||
"exact.json", "controls.json", "skills.json", "ui.json", "assistant.json", "knowledge.json",
|
||||
};
|
||||
|
||||
static readonly HashSet<string> ReservedConfigDirs = new(StringComparer.OrdinalIgnoreCase)
|
||||
@@ -780,7 +908,7 @@ public sealed class AssistentConfig
|
||||
{
|
||||
"persona.json", "bio.json", "voice.json", "humor.json", "craft.json",
|
||||
"appearance.json", "outfits.json", "roleplay.json", "likes.json", "dislikes.json",
|
||||
"rules.json", "controls.json", "exact.json", "extra.md",
|
||||
"rules.json", "controls.json", "exact.json", "knowledge.json", "extra.md",
|
||||
};
|
||||
|
||||
public static bool IsWritableShelfName(string fileName)
|
||||
@@ -934,8 +1062,6 @@ public sealed class AssistentConfig
|
||||
|
||||
public JObject LoadUi(string personaId) => MergeJsonLayers("ui.json", LayerRoots(personaId));
|
||||
|
||||
public JObject LoadTrainingQlora(string personaId) => MergeJsonLayers("training-qlora.json", LayerRoots(personaId));
|
||||
|
||||
/// <summary>Exact (KV) memory: bundled base → persona overlays → disk overlays. Persona keys overwrite base.</summary>
|
||||
public JObject LoadExact(string personaId) => MergeJsonLayers("exact.json", LayerRoots(personaId));
|
||||
|
||||
@@ -1549,40 +1675,6 @@ public sealed class AssistentConfig
|
||||
}
|
||||
}
|
||||
|
||||
public JObject LoadTrainingRunner()
|
||||
=> TryReadJson(Path.Combine(_overlayRoot, "training-runner.json")) ?? new JObject();
|
||||
|
||||
public void SaveTrainingRunner(JObject settings)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
Directory.CreateDirectory(_overlayRoot);
|
||||
string path = Path.Combine(_overlayRoot, "training-runner.json");
|
||||
JObject merged = DeepMerge(LoadTrainingRunner(), settings ?? new JObject());
|
||||
File.WriteAllText(path, merged.ToString(Newtonsoft.Json.Formatting.Indented), Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
|
||||
public JObject LoadTrainingAgent()
|
||||
=> TryReadJson(Path.Combine(_overlayRoot, "training-agent.json"))
|
||||
?? new JObject
|
||||
{
|
||||
["enabled"] = true,
|
||||
["auto_link_on_approve"] = true,
|
||||
["heard_quota"] = 3,
|
||||
};
|
||||
|
||||
public void SaveTrainingAgent(JObject settings)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
Directory.CreateDirectory(_overlayRoot);
|
||||
string path = Path.Combine(_overlayRoot, "training-agent.json");
|
||||
JObject merged = DeepMerge(LoadTrainingAgent(), settings ?? new JObject());
|
||||
File.WriteAllText(path, merged.ToString(Newtonsoft.Json.Formatting.Indented), Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
|
||||
public JObject LoadOllamaRoles()
|
||||
{
|
||||
return TryReadJson(Path.Combine(_overlayRoot, "ollama-roles.json"))
|
||||
@@ -1643,7 +1735,6 @@ public sealed class AssistentConfig
|
||||
JObject ui = LoadUi(id);
|
||||
JObject model = LoadModelProfile(id);
|
||||
JObject exact = LoadExact(id);
|
||||
JObject training = LoadTrainingQlora(id);
|
||||
var packs = ListPacks(id);
|
||||
var skills = ListSkills(id);
|
||||
var personas = ListPersonaCatalog();
|
||||
@@ -1659,7 +1750,6 @@ public sealed class AssistentConfig
|
||||
["ui"] = ui,
|
||||
["model"] = model,
|
||||
["exact"] = exact,
|
||||
["training"] = training,
|
||||
["controls"] = controlsSchema,
|
||||
["control_values"] = controlValues,
|
||||
["persona_source"] = PersonaSource(id),
|
||||
@@ -1687,6 +1777,10 @@ public sealed class AssistentConfig
|
||||
["identity_summary"] = RenderIdentityBlock(id, includeAllShelves: true),
|
||||
["enabled_skills"] = new JArray(ResolveEnabledSkills(id, null)),
|
||||
["patch_keys"] = new JArray(LoadPatchKeys()),
|
||||
["knowledge"] = new JObject
|
||||
{
|
||||
["attach"] = new JArray(ResolveKnowledgeAttach(id)),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user