Ship Assistent 0.11.8: remember session params, slim /debug ask, generate only for real frames.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+94
-59
@@ -1061,11 +1061,12 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Scene / edit / «ещё» — Generate without the magic word «генерируй».
|
* Scene / edit / «ещё» — Generate without the magic word «генерируй».
|
||||||
* Bare «давай» and trivia questions stay false.
|
* Chat, opinions, trivia, look-only stay false. No noun-only fallback
|
||||||
|
* («девушка в студии» in a comment must not start a frame).
|
||||||
*/
|
*/
|
||||||
function userImpliesGenerate(text) {
|
function userImpliesGenerate(text) {
|
||||||
const t = String(text || '').trim();
|
const t = String(text || '').trim();
|
||||||
if (!t || userAsksNoGenerate(t)) {
|
if (!t || userAsksNoGenerate(t) || userIsChatNotFrame(t)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (userAsksGenerate(t) || userAsksContinue(t) || isSameButAspectRequest(t)) {
|
if (userAsksGenerate(t) || userAsksContinue(t) || isSameButAspectRequest(t)) {
|
||||||
@@ -1077,63 +1078,74 @@
|
|||||||
const wantsLook = userAsksLook(t);
|
const wantsLook = userAsksLook(t);
|
||||||
const wantsRedraw = cyrTokenRe('поправь|исправь|перегенерир|перерисуй|улучши|переделай').test(t)
|
const wantsRedraw = cyrTokenRe('поправь|исправь|перегенерир|перерисуй|улучши|переделай').test(t)
|
||||||
|| /\b(fix|redo|redraw|improve)\b/i.test(t);
|
|| /\b(fix|redo|redraw|improve)\b/i.test(t);
|
||||||
if (wantsLook && !wantsRedraw && !userAsksGenerate(t)) {
|
if (wantsLook && !wantsRedraw) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (cyrTokenRe(
|
if (cyrTokenRe(
|
||||||
'что\\s+такое|как\\s+работает|зачем\\s+|какие\\s+(лор|модел|чекпоинт)|'
|
'нарису|сгенер|перерису|'
|
||||||
+ 'список\\s+лор|где\\s+настрой|что\\s+значит',
|
+ 'сделай\\s+(картинк|изображен|фото|кадр)|'
|
||||||
).test(t) && !cyrTokenRe('нарису|сгенер|картинк').test(t)) {
|
+ 'хочу\\s+(картинк|изображен|фото|увидеть|видеть)|'
|
||||||
return false;
|
+ 'покажи\\s+как\\s+(она|он|это)|'
|
||||||
}
|
+ 'сделай\\s+(её|ее|его|мне)\\s|'
|
||||||
if (cyrTokenRe(
|
+ 'пусть\\s+будет|'
|
||||||
'нарису|сгенер|перерису|картинк|изображен|'
|
|
||||||
+ 'хочу\\s+(увидеть|видеть)|покажи\\s+(её|ее|его|как)|'
|
|
||||||
+ 'сделай\\s+(её|ее|его|мне)|пусть\\s+будет|'
|
|
||||||
+ 'давай\\s+(её|ее|его|с\\s|в\\s|на\\s)|'
|
|
||||||
+ 'в\\s+(студии|лесу|интерьер|постел)|'
|
|
||||||
+ 'другой\\s+(ракурс|свет|наряд|поза)|'
|
+ 'другой\\s+(ракурс|свет|наряд|поза)|'
|
||||||
+ 'поменяй\\s+(позу|свет|одежд|фон)|добавь\\s+(свет|детал)|'
|
+ 'поменяй\\s+(позу|свет|одежд|фон)|добавь\\s+(свет|детал)|'
|
||||||
+ 'ещё\\s+одн|еще\\s+одн|вариант',
|
+ 'ещё\\s+одн|еще\\s+одн',
|
||||||
).test(t)) {
|
).test(t)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (/\b(draw|paint|render|make her|make him|another one|new frame|in the (studio|forest))\b/i.test(t)) {
|
if (/\b(draw|paint|render|make her|make him|another one|new frame)\b/i.test(t)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const isQuestion = /[??]\s*$/.test(t);
|
const isQuestion = /[??]\s*$/.test(t);
|
||||||
if (isQuestion) {
|
if (isQuestion) {
|
||||||
return cyrTokenRe('нарису|сгенер|можешь\\s+(сделать|нарисовать)|можно\\s+(картинк|сгенер)').test(t);
|
return cyrTokenRe('нарису|сгенер|можешь\\s+(сделать|нарисовать)|можно\\s+(картинк|сгенер)').test(t);
|
||||||
}
|
}
|
||||||
return t.length >= 40 && cyrTokenRe(
|
return false;
|
||||||
'девушк|женщин|парн|мужчин|стоит|сидит|лежит|обнаж|поза|интерьер|студи',
|
}
|
||||||
).test(t);
|
|
||||||
|
/** Opinion / thanks / trivia — not a new frame, even if the model sneaks actions:generate. */
|
||||||
|
function userIsChatNotFrame(text) {
|
||||||
|
const t = String(text || '').trim();
|
||||||
|
if (!t) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (/^(ок|окей|ok|okay|ладно|хорошо|понял|ясно|спасибо|thanks)([!.…\s]*)$/i.test(t)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (cyrTokenRe(
|
||||||
|
'что\\s+такое|как\\s+работает|зачем\\s+|какие\\s+(лор|модел|чекпоинт)|'
|
||||||
|
+ 'список\\s+лор|где\\s+настрой|что\\s+значит|'
|
||||||
|
+ 'нравит|спасибо|благодар|почему\\s+так|что\\s+ты\\s+(сделал|изменил)|'
|
||||||
|
+ 'только\\s+(ответь|скажи|объясни)|без\\s+(кадр|генерац)|не\\s+надо\\s+кадр',
|
||||||
|
).test(t) && !userAsksGenerate(t) && !userAsksContinue(t)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function packBlocksAutoGenerate(pack) {
|
function packBlocksAutoGenerate(pack) {
|
||||||
const p = String(pack || '');
|
const p = String(pack || '');
|
||||||
return p === 'describe_ref' || p === 'catalog_card' || p === 'author_persona';
|
return p === 'describe_ref' || p === 'catalog_card' || p === 'author_persona' || p === 'debug_explain';
|
||||||
}
|
}
|
||||||
|
|
||||||
/** If the turn is a frame, put actions:["generate"] on the patch even when the model omitted it. */
|
/** Inject generate only when the user turn is a frame; strip it on chat/Q&A. */
|
||||||
function ensureGenerateAction(patch, userText) {
|
function ensureGenerateAction(patch, userText) {
|
||||||
if (!patch || typeof patch !== 'object') {
|
if (!patch || typeof patch !== 'object') {
|
||||||
return patch;
|
return patch;
|
||||||
}
|
}
|
||||||
if (userAsksNoGenerate(userText)) {
|
if (userAsksNoGenerate(userText) || userIsChatNotFrame(userText)) {
|
||||||
return patch;
|
return stripGenerateAction(patch);
|
||||||
}
|
|
||||||
if (Array.isArray(patch.actions) && patch.actions.map(String).includes('generate')) {
|
|
||||||
return patch;
|
|
||||||
}
|
}
|
||||||
const pack = $('sa_pack')?.value || '';
|
const pack = $('sa_pack')?.value || '';
|
||||||
if (packBlocksAutoGenerate(pack) && !userAsksGenerate(userText) && !userAsksContinue(userText)) {
|
if (packBlocksAutoGenerate(pack) && !userAsksGenerate(userText) && !userAsksContinue(userText)) {
|
||||||
|
return stripGenerateAction(patch);
|
||||||
|
}
|
||||||
|
const hasAction = Array.isArray(patch.actions) && patch.actions.map(String).includes('generate');
|
||||||
|
if (hasAction) {
|
||||||
return patch;
|
return patch;
|
||||||
}
|
}
|
||||||
const hasFrame = patch.prompt != null
|
if (!userImpliesGenerate(userText)) {
|
||||||
|| (Array.isArray(patch.variants) && patch.variants.length > 0);
|
|
||||||
const implied = userImpliesGenerate(userText);
|
|
||||||
if (!hasFrame && !implied) {
|
|
||||||
return patch;
|
return patch;
|
||||||
}
|
}
|
||||||
const out = { ...patch };
|
const out = { ...patch };
|
||||||
@@ -1343,6 +1355,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Remember applied params when they differ from Exact (or the user asked). */
|
||||||
|
function shouldRememberSessionParam(key, value) {
|
||||||
|
if (state.restoringChat || value == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (state.lastUserParamIntent) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const exactVal = exactDefaultFor(key);
|
||||||
|
if (exactVal == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return String(value) !== String(exactVal);
|
||||||
|
}
|
||||||
|
|
||||||
function fillEmptyParamsFromExact() {
|
function fillEmptyParamsFromExact() {
|
||||||
const defaults = mergedGenerationDefaults();
|
const defaults = mergedGenerationDefaults();
|
||||||
if (isEmptyParamField(val('input_steps'), { treatZeroEmpty: true }) && defaults.steps != null) {
|
if (isEmptyParamField(val('input_steps'), { treatZeroEmpty: true }) && defaults.steps != null) {
|
||||||
@@ -4207,14 +4234,13 @@
|
|||||||
|
|
||||||
if (doParams) {
|
if (doParams) {
|
||||||
const defaults = mergedGenerationDefaults();
|
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 (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) {
|
if (shouldRememberSessionParam('aspect', patch.aspect)) {
|
||||||
rememberSessionExact({ aspect: patch.aspect });
|
rememberSessionExact({ aspect: patch.aspect });
|
||||||
}
|
}
|
||||||
} else if (patch.aspect == null && isEmptyParamField(val('input_width'), { treatZeroEmpty: true })
|
} else if (patch.aspect == null && isEmptyParamField(val('input_width'), { treatZeroEmpty: true })
|
||||||
@@ -4227,7 +4253,7 @@
|
|||||||
} else {
|
} else {
|
||||||
if (patch.width != null && !shouldSkipSessionRollback('width', patch.width)) {
|
if (patch.width != null && !shouldSkipSessionRollback('width', patch.width)) {
|
||||||
setVal('input_width', String(patch.width));
|
setVal('input_width', String(patch.width));
|
||||||
if (capture) {
|
if (shouldRememberSessionParam('width', patch.width)) {
|
||||||
rememberSessionExact({ width: patch.width });
|
rememberSessionExact({ width: patch.width });
|
||||||
}
|
}
|
||||||
} else if (patch.width == null && isEmptyParamField(val('input_width'), { treatZeroEmpty: true }) && defaults.width != null) {
|
} else if (patch.width == null && isEmptyParamField(val('input_width'), { treatZeroEmpty: true }) && defaults.width != null) {
|
||||||
@@ -4235,7 +4261,7 @@
|
|||||||
}
|
}
|
||||||
if (patch.height != null && !shouldSkipSessionRollback('height', patch.height)) {
|
if (patch.height != null && !shouldSkipSessionRollback('height', patch.height)) {
|
||||||
setVal('input_height', String(patch.height));
|
setVal('input_height', String(patch.height));
|
||||||
if (capture) {
|
if (shouldRememberSessionParam('height', patch.height)) {
|
||||||
rememberSessionExact({ height: patch.height });
|
rememberSessionExact({ height: patch.height });
|
||||||
}
|
}
|
||||||
} else if (patch.height == null && isEmptyParamField(val('input_height'), { treatZeroEmpty: true }) && defaults.height != null) {
|
} else if (patch.height == null && isEmptyParamField(val('input_height'), { treatZeroEmpty: true }) && defaults.height != null) {
|
||||||
@@ -4244,7 +4270,7 @@
|
|||||||
}
|
}
|
||||||
if (patch.steps != null && !shouldSkipSessionRollback('steps', patch.steps)) {
|
if (patch.steps != null && !shouldSkipSessionRollback('steps', patch.steps)) {
|
||||||
setVal('input_steps', String(patch.steps));
|
setVal('input_steps', String(patch.steps));
|
||||||
if (capture) {
|
if (shouldRememberSessionParam('steps', patch.steps)) {
|
||||||
rememberSessionExact({ steps: patch.steps });
|
rememberSessionExact({ steps: patch.steps });
|
||||||
}
|
}
|
||||||
} else if (patch.steps == null && isEmptyParamField(val('input_steps'), { treatZeroEmpty: true }) && defaults.steps != null) {
|
} else if (patch.steps == null && isEmptyParamField(val('input_steps'), { treatZeroEmpty: true }) && defaults.steps != null) {
|
||||||
@@ -4256,7 +4282,7 @@
|
|||||||
} else {
|
} else {
|
||||||
setVal('input_cfg', String(patch.cfg));
|
setVal('input_cfg', String(patch.cfg));
|
||||||
}
|
}
|
||||||
if (capture) {
|
if (shouldRememberSessionParam('cfg', patch.cfg)) {
|
||||||
rememberSessionExact({ cfg: patch.cfg });
|
rememberSessionExact({ cfg: patch.cfg });
|
||||||
}
|
}
|
||||||
} else if (patch.cfg == null) {
|
} else if (patch.cfg == null) {
|
||||||
@@ -4278,13 +4304,13 @@
|
|||||||
}
|
}
|
||||||
} else if (patch.seed != null && !shouldSkipSessionRollback('seed', patch.seed)) {
|
} else if (patch.seed != null && !shouldSkipSessionRollback('seed', patch.seed)) {
|
||||||
setVal('input_seed', String(patch.seed));
|
setVal('input_seed', String(patch.seed));
|
||||||
if (capture) {
|
if (shouldRememberSessionParam('seed', patch.seed)) {
|
||||||
rememberSessionExact({ seed: patch.seed });
|
rememberSessionExact({ seed: patch.seed });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (patch.sigma_shift != null && !shouldSkipSessionRollback('sigma_shift', patch.sigma_shift)) {
|
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) {
|
if (shouldRememberSessionParam('sigma_shift', patch.sigma_shift)) {
|
||||||
rememberSessionExact({ sigma_shift: patch.sigma_shift });
|
rememberSessionExact({ sigma_shift: patch.sigma_shift });
|
||||||
}
|
}
|
||||||
} else if (patch.sigma_shift == null && isEmptyParamField(val('input_sigmashift')) && defaults.sigma_shift != null) {
|
} else if (patch.sigma_shift == null && isEmptyParamField(val('input_sigmashift')) && defaults.sigma_shift != null) {
|
||||||
@@ -4294,13 +4320,13 @@
|
|||||||
if (document.getElementById('input_sampler')) {
|
if (document.getElementById('input_sampler')) {
|
||||||
setVal('input_sampler', String(patch.sampler));
|
setVal('input_sampler', String(patch.sampler));
|
||||||
}
|
}
|
||||||
if (capture) {
|
if (shouldRememberSessionParam('sampler', patch.sampler)) {
|
||||||
rememberSessionExact({ sampler: patch.sampler });
|
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) {
|
if (shouldRememberSessionParam('scheduler', patch.scheduler)) {
|
||||||
rememberSessionExact({ scheduler: patch.scheduler });
|
rememberSessionExact({ scheduler: patch.scheduler });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4311,7 +4337,7 @@
|
|||||||
} else if (document.getElementById('input_batchsize')) {
|
} else if (document.getElementById('input_batchsize')) {
|
||||||
setVal('input_batchsize', String(batch));
|
setVal('input_batchsize', String(batch));
|
||||||
}
|
}
|
||||||
if (capture) {
|
if (shouldRememberSessionParam('images', batch)) {
|
||||||
rememberSessionExact({ images: batch });
|
rememberSessionExact({ images: batch });
|
||||||
}
|
}
|
||||||
} else if (batch == null) {
|
} else if (batch == null) {
|
||||||
@@ -8133,11 +8159,12 @@
|
|||||||
state.pendingSilentGen = false;
|
state.pendingSilentGen = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const wantsGen = !suppressGen && !!(opts.userWantsGenerate || state.pendingSilentGen
|
const hasGenAction = Array.isArray(effective?.actions) && effective.actions.map(String).includes('generate');
|
||||||
|| userImpliesGenerate(opts.userText || '')
|
const implied = userImpliesGenerate(opts.userText || '');
|
||||||
|| (Array.isArray(effective?.actions) && effective.actions.map(String).includes('generate')));
|
const wantsGen = !suppressGen && !!(opts.userWantsGenerate || state.pendingSilentGen || implied
|
||||||
const willGen = !!(effective && !fromAutoCritique && !suppressGen
|
|| (hasGenAction && !userIsChatNotFrame(opts.userText || '')));
|
||||||
&& (wantsGen || $('sa_auto_generate')?.checked));
|
// Auto-Generate = skip Apply buttons when this turn is a frame — not "every patch".
|
||||||
|
const willGen = !!(effective && !fromAutoCritique && !suppressGen && wantsGen);
|
||||||
// Maximize chat-model prep: structure + EN for Krea before Swarm Generate runs.
|
// Maximize chat-model prep: structure + EN for Krea before Swarm Generate runs.
|
||||||
if (willGen && effective?.prompt && promptNeedsKreaPrep(effective.prompt)
|
if (willGen && effective?.prompt && promptNeedsKreaPrep(effective.prompt)
|
||||||
&& !opts.fromPromptEnRetry && !fromVisionHop && !fromDebug) {
|
&& !opts.fromPromptEnRetry && !fromVisionHop && !fromDebug) {
|
||||||
@@ -8165,7 +8192,7 @@
|
|||||||
updateTasteFromPatch(effective, opts.userText || '');
|
updateTasteFromPatch(effective, opts.userText || '');
|
||||||
// Auto-Generate must not fire on «запомни / шаблон» turns — even if the model
|
// Auto-Generate must not fire on «запомни / шаблон» turns — even if the model
|
||||||
// echoed a prompt patch or sneaked actions:["generate"].
|
// echoed a prompt patch or sneaked actions:["generate"].
|
||||||
if (!fromAutoCritique && !suppressGen && (wantsGen || $('sa_auto_generate')?.checked)) {
|
if (!fromAutoCritique && !suppressGen && wantsGen) {
|
||||||
if (effective?.prompt && promptNeedsKreaPrep(effective.prompt)) {
|
if (effective?.prompt && promptNeedsKreaPrep(effective.prompt)) {
|
||||||
setStatus('Промпт всё ещё не EN/Krea-ready — Generate с тем что есть');
|
setStatus('Промпт всё ещё не EN/Krea-ready — Generate с тем что есть');
|
||||||
}
|
}
|
||||||
@@ -8368,6 +8395,7 @@
|
|||||||
skipSlash: true,
|
skipSlash: true,
|
||||||
skipAutoPack: true,
|
skipAutoPack: true,
|
||||||
fromDebug: true,
|
fromDebug: true,
|
||||||
|
skipAppendUser: true,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setStatus('/debug');
|
setStatus('/debug');
|
||||||
@@ -8651,7 +8679,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!opts.skipAutoPack && !opts.fromAutoCritique && !opts.fromDownload && !opts.fromVisionHop && !opts.fromCards) {
|
if (!opts.skipAutoPack && !opts.fromAutoCritique && !opts.fromDownload && !opts.fromVisionHop && !opts.fromCards && !opts.fromDebug) {
|
||||||
const guessed = autoSelectPack(text);
|
const guessed = autoSelectPack(text);
|
||||||
if (guessed) {
|
if (guessed) {
|
||||||
setPackValue(guessed, { flash: true });
|
setPackValue(guessed, { flash: true });
|
||||||
@@ -8659,11 +8687,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cards mode must not be overridden by auto-pack; keep catalog_card.
|
// Cards mode must not be overridden by auto-pack; keep catalog_card.
|
||||||
if (opts.fromCards || state.view === 'cards') {
|
if (!opts.fromDebug && (opts.fromCards || state.view === 'cards')) {
|
||||||
setPackValue('catalog_card', { flash: false });
|
setPackValue('catalog_card', { flash: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
const pack = $('sa_pack')?.value || defaultPackId();
|
const pack = opts.fromDebug ? 'debug_explain' : ($('sa_pack')?.value || defaultPackId());
|
||||||
const persona = $('sa_persona')?.value || localStorage.getItem(LS_PERSONA) || 'neutral';
|
const persona = $('sa_persona')?.value || localStorage.getItem(LS_PERSONA) || 'neutral';
|
||||||
const model = $('sa_model')?.value;
|
const model = $('sa_model')?.value;
|
||||||
if (!model) {
|
if (!model) {
|
||||||
@@ -8744,14 +8772,18 @@
|
|||||||
pack,
|
pack,
|
||||||
silentPatch: !!state.pendingSilentGen,
|
silentPatch: !!state.pendingSilentGen,
|
||||||
};
|
};
|
||||||
state.history.push({ role: 'user', content: text });
|
if (!opts.skipAppendUser) {
|
||||||
if (state.pendingPersonaNote) {
|
state.history.push({ role: 'user', content: opts.historyUserText || text });
|
||||||
state.history.push({ role: 'user', content: state.pendingPersonaNote, systemish: true });
|
if (state.pendingPersonaNote) {
|
||||||
state.pendingPersonaNote = null;
|
state.history.push({ role: 'user', content: state.pendingPersonaNote, systemish: true });
|
||||||
|
state.pendingPersonaNote = null;
|
||||||
|
}
|
||||||
|
appendMessage('user', opts.historyUserText || text);
|
||||||
|
if ($('sa_input')) {
|
||||||
|
$('sa_input').value = '';
|
||||||
|
}
|
||||||
|
persistHistory();
|
||||||
}
|
}
|
||||||
appendMessage('user', text);
|
|
||||||
$('sa_input').value = '';
|
|
||||||
persistHistory();
|
|
||||||
|
|
||||||
const context = collectLiveContext();
|
const context = collectLiveContext();
|
||||||
// has_vision_image = board has a real frame (even when JPEG is not in this request).
|
// has_vision_image = board has a real frame (even when JPEG is not in this request).
|
||||||
@@ -8782,6 +8814,9 @@
|
|||||||
}
|
}
|
||||||
return { role: m.role, content: content.slice(0, 4000) };
|
return { role: m.role, content: content.slice(0, 4000) };
|
||||||
});
|
});
|
||||||
|
if (opts.skipAppendUser) {
|
||||||
|
messages.push({ role: 'user', content: text });
|
||||||
|
}
|
||||||
if (images && messages.length) {
|
if (images && messages.length) {
|
||||||
messages[messages.length - 1].images = images;
|
messages[messages.length - 1].images = images;
|
||||||
}
|
}
|
||||||
@@ -8795,10 +8830,10 @@
|
|||||||
model,
|
model,
|
||||||
pack,
|
pack,
|
||||||
persona,
|
persona,
|
||||||
includeBase: true,
|
includeBase: !opts.fromDebug,
|
||||||
messages,
|
messages,
|
||||||
context_json: JSON.stringify(context),
|
context_json: JSON.stringify(context),
|
||||||
skills: state.enabledSkills || [],
|
skills: opts.fromDebug ? [] : (state.enabledSkills || []),
|
||||||
embed_model: $('sa_embed_model')?.value || state.preferredEmbed || '',
|
embed_model: $('sa_embed_model')?.value || state.preferredEmbed || '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+42
-22
@@ -18,6 +18,9 @@ public partial class SwarmAssistentExtension
|
|||||||
const int MaxCivitaiHopsFallback = 2;
|
const int MaxCivitaiHopsFallback = 2;
|
||||||
const int MaxToolHopsFallback = 4;
|
const int MaxToolHopsFallback = 4;
|
||||||
|
|
||||||
|
static bool IsSlimDebugPack(string packName) =>
|
||||||
|
string.Equals(packName, "debug_explain", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
(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> 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 = [];
|
List<JObject> ollamaMessages = [];
|
||||||
@@ -45,7 +48,9 @@ public partial class SwarmAssistentExtension
|
|||||||
AddLayer("core", Config.LoadCorePrompt(pid));
|
AddLayer("core", Config.LoadCorePrompt(pid));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Memory is not null)
|
bool slimDebug = IsSlimDebugPack(packName);
|
||||||
|
|
||||||
|
if (Memory is not null && !slimDebug)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -72,23 +77,25 @@ public partial class SwarmAssistentExtension
|
|||||||
+ exact.ToString(Newtonsoft.Json.Formatting.None) + "\n```");
|
+ exact.ToString(Newtonsoft.Json.Formatting.None) + "\n```");
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder skillsBlock = new();
|
if (!slimDebug)
|
||||||
foreach (string skillId in skillIds ?? Config.ResolveEnabledSkills(pid, null))
|
|
||||||
{
|
{
|
||||||
string skillText = Config.LoadSkillPrompt(pid, skillId);
|
StringBuilder skillsBlock = new();
|
||||||
if (!string.IsNullOrWhiteSpace(skillText))
|
foreach (string skillId in skillIds ?? Config.ResolveEnabledSkills(pid, null))
|
||||||
{
|
{
|
||||||
if (skillsBlock.Length > 0)
|
string skillText = Config.LoadSkillPrompt(pid, skillId);
|
||||||
|
if (!string.IsNullOrWhiteSpace(skillText))
|
||||||
{
|
{
|
||||||
skillsBlock.AppendLine();
|
if (skillsBlock.Length > 0)
|
||||||
|
{
|
||||||
|
skillsBlock.AppendLine();
|
||||||
|
}
|
||||||
|
skillsBlock.AppendLine($"## Skill: {skillId}");
|
||||||
|
skillsBlock.AppendLine(skillText.TrimEnd());
|
||||||
}
|
}
|
||||||
skillsBlock.AppendLine($"## Skill: {skillId}");
|
|
||||||
skillsBlock.AppendLine(skillText.TrimEnd());
|
|
||||||
}
|
}
|
||||||
|
AddLayer("skills", skillsBlock.ToString());
|
||||||
|
AddLayer("identity", Config.RenderIdentityBlock(pid));
|
||||||
}
|
}
|
||||||
AddLayer("skills", skillsBlock.ToString());
|
|
||||||
|
|
||||||
AddLayer("identity", Config.RenderIdentityBlock(pid));
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(packName))
|
if (!string.IsNullOrWhiteSpace(packName))
|
||||||
{
|
{
|
||||||
@@ -169,20 +176,27 @@ public partial class SwarmAssistentExtension
|
|||||||
Logs.Debug($"Assistent memory seed: {ex.Message}");
|
Logs.Debug($"Assistent memory seed: {ex.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
string retrieveQuery = BuildRetrieveQuery(userMessages, contextJson, packName);
|
bool slimDebug = IsSlimDebugPack(packName);
|
||||||
JArray hits = [];
|
JArray hits = [];
|
||||||
try
|
if (!slimDebug)
|
||||||
{
|
{
|
||||||
AssistentMemory.RetrieveOptions opt = MemoryRetrieveOptions(pid);
|
string retrieveQuery = BuildRetrieveQuery(userMessages, contextJson, packName);
|
||||||
hits = await Memory.RetrieveAsync(root, retrieveQuery, opt.TopK, embed, Config.PersonaExtendsChain(pid), opt);
|
try
|
||||||
}
|
{
|
||||||
catch (Exception ex)
|
AssistentMemory.RetrieveOptions opt = MemoryRetrieveOptions(pid);
|
||||||
{
|
hits = await Memory.RetrieveAsync(root, retrieveQuery, opt.TopK, embed, Config.PersonaExtendsChain(pid), opt);
|
||||||
Logs.Debug($"Assistent memory retrieve: {ex.Message}");
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logs.Debug($"Assistent memory retrieve: {ex.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string enrichedContext = InjectMemoryHits(contextJson, hits);
|
string enrichedContext = InjectMemoryHits(contextJson, hits);
|
||||||
enrichedContext = EnrichPersonaContext(enrichedContext, pid, packName);
|
if (!slimDebug)
|
||||||
|
{
|
||||||
|
enrichedContext = EnrichPersonaContext(enrichedContext, pid, packName);
|
||||||
|
}
|
||||||
(List<JObject> messages, JObject systemLayers) = BuildOllamaMessages(packName, includeBase, enrichedContext, userMessages, personaId: pid, skillIds: skills);
|
(List<JObject> messages, JObject systemLayers) = BuildOllamaMessages(packName, includeBase, enrichedContext, userMessages, personaId: pid, skillIds: skills);
|
||||||
int systemChars = systemLayers["total"]?.Value<int?>()
|
int systemChars = systemLayers["total"]?.Value<int?>()
|
||||||
?? messages.FirstOrDefault(m => string.Equals(m["role"]?.ToString(), "system", StringComparison.OrdinalIgnoreCase))?["content"]?.ToString()?.Length
|
?? messages.FirstOrDefault(m => string.Equals(m["role"]?.ToString(), "system", StringComparison.OrdinalIgnoreCase))?["content"]?.ToString()?.Length
|
||||||
@@ -190,7 +204,9 @@ public partial class SwarmAssistentExtension
|
|||||||
JArray civitaiResults = [];
|
JArray civitaiResults = [];
|
||||||
string reply = "";
|
string reply = "";
|
||||||
JObject lastRaw = null;
|
JObject lastRaw = null;
|
||||||
int maxHops = Math.Max(CfgInt("max_civitai_hops", MaxCivitaiHopsFallback), CfgInt("max_tool_hops", MaxToolHopsFallback));
|
int maxHops = slimDebug
|
||||||
|
? 1
|
||||||
|
: Math.Max(CfgInt("max_civitai_hops", MaxCivitaiHopsFallback), CfgInt("max_tool_hops", MaxToolHopsFallback));
|
||||||
HashSet<string> hopDone = new(StringComparer.OrdinalIgnoreCase);
|
HashSet<string> hopDone = new(StringComparer.OrdinalIgnoreCase);
|
||||||
var chain = Config.PersonaExtendsChain(pid);
|
var chain = Config.PersonaExtendsChain(pid);
|
||||||
for (int hop = 0; hop < maxHops; hop++)
|
for (int hop = 0; hop < maxHops; hop++)
|
||||||
@@ -200,6 +216,10 @@ public partial class SwarmAssistentExtension
|
|||||||
await onHopStart(hop);
|
await onHopStart(hop);
|
||||||
}
|
}
|
||||||
(reply, lastRaw) = await CallOllamaChat(root, modelName, messages, stream: onDelta is not null, onDelta, pid);
|
(reply, lastRaw) = await CallOllamaChat(root, modelName, messages, stream: onDelta is not null, onDelta, pid);
|
||||||
|
if (slimDebug)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
JObject patch = TryParsePatch(reply);
|
JObject patch = TryParsePatch(reply);
|
||||||
await ApplyMemoryActions(root, patch, embed, pid);
|
await ApplyMemoryActions(root, patch, embed, pid);
|
||||||
ApplyUserPrefActions(patch, pid);
|
ApplyUserPrefActions(patch, pid);
|
||||||
|
|||||||
@@ -979,6 +979,12 @@ public sealed class AssistentConfig
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
bool hidden = meta["hidden"]?.Value<bool?>() == true;
|
||||||
|
if (hidden)
|
||||||
|
{
|
||||||
|
byId.Remove(id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
bool enabled = meta["enabled"]?.Value<bool?>() != false;
|
bool enabled = meta["enabled"]?.Value<bool?>() != false;
|
||||||
string[] aliases = (meta["aliases"] as JArray)?.Select(t => t?.ToString()).Where(s => !string.IsNullOrWhiteSpace(s)).ToArray() ?? [];
|
string[] aliases = (meta["aliases"] as JArray)?.Select(t => t?.ToString()).Where(s => !string.IsNullOrWhiteSpace(s)).ToArray() ?? [];
|
||||||
byId[id] = (
|
byId[id] = (
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ Several options in one ask (still one fence):
|
|||||||
|
|
||||||
### Actions / hops
|
### Actions / hops
|
||||||
|
|
||||||
- `"generate"` — Apply + start generation. Emit this whenever the turn is a **new/updated frame** (scene brief, “her in the studio”, pose/light/wardrobe change, «ещё», variants) — not only if they typed «генерируй». Omit `generate` only for: Pure Q&A, remember/save prompt, look/critique without a redraw, describe_ref without a gen ask, Cards/authoring.
|
- `"generate"` — Apply + start generation when this turn is a **new/updated frame** (they described a shot, asked to draw/edit/«ещё», or clearly want to see a result). They do **not** have to type «генерируй». **Do not** emit `generate` for chat, opinions («нравится»), trivia, look/critique without a redraw, remember/save, describe_ref, Cards/authoring. Chat-only turns: prose, no JSON patch (or prompt-only without `actions`).
|
||||||
- If the user only asks to **remember / save** a prompt as base/template («запомни», «как базовый промпт», «шаблон») and did **not** ask for a new image: **omit** `actions: ["generate"]`, do **not** `look_at`. Prefer `memory_upsert` (kind `note`, key like `base_prompt`) or a short ack; you may echo `prompt` in the patch only to sync the SwarmUI box — the UI will not Generate on remember turns.
|
- If the user only asks to **remember / save** a prompt as base/template («запомни», «как базовый промпт», «шаблон») and did **not** ask for a new image: **omit** `actions: ["generate"]`, do **not** `look_at`. Prefer `memory_upsert` (kind `note`, key like `base_prompt`) or a short ack; you may echo `prompt` in the patch only to sync the SwarmUI box — the UI will not Generate on remember turns.
|
||||||
- `"search_civitai"` + **required** short `search_query` — Civitai hop (user Confirms downloads). Without `search_query` the hop is skipped (never search the whole user message).
|
- `"search_civitai"` + **required** short `search_query` — Civitai hop (user Confirms downloads). Without `search_query` the hop is skipped (never search the whole user message).
|
||||||
- `"interrupt"` — stop generation.
|
- `"interrupt"` — stop generation.
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"id": "debug_explain",
|
||||||
|
"title": "Debug explain",
|
||||||
|
"order": 999,
|
||||||
|
"hidden": true,
|
||||||
|
"enabled": true,
|
||||||
|
"aliases": ["debug_explain"],
|
||||||
|
"prompt_file": "debug_explain.md"
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Mode: debug_explain (hidden)
|
||||||
|
|
||||||
|
You are answering a **debug Q&A** about the current Assistent UI dump. This is not a generation turn.
|
||||||
|
|
||||||
|
## Hard rules
|
||||||
|
|
||||||
|
- Explain in the user's language, **5–10 short lines**.
|
||||||
|
- **No** fenced JSON. **No** `### JSON Patch`. **No** `actions`. **No** `generate`. **No** `look_at`. **No** LoRA/search hops.
|
||||||
|
- Do **not** roleplay a patch, and do **not** tell the user to type «сгенерируй».
|
||||||
|
- Ignore persona voice if it conflicts with this: be a concise technician.
|
||||||
|
|
||||||
|
## What to cover
|
||||||
|
|
||||||
|
Use the dump in the user message plus Exact / live JSON if present:
|
||||||
|
|
||||||
|
1. What prompt / negative / LoRAs / params are live now.
|
||||||
|
2. What comes from Exact vs `session_exact` vs live Swarm fields vs last patch.
|
||||||
|
3. Why the last turn behaved that way (apply / generate / look / chat-only).
|
||||||
|
|
||||||
|
If a field is missing from the dump, say so — do not invent numbers.
|
||||||
@@ -4,7 +4,7 @@ Default all-rounder. Handle this turn from the user message + live context — d
|
|||||||
|
|
||||||
## What you cover here
|
## What you cover here
|
||||||
|
|
||||||
- **Write / improve prompt** → patch with `prompt` + `negative` (+ `loras` when useful) and `actions: ["generate"]` when this turn is a frame (scene, edit, «ещё») — **not** only if they typed «генерируй». Do **not** `look_at` the last frame first. `negative`: create / supplement / echo live — do not omit on Generate.
|
- **Write / improve prompt** → patch with `prompt` + `negative` (+ `loras`) and `actions: ["generate"]` only when they want a **new frame** (scene to draw, edit, «ещё») — context is enough, magic word is not required. Chat / «нравится» / Q&A → prose only, **no** generate. Do **not** `look_at` the last frame first. `negative` on Generate: create / supplement / echo live.
|
||||||
- **Light critique / improve last frame** → only when the user asks to look / critique / describe the picture. Then `look_at: ["generate"]` if `images_in_request` is false. Otherwise edit the prompt from text; `has_vision_image` alone is not a reason to look.
|
- **Light critique / improve last frame** → only when the user asks to look / critique / describe the picture. Then `look_at: ["generate"]` if `images_in_request` is false. Otherwise edit the prompt from text; `has_vision_image` alone is not a reason to look.
|
||||||
- **Scene / mood** → compose direction into the prompt (same patch rules).
|
- **Scene / mood** → compose direction into the prompt (same patch rules).
|
||||||
- **Params** → only when they ask (steps/CFG/aspect/seed); omit Exact-matching numbers otherwise.
|
- **Params** → only when they ask (steps/CFG/aspect/seed); omit Exact-matching numbers otherwise.
|
||||||
@@ -26,6 +26,6 @@ Otherwise **stay in ordinary** and just do the work.
|
|||||||
|
|
||||||
## Deliverable
|
## Deliverable
|
||||||
|
|
||||||
Same as write_prompt: short reply + one fenced JSON patch; `actions: ["generate"]` when the turn is a frame (context is enough — they do not have to say «генерируй»).
|
Same as write_prompt: short reply + fenced JSON **only if** this turn is a frame; then `actions: ["generate"]`. Chat/Q&A/opinion: no generate (prose is enough).
|
||||||
«давай дальше» / next frame = new English `prompt` + `negative` (echo live if unchanged) + `actions:["generate"]` in the **same** turn — never leave an empty `### JSON Patch` header. Chat may be RU; **Generate `prompt` is always EN** (skill `prompting`).
|
«давай дальше» / next frame = new English `prompt` + `negative` (echo live if unchanged) + `actions:["generate"]` in the **same** turn — never leave an empty `### JSON Patch` header. Chat may be RU; **Generate `prompt` is always EN** (skill `prompting`).
|
||||||
Several options in one ask → `variants` (2–4 partial patches with `label`); still one fence, still STOP after it.
|
Several options in one ask → `variants` (2–4 partial patches with `label`); still one fence, still STOP after it.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Goal: craft or improve a **Krea 2** prompt that will generate well on Turbo (loc
|
|||||||
|
|
||||||
- Brief note of what you changed.
|
- Brief note of what you changed.
|
||||||
- JSON patch with at least `prompt` and `negative` (create / supplement / echo live), and `loras` when relevant.
|
- JSON patch with at least `prompt` and `negative` (create / supplement / echo live), and `loras` when relevant.
|
||||||
- `actions: ["generate"]` when this turn is a new/updated image (scene brief, edit, «ещё») — they do **not** have to type «генерируй». Skip generate only for Q&A / remember / look-only. Do **not** `look_at` unless they asked to see/critique the last frame.
|
- `actions: ["generate"]` only when they want a new/updated image (scene, edit, «ещё») — they do **not** have to type «генерируй». Skip generate for chat / Q&A / remember / look-only. Do **not** `look_at` unless they asked to see/critique the last frame.
|
||||||
- Prefer Exact Turbo defaults / `recommended_params`. Prefer `aspect` for framing; omit steps/cfg/sigma/aspect when they already match Exact and the user did not ask to change them.
|
- Prefer Exact Turbo defaults / `recommended_params`. Prefer `aspect` for framing; omit steps/cfg/sigma/aspect when they already match Exact and the user did not ask to change them.
|
||||||
- Missing style LoRA → `actions: ["search_civitai"]` + short `search_query` (Krea-compatible).
|
- Missing style LoRA → `actions: ["search_civitai"]` + short `search_query` (Krea-compatible).
|
||||||
- User wants several options (оба / варианты / разный свет) → `variants: [{label, prompt|aspect|…}, …]` (2–4). Base keys inherit; each item overrides only its diffs. Still one fence.
|
- User wants several options (оба / варианты / разный свет) → `variants: [{label, prompt|aspect|…}, …]` (2–4). Base keys inherit; each item overrides only its diffs. Still one fence.
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
SwarmUI extension for **collaborative Krea 2** prompting via **Ollama**: chat + board (Generate | Refs tabs), LoRA chips, **persona presets** (`Config/personas/`), **About the user** prefs + craft 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/`), **About the user** prefs + craft vector memory, model cards with Civitai fetch, img2img/inpaint, slash commands, auto Generate.
|
||||||
|
|
||||||
|
**Version 0.11.8** — `session_exact` remembers applied params that differ from Exact (not only when the user typed the knob). `/debug ask` uses a hidden Q&A pack: 5–10 line explain, no JSON/generate, dump stays a system note. Builds on 0.11.7.
|
||||||
|
|
||||||
|
**Version 0.11.7** — Generate only for a real frame request: chat/opinions no longer auto-run Swarm. Context still counts («нарисуй», «ещё одну», «другая поза»), not only «генерируй». Builds on 0.11.6.
|
||||||
|
|
||||||
**Version 0.11.6** — Generate from context, not only «генерируй»: scene briefs / edits / «ещё» emit `actions:["generate"]`; client injects it if the model forgets. Builds on 0.11.5.
|
**Version 0.11.6** — Generate from context, not only «генерируй»: scene briefs / edits / «ещё» emit `actions:["generate"]`; client injects it if the model forgets. Builds on 0.11.5.
|
||||||
|
|
||||||
**Version 0.11.5** — Generate always carries a negative: model creates / supplements / echoes live; client pass-through if omitted. Builds on 0.11.4.
|
**Version 0.11.5** — Generate always carries a negative: model creates / supplements / echoes live; client pass-through if omitted. Builds on 0.11.4.
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public partial class SwarmAssistentExtension : Extension
|
|||||||
ExtensionAuthor = "mrleo1nid";
|
ExtensionAuthor = "mrleo1nid";
|
||||||
Description = "Collaborative Krea 2 assistant: Ollama chat, persona presets, vector memory, model cards, Generate loop.";
|
Description = "Collaborative Krea 2 assistant: Ollama chat, persona presets, vector memory, model cards, Generate loop.";
|
||||||
License = "MIT";
|
License = "MIT";
|
||||||
Version = "0.11.6";
|
Version = "0.11.8";
|
||||||
Tags = ["tabs", "ui", "llm", "ollama", "krea", "inpaint", "memory"];
|
Tags = ["tabs", "ui", "llm", "ollama", "krea", "inpaint", "memory"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -170,7 +170,7 @@
|
|||||||
<p class="sa-settings-hint">Автодействия после ответа модели и скилы текущей личности.</p>
|
<p class="sa-settings-hint">Автодействия после ответа модели и скилы текущей личности.</p>
|
||||||
<label class="sa-check" title="По умолчанию выкл. Кадр смотрит по кнопке «Посмотри результат», /look, или когда модель сама шлёт look_at. Эта галка — после каждого Generate."><input type="checkbox" id="sa_auto_vision" /> После Generate — look_at кадра</label>
|
<label class="sa-check" title="По умолчанию выкл. Кадр смотрит по кнопке «Посмотри результат», /look, или когда модель сама шлёт look_at. Эта галка — после каждого Generate."><input type="checkbox" id="sa_auto_vision" /> После Generate — look_at кадра</label>
|
||||||
<label class="sa-check"><input type="checkbox" id="sa_auto_apply" checked /> Авто-применять патч</label>
|
<label class="sa-check"><input type="checkbox" id="sa_auto_apply" checked /> Авто-применять патч</label>
|
||||||
<label class="sa-check"><input type="checkbox" id="sa_auto_generate" checked /> Авто-Generate после патча</label>
|
<label class="sa-check" title="Если ход — кадр, Generate без кнопок Apply. Чат и «нравится» не запускают картинку."><input type="checkbox" id="sa_auto_generate" checked /> Авто-Generate после патча</label>
|
||||||
<label class="sa-check" title="По умолчанию выкл. Критика кадра — кнопка «Посмотри результат» или /look. Галка шлёт JPEG после каждого Generate."><input type="checkbox" id="sa_auto_critique" /> Авто-критика после Generate</label>
|
<label class="sa-check" title="По умолчанию выкл. Критика кадра — кнопка «Посмотри результат» или /look. Галка шлёт JPEG после каждого Generate."><input type="checkbox" id="sa_auto_critique" /> Авто-критика после Generate</label>
|
||||||
<label class="sa-check" title="Выгружает чат-модель перед Generate (keep_alive:0). Для VL 7B обратная загрузка часто 1–2 мин — включай только если Generate падает по VRAM."><input type="checkbox" id="sa_park_llm" /> Park LLM перед Generate (VRAM)</label>
|
<label class="sa-check" title="Выгружает чат-модель перед Generate (keep_alive:0). Для VL 7B обратная загрузка часто 1–2 мин — включай только если Generate падает по VRAM."><input type="checkbox" id="sa_park_llm" /> Park LLM перед Generate (VRAM)</label>
|
||||||
<label class="sa-check sa-danger" title="Опасно — скачивает без Confirm"><input type="checkbox" id="sa_auto_download" /> Авто-скачивание Civitai (выкл)</label>
|
<label class="sa-check sa-danger" title="Опасно — скачивает без Confirm"><input type="checkbox" id="sa_auto_download" /> Авто-скачивание Civitai (выкл)</label>
|
||||||
|
|||||||
Reference in New Issue
Block a user