Ship persona shelves, Exact controls, and overlay author pipeline.
Add Leonid as a shelf-based example with preference_bias slider; support /persona new clone-to-overlay and UI-only delete. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -153,6 +153,7 @@ 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);
|
||||
JArray civitaiResults = [];
|
||||
string reply = "";
|
||||
@@ -169,6 +170,7 @@ public partial class SwarmAssistentExtension
|
||||
(reply, lastRaw) = await CallOllamaChat(root, modelName, messages, stream: onDelta is not null, onDelta, pid);
|
||||
JObject patch = TryParsePatch(reply);
|
||||
await ApplyMemoryActions(root, patch, embed, pid);
|
||||
ApplyPersonaActions(patch, ref pid);
|
||||
if (hop + 1 >= maxHops)
|
||||
{
|
||||
break;
|
||||
@@ -450,6 +452,124 @@ public partial class SwarmAssistentExtension
|
||||
return AssistentConfig.SafeId(currentPersonaId) ?? AssistentMemory.SharedPersona;
|
||||
}
|
||||
|
||||
string EnrichPersonaContext(string contextJson, string personaId, string packName)
|
||||
{
|
||||
JObject ctx;
|
||||
try
|
||||
{
|
||||
ctx = string.IsNullOrWhiteSpace(contextJson) ? new JObject() : JObject.Parse(contextJson);
|
||||
}
|
||||
catch
|
||||
{
|
||||
ctx = new JObject { ["_raw_context"] = contextJson };
|
||||
}
|
||||
string pid = AssistentConfig.SafeId(personaId) ?? Config.DefaultPersonaId();
|
||||
ctx["persona_source"] = Config.PersonaSource(pid);
|
||||
JObject schema = Config.LoadControlsSchema(pid);
|
||||
JObject values = Config.LoadControlValues(pid);
|
||||
if (schema.Properties().Any())
|
||||
{
|
||||
ctx["persona_controls"] = new JObject
|
||||
{
|
||||
["schema"] = schema,
|
||||
["values"] = values,
|
||||
};
|
||||
}
|
||||
JArray catalog = [];
|
||||
foreach (var p in Config.ListPersonaCatalog())
|
||||
{
|
||||
catalog.Add(new JObject
|
||||
{
|
||||
["id"] = p.id,
|
||||
["title"] = p.title,
|
||||
["source"] = p.source,
|
||||
});
|
||||
}
|
||||
ctx["personas"] = catalog;
|
||||
if (string.Equals(packName, "author_persona", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(packName, "persona", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
JObject shelves = Config.LoadIdentityParts(pid);
|
||||
shelves.Remove("extra");
|
||||
ctx["persona_shelves"] = shelves;
|
||||
ctx["persona_controls_schema"] = schema;
|
||||
}
|
||||
return ctx.ToString(Newtonsoft.Json.Formatting.None);
|
||||
}
|
||||
|
||||
/// <summary>Apply overlay persona clone/write from patch. Ignores persona_delete. Updates pid ref after switch.</summary>
|
||||
void ApplyPersonaActions(JObject patch, ref string personaId)
|
||||
{
|
||||
if (patch is null || Config is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Never honor delete from the model.
|
||||
bool wantClone = false, wantWrite = false;
|
||||
if (patch["actions"] is JArray acts)
|
||||
{
|
||||
foreach (JToken a in acts)
|
||||
{
|
||||
string s = a?.ToString() ?? "";
|
||||
if (string.Equals(s, "persona_clone", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
wantClone = true;
|
||||
}
|
||||
if (string.Equals(s, "persona_write", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
wantWrite = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (patch["persona_clone"] is JObject)
|
||||
{
|
||||
wantClone = true;
|
||||
}
|
||||
if (patch["persona_shelves"] is JObject)
|
||||
{
|
||||
wantWrite = true;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (wantClone && patch["persona_clone"] is JObject clone)
|
||||
{
|
||||
string from = AssistentConfig.SafeId(clone["from"]?.ToString()) ?? personaId;
|
||||
string to = AssistentConfig.SafeId(clone["to"]?.ToString());
|
||||
string title = clone["title"]?.ToString();
|
||||
bool overwrite = clone["overwrite"]?.Value<bool?>() == true;
|
||||
if (to is not null)
|
||||
{
|
||||
Config.ClonePersonaToOverlay(from, to, title, overwrite);
|
||||
personaId = to;
|
||||
patch["_persona_cloned"] = to;
|
||||
}
|
||||
}
|
||||
if (wantWrite && patch["persona_shelves"] is JObject shelves)
|
||||
{
|
||||
string target = AssistentConfig.SafeId(patch["persona"]?.ToString())
|
||||
?? AssistentConfig.SafeId(patch["persona_clone"]?["to"]?.ToString())
|
||||
?? personaId;
|
||||
if (target is not null)
|
||||
{
|
||||
Config.SavePersonaShelves(target, shelves);
|
||||
patch["_persona_written"] = target;
|
||||
}
|
||||
}
|
||||
// Control values from model patch (Exact).
|
||||
if (patch["controls"] is JObject ctrlVals)
|
||||
{
|
||||
string ctrlPid = AssistentConfig.SafeId(patch["persona"]?.ToString()) ?? personaId;
|
||||
Config.SaveControlValues(ctrlPid, ctrlVals);
|
||||
patch["_controls_saved"] = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logs.Warning($"Assistent persona actions: {ex.Message}");
|
||||
patch["_persona_error"] = ex.Message;
|
||||
}
|
||||
}
|
||||
|
||||
async Task ApplyMemoryActions(string root, JObject patch, string embedModel, string personaId)
|
||||
{
|
||||
if (patch is null || Memory is null)
|
||||
|
||||
Reference in New Issue
Block a user