Ship Assistent 0.10.13: ordinary pack, senior chat default, aspect/critique fixes, prose UI.

Default pack is Обычный; prefer default_chat/senior Ollama tag; client-apply same-but-aspect; leave critique pack after hops; render Critique as Критика instead of raw ### markdown.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-22 03:50:58 +03:00
co-authored by Cursor
parent 6e7272ffc9
commit 43984208fc
18 changed files with 687 additions and 198 deletions
+118 -59
View File
@@ -18,19 +18,31 @@ public partial class SwarmAssistentExtension
const int MaxCivitaiHopsFallback = 2;
const int MaxToolHopsFallback = 4;
List<JObject> BuildOllamaMessages(string packName, bool includeBase, string contextJson, JArray userMessages, string extraSystem = null, string personaId = null, IEnumerable<string> skillIds = null)
(List<JObject> messages, JObject systemLayers) BuildOllamaMessages(string packName, bool includeBase, string contextJson, JArray userMessages, string extraSystem = null, string personaId = null, IEnumerable<string> skillIds = null)
{
List<JObject> ollamaMessages = [];
StringBuilder system = new();
JObject layers = new();
string pid = AssistentConfig.SafeId(personaId) ?? Config.DefaultPersonaId();
void AddLayer(string name, string block)
{
if (string.IsNullOrWhiteSpace(block))
{
return;
}
if (system.Length > 0)
{
system.AppendLine();
}
int before = system.Length;
system.AppendLine(block.TrimEnd());
layers[name] = system.Length - before;
}
if (includeBase)
{
string core = Config.LoadCorePrompt(pid);
if (!string.IsNullOrWhiteSpace(core))
{
system.AppendLine(core);
}
AddLayer("core", Config.LoadCorePrompt(pid));
}
if (Memory is not null)
@@ -40,12 +52,7 @@ public partial class SwarmAssistentExtension
JObject asst = Config.LoadAssistant(pid);
double weight = asst["user_prefs_weight"]?.Value<double?>() ?? 1.0;
int maxPrefs = asst["user_prefs_max"]?.Value<int?>() ?? 16;
string about = Memory.FormatUserPrefsBlock(pid, weight, maxPrefs);
if (!string.IsNullOrWhiteSpace(about))
{
system.AppendLine();
system.AppendLine(about);
}
AddLayer("prefs", Memory.FormatUserPrefsBlock(pid, weight, maxPrefs));
}
catch (Exception ex)
{
@@ -54,56 +61,55 @@ public partial class SwarmAssistentExtension
}
JObject exact = Config.LoadExactForPrompt(pid);
if (exact is not null && exact.Count > 0 && LiveContextHasSize(contextJson))
{
exact.Remove("aspect_table");
}
if (exact is not null && exact.Count > 0)
{
system.AppendLine();
system.AppendLine("## Exact memory (canonical KV defaults — prefer over RAG for numbers)");
system.AppendLine("```json");
system.AppendLine(exact.ToString(Newtonsoft.Json.Formatting.None));
system.AppendLine("```");
AddLayer("exact",
"## Exact memory (canonical KV defaults — prefer over RAG for numbers)\n```json\n"
+ exact.ToString(Newtonsoft.Json.Formatting.None) + "\n```");
}
StringBuilder skillsBlock = new();
foreach (string skillId in skillIds ?? Config.ResolveEnabledSkills(pid, null))
{
string skillText = Config.LoadSkillPrompt(pid, skillId);
if (!string.IsNullOrWhiteSpace(skillText))
{
system.AppendLine();
system.AppendLine($"## Skill: {skillId}");
system.AppendLine(skillText);
if (skillsBlock.Length > 0)
{
skillsBlock.AppendLine();
}
skillsBlock.AppendLine($"## Skill: {skillId}");
skillsBlock.AppendLine(skillText.TrimEnd());
}
}
AddLayer("skills", skillsBlock.ToString());
string identity = Config.RenderIdentityBlock(pid);
if (!string.IsNullOrWhiteSpace(identity))
{
system.AppendLine();
system.AppendLine(identity);
}
AddLayer("identity", Config.RenderIdentityBlock(pid));
if (!string.IsNullOrWhiteSpace(packName))
{
string situational = Config.LoadPackPrompt(pid, packName);
if (!string.IsNullOrWhiteSpace(situational))
{
system.AppendLine();
system.AppendLine($"## Active mode: {packName}");
system.AppendLine(situational);
AddLayer("pack", $"## Active mode: {packName}\n{situational.TrimEnd()}");
}
}
if (!string.IsNullOrWhiteSpace(contextJson))
{
system.AppendLine();
system.AppendLine("## Live SwarmUI context (JSON — trust this over guesses)");
system.AppendLine("```json");
system.AppendLine(contextJson);
system.AppendLine("```");
AddLayer("live",
"## Live SwarmUI context (JSON — trust this over guesses)\n```json\n"
+ contextJson + "\n```");
}
if (!string.IsNullOrWhiteSpace(extraSystem))
{
system.AppendLine();
system.AppendLine(extraSystem);
AddLayer("extra", extraSystem);
}
layers["total"] = system.Length;
if (system.Length > 0)
{
ollamaMessages.Add(new JObject
@@ -129,10 +135,10 @@ public partial class SwarmAssistentExtension
}
ollamaMessages.Add(copy);
}
return ollamaMessages;
return (ollamaMessages, layers);
}
async Task<(string reply, JObject raw, JArray civitaiResults, int systemChars)> RunChatWithHops(
async Task<(string reply, JObject raw, JArray civitaiResults, int systemChars, JObject systemLayers)> RunChatWithHops(
Session session,
string root,
string modelName,
@@ -177,16 +183,10 @@ public partial class SwarmAssistentExtension
string enrichedContext = InjectMemoryHits(contextJson, hits);
enrichedContext = EnrichPersonaContext(enrichedContext, pid, packName);
List<JObject> messages = BuildOllamaMessages(packName, includeBase, enrichedContext, userMessages, personaId: pid, skillIds: skills);
int systemChars = 0;
foreach (JObject m in messages)
{
if (string.Equals(m["role"]?.ToString(), "system", StringComparison.OrdinalIgnoreCase))
{
systemChars = m["content"]?.ToString()?.Length ?? 0;
break;
}
}
(List<JObject> messages, JObject systemLayers) = BuildOllamaMessages(packName, includeBase, enrichedContext, userMessages, personaId: pid, skillIds: skills);
int systemChars = systemLayers["total"]?.Value<int?>()
?? messages.FirstOrDefault(m => string.Equals(m["role"]?.ToString(), "system", StringComparison.OrdinalIgnoreCase))?["content"]?.ToString()?.Length
?? 0;
JArray civitaiResults = [];
string reply = "";
JObject lastRaw = null;
@@ -229,7 +229,7 @@ public partial class SwarmAssistentExtension
messages.Add(new JObject { ["role"] = "assistant", ["content"] = assistantContent });
messages.Add(new JObject { ["role"] = "user", ["content"] = follow });
}
return (reply, lastRaw, civitaiResults, systemChars);
return (reply, lastRaw, civitaiResults, systemChars, systemLayers);
}
static string BuildRetrieveQuery(JArray userMessages, string contextJson, string packName = null)
@@ -249,7 +249,18 @@ public partial class SwarmAssistentExtension
{
sb.Append(ckpt).Append(' ');
}
if (ctx["enabled_loras"] is JArray en)
if (ctx["selected_loras"] is JArray selLoras)
{
foreach (JToken t in selLoras.Take(12))
{
string n = t?["name"]?.ToString() ?? t?.ToString();
if (!string.IsNullOrWhiteSpace(n))
{
sb.Append(n).Append(' ');
}
}
}
else if (ctx["enabled_loras"] is JArray en)
{
foreach (JToken t in en.Take(12))
{
@@ -587,6 +598,10 @@ public partial class SwarmAssistentExtension
{
continue;
}
if (IsExactPointerHit(ho))
{
continue;
}
JObject copy = (JObject)ho.DeepClone();
string text = copy["text"]?.ToString() ?? "";
if (text.Length > hitChars)
@@ -598,6 +613,7 @@ public partial class SwarmAssistentExtension
}
ctx["memory_hits"] = clippedHits;
ctx.Remove("taste_profile");
ctx.Remove("enabled_loras"); // alias of selected_loras — do not double-feed
try
{
string pid = AssistentConfig.SafeId(ctx["persona"]?.ToString()) ?? Config?.DefaultPersonaId() ?? "neutral";
@@ -623,6 +639,49 @@ public partial class SwarmAssistentExtension
return ctx.ToString(Newtonsoft.Json.Formatting.None);
}
static bool LiveContextHasSize(string contextJson)
{
if (string.IsNullOrWhiteSpace(contextJson))
{
return false;
}
try
{
JObject ctx = JObject.Parse(contextJson);
int? w = ctx["width"]?.Value<int?>();
int? h = ctx["height"]?.Value<int?>();
return w is > 0 && h is > 0;
}
catch
{
return false;
}
}
/// <summary>Drop RAG rows that only point at Exact (legacy krea_facts seed / "see Exact memory…").</summary>
static bool IsExactPointerHit(JObject ho)
{
if (ho is null)
{
return false;
}
string key = (ho["key"]?.ToString() ?? "").Trim().ToLowerInvariant();
if (key.StartsWith("krea2_", StringComparison.Ordinal))
{
return true;
}
string text = (ho["text"]?.ToString() ?? "").ToLowerInvariant();
if (string.IsNullOrWhiteSpace(text))
{
return false;
}
return text.Contains("live in exact memory", StringComparison.Ordinal)
|| text.Contains("see exact memory", StringComparison.Ordinal)
|| text.Contains("prefer exact kv", StringComparison.Ordinal)
|| text.Contains("exact.facts.", StringComparison.Ordinal)
|| text.Contains("exact memory profiles.", StringComparison.Ordinal);
}
void SlimAvailableLorasInContext(JObject ctx, JArray hits)
{
if (ctx["available_loras"] is not JArray allLoras || allLoras.Count == 0)
@@ -851,17 +910,17 @@ public partial class SwarmAssistentExtension
}
}
JArray catalog = [];
foreach (var p in Config.ListPersonaCatalog())
{
catalog.Add(new JObject
{
["id"] = p.id,
["title"] = p.title,
});
}
ctx["personas"] = catalog;
if (authorPack)
{
foreach (var p in Config.ListPersonaCatalog())
{
catalog.Add(new JObject
{
["id"] = p.id,
["title"] = p.title,
});
}
ctx["personas"] = catalog;
JObject shelves = Config.LoadIdentityParts(pid);
shelves.Remove("extra");
ctx["persona_shelves"] = shelves;