Add exact KV memory with persona overlays and param priority.
Canonical defaults live in exact.json (UI fills empties without an LLM call); chat session overrides beat file defaults until clear/persona change. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+275
-28
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Swarm Assistent — Krea 2 collaborative chat (Ollama via Swarm API).
|
* Swarm Assistent — Krea 2 collaborative chat (Ollama via Swarm API).
|
||||||
* v0.7.0: Config presets, persona folders, vector memory, chat|memory model roles.
|
* v0.7.0: Config presets, persona folders, exact KV + vector memory, chat|memory model roles.
|
||||||
*/
|
*/
|
||||||
(function () {
|
(function () {
|
||||||
const LS_BASE = 'swarm_assistent_base_url';
|
const LS_BASE = 'swarm_assistent_base_url';
|
||||||
@@ -99,6 +99,9 @@
|
|||||||
history: [],
|
history: [],
|
||||||
packsLoaded: false,
|
packsLoaded: false,
|
||||||
config: null,
|
config: null,
|
||||||
|
exact: null,
|
||||||
|
sessionExact: {},
|
||||||
|
lastUserParamIntent: false,
|
||||||
enabledSkills: [],
|
enabledSkills: [],
|
||||||
kreaProfiles: { turbo: { steps: 8, cfg: 1, sigma_shift: 1.15 }, raw: { steps: 28, cfg: 4.5 } },
|
kreaProfiles: { turbo: { steps: 8, cfg: 1, sigma_shift: 1.15 }, raw: { steps: 28, cfg: 4.5 } },
|
||||||
preferredEmbed: null,
|
preferredEmbed: null,
|
||||||
@@ -424,6 +427,158 @@
|
|||||||
el.dispatchEvent(new Event('change', { bubbles: true }));
|
el.dispatchEvent(new Event('change', { bubbles: true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isEmptyParamField(raw, { treatZeroEmpty = false } = {}) {
|
||||||
|
if (raw == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const s = String(raw).trim();
|
||||||
|
if (s === '') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (treatZeroEmpty && (s === '0' || Number(s) === 0)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function userTextMentionsParams(text) {
|
||||||
|
return /\b(steps?|cfg|seed|sigma|размер|aspect|ширин|высот|resolution|batch|турбо|turbo|raw)\b/i.test(String(text || ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyAspectTableFrom(obj) {
|
||||||
|
if (!obj || typeof obj !== 'object') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const next = {};
|
||||||
|
for (const [k, v] of Object.entries(obj)) {
|
||||||
|
if (Array.isArray(v) && v.length >= 2) {
|
||||||
|
next[k] = [Number(v[0]), Number(v[1])];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!Object.keys(next).length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ASPECT_TABLE = next;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveExactBundle() {
|
||||||
|
const exact = state.exact || state.config?.exact || {};
|
||||||
|
const profiles = exact.profiles || state.kreaProfiles || {};
|
||||||
|
return { exact, profiles };
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectKreaProfileName() {
|
||||||
|
try {
|
||||||
|
const model = resolveCurrentCheckpoint();
|
||||||
|
const blob = `${model?.name || ''} ${model?.title || ''}`.toLowerCase();
|
||||||
|
const hasRaw = /\braw\b/.test(blob);
|
||||||
|
const hasTurbo = /\bturbo\b/.test(blob);
|
||||||
|
return hasRaw && !hasTurbo ? 'raw' : 'turbo';
|
||||||
|
} catch (e) {
|
||||||
|
return (state.exact?.generation?.profile) || 'turbo';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergedGenerationDefaults(profileName) {
|
||||||
|
const { exact, profiles } = resolveExactBundle();
|
||||||
|
const gen = exact.generation && typeof exact.generation === 'object' ? { ...exact.generation } : {};
|
||||||
|
const profile = profileName || gen.profile || detectKreaProfileName();
|
||||||
|
const fromProfile = profiles[profile] && typeof profiles[profile] === 'object' ? { ...profiles[profile] } : {};
|
||||||
|
const session = state.sessionExact && typeof state.sessionExact === 'object' ? { ...state.sessionExact } : {};
|
||||||
|
// Profile (turbo/raw) overrides generation defaults; session overrides both.
|
||||||
|
return { ...gen, ...fromProfile, profile, ...session };
|
||||||
|
}
|
||||||
|
|
||||||
|
function exactDefaultFor(key, profileName) {
|
||||||
|
const { exact, profiles } = resolveExactBundle();
|
||||||
|
const profile = profileName || exact.generation?.profile || detectKreaProfileName();
|
||||||
|
const fromProfile = profiles[profile]?.[key];
|
||||||
|
if (fromProfile != null) {
|
||||||
|
return fromProfile;
|
||||||
|
}
|
||||||
|
return exact.generation?.[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
function rememberSessionExact(partial) {
|
||||||
|
if (!partial || typeof partial !== 'object') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const keys = ['steps', 'cfg', 'sigma_shift', 'aspect', 'width', 'height', 'images', 'batch', 'seed', 'sampler', 'scheduler'];
|
||||||
|
for (const k of keys) {
|
||||||
|
if (partial[k] != null) {
|
||||||
|
state.sessionExact[k] = partial[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (partial.images == null && partial.batch != null) {
|
||||||
|
state.sessionExact.images = partial.batch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillEmptyParamsFromExact() {
|
||||||
|
const defaults = mergedGenerationDefaults();
|
||||||
|
if (isEmptyParamField(val('input_steps'), { treatZeroEmpty: true }) && defaults.steps != null) {
|
||||||
|
setVal('input_steps', String(defaults.steps));
|
||||||
|
}
|
||||||
|
const cfgRaw = val('input_cfgscale') || val('input_cfg');
|
||||||
|
if (isEmptyParamField(cfgRaw, { treatZeroEmpty: true }) && defaults.cfg != null) {
|
||||||
|
if (document.getElementById('input_cfgscale')) {
|
||||||
|
setVal('input_cfgscale', String(defaults.cfg));
|
||||||
|
} else if (document.getElementById('input_cfg')) {
|
||||||
|
setVal('input_cfg', String(defaults.cfg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isEmptyParamField(val('input_sigmashift')) && defaults.sigma_shift != null) {
|
||||||
|
setVal('input_sigmashift', String(defaults.sigma_shift));
|
||||||
|
}
|
||||||
|
const wEmpty = isEmptyParamField(val('input_width'), { treatZeroEmpty: true });
|
||||||
|
const hEmpty = isEmptyParamField(val('input_height'), { treatZeroEmpty: true });
|
||||||
|
if ((wEmpty || hEmpty) && defaults.aspect) {
|
||||||
|
const size = sizeFromAspect(defaults.aspect);
|
||||||
|
if (size) {
|
||||||
|
if (wEmpty) {
|
||||||
|
setVal('input_width', String(size[0]));
|
||||||
|
}
|
||||||
|
if (hEmpty) {
|
||||||
|
setVal('input_height', String(size[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (wEmpty && defaults.width != null) {
|
||||||
|
setVal('input_width', String(defaults.width));
|
||||||
|
}
|
||||||
|
if (hEmpty && defaults.height != null) {
|
||||||
|
setVal('input_height', String(defaults.height));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const batchId = document.getElementById('input_images') ? 'input_images' : (document.getElementById('input_batchsize') ? 'input_batchsize' : null);
|
||||||
|
if (batchId && isEmptyParamField(val(batchId), { treatZeroEmpty: true })) {
|
||||||
|
const batch = defaults.images != null ? defaults.images : defaults.batch;
|
||||||
|
if (batch != null) {
|
||||||
|
setVal(batchId, String(batch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldSkipSessionRollback(key, patchValue) {
|
||||||
|
if (state.lastUserParamIntent) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (state.sessionExact[key] == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const sessionVal = state.sessionExact[key];
|
||||||
|
if (String(sessionVal) === String(patchValue)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const exactVal = exactDefaultFor(key);
|
||||||
|
if (exactVal == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Model trying to restore file exact while session override differs — keep session.
|
||||||
|
return String(patchValue) === String(exactVal);
|
||||||
|
}
|
||||||
|
|
||||||
function openAssistentTab() {
|
function openAssistentTab() {
|
||||||
const tab = document.getElementById(TAB_BUTTON_ID);
|
const tab = document.getElementById(TAB_BUTTON_ID);
|
||||||
if (tab) {
|
if (tab) {
|
||||||
@@ -1143,6 +1298,8 @@
|
|||||||
|
|
||||||
function onPersonaChanged() {
|
function onPersonaChanged() {
|
||||||
const id = $('sa_persona')?.value || 'neutral';
|
const id = $('sa_persona')?.value || 'neutral';
|
||||||
|
state.sessionExact = {};
|
||||||
|
state.lastUserParamIntent = false;
|
||||||
saveSettings();
|
saveSettings();
|
||||||
loadConfig(id, (data) => {
|
loadConfig(id, (data) => {
|
||||||
const title = data?.personas?.find((p) => p.id === id)?.title
|
const title = data?.personas?.find((p) => p.id === id)?.title
|
||||||
@@ -1159,6 +1316,7 @@
|
|||||||
$('sa_pack').value = packId;
|
$('sa_pack').value = packId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fillEmptyParamsFromExact();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1480,15 +1638,32 @@
|
|||||||
const blob = `${model.name || ''} ${model.title || ''}`.toLowerCase();
|
const blob = `${model.name || ''} ${model.title || ''}`.toLowerCase();
|
||||||
const hasRaw = /\braw\b/.test(blob);
|
const hasRaw = /\braw\b/.test(blob);
|
||||||
const hasTurbo = /\bturbo\b/.test(blob);
|
const hasTurbo = /\bturbo\b/.test(blob);
|
||||||
ctx.krea_profile = hasRaw && !hasTurbo ? 'raw' : 'turbo';
|
const profile = hasRaw && !hasTurbo ? 'raw' : 'turbo';
|
||||||
ctx.recommended_params = ctx.krea_profile === 'raw'
|
ctx.krea_profile = profile;
|
||||||
? { steps: 28, cfg: 4.5 }
|
const defaults = mergedGenerationDefaults(profile);
|
||||||
: { steps: 8, cfg: 1, sigma_shift: 1.15 };
|
ctx.recommended_params = {
|
||||||
|
steps: defaults.steps ?? 8,
|
||||||
|
cfg: defaults.cfg ?? 1,
|
||||||
|
sigma_shift: defaults.sigma_shift ?? 1.15,
|
||||||
|
};
|
||||||
|
if (defaults.aspect) {
|
||||||
|
ctx.recommended_params.aspect = defaults.aspect;
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ctx.krea_profile = 'turbo';
|
ctx.krea_profile = 'turbo';
|
||||||
ctx.recommended_params = { steps: 8, cfg: 1, sigma_shift: 1.15 };
|
const defaults = mergedGenerationDefaults('turbo');
|
||||||
|
ctx.recommended_params = {
|
||||||
|
steps: defaults.steps ?? 8,
|
||||||
|
cfg: defaults.cfg ?? 1,
|
||||||
|
sigma_shift: defaults.sigma_shift ?? 1.15,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx.exact = state.exact || state.config?.exact || null;
|
||||||
|
ctx.session_exact = state.sessionExact && Object.keys(state.sessionExact).length
|
||||||
|
? { ...state.sessionExact }
|
||||||
|
: {};
|
||||||
|
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1901,7 +2076,7 @@
|
|||||||
if (/\b(inpaint|замажь|закрась|руки|лицо|маск|mask|img2img|init\s*image)\b/i.test(t)) {
|
if (/\b(inpaint|замажь|закрась|руки|лицо|маск|mask|img2img|init\s*image)\b/i.test(t)) {
|
||||||
return 'inpaint_edit';
|
return 'inpaint_edit';
|
||||||
}
|
}
|
||||||
if (/\b(steps?|cfg|seed|sigma|размер|aspect|ширин|высот|resolution|batch)\b/i.test(t)) {
|
if (userTextMentionsParams(t)) {
|
||||||
return 'fix_params';
|
return 'fix_params';
|
||||||
}
|
}
|
||||||
if (/\b(сцен|moodboard|атмосфер|compose|scene|мизансцен)\b/i.test(t)) {
|
if (/\b(сцен|moodboard|атмосфер|compose|scene|мизансцен)\b/i.test(t)) {
|
||||||
@@ -2010,27 +2185,68 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (doParams) {
|
if (doParams) {
|
||||||
|
const defaults = mergedGenerationDefaults();
|
||||||
|
const capture = state.lastUserParamIntent;
|
||||||
const aspectSize = sizeFromAspect(patch.aspect);
|
const aspectSize = sizeFromAspect(patch.aspect);
|
||||||
|
if (patch.aspect != null && !shouldSkipSessionRollback('aspect', patch.aspect)) {
|
||||||
if (aspectSize) {
|
if (aspectSize) {
|
||||||
setVal('input_width', String(aspectSize[0]));
|
setVal('input_width', String(aspectSize[0]));
|
||||||
setVal('input_height', String(aspectSize[1]));
|
setVal('input_height', String(aspectSize[1]));
|
||||||
|
}
|
||||||
|
if (capture) {
|
||||||
|
rememberSessionExact({ aspect: patch.aspect });
|
||||||
|
}
|
||||||
|
} else if (patch.aspect == null && isEmptyParamField(val('input_width'), { treatZeroEmpty: true })
|
||||||
|
&& isEmptyParamField(val('input_height'), { treatZeroEmpty: true }) && defaults.aspect) {
|
||||||
|
const fill = sizeFromAspect(defaults.aspect);
|
||||||
|
if (fill) {
|
||||||
|
setVal('input_width', String(fill[0]));
|
||||||
|
setVal('input_height', String(fill[1]));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (patch.width != null) {
|
if (patch.width != null && !shouldSkipSessionRollback('width', patch.width)) {
|
||||||
setVal('input_width', String(patch.width));
|
setVal('input_width', String(patch.width));
|
||||||
|
if (capture) {
|
||||||
|
rememberSessionExact({ width: patch.width });
|
||||||
}
|
}
|
||||||
if (patch.height != null) {
|
} else if (patch.width == null && isEmptyParamField(val('input_width'), { treatZeroEmpty: true }) && defaults.width != null) {
|
||||||
|
setVal('input_width', String(defaults.width));
|
||||||
|
}
|
||||||
|
if (patch.height != null && !shouldSkipSessionRollback('height', patch.height)) {
|
||||||
setVal('input_height', String(patch.height));
|
setVal('input_height', String(patch.height));
|
||||||
|
if (capture) {
|
||||||
|
rememberSessionExact({ height: patch.height });
|
||||||
|
}
|
||||||
|
} else if (patch.height == null && isEmptyParamField(val('input_height'), { treatZeroEmpty: true }) && defaults.height != null) {
|
||||||
|
setVal('input_height', String(defaults.height));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (patch.steps != null) {
|
if (patch.steps != null && !shouldSkipSessionRollback('steps', patch.steps)) {
|
||||||
setVal('input_steps', String(patch.steps));
|
setVal('input_steps', String(patch.steps));
|
||||||
|
if (capture) {
|
||||||
|
rememberSessionExact({ steps: patch.steps });
|
||||||
}
|
}
|
||||||
if (patch.cfg != null) {
|
} else if (patch.steps == null && isEmptyParamField(val('input_steps'), { treatZeroEmpty: true }) && defaults.steps != null) {
|
||||||
|
setVal('input_steps', String(defaults.steps));
|
||||||
|
}
|
||||||
|
if (patch.cfg != null && !shouldSkipSessionRollback('cfg', patch.cfg)) {
|
||||||
if (document.getElementById('input_cfgscale')) {
|
if (document.getElementById('input_cfgscale')) {
|
||||||
setVal('input_cfgscale', String(patch.cfg));
|
setVal('input_cfgscale', String(patch.cfg));
|
||||||
} else {
|
} else {
|
||||||
setVal('input_cfg', String(patch.cfg));
|
setVal('input_cfg', String(patch.cfg));
|
||||||
}
|
}
|
||||||
|
if (capture) {
|
||||||
|
rememberSessionExact({ cfg: patch.cfg });
|
||||||
|
}
|
||||||
|
} else if (patch.cfg == null) {
|
||||||
|
const cfgRaw = val('input_cfgscale') || val('input_cfg');
|
||||||
|
if (isEmptyParamField(cfgRaw, { treatZeroEmpty: true }) && defaults.cfg != null) {
|
||||||
|
if (document.getElementById('input_cfgscale')) {
|
||||||
|
setVal('input_cfgscale', String(defaults.cfg));
|
||||||
|
} else if (document.getElementById('input_cfg')) {
|
||||||
|
setVal('input_cfg', String(defaults.cfg));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (patch.vary === true) {
|
if (patch.vary === true) {
|
||||||
setVal('input_seed', '-1');
|
setVal('input_seed', '-1');
|
||||||
@@ -2039,27 +2255,50 @@
|
|||||||
if (cur && String(cur) !== '-1') {
|
if (cur && String(cur) !== '-1') {
|
||||||
setVal('input_seed', cur);
|
setVal('input_seed', cur);
|
||||||
}
|
}
|
||||||
} else if (patch.seed != null) {
|
} else if (patch.seed != null && !shouldSkipSessionRollback('seed', patch.seed)) {
|
||||||
setVal('input_seed', String(patch.seed));
|
setVal('input_seed', String(patch.seed));
|
||||||
|
if (capture) {
|
||||||
|
rememberSessionExact({ seed: patch.seed });
|
||||||
}
|
}
|
||||||
if (patch.sigma_shift != null) {
|
}
|
||||||
|
if (patch.sigma_shift != null && !shouldSkipSessionRollback('sigma_shift', patch.sigma_shift)) {
|
||||||
setVal('input_sigmashift', String(patch.sigma_shift));
|
setVal('input_sigmashift', String(patch.sigma_shift));
|
||||||
|
if (capture) {
|
||||||
|
rememberSessionExact({ sigma_shift: patch.sigma_shift });
|
||||||
|
}
|
||||||
|
} else if (patch.sigma_shift == null && isEmptyParamField(val('input_sigmashift')) && defaults.sigma_shift != null) {
|
||||||
|
setVal('input_sigmashift', String(defaults.sigma_shift));
|
||||||
}
|
}
|
||||||
if (patch.sampler != null) {
|
if (patch.sampler != null) {
|
||||||
if (document.getElementById('input_sampler')) {
|
if (document.getElementById('input_sampler')) {
|
||||||
setVal('input_sampler', String(patch.sampler));
|
setVal('input_sampler', String(patch.sampler));
|
||||||
}
|
}
|
||||||
|
if (capture) {
|
||||||
|
rememberSessionExact({ sampler: patch.sampler });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (patch.scheduler != null && document.getElementById('input_scheduler')) {
|
if (patch.scheduler != null && document.getElementById('input_scheduler')) {
|
||||||
setVal('input_scheduler', String(patch.scheduler));
|
setVal('input_scheduler', String(patch.scheduler));
|
||||||
|
if (capture) {
|
||||||
|
rememberSessionExact({ scheduler: patch.scheduler });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const batch = patch.images != null ? patch.images : patch.batch;
|
const batch = patch.images != null ? patch.images : patch.batch;
|
||||||
if (batch != null) {
|
if (batch != null && !shouldSkipSessionRollback('images', batch)) {
|
||||||
if (document.getElementById('input_images')) {
|
if (document.getElementById('input_images')) {
|
||||||
setVal('input_images', String(batch));
|
setVal('input_images', String(batch));
|
||||||
} else if (document.getElementById('input_batchsize')) {
|
} else if (document.getElementById('input_batchsize')) {
|
||||||
setVal('input_batchsize', String(batch));
|
setVal('input_batchsize', String(batch));
|
||||||
}
|
}
|
||||||
|
if (capture) {
|
||||||
|
rememberSessionExact({ images: batch });
|
||||||
|
}
|
||||||
|
} else if (batch == null) {
|
||||||
|
const batchId = document.getElementById('input_images') ? 'input_images' : (document.getElementById('input_batchsize') ? 'input_batchsize' : null);
|
||||||
|
const defBatch = defaults.images != null ? defaults.images : defaults.batch;
|
||||||
|
if (batchId && isEmptyParamField(val(batchId), { treatZeroEmpty: true }) && defBatch != null) {
|
||||||
|
setVal(batchId, String(defBatch));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2835,19 +3074,16 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.config = data;
|
state.config = data;
|
||||||
if (data.model?.aspect_table && typeof data.model.aspect_table === 'object') {
|
if (data.exact && typeof data.exact === 'object') {
|
||||||
const next = {};
|
state.exact = data.exact;
|
||||||
for (const [k, v] of Object.entries(data.model.aspect_table)) {
|
|
||||||
if (Array.isArray(v) && v.length >= 2) {
|
|
||||||
next[k] = [Number(v[0]), Number(v[1])];
|
|
||||||
}
|
}
|
||||||
|
const aspectSource = data.exact?.aspect_table || data.model?.aspect_table;
|
||||||
|
if (aspectSource && typeof aspectSource === 'object') {
|
||||||
|
applyAspectTableFrom(aspectSource);
|
||||||
}
|
}
|
||||||
if (Object.keys(next).length) {
|
const profileSource = data.exact?.profiles || data.model?.profiles;
|
||||||
ASPECT_TABLE = next;
|
if (profileSource && typeof profileSource === 'object') {
|
||||||
}
|
state.kreaProfiles = profileSource;
|
||||||
}
|
|
||||||
if (data.model?.profiles) {
|
|
||||||
state.kreaProfiles = data.model.profiles;
|
|
||||||
}
|
}
|
||||||
if (data.ui?.pack_aliases) {
|
if (data.ui?.pack_aliases) {
|
||||||
PACK_ALIASES = { ...PACK_ALIASES, ...data.ui.pack_aliases };
|
PACK_ALIASES = { ...PACK_ALIASES, ...data.ui.pack_aliases };
|
||||||
@@ -2879,6 +3115,9 @@
|
|||||||
if (data.assistant?.embed_model && !state.preferredEmbed) {
|
if (data.assistant?.embed_model && !state.preferredEmbed) {
|
||||||
state.preferredEmbed = data.assistant.embed_model;
|
state.preferredEmbed = data.assistant.embed_model;
|
||||||
}
|
}
|
||||||
|
if (applyDefaults || data.exact) {
|
||||||
|
fillEmptyParamsFromExact();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderPersonaOptions(personas, selected) {
|
function renderPersonaOptions(personas, selected) {
|
||||||
@@ -3958,7 +4197,10 @@
|
|||||||
if (!Array.isArray(withActions.actions) && patchHasGenTrigger(withActions)) {
|
if (!Array.isArray(withActions.actions) && patchHasGenTrigger(withActions)) {
|
||||||
withActions.actions = ['generate'];
|
withActions.actions = ['generate'];
|
||||||
}
|
}
|
||||||
|
const prevIntent = state.lastUserParamIntent;
|
||||||
|
state.lastUserParamIntent = true;
|
||||||
await applyPatch(withActions, 'all');
|
await applyPatch(withActions, 'all');
|
||||||
|
state.lastUserParamIntent = prevIntent;
|
||||||
setStatus(note || 'Applied');
|
setStatus(note || 'Applied');
|
||||||
if ($('sa_auto_generate')?.checked) {
|
if ($('sa_auto_generate')?.checked) {
|
||||||
await runGenerateFromPatch(withActions);
|
await runGenerateFromPatch(withActions);
|
||||||
@@ -4209,6 +4451,9 @@
|
|||||||
if (!text) {
|
if (!text) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!opts.fromAutoCritique && !opts.fromDownload && !opts.fromVisionHop) {
|
||||||
|
state.lastUserParamIntent = userTextMentionsParams(text);
|
||||||
|
}
|
||||||
|
|
||||||
if (!opts.fromAutoCritique && !opts.fromDownload && !opts.fromVisionHop && !opts.skipSlash) {
|
if (!opts.fromAutoCritique && !opts.fromDownload && !opts.fromVisionHop && !opts.skipSlash) {
|
||||||
if (rawInput.startsWith('/')) {
|
if (rawInput.startsWith('/')) {
|
||||||
@@ -4745,6 +4990,8 @@
|
|||||||
state.visionHopUsed = false;
|
state.visionHopUsed = false;
|
||||||
state.packUserTouched = false;
|
state.packUserTouched = false;
|
||||||
state.pendingPersonaNote = null;
|
state.pendingPersonaNote = null;
|
||||||
|
state.sessionExact = {};
|
||||||
|
state.lastUserParamIntent = false;
|
||||||
clearPersistedHistory();
|
clearPersistedHistory();
|
||||||
const box = $('sa_messages');
|
const box = $('sa_messages');
|
||||||
if (box) {
|
if (box) {
|
||||||
@@ -4786,11 +5033,11 @@
|
|||||||
} else if (vary) {
|
} else if (vary) {
|
||||||
await applyQuickPatch({ vary: true, seed: -1, actions: ['generate'] }, 'Vary');
|
await applyQuickPatch({ vary: true, seed: -1, actions: ['generate'] }, 'Vary');
|
||||||
} else if (profile === 'turbo') {
|
} else if (profile === 'turbo') {
|
||||||
const p = state.kreaProfiles?.turbo || { steps: 8, cfg: 1, sigma_shift: 1.15 };
|
const p = state.kreaProfiles?.turbo || mergedGenerationDefaults('turbo');
|
||||||
await applyQuickPatch({ steps: p.steps ?? 8, cfg: p.cfg ?? 1, sigma_shift: p.sigma_shift ?? 1.15, actions: ['generate'] }, 'Turbo');
|
await applyQuickPatch({ steps: p.steps ?? 8, cfg: p.cfg ?? 1, sigma_shift: p.sigma_shift ?? 1.15, actions: ['generate'] }, 'Turbo');
|
||||||
} else if (profile === 'raw') {
|
} else if (profile === 'raw') {
|
||||||
const p = state.kreaProfiles?.raw || { steps: 28, cfg: 4.5 };
|
const p = state.kreaProfiles?.raw || mergedGenerationDefaults('raw');
|
||||||
await applyQuickPatch({ steps: p.steps ?? 28, cfg: p.cfg ?? 4.5, actions: ['generate'] }, 'RAW');
|
await applyQuickPatch({ steps: p.steps ?? 28, cfg: p.cfg ?? 4.5, sigma_shift: p.sigma_shift, actions: ['generate'] }, 'RAW');
|
||||||
}
|
}
|
||||||
renderLoraChips();
|
renderLoraChips();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -319,6 +319,9 @@ public sealed class AssistentConfig
|
|||||||
|
|
||||||
public JObject LoadUi(string personaId) => MergeJsonLayers("ui.json", LayerRoots(personaId));
|
public JObject LoadUi(string personaId) => MergeJsonLayers("ui.json", LayerRoots(personaId));
|
||||||
|
|
||||||
|
/// <summary>Exact (KV) memory: bundled base → persona overlays → disk overlays. Persona keys overwrite base.</summary>
|
||||||
|
public JObject LoadExact(string personaId) => MergeJsonLayers("exact.json", LayerRoots(personaId));
|
||||||
|
|
||||||
public JObject LoadModelProfile(string personaId)
|
public JObject LoadModelProfile(string personaId)
|
||||||
{
|
{
|
||||||
JObject assistant = LoadAssistant(personaId);
|
JObject assistant = LoadAssistant(personaId);
|
||||||
@@ -712,6 +715,7 @@ public sealed class AssistentConfig
|
|||||||
JObject assistant = LoadAssistant(id);
|
JObject assistant = LoadAssistant(id);
|
||||||
JObject ui = LoadUi(id);
|
JObject ui = LoadUi(id);
|
||||||
JObject model = LoadModelProfile(id);
|
JObject model = LoadModelProfile(id);
|
||||||
|
JObject exact = LoadExact(id);
|
||||||
var packs = ListPacks(id);
|
var packs = ListPacks(id);
|
||||||
var skills = ListSkills(id);
|
var skills = ListSkills(id);
|
||||||
var personas = ListPersonaCatalog();
|
var personas = ListPersonaCatalog();
|
||||||
@@ -724,6 +728,7 @@ public sealed class AssistentConfig
|
|||||||
["assistant"] = assistant,
|
["assistant"] = assistant,
|
||||||
["ui"] = ui,
|
["ui"] = ui,
|
||||||
["model"] = model,
|
["model"] = model,
|
||||||
|
["exact"] = exact,
|
||||||
["packs"] = new JArray(packs.Select(p => new JObject
|
["packs"] = new JArray(packs.Select(p => new JObject
|
||||||
{
|
{
|
||||||
["id"] = p.id,
|
["id"] = p.id,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
"default_persona": "neutral",
|
"default_persona": "neutral",
|
||||||
"embed_model": "nomic-embed-text",
|
"embed_model": "nomic-embed-text",
|
||||||
"memory_top_k": 10,
|
"memory_top_k": 10,
|
||||||
"seed_version": 1,
|
"seed_version": 2,
|
||||||
"gate": {
|
"gate": {
|
||||||
"architecture": "krea2",
|
"architecture": "krea2",
|
||||||
"keywords": ["krea"]
|
"keywords": ["krea"]
|
||||||
|
|||||||
@@ -2,13 +2,28 @@
|
|||||||
|
|
||||||
You are **Swarm Assistent**, a collaborative art director for image generation inside SwarmUI.
|
You are **Swarm Assistent**, a collaborative art director for image generation inside SwarmUI.
|
||||||
|
|
||||||
|
## Priority (mandatory)
|
||||||
|
|
||||||
|
When instructions conflict, apply this order (highest wins):
|
||||||
|
|
||||||
|
1. **This core contract** — output format, never invent LoRA/checkpoint names, never use CFG 0.
|
||||||
|
2. **Current user message** — explicit “use steps 20 / aspect 16:9 now” wins for that turn.
|
||||||
|
3. **Live `session_exact`** — prior user overrides this chat (until persona change / clear chat).
|
||||||
|
4. **Exact memory** (`## Exact memory` JSON) — canonical defaults (steps/CFG/aspect/facts). Persona overlays are already merged into it.
|
||||||
|
5. **Filled live SwarmUI fields** — respect what is already set unless the user or pack asks to change.
|
||||||
|
6. **`memory_hits` (vector RAG)** — notes, pitfalls, LoRA blurbs. Never override exact numbers or the user’s param request.
|
||||||
|
7. Guesses — last resort only.
|
||||||
|
|
||||||
|
Exact = encyclopedia of defaults. RAG = soft notes. Do **not** re-emit `steps` / `cfg` / `sigma_shift` / `aspect` when they already match exact (or session_exact) and the user did not ask to change them.
|
||||||
|
|
||||||
## Live context
|
## Live context
|
||||||
|
|
||||||
A JSON block named "Live SwarmUI context" is attached. Treat it as ground truth — refreshed every chat turn:
|
A JSON block named "Live SwarmUI context" is attached. Treat it as ground truth — refreshed every chat turn:
|
||||||
|
|
||||||
- Use only LoRAs listed in `available_loras` / `enabled_loras` (exact `name`), or Civitai search candidates.
|
- Use only LoRAs listed in `available_loras` / `enabled_loras` (exact `name`), or Civitai search candidates.
|
||||||
- Prefer listed `trigger_phrase` / `triggers` — **never invent** trigger words.
|
- Prefer listed `trigger_phrase` / `triggers` — **never invent** trigger words.
|
||||||
- `memory_hits` are retrieved facts (model knowledge, LoRA notes, pitfalls). Trust them over guesses.
|
- `exact` / `session_exact` / `recommended_params` — generation defaults; see Priority.
|
||||||
|
- `memory_hits` are retrieved notes (LoRA tips, pitfalls). Trust them over guesses, but **not** over exact or the user.
|
||||||
- `model_cards` for **enabled** models beat generic blurbs — follow `when` / `avoid` / `prompt_hint` / `triggers`.
|
- `model_cards` for **enabled** models beat generic blurbs — follow `when` / `avoid` / `prompt_hint` / `triggers`.
|
||||||
- `taste_profile` is the user's remembered preferences — bias toward it unless they override.
|
- `taste_profile` is the user's remembered preferences — bias toward it unless they override.
|
||||||
- Prefer `krea_likely` / Krea architecture entries; ignore FLUX/SDXL LoRAs.
|
- Prefer `krea_likely` / Krea architecture entries; ignore FLUX/SDXL LoRAs.
|
||||||
@@ -67,6 +82,7 @@ A JSON block named "Live SwarmUI context" is attached. Treat it as ground truth
|
|||||||
### Patch rules
|
### Patch rules
|
||||||
|
|
||||||
- Omit keys you are not changing.
|
- Omit keys you are not changing.
|
||||||
|
- Prefer omitting `steps` / `cfg` / `sigma_shift` / `aspect` when they already match Exact memory (or `session_exact`) and the user did not request a change — the UI fills empties from exact.
|
||||||
- `loras` replaces the intended LoRA set for Apply (list all that should be on).
|
- `loras` replaces the intended LoRA set for Apply (list all that should be on).
|
||||||
- Prefer `aspect` over raw width/height when framing changes.
|
- Prefer `aspect` over raw width/height when framing changes.
|
||||||
- `vary: true` — new random seed. `lock_seed: true` — reuse current seed.
|
- `vary: true` — new random seed. `lock_seed: true` — reuse current seed.
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"generation": {
|
||||||
|
"profile": "turbo",
|
||||||
|
"steps": 8,
|
||||||
|
"cfg": 1,
|
||||||
|
"sigma_shift": 1.15,
|
||||||
|
"images": 1
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"turbo": {
|
||||||
|
"steps": 8,
|
||||||
|
"cfg": 1,
|
||||||
|
"sigma_shift": 1.15
|
||||||
|
},
|
||||||
|
"raw": {
|
||||||
|
"steps": 28,
|
||||||
|
"cfg": 4.5,
|
||||||
|
"sigma_shift": 1.15
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"aspect_table": {
|
||||||
|
"1:1": [1024, 1024],
|
||||||
|
"4:3": [1184, 896],
|
||||||
|
"3:2": [1248, 832],
|
||||||
|
"16:9": [1376, 768],
|
||||||
|
"2.35:1": [1568, 672],
|
||||||
|
"4:5": [928, 1152],
|
||||||
|
"2:3": [832, 1248],
|
||||||
|
"9:16": [768, 1376]
|
||||||
|
},
|
||||||
|
"facts": {
|
||||||
|
"architecture": "Krea 2 is a 12B DiT architecture. Not FLUX, not SDXL, not FLUX.1-Krea. Text encoder: Qwen3-VL 4B. VAE: Qwen Image VAE. Use only Krea2-trained LoRAs — never suggest FLUX/SDXL LoRAs.",
|
||||||
|
"negatives": "Negative prompts are nearly useless with Qwen3-VL. Prefer positives (sharp focus, empty street) over no blur / no people. Built-in NSFW text-refiner may strip risque words; LoRAs/finetunes may restore — stay practical.",
|
||||||
|
"prompt_images": "Prompt Images (refs in the prompt box) often overpower text — use sparingly and warn. Init Image = structure (img2img). Mask = local fix. They are not interchangeable. Cloud-only features (moodboards, Generative Sliders) are not in Swarm — emulate with prompt language + board refs.",
|
||||||
|
"turbo": "Krea 2 Turbo defaults live in exact.generation / exact.profiles.turbo. Never use CFG 0 — broken output. Side ~1024 (128–4096 OK).",
|
||||||
|
"raw": "Krea 2 RAW/Base: prefer exact.profiles.raw when checkpoint name/title looks like RAW (not Turbo). If a turbo LoRA exists, weight ~0.6 for photoreal (1.0 ≈ full turbo). Swarm Generate cannot run dual-sampler Comfy graphs — only suggest LoRA weight + steps/CFG the UI can set."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,50 +1,8 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"kind": "aspect",
|
"kind": "aspect",
|
||||||
"key": "1:1",
|
"key": "table",
|
||||||
"tags": ["aspect", "1k"],
|
"tags": ["aspect", "1k"],
|
||||||
"text": "Aspect 1:1 maps to 1024×1024 on the official Krea 1K table. Prefer patch field aspect over raw width/height."
|
"text": "Official Krea 1K aspect → width/height table lives in Exact memory aspect_table. Prefer patch field aspect over raw width/height. UI fills empties from Exact."
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "aspect",
|
|
||||||
"key": "4:3",
|
|
||||||
"tags": ["aspect", "1k"],
|
|
||||||
"text": "Aspect 4:3 maps to 1184×896."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "aspect",
|
|
||||||
"key": "3:2",
|
|
||||||
"tags": ["aspect", "1k"],
|
|
||||||
"text": "Aspect 3:2 maps to 1248×832."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "aspect",
|
|
||||||
"key": "16:9",
|
|
||||||
"tags": ["aspect", "1k", "widescreen"],
|
|
||||||
"text": "Aspect 16:9 maps to 1376×768."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "aspect",
|
|
||||||
"key": "2.35:1",
|
|
||||||
"tags": ["aspect", "1k", "cinematic"],
|
|
||||||
"text": "Aspect 2.35:1 (cinematic ultrawide) maps to 1568×672."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "aspect",
|
|
||||||
"key": "4:5",
|
|
||||||
"tags": ["aspect", "1k", "portrait"],
|
|
||||||
"text": "Aspect 4:5 maps to 928×1152 — good for portrait."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "aspect",
|
|
||||||
"key": "2:3",
|
|
||||||
"tags": ["aspect", "1k", "portrait"],
|
|
||||||
"text": "Aspect 2:3 maps to 832×1248."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "aspect",
|
|
||||||
"key": "9:16",
|
|
||||||
"tags": ["aspect", "1k", "stories"],
|
|
||||||
"text": "Aspect 9:16 maps to 768×1376 — vertical / stories."
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -3,30 +3,30 @@
|
|||||||
"kind": "model",
|
"kind": "model",
|
||||||
"key": "krea2_architecture",
|
"key": "krea2_architecture",
|
||||||
"tags": ["krea", "architecture"],
|
"tags": ["krea", "architecture"],
|
||||||
"text": "Krea 2 is a 12B DiT architecture. Not FLUX, not SDXL, not FLUX.1-Krea. Text encoder: Qwen3-VL 4B. VAE: Qwen Image VAE. Use only Krea2-trained LoRAs — never suggest FLUX/SDXL LoRAs."
|
"text": "Krea 2 architecture facts live in Exact memory (exact.facts.architecture). Prefer Exact KV over this note. Never suggest FLUX/SDXL LoRAs."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"kind": "model",
|
"kind": "model",
|
||||||
"key": "krea2_turbo",
|
"key": "krea2_turbo",
|
||||||
"tags": ["krea", "turbo", "params"],
|
"tags": ["krea", "turbo", "params"],
|
||||||
"text": "Krea 2 Turbo defaults: steps 8 (min 4), CFG 1 (never CFG 0 — broken output), sigma shift 1.15, side ~1024 (128–4096 OK)."
|
"text": "Turbo numeric defaults (steps/CFG/sigma) live in Exact memory profiles.turbo / generation — do not invent numbers; never use CFG 0."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"kind": "model",
|
"kind": "model",
|
||||||
"key": "krea2_raw",
|
"key": "krea2_raw",
|
||||||
"tags": ["krea", "raw", "params"],
|
"tags": ["krea", "raw", "params"],
|
||||||
"text": "Krea 2 RAW/Base: steps ~20–52, CFG ~4–4.5. If checkpoint name/title looks like RAW (not Turbo), prefer RAW settings. If a turbo LoRA exists, weight ~0.6 for photoreal (1.0 ≈ full turbo). Swarm Generate cannot run dual-sampler Comfy graphs — only suggest LoRA weight + steps/CFG the UI can set."
|
"text": "RAW numeric defaults live in Exact memory profiles.raw. If checkpoint looks like RAW (not Turbo), use that profile. Turbo LoRA weight ~0.6 for photoreal when needed."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"kind": "model",
|
"kind": "model",
|
||||||
"key": "krea2_negatives",
|
"key": "krea2_negatives",
|
||||||
"tags": ["krea", "prompting"],
|
"tags": ["krea", "prompting"],
|
||||||
"text": "Negative prompts are nearly useless with Qwen3-VL. Prefer positives (sharp focus, empty street) over no blur / no people. Built-in NSFW text-refiner may strip risque words; LoRAs/finetunes may restore — stay practical."
|
"text": "See Exact memory facts.negatives: negatives are nearly useless with Qwen3-VL — prefer positive phrasing."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"kind": "model",
|
"kind": "model",
|
||||||
"key": "krea2_prompt_images",
|
"key": "krea2_prompt_images",
|
||||||
"tags": ["krea", "board"],
|
"tags": ["krea", "board"],
|
||||||
"text": "Prompt Images (refs in the prompt box) often overpower text — use sparingly and warn. Init Image = structure (img2img). Mask = local fix. They are not interchangeable. Cloud-only features (moodboards, Generative Sliders) are not in Swarm — emulate with prompt language + board refs."
|
"text": "See Exact memory facts.prompt_images: Prompt Images overpower text; Init ≠ Mask ≠ Prompt Images."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,26 +1,4 @@
|
|||||||
{
|
{
|
||||||
"id": "krea2",
|
"id": "krea2",
|
||||||
"gate_keywords": ["krea"],
|
"gate_keywords": ["krea"]
|
||||||
"profiles": {
|
|
||||||
"turbo": {
|
|
||||||
"steps": 8,
|
|
||||||
"cfg": 1,
|
|
||||||
"sigma_shift": 1.15
|
|
||||||
},
|
|
||||||
"raw": {
|
|
||||||
"steps": 28,
|
|
||||||
"cfg": 4.5,
|
|
||||||
"sigma_shift": 1.15
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"aspect_table": {
|
|
||||||
"1:1": [1024, 1024],
|
|
||||||
"4:3": [1184, 896],
|
|
||||||
"3:2": [1248, 832],
|
|
||||||
"16:9": [1376, 768],
|
|
||||||
"2.35:1": [1568, 672],
|
|
||||||
"4:5": [928, 1152],
|
|
||||||
"2:3": [832, 1248],
|
|
||||||
"9:16": [768, 1376]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,16 @@ Goal: adjust **generation parameters** for Krea 2 Turbo (or RAW if context says
|
|||||||
|
|
||||||
## Guidelines
|
## Guidelines
|
||||||
|
|
||||||
- **Turbo:** steps 4–12 (default **8**), CFG **1** (never 0), sigma shift ~**1.15**.
|
- Prefer **Exact memory** (`profiles.turbo` / `profiles.raw`), live `recommended_params`, and `session_exact` over invented numbers. Never CFG 0.
|
||||||
- **RAW/base:** steps 20–52, CFG ~4–4.5 — only if checkpoint/context indicates Raw. If a turbo-distill LoRA is available, weight **0.6** is the usual photoreal compromise (UI LoRA only — no dual-sampler).
|
- Prefer live context field `krea_profile` (`turbo` | `raw`) when present.
|
||||||
- Prefer live context field `krea_profile` (`turbo` | `raw`) and `recommended_params` when present.
|
- **Aspect:** prefer patch field `aspect` from Exact `aspect_table` — UI maps to official 1K sizes. Else set width/height near 1024.
|
||||||
- **Aspect:** prefer patch field `aspect` (`1:1`, `4:5`, `2:3`, `16:9`, `9:16`, `4:3`, `3:2`, `2.35:1`) — UI maps to official 1K sizes. Else set width/height near 1024.
|
|
||||||
- **Batch:** `images` or `batch` (1–4 typical).
|
- **Batch:** `images` or `batch` (1–4 typical).
|
||||||
- **Seed:** `lock_seed: true` to reuse current; `vary: true` or `seed: -1` for a new roll; set numeric `seed` for exact reproducibility.
|
- **Seed:** `lock_seed: true` to reuse current; `vary: true` or `seed: -1` for a new roll; set numeric `seed` for exact reproducibility.
|
||||||
- **Sampler/scheduler:** leave alone unless the user asks (Swarm default is fine; community Turbo often Euler + Simple).
|
- **Sampler/scheduler:** leave alone unless the user asks (Swarm default is fine; community Turbo often Euler + Simple).
|
||||||
- **Init creativity** only when `has_init_image` or enabling img2img — see `inpaint_edit`.
|
- **Init creativity** only when `has_init_image` or enabling img2img — see `inpaint_edit`.
|
||||||
- Do not change the prompt unless needed for the new framing.
|
- Do not change the prompt unless needed for the new framing.
|
||||||
- Keep LoRAs unless asked to drop them.
|
- Keep LoRAs unless asked to drop them.
|
||||||
|
- User’s requested params for this turn beat Exact; do not silently roll them back to Exact defaults.
|
||||||
|
|
||||||
## Deliverable
|
## Deliverable
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ Structure as flowing prose (not tag soup):
|
|||||||
|
|
||||||
- Put **LoRA trigger phrases** near the subject they affect.
|
- Put **LoRA trigger phrases** near the subject they affect.
|
||||||
- Short user ideas → expand. User already wrote a polished paragraph → keep it; only fix tags/weights/negatives-as-positives.
|
- Short user ideas → expand. User already wrote a polished paragraph → keep it; only fix tags/weights/negatives-as-positives.
|
||||||
- Keep Turbo defaults unless asked (steps 8, cfg 1). Prefer `aspect` for framing.
|
- Keep Exact Turbo defaults unless asked (see Exact memory / `recommended_params`). Prefer `aspect` for framing. Omit steps/cfg/sigma/aspect from the patch when they already match Exact and the user did not ask to change them.
|
||||||
- Missing style LoRA → `actions: ["search_civitai"]` + clear `search_query` (Krea-compatible).
|
- Missing style LoRA → `actions: ["search_civitai"]` + clear `search_query` (Krea-compatible).
|
||||||
- Optional `creativity` / intensity/complexity/movement: expand or restrain wording accordingly; bake slider intent into the prose.
|
- Optional `creativity` / intensity/complexity/movement: expand or restrain wording accordingly; bake slider intent into the prose.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"id": "memory",
|
"id": "memory",
|
||||||
"title": "Vector memory",
|
"title": "Exact + vector memory",
|
||||||
"default": true,
|
"default": true,
|
||||||
"prompt_file": "memory.md"
|
"prompt_file": "memory.md"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
# Skill: memory
|
# Skill: memory
|
||||||
|
|
||||||
You have a persistent vector memory (`memory_hits` in live context).
|
You have two memory layers:
|
||||||
|
|
||||||
## When to write
|
1. **Exact memory** (`## Exact memory` + live `exact` / `session_exact`) — canonical KV defaults (generation params, aspect table, architecture facts). Always prefer Exact over RAG for numbers and defaults.
|
||||||
|
2. **Vector memory** (`memory_hits`) — soft notes from retrieve (LoRA tips, pitfalls, paths).
|
||||||
|
|
||||||
|
## Priority
|
||||||
|
|
||||||
|
User (this turn) > `session_exact` > Exact KV > filled live fields > `memory_hits` > guesses. Never let a vector hit override Exact steps/CFG/aspect or an explicit user param request.
|
||||||
|
|
||||||
|
## When to write (vector only)
|
||||||
|
|
||||||
- Durable facts about a LoRA/checkpoint (when it works, what it breaks, good weight).
|
- Durable facts about a LoRA/checkpoint (when it works, what it breaks, good weight).
|
||||||
- Bad paths / pitfalls you discovered this session.
|
- Bad paths / pitfalls you discovered this session.
|
||||||
@@ -10,6 +17,7 @@ You have a persistent vector memory (`memory_hits` in live context).
|
|||||||
|
|
||||||
## When not to write
|
## When not to write
|
||||||
|
|
||||||
|
- Do not dump Exact defaults into vector memory — they already live in `exact.json`.
|
||||||
- Do not dump the full inventory — retrieve already surfaces relevant blurbs.
|
- Do not dump the full inventory — retrieve already surfaces relevant blurbs.
|
||||||
- Do not store the user's taste profile (that is `taste_profile` / taste.json).
|
- Do not store the user's taste profile (that is `taste_profile` / taste.json).
|
||||||
- Do not upsert trivia that is already in `memory_hits` with the same meaning.
|
- Do not upsert trivia that is already in `memory_hits` with the same meaning.
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"generation": {
|
||||||
|
"aspect": "2.35:1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
SwarmUI extension for **collaborative Krea 2** prompting via **Ollama**: chat + board (Generate | Refs tabs), LoRA chips, **persona presets** (`Config/personas/`), **vector memory**, model cards with Civitai fetch, img2img/inpaint, slash commands, auto Generate.
|
SwarmUI extension for **collaborative Krea 2** prompting via **Ollama**: chat + board (Generate | Refs tabs), LoRA chips, **persona presets** (`Config/personas/`), **vector memory**, model cards with Civitai fetch, img2img/inpaint, slash commands, auto Generate.
|
||||||
|
|
||||||
**Version 0.7.0** — Config/_base + persona folders, skills, memory-seed → SQLite, Ollama `use: chat|memory`, slim inventory via retrieve.
|
**Version 0.7.0** — Config/_base + persona folders, skills, **exact KV memory** + vector memory-seed → SQLite, Ollama `use: chat|memory`, slim inventory via retrieve.
|
||||||
|
|
||||||
## Layout
|
## Layout
|
||||||
|
|
||||||
@@ -15,20 +15,29 @@ SwarmUI extension for **collaborative Krea 2** prompting via **Ollama**: chat +
|
|||||||
|
|
||||||
```
|
```
|
||||||
Config/
|
Config/
|
||||||
_base/ # defaults (assistant, ui, models/krea2, core, packs, skills, memory-seed, identity)
|
_base/ # defaults (assistant, ui, models/krea2, exact.json, core, packs, skills, memory-seed, identity)
|
||||||
personas/<id>/ # sparse preset: persona/voice/likes/dislikes/rules + optional overrides
|
personas/<id>/ # sparse preset: persona/voice/likes/dislikes/rules + optional exact.json / overrides
|
||||||
```
|
```
|
||||||
|
|
||||||
Disk overlay (wins over bundled): `/mnt/swarm_data/Assistent/` — same layout, plus `settings.json`, `taste.json`, `personas.json` (legacy prompt overlay), `ollama-roles.json`, `memory/assistent.sqlite`.
|
Disk overlay (wins over bundled): `/mnt/swarm_data/Assistent/` — same layout, plus `settings.json`, `taste.json`, `personas.json` (legacy prompt overlay), `ollama-roles.json`, `memory/assistent.sqlite`.
|
||||||
|
|
||||||
Copy `personas/cinema/` → `noir/`, edit only differing JSON files.
|
Copy `personas/cinema/` → `noir/`, edit only differing JSON files.
|
||||||
|
|
||||||
|
## Exact memory (KV)
|
||||||
|
|
||||||
|
- `Config/_base/exact.json` — canonical generation defaults, profiles (turbo/raw), aspect table, short facts
|
||||||
|
- Persona / disk overlays merge via DeepMerge (matching keys overwrite)
|
||||||
|
- Always injected into the system prompt; UI fills **empty** SwarmUI fields from Exact (no LLM call)
|
||||||
|
- Chat-session overrides (`session_exact`) last until persona change or clear chat — not written to disk
|
||||||
|
- Priority: core contract → current user → session_exact → exact (+ persona) → live fields → vector `memory_hits`
|
||||||
|
|
||||||
## Vector memory
|
## Vector memory
|
||||||
|
|
||||||
- SQLite + Ollama `/api/embed` (default `nomic-embed-text`, pick in ⚙)
|
- SQLite + Ollama `/api/embed` (default `nomic-embed-text`, pick in ⚙)
|
||||||
- First chat seeds `Config/_base/memory-seed/` (Krea facts, aspect, pitfalls)
|
- First chat seeds `Config/_base/memory-seed/` (pointers + pitfalls; numbers live in Exact)
|
||||||
- Agents upsert via patch `memory_upsert` / `memory_forget`
|
- Agents upsert via patch `memory_upsert` / `memory_forget`
|
||||||
- Cards ingest on save; retrieve → `memory_hits` in live context (inventory slimmed)
|
- Cards ingest on save; retrieve → `memory_hits` in live context (inventory slimmed)
|
||||||
|
- Soft notes only — Exact and the user beat RAG for params
|
||||||
|
|
||||||
## UX
|
## UX
|
||||||
|
|
||||||
@@ -76,7 +85,7 @@ Restart / rebuild SwarmUI after clone. gpu-rent: `seed-extensions` + restart.
|
|||||||
|
|
||||||
**Packs** (one active): `write_prompt`, `critique_image`, `compose_scene`, `fix_params`, `inpaint_edit`, `describe_ref`, `catalog_card`.
|
**Packs** (one active): `write_prompt`, `critique_image`, `compose_scene`, `fix_params`, `inpaint_edit`, `describe_ref`, `catalog_card`.
|
||||||
|
|
||||||
**Skills** (checkboxes): `prompting`, `creativity_sliders`, `memory` — procedures, not model encyclopedia (facts live in memory-seed).
|
**Skills** (checkboxes): `prompting`, `creativity_sliders`, `memory` — procedures; encyclopedia numbers live in Exact, soft notes in memory-seed / RAG.
|
||||||
|
|
||||||
**Personas:** `neutral`, `lewd`, `aggressive`, `cinema`, `terse` under `Config/personas/`.
|
**Personas:** `neutral`, `lewd`, `aggressive`, `cinema`, `terse` under `Config/personas/`.
|
||||||
|
|
||||||
|
|||||||
@@ -1347,6 +1347,16 @@ public class SwarmAssistentExtension : Extension
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JObject exact = Config.LoadExact(pid);
|
||||||
|
if (exact is not null && exact.Count > 0)
|
||||||
|
{
|
||||||
|
system.AppendLine();
|
||||||
|
system.AppendLine("## Exact memory (canonical KV — always trust over RAG guesses)");
|
||||||
|
system.AppendLine("```json");
|
||||||
|
system.AppendLine(exact.ToString(Newtonsoft.Json.Formatting.None));
|
||||||
|
system.AppendLine("```");
|
||||||
|
}
|
||||||
|
|
||||||
foreach (string skillId in skillIds ?? Config.ResolveEnabledSkills(pid, null))
|
foreach (string skillId in skillIds ?? Config.ResolveEnabledSkills(pid, null))
|
||||||
{
|
{
|
||||||
string skillText = Config.LoadSkillPrompt(pid, skillId);
|
string skillText = Config.LoadSkillPrompt(pid, skillId);
|
||||||
@@ -1564,7 +1574,7 @@ public class SwarmAssistentExtension : Extension
|
|||||||
Logs.Debug($"Assistent memory retrieve: {ex.Message}");
|
Logs.Debug($"Assistent memory retrieve: {ex.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
string enrichedContext = InjectMemoryHits(contextJson, hits);
|
string enrichedContext = InjectMemoryHits(contextJson, hits, Config.LoadExact(pid));
|
||||||
List<JObject> messages = BuildOllamaMessages(packName, includeBase, enrichedContext, userMessages, personaId: pid, skillIds: skills);
|
List<JObject> messages = BuildOllamaMessages(packName, includeBase, enrichedContext, userMessages, personaId: pid, skillIds: skills);
|
||||||
JArray civitaiResults = [];
|
JArray civitaiResults = [];
|
||||||
string reply = "";
|
string reply = "";
|
||||||
@@ -1659,7 +1669,7 @@ public class SwarmAssistentExtension : Extension
|
|||||||
return string.IsNullOrWhiteSpace(q) ? "krea2 prompting" : q;
|
return string.IsNullOrWhiteSpace(q) ? "krea2 prompting" : q;
|
||||||
}
|
}
|
||||||
|
|
||||||
static string InjectMemoryHits(string contextJson, JArray hits)
|
static string InjectMemoryHits(string contextJson, JArray hits, JObject exact = null)
|
||||||
{
|
{
|
||||||
JObject ctx;
|
JObject ctx;
|
||||||
try
|
try
|
||||||
@@ -1671,6 +1681,29 @@ public class SwarmAssistentExtension : Extension
|
|||||||
ctx = new JObject { ["_raw_context"] = contextJson };
|
ctx = new JObject { ["_raw_context"] = contextJson };
|
||||||
}
|
}
|
||||||
ctx["memory_hits"] = hits ?? new JArray();
|
ctx["memory_hits"] = hits ?? new JArray();
|
||||||
|
if (exact is not null && exact.Count > 0 && ctx["exact"] is null)
|
||||||
|
{
|
||||||
|
ctx["exact"] = exact;
|
||||||
|
}
|
||||||
|
if (ctx["session_exact"] is null)
|
||||||
|
{
|
||||||
|
ctx["session_exact"] = new JObject();
|
||||||
|
}
|
||||||
|
if (ctx["recommended_params"] is null && exact?["generation"] is JObject gen)
|
||||||
|
{
|
||||||
|
JObject rec = new();
|
||||||
|
foreach (string key in new[] { "steps", "cfg", "sigma_shift", "aspect", "images" })
|
||||||
|
{
|
||||||
|
if (gen[key] is not null)
|
||||||
|
{
|
||||||
|
rec[key] = gen[key].DeepClone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rec.Count > 0)
|
||||||
|
{
|
||||||
|
ctx["recommended_params"] = rec;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Slim inventory for LLM: keep enabled + current, drop full dump if present
|
// Slim inventory for LLM: keep enabled + current, drop full dump if present
|
||||||
if (ctx["available_loras"] is JArray allLoras && allLoras.Count > 24)
|
if (ctx["available_loras"] is JArray allLoras && allLoras.Count > 24)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user