Ship Assistent 0.12.1: training tab, dataset pipeline, and heard RAG.

Restructure UI with app-level tabs and chat history drawer; add dataset curation,
HF import, Modelfile/QLoRA hooks, and link approved samples to the agent immediately
via heard vector memory without waiting for fine-tuning.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-22 14:27:59 +03:00
co-authored by Cursor
parent e8bb012885
commit 1a03c3178f
21 changed files with 4919 additions and 638 deletions
+89 -8
View File
@@ -185,6 +185,7 @@ public partial class SwarmAssistentExtension
{
AssistentMemory.RetrieveOptions opt = MemoryRetrieveOptions(pid);
hits = await Memory.RetrieveAsync(root, retrieveQuery, opt.TopK, embed, Config.PersonaExtendsChain(pid), opt);
hits = FilterHeardHitsIfDisabled(hits);
}
catch (Exception ex)
{
@@ -192,7 +193,7 @@ public partial class SwarmAssistentExtension
}
}
string enrichedContext = InjectMemoryHits(contextJson, hits);
string enrichedContext = InjectMemoryHits(contextJson, hits, pid);
if (!slimDebug)
{
enrichedContext = EnrichPersonaContext(enrichedContext, pid, packName);
@@ -338,24 +339,51 @@ public partial class SwarmAssistentExtension
AssistentMemory.RetrieveOptions MemoryRetrieveOptions(string pid)
{
JObject a = Config.LoadAssistant(pid) ?? new JObject();
JObject agent = Config.LoadTrainingAgent();
AssistentMemory.RetrieveOptions opt = new()
{
TopK = a["memory_top_k"]?.Value<int?>() ?? 8,
MinScore = a["memory_min_score"]?.Value<float?>() ?? 0.32f,
ApplyQuotas = true,
};
if (a["memory_quotas"] is JObject quotas)
Dictionary<string, int> quotas = AssistentMemory.CopyDefaultQuotas();
if (a["memory_quotas"] is JObject qOverrides)
{
Dictionary<string, int> d = new(StringComparer.OrdinalIgnoreCase);
foreach (JProperty p in quotas.Properties())
foreach (JProperty p in qOverrides.Properties())
{
d[p.Name] = p.Value?.Value<int?>() ?? 2;
quotas[p.Name] = p.Value?.Value<int?>() ?? 2;
}
opt.Quotas = d;
}
if (agent["enabled"]?.Value<bool?>() != false)
{
quotas["heard"] = agent["heard_quota"]?.Value<int?>() ?? 3;
}
else
{
quotas.Remove("heard");
}
opt.Quotas = quotas;
return opt;
}
JArray FilterHeardHitsIfDisabled(JArray hits)
{
if (Config.LoadTrainingAgent()["enabled"]?.Value<bool?>() != false)
{
return hits;
}
JArray filtered = [];
foreach (JToken t in hits ?? [])
{
if (t is JObject ho && string.Equals(ho["kind"]?.ToString(), AssistentMemory.HeardKind, StringComparison.OrdinalIgnoreCase))
{
continue;
}
filtered.Add(t);
}
return filtered;
}
async Task<(string follow, JArray civitai)> RunToolHop(
Session session,
string root,
@@ -413,6 +441,38 @@ public partial class SwarmAssistentExtension
+ rows.ToString(Newtonsoft.Json.Formatting.None) + "\n```",
null);
}
if (tool == "heard_search")
{
if (Config.LoadTrainingAgent()["enabled"]?.Value<bool?>() == false)
{
return ("heard_search disabled in training-agent settings.", null);
}
string q = patch["memory_query"]?.ToString()?.Trim()
?? patch["search_query"]?.ToString()?.Trim()
?? ExtractMemoryQuery(patch);
if (string.IsNullOrWhiteSpace(q) || !hopDone.Add("heard:" + q))
{
return (null, null);
}
int topK = Config.LoadTrainingAgent()["heard_quota"]?.Value<int?>() ?? 3;
JArray rows = await Memory.SearchAsync(root, q, AssistentMemory.HeardKind, topK, embed, chain);
JArray examples = [];
foreach (JToken t in rows)
{
if (t is JObject ho)
{
JObject ex = Memory.BuildHeardExampleFromHit(ho, chain);
if (ex is not null)
{
examples.Add(ex);
}
}
}
return (
"heard_search — curated dialogue examples the assistant learned (style/reference, not hard rules). Use tone and structure; omit heard_search unless you need more examples.\n```json\n"
+ examples.ToString(Newtonsoft.Json.Formatting.None) + "\n```",
null);
}
if (tool == "lookup_tags")
{
string q = ExtractTagQuery(patch);
@@ -599,7 +659,7 @@ public partial class SwarmAssistentExtension
return outRows;
}
string InjectMemoryHits(string contextJson, JArray hits, JObject exact = null)
string InjectMemoryHits(string contextJson, JArray hits, string personaId = null)
{
JObject ctx;
try
@@ -614,7 +674,7 @@ public partial class SwarmAssistentExtension
int hitChars = 240;
try
{
string pidHit = AssistentConfig.SafeId(ctx["persona"]?.ToString()) ?? Config?.DefaultPersonaId() ?? "neutral";
string pidHit = AssistentConfig.SafeId(ctx["persona"]?.ToString()) ?? personaId ?? Config?.DefaultPersonaId() ?? "neutral";
hitChars = Config?.LoadAssistant(pidHit)?["memory_hit_chars"]?.Value<int?>() ?? 240;
}
catch
@@ -623,7 +683,11 @@ public partial class SwarmAssistentExtension
}
hitChars = Math.Max(80, Math.Min(hitChars, 800));
string pid = AssistentConfig.SafeId(ctx["persona"]?.ToString()) ?? personaId ?? Config?.DefaultPersonaId() ?? "neutral";
IEnumerable<string> chain = Config?.PersonaExtendsChain(pid) ?? [];
JArray clippedHits = [];
JArray heardExamples = [];
foreach (JToken t in hits ?? [])
{
if (t is not JObject ho)
@@ -634,6 +698,15 @@ public partial class SwarmAssistentExtension
{
continue;
}
if (string.Equals(ho["kind"]?.ToString(), AssistentMemory.HeardKind, StringComparison.OrdinalIgnoreCase))
{
JObject ex = Memory?.BuildHeardExampleFromHit(ho, chain);
if (ex is not null)
{
heardExamples.Add(ex);
}
continue;
}
JObject copy = (JObject)ho.DeepClone();
string text = copy["text"]?.ToString() ?? "";
if (text.Length > hitChars)
@@ -644,6 +717,14 @@ public partial class SwarmAssistentExtension
clippedHits.Add(copy);
}
ctx["memory_hits"] = clippedHits;
if (heardExamples.Count > 0)
{
ctx["heard_examples"] = heardExamples;
}
else
{
ctx.Remove("heard_examples");
}
ctx.Remove("taste_profile");
ctx.Remove("enabled_loras"); // alias of selected_loras — do not double-feed
try