Separate About-the-user memory (global + per-persona) from craft RAG, add tabbed settings with persona export/import and craft clear APIs. Co-authored-by: Cursor <cursoragent@cursor.com>
259 lines
9.0 KiB
C#
259 lines
9.0 KiB
C#
using System;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Newtonsoft.Json.Linq;
|
|
using SwarmUI.Accounts;
|
|
using SwarmUI.Utils;
|
|
|
|
namespace Mrleo1nid.SwarmAssistent;
|
|
|
|
/// <summary>Persona overlay CRUD: controls Exact, clone/write shelves, UI-only delete.</summary>
|
|
public partial class SwarmAssistentExtension
|
|
{
|
|
public async Task<JObject> AssistentSaveControls(Session session, string persona = null, JObject controls = null)
|
|
{
|
|
await Task.CompletedTask;
|
|
string pid = AssistentConfig.SafeId(persona) ?? Config.DefaultPersonaId();
|
|
try
|
|
{
|
|
JObject saved = Config.SaveControlValues(pid, controls ?? new JObject());
|
|
return new JObject
|
|
{
|
|
["success"] = true,
|
|
["persona"] = pid,
|
|
["control_values"] = saved,
|
|
["controls"] = Config.LoadControlsSchema(pid),
|
|
};
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new JObject { ["error"] = $"controls save: {ex.Message}" };
|
|
}
|
|
}
|
|
|
|
public async Task<JObject> AssistentGetPersonaShelves(Session session, string persona = null)
|
|
{
|
|
await Task.CompletedTask;
|
|
string pid = AssistentConfig.SafeId(persona) ?? Config.DefaultPersonaId();
|
|
return new JObject
|
|
{
|
|
["success"] = true,
|
|
["persona"] = pid,
|
|
["source"] = Config.PersonaSource(pid),
|
|
["shelves"] = Config.LoadIdentityParts(pid),
|
|
["controls"] = Config.LoadControlsSchema(pid),
|
|
["control_values"] = Config.LoadControlValues(pid),
|
|
["identity_summary"] = Config.RenderIdentityBlock(pid),
|
|
};
|
|
}
|
|
|
|
public async Task<JObject> AssistentClonePersona(Session session, string from = null, string to = null, string title = null, bool overwrite = false)
|
|
{
|
|
await Task.CompletedTask;
|
|
string src = AssistentConfig.SafeId(from) ?? Config.DefaultPersonaId();
|
|
string dest = AssistentConfig.SafeId(to);
|
|
if (dest is null)
|
|
{
|
|
return new JObject { ["error"] = "invalid to id" };
|
|
}
|
|
try
|
|
{
|
|
JObject meta = Config.ClonePersonaToOverlay(src, dest, title, overwrite);
|
|
return new JObject
|
|
{
|
|
["success"] = true,
|
|
["persona"] = meta,
|
|
["personas"] = new JArray(Config.ListPersonaCatalog().Select(p => new JObject
|
|
{
|
|
["id"] = p.id,
|
|
["title"] = p.title,
|
|
["accent"] = p.accent,
|
|
["source"] = p.source,
|
|
})),
|
|
};
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new JObject { ["error"] = ex.Message };
|
|
}
|
|
}
|
|
|
|
public async Task<JObject> AssistentSavePersona(Session session, string persona = null, JObject shelves = null)
|
|
{
|
|
await Task.CompletedTask;
|
|
string pid = AssistentConfig.SafeId(persona);
|
|
if (pid is null)
|
|
{
|
|
return new JObject { ["error"] = "invalid persona id" };
|
|
}
|
|
try
|
|
{
|
|
JObject saved = Config.SavePersonaShelves(pid, shelves);
|
|
return new JObject
|
|
{
|
|
["success"] = true,
|
|
["persona"] = pid,
|
|
["source"] = Config.PersonaSource(pid),
|
|
["shelves"] = saved,
|
|
};
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new JObject { ["error"] = ex.Message };
|
|
}
|
|
}
|
|
|
|
/// <summary>UI-only. Never called from LLM patch actions.</summary>
|
|
public async Task<JObject> AssistentDeletePersona(Session session, string persona = null)
|
|
{
|
|
await Task.CompletedTask;
|
|
string pid = AssistentConfig.SafeId(persona);
|
|
if (pid is null)
|
|
{
|
|
return new JObject { ["error"] = "invalid persona id" };
|
|
}
|
|
if (!Config.IsOverlayPersona(pid))
|
|
{
|
|
return new JObject { ["error"] = "only overlay personas can be deleted" };
|
|
}
|
|
try
|
|
{
|
|
bool ok = Config.DeleteOverlayPersona(pid);
|
|
string next = Config.DefaultPersonaId();
|
|
return new JObject
|
|
{
|
|
["success"] = ok,
|
|
["deleted"] = pid,
|
|
["default_persona"] = next,
|
|
["personas"] = new JArray(Config.ListPersonaCatalog().Select(p => new JObject
|
|
{
|
|
["id"] = p.id,
|
|
["title"] = p.title,
|
|
["accent"] = p.accent,
|
|
["source"] = p.source,
|
|
})),
|
|
};
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new JObject { ["error"] = ex.Message };
|
|
}
|
|
}
|
|
|
|
public async Task<JObject> AssistentExportPersona(Session session, string persona = null)
|
|
{
|
|
await Task.CompletedTask;
|
|
string pid = AssistentConfig.SafeId(persona) ?? Config.DefaultPersonaId();
|
|
try
|
|
{
|
|
JObject pack = Config.ExportPersonaPack(pid);
|
|
return new JObject { ["success"] = true, ["pack"] = pack };
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new JObject { ["error"] = $"export persona: {ex.Message}" };
|
|
}
|
|
}
|
|
|
|
public async Task<JObject> AssistentImportPersona(Session session, JObject pack = null, string new_id = null, bool overwrite = false)
|
|
{
|
|
await Task.CompletedTask;
|
|
try
|
|
{
|
|
JObject meta = Config.ImportPersonaPack(pack, new_id, overwrite);
|
|
return new JObject
|
|
{
|
|
["success"] = true,
|
|
["persona"] = meta,
|
|
["personas"] = new JArray(Config.ListPersonaCatalog().Select(p => new JObject
|
|
{
|
|
["id"] = p.id,
|
|
["title"] = p.title,
|
|
["accent"] = p.accent,
|
|
["source"] = p.source,
|
|
})),
|
|
};
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new JObject { ["error"] = ex.Message };
|
|
}
|
|
}
|
|
|
|
public async Task<JObject> AssistentSaveKnobs(Session session, JObject assistant = null, JObject exact = null)
|
|
{
|
|
await Task.CompletedTask;
|
|
try
|
|
{
|
|
JObject asstOut = null;
|
|
JObject exactOut = null;
|
|
if (assistant is not null && assistant.Count > 0)
|
|
{
|
|
JObject sparse = new();
|
|
foreach (string key in new[] { "num_ctx", "history_keep_turns", "memory_top_k", "user_prefs_weight", "user_prefs_max" })
|
|
{
|
|
if (assistant[key] is not null)
|
|
{
|
|
sparse[key] = assistant[key];
|
|
}
|
|
}
|
|
if (sparse.Count > 0)
|
|
{
|
|
asstOut = Config.MergeOverlayBaseJson("assistant.json", sparse);
|
|
}
|
|
}
|
|
if (exact is not null && exact.Count > 0)
|
|
{
|
|
JObject sparse = new();
|
|
if (exact["profiles"] is JObject profiles)
|
|
{
|
|
JObject slimProfiles = new();
|
|
foreach (string name in new[] { "turbo", "raw" })
|
|
{
|
|
if (profiles[name] is JObject p)
|
|
{
|
|
JObject slim = new();
|
|
foreach (string k in new[] { "steps", "cfg", "sigma_shift" })
|
|
{
|
|
if (p[k] is not null)
|
|
{
|
|
slim[k] = p[k];
|
|
}
|
|
}
|
|
if (slim.Count > 0)
|
|
{
|
|
slimProfiles[name] = slim;
|
|
}
|
|
}
|
|
}
|
|
if (slimProfiles.Count > 0)
|
|
{
|
|
sparse["profiles"] = slimProfiles;
|
|
}
|
|
}
|
|
if (exact["generation"] is JObject gen)
|
|
{
|
|
sparse["generation"] = gen;
|
|
}
|
|
if (sparse.Count > 0)
|
|
{
|
|
exactOut = Config.MergeOverlayBaseJson("exact.json", sparse);
|
|
}
|
|
}
|
|
string pid = Config.DefaultPersonaId();
|
|
return new JObject
|
|
{
|
|
["success"] = true,
|
|
["assistant"] = Config.LoadAssistant(pid),
|
|
["exact"] = Config.LoadExact(pid),
|
|
["overlay_assistant"] = asstOut,
|
|
["overlay_exact"] = exactOut,
|
|
};
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new JObject { ["error"] = $"save knobs: {ex.Message}" };
|
|
}
|
|
}
|
|
}
|