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 «генерируй».
|
||||
* 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) {
|
||||
const t = String(text || '').trim();
|
||||
if (!t || userAsksNoGenerate(t)) {
|
||||
if (!t || userAsksNoGenerate(t) || userIsChatNotFrame(t)) {
|
||||
return false;
|
||||
}
|
||||
if (userAsksGenerate(t) || userAsksContinue(t) || isSameButAspectRequest(t)) {
|
||||
@@ -1077,63 +1078,74 @@
|
||||
const wantsLook = userAsksLook(t);
|
||||
const wantsRedraw = cyrTokenRe('поправь|исправь|перегенерир|перерисуй|улучши|переделай').test(t)
|
||||
|| /\b(fix|redo|redraw|improve)\b/i.test(t);
|
||||
if (wantsLook && !wantsRedraw && !userAsksGenerate(t)) {
|
||||
if (wantsLook && !wantsRedraw) {
|
||||
return false;
|
||||
}
|
||||
if (cyrTokenRe(
|
||||
'что\\s+такое|как\\s+работает|зачем\\s+|какие\\s+(лор|модел|чекпоинт)|'
|
||||
+ 'список\\s+лор|где\\s+настрой|что\\s+значит',
|
||||
).test(t) && !cyrTokenRe('нарису|сгенер|картинк').test(t)) {
|
||||
return false;
|
||||
}
|
||||
if (cyrTokenRe(
|
||||
'нарису|сгенер|перерису|картинк|изображен|'
|
||||
+ 'хочу\\s+(увидеть|видеть)|покажи\\s+(её|ее|его|как)|'
|
||||
+ 'сделай\\s+(её|ее|его|мне)|пусть\\s+будет|'
|
||||
+ 'давай\\s+(её|ее|его|с\\s|в\\s|на\\s)|'
|
||||
+ 'в\\s+(студии|лесу|интерьер|постел)|'
|
||||
'нарису|сгенер|перерису|'
|
||||
+ 'сделай\\s+(картинк|изображен|фото|кадр)|'
|
||||
+ 'хочу\\s+(картинк|изображен|фото|увидеть|видеть)|'
|
||||
+ 'покажи\\s+как\\s+(она|он|это)|'
|
||||
+ 'сделай\\s+(её|ее|его|мне)\\s|'
|
||||
+ 'пусть\\s+будет|'
|
||||
+ 'другой\\s+(ракурс|свет|наряд|поза)|'
|
||||
+ 'поменяй\\s+(позу|свет|одежд|фон)|добавь\\s+(свет|детал)|'
|
||||
+ 'ещё\\s+одн|еще\\s+одн|вариант',
|
||||
+ 'ещё\\s+одн|еще\\s+одн',
|
||||
).test(t)) {
|
||||
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;
|
||||
}
|
||||
const isQuestion = /[??]\s*$/.test(t);
|
||||
if (isQuestion) {
|
||||
return cyrTokenRe('нарису|сгенер|можешь\\s+(сделать|нарисовать)|можно\\s+(картинк|сгенер)').test(t);
|
||||
}
|
||||
return t.length >= 40 && cyrTokenRe(
|
||||
'девушк|женщин|парн|мужчин|стоит|сидит|лежит|обнаж|поза|интерьер|студи',
|
||||
).test(t);
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 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) {
|
||||
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) {
|
||||
if (!patch || typeof patch !== 'object') {
|
||||
return patch;
|
||||
}
|
||||
if (userAsksNoGenerate(userText)) {
|
||||
return patch;
|
||||
}
|
||||
if (Array.isArray(patch.actions) && patch.actions.map(String).includes('generate')) {
|
||||
return patch;
|
||||
if (userAsksNoGenerate(userText) || userIsChatNotFrame(userText)) {
|
||||
return stripGenerateAction(patch);
|
||||
}
|
||||
const pack = $('sa_pack')?.value || '';
|
||||
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;
|
||||
}
|
||||
const hasFrame = patch.prompt != null
|
||||
|| (Array.isArray(patch.variants) && patch.variants.length > 0);
|
||||
const implied = userImpliesGenerate(userText);
|
||||
if (!hasFrame && !implied) {
|
||||
if (!userImpliesGenerate(userText)) {
|
||||
return 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() {
|
||||
const defaults = mergedGenerationDefaults();
|
||||
if (isEmptyParamField(val('input_steps'), { treatZeroEmpty: true }) && defaults.steps != null) {
|
||||
@@ -4207,14 +4234,13 @@
|
||||
|
||||
if (doParams) {
|
||||
const defaults = mergedGenerationDefaults();
|
||||
const capture = state.lastUserParamIntent;
|
||||
const aspectSize = sizeFromAspect(patch.aspect);
|
||||
if (patch.aspect != null && !shouldSkipSessionRollback('aspect', patch.aspect)) {
|
||||
if (aspectSize) {
|
||||
setVal('input_width', String(aspectSize[0]));
|
||||
setVal('input_height', String(aspectSize[1]));
|
||||
}
|
||||
if (capture) {
|
||||
if (shouldRememberSessionParam('aspect', patch.aspect)) {
|
||||
rememberSessionExact({ aspect: patch.aspect });
|
||||
}
|
||||
} else if (patch.aspect == null && isEmptyParamField(val('input_width'), { treatZeroEmpty: true })
|
||||
@@ -4227,7 +4253,7 @@
|
||||
} else {
|
||||
if (patch.width != null && !shouldSkipSessionRollback('width', patch.width)) {
|
||||
setVal('input_width', String(patch.width));
|
||||
if (capture) {
|
||||
if (shouldRememberSessionParam('width', patch.width)) {
|
||||
rememberSessionExact({ width: patch.width });
|
||||
}
|
||||
} 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)) {
|
||||
setVal('input_height', String(patch.height));
|
||||
if (capture) {
|
||||
if (shouldRememberSessionParam('height', patch.height)) {
|
||||
rememberSessionExact({ height: patch.height });
|
||||
}
|
||||
} 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)) {
|
||||
setVal('input_steps', String(patch.steps));
|
||||
if (capture) {
|
||||
if (shouldRememberSessionParam('steps', patch.steps)) {
|
||||
rememberSessionExact({ steps: patch.steps });
|
||||
}
|
||||
} else if (patch.steps == null && isEmptyParamField(val('input_steps'), { treatZeroEmpty: true }) && defaults.steps != null) {
|
||||
@@ -4256,7 +4282,7 @@
|
||||
} else {
|
||||
setVal('input_cfg', String(patch.cfg));
|
||||
}
|
||||
if (capture) {
|
||||
if (shouldRememberSessionParam('cfg', patch.cfg)) {
|
||||
rememberSessionExact({ cfg: patch.cfg });
|
||||
}
|
||||
} else if (patch.cfg == null) {
|
||||
@@ -4278,13 +4304,13 @@
|
||||
}
|
||||
} else if (patch.seed != null && !shouldSkipSessionRollback('seed', patch.seed)) {
|
||||
setVal('input_seed', String(patch.seed));
|
||||
if (capture) {
|
||||
if (shouldRememberSessionParam('seed', patch.seed)) {
|
||||
rememberSessionExact({ seed: patch.seed });
|
||||
}
|
||||
}
|
||||
if (patch.sigma_shift != null && !shouldSkipSessionRollback('sigma_shift', 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 });
|
||||
}
|
||||
} else if (patch.sigma_shift == null && isEmptyParamField(val('input_sigmashift')) && defaults.sigma_shift != null) {
|
||||
@@ -4294,13 +4320,13 @@
|
||||
if (document.getElementById('input_sampler')) {
|
||||
setVal('input_sampler', String(patch.sampler));
|
||||
}
|
||||
if (capture) {
|
||||
if (shouldRememberSessionParam('sampler', patch.sampler)) {
|
||||
rememberSessionExact({ sampler: patch.sampler });
|
||||
}
|
||||
}
|
||||
if (patch.scheduler != null && document.getElementById('input_scheduler')) {
|
||||
setVal('input_scheduler', String(patch.scheduler));
|
||||
if (capture) {
|
||||
if (shouldRememberSessionParam('scheduler', patch.scheduler)) {
|
||||
rememberSessionExact({ scheduler: patch.scheduler });
|
||||
}
|
||||
}
|
||||
@@ -4311,7 +4337,7 @@
|
||||
} else if (document.getElementById('input_batchsize')) {
|
||||
setVal('input_batchsize', String(batch));
|
||||
}
|
||||
if (capture) {
|
||||
if (shouldRememberSessionParam('images', batch)) {
|
||||
rememberSessionExact({ images: batch });
|
||||
}
|
||||
} else if (batch == null) {
|
||||
@@ -8133,11 +8159,12 @@
|
||||
state.pendingSilentGen = false;
|
||||
return;
|
||||
}
|
||||
const wantsGen = !suppressGen && !!(opts.userWantsGenerate || state.pendingSilentGen
|
||||
|| userImpliesGenerate(opts.userText || '')
|
||||
|| (Array.isArray(effective?.actions) && effective.actions.map(String).includes('generate')));
|
||||
const willGen = !!(effective && !fromAutoCritique && !suppressGen
|
||||
&& (wantsGen || $('sa_auto_generate')?.checked));
|
||||
const hasGenAction = Array.isArray(effective?.actions) && effective.actions.map(String).includes('generate');
|
||||
const implied = userImpliesGenerate(opts.userText || '');
|
||||
const wantsGen = !suppressGen && !!(opts.userWantsGenerate || state.pendingSilentGen || implied
|
||||
|| (hasGenAction && !userIsChatNotFrame(opts.userText || '')));
|
||||
// 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.
|
||||
if (willGen && effective?.prompt && promptNeedsKreaPrep(effective.prompt)
|
||||
&& !opts.fromPromptEnRetry && !fromVisionHop && !fromDebug) {
|
||||
@@ -8165,7 +8192,7 @@
|
||||
updateTasteFromPatch(effective, opts.userText || '');
|
||||
// Auto-Generate must not fire on «запомни / шаблон» turns — even if the model
|
||||
// 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)) {
|
||||
setStatus('Промпт всё ещё не EN/Krea-ready — Generate с тем что есть');
|
||||
}
|
||||
@@ -8368,6 +8395,7 @@
|
||||
skipSlash: true,
|
||||
skipAutoPack: true,
|
||||
fromDebug: true,
|
||||
skipAppendUser: true,
|
||||
});
|
||||
} else {
|
||||
setStatus('/debug');
|
||||
@@ -8651,7 +8679,7 @@
|
||||
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);
|
||||
if (guessed) {
|
||||
setPackValue(guessed, { flash: true });
|
||||
@@ -8659,11 +8687,11 @@
|
||||
}
|
||||
|
||||
// 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 });
|
||||
}
|
||||
|
||||
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 model = $('sa_model')?.value;
|
||||
if (!model) {
|
||||
@@ -8744,14 +8772,18 @@
|
||||
pack,
|
||||
silentPatch: !!state.pendingSilentGen,
|
||||
};
|
||||
state.history.push({ role: 'user', content: text });
|
||||
if (state.pendingPersonaNote) {
|
||||
state.history.push({ role: 'user', content: state.pendingPersonaNote, systemish: true });
|
||||
state.pendingPersonaNote = null;
|
||||
if (!opts.skipAppendUser) {
|
||||
state.history.push({ role: 'user', content: opts.historyUserText || text });
|
||||
if (state.pendingPersonaNote) {
|
||||
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();
|
||||
// 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) };
|
||||
});
|
||||
if (opts.skipAppendUser) {
|
||||
messages.push({ role: 'user', content: text });
|
||||
}
|
||||
if (images && messages.length) {
|
||||
messages[messages.length - 1].images = images;
|
||||
}
|
||||
@@ -8795,10 +8830,10 @@
|
||||
model,
|
||||
pack,
|
||||
persona,
|
||||
includeBase: true,
|
||||
includeBase: !opts.fromDebug,
|
||||
messages,
|
||||
context_json: JSON.stringify(context),
|
||||
skills: state.enabledSkills || [],
|
||||
skills: opts.fromDebug ? [] : (state.enabledSkills || []),
|
||||
embed_model: $('sa_embed_model')?.value || state.preferredEmbed || '',
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user