Add FTS lookup for seeded Civitai Krea2 prompt examples.
Index Assistent/civitai-examples.jsonl without embeddings and expose AssistentLookupExamples plus ask:examples hop for remix references. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -421,6 +421,30 @@ public partial class SwarmAssistentExtension
|
|||||||
+ "omit ask:inventory unless you need a different query.\n```json\n"
|
+ "omit ask:inventory unless you need a different query.\n```json\n"
|
||||||
+ rows.ToString(Newtonsoft.Json.Formatting.None) + "\n```";
|
+ rows.ToString(Newtonsoft.Json.Formatting.None) + "\n```";
|
||||||
}
|
}
|
||||||
|
if (tool == "ask_examples")
|
||||||
|
{
|
||||||
|
string q = patch?["example_query"]?.ToString()?.Trim()
|
||||||
|
?? patch?["memory_query"]?.ToString()?.Trim()
|
||||||
|
?? "";
|
||||||
|
string rating = patch?["example_rating"]?.ToString()?.Trim();
|
||||||
|
string sig = "ask_examples:" + q.ToLowerInvariant() + ":" + (rating ?? "");
|
||||||
|
if (!hopDone.Add(sig))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (string.IsNullOrWhiteSpace(q))
|
||||||
|
{
|
||||||
|
return
|
||||||
|
"ask:examples needs example_query (tags / short scene). "
|
||||||
|
+ "Retry with \"ask\":[\"examples\"], \"example_query\":\"redhead stockings cinematic\".";
|
||||||
|
}
|
||||||
|
int lim = Config.LoadAssistant(pid)["examples_hop_limit"]?.Value<int?>() ?? 5;
|
||||||
|
JArray examples = Memory?.LookupExamples(q, lim, rating) ?? [];
|
||||||
|
return
|
||||||
|
"ask:examples — Civitai Krea2 prompt references (FTS, no embeddings). "
|
||||||
|
+ "These are EXAMPLES to remix, not copy 1:1. Prefer craft over pasting.\n```json\n"
|
||||||
|
+ examples.ToString(Newtonsoft.Json.Formatting.None) + "\n```";
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,383 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.Data.Sqlite;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using SwarmUI.Utils;
|
||||||
|
|
||||||
|
namespace Mrleo1nid.SwarmAssistent;
|
||||||
|
|
||||||
|
/// <summary>Civitai Krea2 example prompts — FTS only (no embeddings), like Danbooru tags.</summary>
|
||||||
|
public sealed partial class AssistentMemory
|
||||||
|
{
|
||||||
|
const string ExamplesMetaFp = "civitai_examples_fp";
|
||||||
|
const string ExamplesMetaCount = "civitai_examples_count";
|
||||||
|
|
||||||
|
void TryIndexExamples()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EnsureExamplesIndex();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logs.Debug($"AssistentMemory examples index: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnsureExamplesSchema()
|
||||||
|
{
|
||||||
|
Exec(
|
||||||
|
"""
|
||||||
|
CREATE TABLE IF NOT EXISTS civitai_examples (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
rating TEXT NOT NULL DEFAULT 'pg',
|
||||||
|
score INTEGER NOT NULL DEFAULT 0,
|
||||||
|
kind TEXT NOT NULL DEFAULT '',
|
||||||
|
model_version_id INTEGER NOT NULL DEFAULT 0,
|
||||||
|
tags TEXT NOT NULL DEFAULT '',
|
||||||
|
prompt TEXT NOT NULL DEFAULT '',
|
||||||
|
negative TEXT NOT NULL DEFAULT '',
|
||||||
|
params_json TEXT NOT NULL DEFAULT '{}',
|
||||||
|
loras_json TEXT NOT NULL DEFAULT '[]',
|
||||||
|
search_blob TEXT NOT NULL DEFAULT ''
|
||||||
|
);
|
||||||
|
""");
|
||||||
|
Exec("CREATE INDEX IF NOT EXISTS idx_civitai_examples_score ON civitai_examples(score DESC);");
|
||||||
|
Exec(
|
||||||
|
"""
|
||||||
|
CREATE VIRTUAL TABLE IF NOT EXISTS civitai_examples_fts USING fts5(
|
||||||
|
tags,
|
||||||
|
prompt,
|
||||||
|
search_blob,
|
||||||
|
tokenize = 'unicode61 remove_diacritics 2'
|
||||||
|
);
|
||||||
|
""");
|
||||||
|
Exec(
|
||||||
|
"""
|
||||||
|
CREATE TRIGGER IF NOT EXISTS civitai_examples_fts_ai AFTER INSERT ON civitai_examples BEGIN
|
||||||
|
INSERT INTO civitai_examples_fts(rowid, tags, prompt, search_blob)
|
||||||
|
VALUES (new.id, new.tags, new.prompt, new.search_blob);
|
||||||
|
END;
|
||||||
|
""");
|
||||||
|
Exec(
|
||||||
|
"""
|
||||||
|
CREATE TRIGGER IF NOT EXISTS civitai_examples_fts_ad AFTER DELETE ON civitai_examples BEGIN
|
||||||
|
INSERT INTO civitai_examples_fts(civitai_examples_fts, rowid) VALUES('delete', old.id);
|
||||||
|
END;
|
||||||
|
""");
|
||||||
|
Exec(
|
||||||
|
"""
|
||||||
|
CREATE TRIGGER IF NOT EXISTS civitai_examples_fts_au AFTER UPDATE ON civitai_examples BEGIN
|
||||||
|
INSERT INTO civitai_examples_fts(civitai_examples_fts, rowid) VALUES('delete', old.id);
|
||||||
|
INSERT INTO civitai_examples_fts(rowid, tags, prompt, search_blob)
|
||||||
|
VALUES (new.id, new.tags, new.prompt, new.search_blob);
|
||||||
|
END;
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FindCivitaiExamplesJsonl()
|
||||||
|
{
|
||||||
|
string[] candidates =
|
||||||
|
[
|
||||||
|
Path.Combine(_dataRoot, "Assistent", "civitai-examples.jsonl"),
|
||||||
|
Path.Combine(_dataRoot, "civitai-examples.jsonl"),
|
||||||
|
];
|
||||||
|
foreach (string path in candidates)
|
||||||
|
{
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string FileFingerprint(string path)
|
||||||
|
{
|
||||||
|
var info = new FileInfo(path);
|
||||||
|
using SHA256 sha = SHA256.Create();
|
||||||
|
using FileStream fs = File.OpenRead(path);
|
||||||
|
byte[] hash = sha.ComputeHash(fs);
|
||||||
|
string hex = Convert.ToHexString(hash).ToLowerInvariant();
|
||||||
|
return $"{info.Length}:{info.LastWriteTimeUtc.Ticks}:{hex}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Load search.jsonl into FTS (no embeddings). No-op if fingerprint matches.</summary>
|
||||||
|
public int EnsureExamplesIndex()
|
||||||
|
{
|
||||||
|
string path = FindCivitaiExamplesJsonl();
|
||||||
|
if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
string fp = FileFingerprint(path);
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
EnsureOpen();
|
||||||
|
EnsureExamplesSchema();
|
||||||
|
if (string.Equals(GetMeta(ExamplesMetaFp), fp, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
using SqliteCommand c = _conn.CreateCommand();
|
||||||
|
c.CommandText = "SELECT COUNT(*) FROM civitai_examples";
|
||||||
|
return Convert.ToInt32(c.ExecuteScalar());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<(long id, string rating, int score, string kind, int mvid, string tags, string prompt, string negative, string paramsJson, string lorasJson, string blob)> rows = [];
|
||||||
|
foreach (string line in File.ReadLines(path, Encoding.UTF8))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(line))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
JObject o;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
o = JObject.Parse(line);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
long id = o["id"]?.Value<long?>() ?? 0;
|
||||||
|
if (id <= 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
string prompt = o["prompt"]?.ToString() ?? "";
|
||||||
|
if (string.IsNullOrWhiteSpace(prompt))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
string tagsJoined = "";
|
||||||
|
if (o["tags"] is JArray tagArr)
|
||||||
|
{
|
||||||
|
tagsJoined = string.Join(", ", tagArr.Select(t => t?.ToString()?.Trim()).Where(t => !string.IsNullOrWhiteSpace(t)));
|
||||||
|
}
|
||||||
|
string rating = o["rating"]?.ToString() ?? "pg";
|
||||||
|
int score = o["score"]?.Value<int?>() ?? 0;
|
||||||
|
string kind = o["kind"]?.ToString() ?? "";
|
||||||
|
int mvid = o["modelVersionId"]?.Value<int?>() ?? 0;
|
||||||
|
string negative = o["negative"]?.ToString() ?? "";
|
||||||
|
string paramsJson = (o["params"] as JObject)?.ToString(Newtonsoft.Json.Formatting.None) ?? "{}";
|
||||||
|
string lorasJson = (o["loras"] as JArray)?.ToString(Newtonsoft.Json.Formatting.None) ?? "[]";
|
||||||
|
string blob = $"{tagsJoined}\n{prompt}\n{rating}\n{kind}";
|
||||||
|
rows.Add((id, rating, score, kind, mvid, tagsJoined, prompt, negative, paramsJson, lorasJson, blob));
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
EnsureOpen();
|
||||||
|
EnsureExamplesSchema();
|
||||||
|
using SqliteTransaction tx = _conn.BeginTransaction();
|
||||||
|
using (SqliteCommand del = _conn.CreateCommand())
|
||||||
|
{
|
||||||
|
del.Transaction = tx;
|
||||||
|
del.CommandText = "DELETE FROM civitai_examples";
|
||||||
|
del.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (SqliteCommand delFts = _conn.CreateCommand())
|
||||||
|
{
|
||||||
|
delFts.Transaction = tx;
|
||||||
|
delFts.CommandText = "DELETE FROM civitai_examples_fts";
|
||||||
|
delFts.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// FTS missing
|
||||||
|
}
|
||||||
|
using (SqliteCommand ins = _conn.CreateCommand())
|
||||||
|
{
|
||||||
|
ins.Transaction = tx;
|
||||||
|
ins.CommandText =
|
||||||
|
"""
|
||||||
|
INSERT OR REPLACE INTO civitai_examples(
|
||||||
|
id, rating, score, kind, model_version_id, tags, prompt, negative, params_json, loras_json, search_blob)
|
||||||
|
VALUES($id,$r,$s,$k,$m,$t,$p,$n,$pj,$lj,$b)
|
||||||
|
""";
|
||||||
|
var pid = ins.Parameters.Add("$id", SqliteType.Integer);
|
||||||
|
var pr = ins.Parameters.Add("$r", SqliteType.Text);
|
||||||
|
var ps = ins.Parameters.Add("$s", SqliteType.Integer);
|
||||||
|
var pk = ins.Parameters.Add("$k", SqliteType.Text);
|
||||||
|
var pm = ins.Parameters.Add("$m", SqliteType.Integer);
|
||||||
|
var pt = ins.Parameters.Add("$t", SqliteType.Text);
|
||||||
|
var pp = ins.Parameters.Add("$p", SqliteType.Text);
|
||||||
|
var pn = ins.Parameters.Add("$n", SqliteType.Text);
|
||||||
|
var ppj = ins.Parameters.Add("$pj", SqliteType.Text);
|
||||||
|
var plj = ins.Parameters.Add("$lj", SqliteType.Text);
|
||||||
|
var pb = ins.Parameters.Add("$b", SqliteType.Text);
|
||||||
|
foreach (var row in rows)
|
||||||
|
{
|
||||||
|
pid.Value = row.id;
|
||||||
|
pr.Value = row.rating;
|
||||||
|
ps.Value = row.score;
|
||||||
|
pk.Value = row.kind;
|
||||||
|
pm.Value = row.mvid;
|
||||||
|
pt.Value = row.tags;
|
||||||
|
pp.Value = row.prompt;
|
||||||
|
pn.Value = row.negative;
|
||||||
|
ppj.Value = row.paramsJson;
|
||||||
|
plj.Value = row.lorasJson;
|
||||||
|
pb.Value = row.blob;
|
||||||
|
ins.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tx.Commit();
|
||||||
|
SetMeta(ExamplesMetaFp, fp);
|
||||||
|
SetMeta(ExamplesMetaCount, rows.Count.ToString(CultureInfo.InvariantCulture));
|
||||||
|
Logs.Info($"AssistentMemory: indexed {rows.Count} civitai examples from {Path.GetFileName(path)}");
|
||||||
|
return rows.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ExampleCount()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
EnsureOpen();
|
||||||
|
if (!TableExists("civitai_examples"))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
using SqliteCommand c = _conn.CreateCommand();
|
||||||
|
c.CommandText = "SELECT COUNT(*) FROM civitai_examples";
|
||||||
|
return Convert.ToInt32(c.ExecuteScalar());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>FTS / LIKE lookup over Civitai example prompts. No embeddings.</summary>
|
||||||
|
public JArray LookupExamples(string query, int limit = 5, string rating = null)
|
||||||
|
{
|
||||||
|
query = (query ?? "").Trim();
|
||||||
|
if (query.Length < 1)
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
TryIndexExamples();
|
||||||
|
int cap = Math.Clamp(limit, 1, 20);
|
||||||
|
string ratingFilter = string.IsNullOrWhiteSpace(rating) ? null : rating.Trim().ToLowerInvariant();
|
||||||
|
List<JObject> hits = [];
|
||||||
|
HashSet<long> seen = [];
|
||||||
|
|
||||||
|
void Add(SqliteDataReader reader)
|
||||||
|
{
|
||||||
|
long id = reader.GetInt64(0);
|
||||||
|
if (!seen.Add(id))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
string tags = reader.IsDBNull(4) ? "" : reader.GetString(4);
|
||||||
|
string prompt = reader.IsDBNull(5) ? "" : reader.GetString(5);
|
||||||
|
string negative = reader.IsDBNull(6) ? "" : reader.GetString(6);
|
||||||
|
string paramsJson = reader.IsDBNull(7) ? "{}" : reader.GetString(7);
|
||||||
|
string lorasJson = reader.IsDBNull(8) ? "[]" : reader.GetString(8);
|
||||||
|
JToken paramsTok = null;
|
||||||
|
JToken lorasTok = null;
|
||||||
|
try { paramsTok = JToken.Parse(paramsJson); } catch { paramsTok = new JObject(); }
|
||||||
|
try { lorasTok = JToken.Parse(lorasJson); } catch { lorasTok = new JArray(); }
|
||||||
|
hits.Add(new JObject
|
||||||
|
{
|
||||||
|
["id"] = id,
|
||||||
|
["rating"] = reader.IsDBNull(1) ? "pg" : reader.GetString(1),
|
||||||
|
["score"] = reader.GetInt32(2),
|
||||||
|
["kind"] = reader.IsDBNull(3) ? "" : reader.GetString(3),
|
||||||
|
["tags"] = tags,
|
||||||
|
["prompt"] = prompt.Length > 1200 ? prompt[..1200] + "…" : prompt,
|
||||||
|
["negative"] = negative.Length > 400 ? negative[..400] + "…" : negative,
|
||||||
|
["params"] = paramsTok,
|
||||||
|
["loras"] = lorasTok,
|
||||||
|
["note"] = "EXAMPLE from Civitai — remix, do not copy 1:1",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
EnsureOpen();
|
||||||
|
if (!TableExists("civitai_examples"))
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
string match = BuildFtsMatch(query);
|
||||||
|
if (!string.IsNullOrWhiteSpace(match) && TableExists("civitai_examples_fts"))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using SqliteCommand cmd = _conn.CreateCommand();
|
||||||
|
string sql =
|
||||||
|
"""
|
||||||
|
SELECT e.id, e.rating, e.score, e.kind, e.tags, e.prompt, e.negative, e.params_json, e.loras_json
|
||||||
|
FROM civitai_examples e
|
||||||
|
WHERE e.id IN (SELECT rowid FROM civitai_examples_fts WHERE civitai_examples_fts MATCH $q)
|
||||||
|
""";
|
||||||
|
if (ratingFilter is not null)
|
||||||
|
{
|
||||||
|
sql += " AND lower(e.rating) = $r";
|
||||||
|
}
|
||||||
|
sql += " ORDER BY e.score DESC LIMIT $lim";
|
||||||
|
cmd.CommandText = sql;
|
||||||
|
cmd.Parameters.AddWithValue("$q", match);
|
||||||
|
if (ratingFilter is not null)
|
||||||
|
{
|
||||||
|
cmd.Parameters.AddWithValue("$r", ratingFilter);
|
||||||
|
}
|
||||||
|
cmd.Parameters.AddWithValue("$lim", cap);
|
||||||
|
using SqliteDataReader reader = cmd.ExecuteReader();
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
Add(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logs.Debug($"AssistentMemory examples FTS: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hits.Count < cap)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using SqliteCommand cmd = _conn.CreateCommand();
|
||||||
|
string sql =
|
||||||
|
"""
|
||||||
|
SELECT id, rating, score, kind, tags, prompt, negative, params_json, loras_json
|
||||||
|
FROM civitai_examples
|
||||||
|
WHERE (tags LIKE $p ESCAPE '\' OR prompt LIKE $p ESCAPE '\' OR search_blob LIKE $p ESCAPE '\')
|
||||||
|
""";
|
||||||
|
if (ratingFilter is not null)
|
||||||
|
{
|
||||||
|
sql += " AND lower(rating) = $r";
|
||||||
|
}
|
||||||
|
sql += " ORDER BY score DESC LIMIT $lim";
|
||||||
|
cmd.CommandText = sql;
|
||||||
|
cmd.Parameters.AddWithValue("$p", "%" + EscapeLike(query) + "%");
|
||||||
|
if (ratingFilter is not null)
|
||||||
|
{
|
||||||
|
cmd.Parameters.AddWithValue("$r", ratingFilter);
|
||||||
|
}
|
||||||
|
cmd.Parameters.AddWithValue("$lim", cap);
|
||||||
|
using SqliteDataReader reader = cmd.ExecuteReader();
|
||||||
|
while (reader.Read() && hits.Count < cap)
|
||||||
|
{
|
||||||
|
Add(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logs.Debug($"AssistentMemory examples LIKE: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JArray(hits.Take(cap));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -414,6 +414,7 @@ public sealed partial class AssistentMemory : IDisposable
|
|||||||
if (docs.Count == 0)
|
if (docs.Count == 0)
|
||||||
{
|
{
|
||||||
TryIndexTags();
|
TryIndexTags();
|
||||||
|
TryIndexExamples();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -434,6 +435,7 @@ public sealed partial class AssistentMemory : IDisposable
|
|||||||
if (!missing)
|
if (!missing)
|
||||||
{
|
{
|
||||||
TryIndexTags();
|
TryIndexTags();
|
||||||
|
TryIndexExamples();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -447,6 +449,7 @@ public sealed partial class AssistentMemory : IDisposable
|
|||||||
{
|
{
|
||||||
Logs.Debug($"AssistentMemory seed defer (embed unavailable): {ex.Message}");
|
Logs.Debug($"AssistentMemory seed defer (embed unavailable): {ex.Message}");
|
||||||
TryIndexTags();
|
TryIndexTags();
|
||||||
|
TryIndexExamples();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,6 +508,7 @@ public sealed partial class AssistentMemory : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TryIndexTags();
|
TryIndexTags();
|
||||||
|
TryIndexExamples();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Upsert(string kind, string key, string text, string source, JToken meta, float[] embedding, string persona = null)
|
public void Upsert(string kind, string key, string text, string source, JToken meta, float[] embedding, string persona = null)
|
||||||
|
|||||||
@@ -240,6 +240,37 @@ public partial class SwarmAssistentExtension
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<JObject> AssistentLookupExamples(Session session, string query, int limit = 5, string rating = null)
|
||||||
|
{
|
||||||
|
await Task.CompletedTask;
|
||||||
|
if (Memory is null)
|
||||||
|
{
|
||||||
|
return new JObject { ["error"] = "memory not ready" };
|
||||||
|
}
|
||||||
|
query = (query ?? "").Trim();
|
||||||
|
if (query.Length < 1)
|
||||||
|
{
|
||||||
|
return new JObject { ["error"] = "query required" };
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JArray examples = Memory.LookupExamples(query, limit, rating);
|
||||||
|
return new JObject
|
||||||
|
{
|
||||||
|
["success"] = true,
|
||||||
|
["query"] = query,
|
||||||
|
["rating"] = rating ?? "",
|
||||||
|
["examples"] = examples,
|
||||||
|
["indexed"] = Memory.ExampleCount(),
|
||||||
|
["path"] = Memory.FindCivitaiExamplesJsonl() ?? "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new JObject { ["error"] = $"example lookup: {ex.Message}" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<JObject> AssistentClearMemory(Session session, string scope = null, string kind = null, string persona = null)
|
public async Task<JObject> AssistentClearMemory(Session session, string scope = null, string kind = null, string persona = null)
|
||||||
{
|
{
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
|
|||||||
+5
-1
@@ -236,7 +236,7 @@ public partial class SwarmAssistentExtension
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Server tool hops are ask-only: settings dump or truncated inventory.</summary>
|
/// <summary>Server tool hops: settings dump, inventory, or Civitai example FTS.</summary>
|
||||||
static string NextToolHop(JObject patch, HashSet<string> skip = null)
|
static string NextToolHop(JObject patch, HashSet<string> skip = null)
|
||||||
{
|
{
|
||||||
if (patch is null)
|
if (patch is null)
|
||||||
@@ -252,6 +252,10 @@ public partial class SwarmAssistentExtension
|
|||||||
{
|
{
|
||||||
return "ask_inventory";
|
return "ask_inventory";
|
||||||
}
|
}
|
||||||
|
if ((AskContains(patch, "examples") || ActionsContain(patch, "lookup_examples")) && !Skip("ask_examples"))
|
||||||
|
{
|
||||||
|
return "ask_examples";
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
# Skill: memory
|
# Skill: memory
|
||||||
|
|
||||||
You have four memory tools:
|
You have five memory tools:
|
||||||
|
|
||||||
1. **Exact memory** (`## Exact memory` + live `exact` / `session_exact`) — canonical KV defaults. Always prefer Exact over RAG for numbers.
|
1. **Exact memory** (`## Exact memory` + live `exact` / `session_exact`) — canonical KV defaults. Always prefer Exact over RAG for numbers.
|
||||||
2. **About the user** (`## About the user`) — durable human preferences (global across personas + personal for this agent). Tunable weight in settings. Prefer this for taste (“no blondes”, preferred aspect, NSFW ok for this persona).
|
2. **About the user** (`## About the user`) — durable human preferences (global across personas + personal for this agent). Tunable weight in settings. Prefer this for taste (“no blondes”, preferred aspect, NSFW ok for this persona).
|
||||||
3. **Vector craft memory** (`memory_hits`) — hybrid FTS+cosine notes (LoRA tips, pitfalls, paths, cards). Shared + this persona; personal overwrites shared on the same `kind`+`key`.
|
3. **Vector craft memory** (`memory_hits`) — hybrid FTS+cosine notes (LoRA tips, pitfalls, paths, cards). Shared + this persona; personal overwrites shared on the same `kind`+`key`.
|
||||||
4. **Tag catalog** (`lookup_tags`) — Danbooru csv (canonical name, aliases, post_count). **Not** RAG. Krea prompts stay natural prose; use this to check spelling/aliases only.
|
4. **Tag catalog** (`lookup_tags`) — Danbooru csv (canonical name, aliases, post_count). **Not** RAG. Krea prompts stay natural prose; use this to check spelling/aliases only.
|
||||||
|
5. **Civitai examples** (`ask: examples` / `lookup_examples`) — popular Krea 2 prompts indexed with FTS (no embeddings). Use when you want a **reference** for how others phrased a scene — remix, do not copy 1:1.
|
||||||
|
|
||||||
## Priority
|
## Priority
|
||||||
|
|
||||||
User (this turn) > About the user > `session_exact` > Exact KV > filled live fields > craft `memory_hits` > guesses. Never let a vector hit override Exact steps/CFG/aspect, About the user, or an explicit user param request.
|
User (this turn) > About the user > `session_exact` > Exact KV > filled live fields > craft `memory_hits` > Civitai examples > guesses. Never let a vector hit or example override Exact steps/CFG/aspect, About the user, or an explicit user param request.
|
||||||
|
|
||||||
## Read tools (hop, like Civitai)
|
## Read tools (hop, like Civitai)
|
||||||
|
|
||||||
- `memory_get` + `memories: [{kind,key}]` — exact craft row (personal overlay if any).
|
- `memory_get` + `memories: [{kind,key}]` — exact craft row (personal overlay if any).
|
||||||
- `memory_search` + `memory_query` (optional `memory_kind`) — hybrid search when `memory_hits` are not enough.
|
- `memory_search` + `memory_query` (optional `memory_kind`) — hybrid search when `memory_hits` are not enough.
|
||||||
- `lookup_tags` + `tag_query` — csv lookup. Do **not** paste tag soup into the prompt.
|
- `lookup_tags` + `tag_query` — csv lookup. Do **not** paste tag soup into the prompt.
|
||||||
|
- `"ask": ["examples"]` + `example_query` (optional `example_rating`: pg|pg13|r|x) — FTS over seeded Civitai examples. Treat hits as style references only.
|
||||||
|
|
||||||
Omit the tool action on the follow-up turn once you have results.
|
Omit the tool action on the follow-up turn once you have results.
|
||||||
|
|
||||||
@@ -39,4 +41,5 @@ Omit the tool action on the follow-up turn once you have results.
|
|||||||
- Do not store the user's taste in craft `memories` — use `user_prefs` instead.
|
- Do not store the user's taste in craft `memories` — use `user_prefs` instead.
|
||||||
- Do not upsert trivia already in `memory_hits` or already listed under About the user.
|
- Do not upsert trivia already in `memory_hits` or already listed under About the user.
|
||||||
- Do not upsert Danbooru tags — the csv catalog already has them.
|
- Do not upsert Danbooru tags — the csv catalog already has them.
|
||||||
|
- Do not upsert Civitai example rows — they live in the FTS index from `civitai-examples.jsonl`.
|
||||||
- `memory_forget` without `scope` only removes the personal overlay.
|
- `memory_forget` without `scope` only removes the personal overlay.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ The **chat model** prepares the Generate-box text for **Krea 2** (Qwen3-VL). Do
|
|||||||
4. Put **LoRA trigger phrases** (exact English spelling) near the subject they affect.
|
4. Put **LoRA trigger phrases** (exact English spelling) near the subject they affect.
|
||||||
5. **`negative` on every Generate** — create if live is empty (Exact `generation.negative`), supplement if the scene needs a specific omit, or echo live unchanged. Put “no blur / no people” ideas as positives in `prompt` instead of stuffing the negative box. Never clear `negative`.
|
5. **`negative` on every Generate** — create if live is empty (Exact `generation.negative`), supplement if the scene needs a specific omit, or echo live unchanged. Put “no blur / no people” ideas as positives in `prompt` instead of stuffing the negative box. Never clear `negative`.
|
||||||
6. Short ideas: expand into a full Krea paragraph. Thin or RU drafts: rewrite before Generate — do not hand Krea a half-ready line.
|
6. Short ideas: expand into a full Krea paragraph. Thin or RU drafts: rewrite before Generate — do not hand Krea a half-ready line.
|
||||||
|
7. Optional: `"ask": ["examples"]` + `example_query` (tags / short scene) to fetch Civitai Krea2 references — remix structure and vocabulary; **never paste an example prompt unchanged**.
|
||||||
|
|
||||||
## Prep checklist (before `"generate": true`)
|
## Prep checklist (before `"generate": true`)
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ public partial class SwarmAssistentExtension : Extension
|
|||||||
API.RegisterAPICall(AssistentSearchMemory, false, PermUse);
|
API.RegisterAPICall(AssistentSearchMemory, false, PermUse);
|
||||||
API.RegisterAPICall(AssistentGetMemory, false, PermUse);
|
API.RegisterAPICall(AssistentGetMemory, false, PermUse);
|
||||||
API.RegisterAPICall(AssistentLookupTags, false, PermUse);
|
API.RegisterAPICall(AssistentLookupTags, false, PermUse);
|
||||||
|
API.RegisterAPICall(AssistentLookupExamples, false, PermUse);
|
||||||
API.RegisterAPICall(AssistentSaveControls, true, PermUse);
|
API.RegisterAPICall(AssistentSaveControls, true, PermUse);
|
||||||
API.RegisterAPICall(AssistentGetPersonaShelves, false, PermUse);
|
API.RegisterAPICall(AssistentGetPersonaShelves, false, PermUse);
|
||||||
API.RegisterAPICall(AssistentClonePersona, true, PermUse);
|
API.RegisterAPICall(AssistentClonePersona, true, PermUse);
|
||||||
|
|||||||
Reference in New Issue
Block a user