Ship Assistent 0.11.6: opt-in vision, negative pass-through, generate from context.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+236
-18
@@ -16,6 +16,8 @@
|
||||
const LS_AUTO_APPLY = 'swarm_assistent_auto_apply';
|
||||
const LS_AUTO_GENERATE = 'swarm_assistent_auto_generate';
|
||||
const LS_AUTO_CRITIQUE = 'swarm_assistent_auto_critique';
|
||||
/** One-shot: turn off auto look_at / auto-critique (vision is button / /look / model look_at). */
|
||||
const LS_VISION_OPTIN_MIG = 'swarm_assistent_vision_optin_v1';
|
||||
const LS_AUTO_DOWNLOAD = 'swarm_assistent_auto_download';
|
||||
/** When '1', unload chat LLM before Generate (frees VRAM; VL reload can take 1–2 min). Default off. */
|
||||
const LS_PARK_LLM = 'swarm_assistent_park_llm';
|
||||
@@ -89,7 +91,7 @@
|
||||
/debug ask — то же + короткий ответ модели
|
||||
/why — сразу /debug ask
|
||||
/gen — Generate сейчас
|
||||
/look generate|refN — прикрепить окно к vision
|
||||
/look generate|refN — показать кадр модели (vision)
|
||||
/init /mask /clear — Init / Mask / Clear Init
|
||||
/interrupt — остановить генерацию
|
||||
/aspect 16:9 — размер из таблицы 1K
|
||||
@@ -656,6 +658,43 @@
|
||||
el.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
|
||||
function liveNegativePrompt() {
|
||||
return String(val('input_negativeprompt') || val('alt_negativeprompt_textbox') || '').trim();
|
||||
}
|
||||
|
||||
function exactDefaultNegative() {
|
||||
const { exact } = resolveExactBundle();
|
||||
const n = exact?.generation?.negative ?? exact?.negative;
|
||||
return n != null ? String(n).trim() : '';
|
||||
}
|
||||
|
||||
function setNegativePrompt(text) {
|
||||
const s = text != null ? String(text) : '';
|
||||
if (document.getElementById('input_negativeprompt')) {
|
||||
setVal('input_negativeprompt', s);
|
||||
}
|
||||
if (document.getElementById('alt_negativeprompt_textbox')) {
|
||||
setVal('alt_negativeprompt_textbox', s);
|
||||
}
|
||||
}
|
||||
|
||||
/** Create / keep / pass-through negative so Generate never drops the Swarm box. */
|
||||
function ensureNegativeForGenerate(patch) {
|
||||
let neg = '';
|
||||
if (patch && patch.negative != null && String(patch.negative).trim() !== '') {
|
||||
neg = String(patch.negative).trim();
|
||||
} else {
|
||||
neg = liveNegativePrompt() || exactDefaultNegative();
|
||||
}
|
||||
if (neg) {
|
||||
setNegativePrompt(neg);
|
||||
if (patch && (patch.negative == null || String(patch.negative).trim() === '')) {
|
||||
patch.negative = neg;
|
||||
}
|
||||
}
|
||||
return neg;
|
||||
}
|
||||
|
||||
function isEmptyParamField(raw, { treatZeroEmpty = false } = {}) {
|
||||
if (raw == null) {
|
||||
return true;
|
||||
@@ -875,7 +914,7 @@
|
||||
/** When the model wrote ### JSON Patch with no fence — build Apply+Generate from prose / last patch. */
|
||||
function synthesizePatchAfterEmptyFence(reply, userText, opts = {}) {
|
||||
const wants = !!(opts.userWantsGenerate || state.pendingSilentGen
|
||||
|| userAsksGenerate(userText) || userAsksContinue(userText));
|
||||
|| userAsksGenerate(userText) || userAsksContinue(userText) || userImpliesGenerate(userText));
|
||||
const missing = replyMissingJsonPatch(reply);
|
||||
if (!wants && !missing) {
|
||||
return null;
|
||||
@@ -930,6 +969,14 @@
|
||||
if (patch.aspect) {
|
||||
keep.aspect = patch.aspect;
|
||||
}
|
||||
if (patch.negative != null && String(patch.negative).trim() !== '') {
|
||||
keep.negative = patch.negative;
|
||||
} else {
|
||||
const liveNeg = liveNegativePrompt() || exactDefaultNegative();
|
||||
if (liveNeg) {
|
||||
keep.negative = liveNeg;
|
||||
}
|
||||
}
|
||||
if (Array.isArray(patch.loras)) {
|
||||
keep.loras = patch.loras;
|
||||
}
|
||||
@@ -947,9 +994,10 @@
|
||||
+ '- Structure & front-load: subject → pose/action → body/wardrobe → setting → materials/textures → camera/framing → lighting → medium/mood.\n'
|
||||
+ '- Expand thin ideas; fix anti-patterns; one coherent scene.\n'
|
||||
+ '- Keep LoRA trigger phrases in English near the subject they affect.\n'
|
||||
+ '- Prefer positives over negatives; preserve meaning and NSFW level from SOURCE.\n'
|
||||
+ '- Always include JSON "negative": keep/supplement SOURCE+live negative, or use Exact default if empty. Never drop it.\n'
|
||||
+ '- Put “no blur / empty street” ideas as positives in prompt, not as a huge negative dump.\n'
|
||||
+ '- One short ack in the user language max, then ONE fenced JSON merging these keys: '
|
||||
+ `${JSON.stringify(keep)} plus the new English "prompt".\n`
|
||||
+ `${JSON.stringify(keep)} plus the new English "prompt" and "negative".\n`
|
||||
+ '- Include actions:["generate"] when an image was requested.\n\n'
|
||||
+ `SOURCE:\n${patch.prompt}`
|
||||
);
|
||||
@@ -965,6 +1013,9 @@
|
||||
...base,
|
||||
...effective,
|
||||
prompt: effective.prompt || base.prompt,
|
||||
negative: (effective.negative != null && String(effective.negative).trim() !== '')
|
||||
? effective.negative
|
||||
: (base.negative || liveNegativePrompt() || exactDefaultNegative() || undefined),
|
||||
actions: (Array.isArray(effective.actions) && effective.actions.length)
|
||||
? effective.actions
|
||||
: (base.actions || ['generate']),
|
||||
@@ -990,6 +1041,137 @@
|
||||
).test(t);
|
||||
}
|
||||
|
||||
/** Button / /look / «посмотри на кадр» — not casual «смотри какая». */
|
||||
function userAsksLook(text) {
|
||||
const t = String(text || '').trim();
|
||||
if (!t) {
|
||||
return false;
|
||||
}
|
||||
if (/\b(look\s+at|critique|criticize|describe\s+(this|the|ref|image)|what\s+do\s+you\s+see)\b/i.test(t)) {
|
||||
return true;
|
||||
}
|
||||
if (cyrTokenRe('критик[а-яё]*|что\\s+не\\s+так|разбери').test(t)) {
|
||||
return true;
|
||||
}
|
||||
if (cyrTokenRe('опиши\\s+(это|эту|реф|изображ[а-яё]*|картинк[а-яё]*|кадр|результат|референс)').test(t)) {
|
||||
return true;
|
||||
}
|
||||
return /(?:^|[^а-яёa-z0-9_])(посмотри|смотри|глянь)\s+(на\s+)?(результат|картинк[а-яё]*|изображен[а-яё]*|кадр|ген|реф)/i.test(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scene / edit / «ещё» — Generate without the magic word «генерируй».
|
||||
* Bare «давай» and trivia questions stay false.
|
||||
*/
|
||||
function userImpliesGenerate(text) {
|
||||
const t = String(text || '').trim();
|
||||
if (!t || userAsksNoGenerate(t)) {
|
||||
return false;
|
||||
}
|
||||
if (userAsksGenerate(t) || userAsksContinue(t) || isSameButAspectRequest(t)) {
|
||||
return true;
|
||||
}
|
||||
if (t.length < 8) {
|
||||
return false;
|
||||
}
|
||||
const wantsLook = userAsksLook(t);
|
||||
const wantsRedraw = cyrTokenRe('поправь|исправь|перегенерир|перерисуй|улучши|переделай').test(t)
|
||||
|| /\b(fix|redo|redraw|improve)\b/i.test(t);
|
||||
if (wantsLook && !wantsRedraw && !userAsksGenerate(t)) {
|
||||
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+одн|вариант',
|
||||
).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)) {
|
||||
return true;
|
||||
}
|
||||
const isQuestion = /[??]\s*$/.test(t);
|
||||
if (isQuestion) {
|
||||
return cyrTokenRe('нарису|сгенер|можешь\\s+(сделать|нарисовать)|можно\\s+(картинк|сгенер)').test(t);
|
||||
}
|
||||
return t.length >= 40 && cyrTokenRe(
|
||||
'девушк|женщин|парн|мужчин|стоит|сидит|лежит|обнаж|поза|интерьер|студи',
|
||||
).test(t);
|
||||
}
|
||||
|
||||
function packBlocksAutoGenerate(pack) {
|
||||
const p = String(pack || '');
|
||||
return p === 'describe_ref' || p === 'catalog_card' || p === 'author_persona';
|
||||
}
|
||||
|
||||
/** If the turn is a frame, put actions:["generate"] on the patch even when the model omitted it. */
|
||||
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;
|
||||
}
|
||||
const pack = $('sa_pack')?.value || '';
|
||||
if (packBlocksAutoGenerate(pack) && !userAsksGenerate(userText) && !userAsksContinue(userText)) {
|
||||
return patch;
|
||||
}
|
||||
const hasFrame = patch.prompt != null
|
||||
|| (Array.isArray(patch.variants) && patch.variants.length > 0);
|
||||
const implied = userImpliesGenerate(userText);
|
||||
if (!hasFrame && !implied) {
|
||||
return patch;
|
||||
}
|
||||
const out = { ...patch };
|
||||
const acts = Array.isArray(patch.actions) ? patch.actions.map(String).filter((a) => a && a !== 'generate') : [];
|
||||
acts.push('generate');
|
||||
out.actions = acts;
|
||||
return out;
|
||||
}
|
||||
|
||||
function packWantsVision(pack) {
|
||||
const p = String(pack || '');
|
||||
return p === 'critique_image' || p === 'describe_ref' || p === 'compose_scene' || p === 'inpaint_edit';
|
||||
}
|
||||
|
||||
/** Honor model look_at only when asked, pack needs it, or look-only (no Generate this turn). */
|
||||
function shouldHonorLookAt(patch, opts = {}) {
|
||||
if (!patch || typeof patch !== 'object') {
|
||||
return false;
|
||||
}
|
||||
if (patch.look_at == null && patch.vision_from == null && patch.vision_slots == null) {
|
||||
return false;
|
||||
}
|
||||
if (opts.fromAutoCritique || opts.fromVisionHop) {
|
||||
return true;
|
||||
}
|
||||
if (userAsksLook(opts.userText || '')) {
|
||||
return true;
|
||||
}
|
||||
if (packWantsVision($('sa_pack')?.value)) {
|
||||
return true;
|
||||
}
|
||||
// look_at + generate on a write turn stares at the old frame and delays the new one.
|
||||
if (patchHasGenTrigger(patch)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function stripGenerateAction(patch) {
|
||||
if (!patch || typeof patch !== 'object') {
|
||||
return patch;
|
||||
@@ -1204,6 +1386,12 @@
|
||||
setVal(batchId, String(batch));
|
||||
}
|
||||
}
|
||||
if (!liveNegativePrompt()) {
|
||||
const neg = exactDefaultNegative();
|
||||
if (neg) {
|
||||
setNegativePrompt(neg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function shouldSkipSessionRollback(key, patchValue) {
|
||||
@@ -2274,6 +2462,9 @@
|
||||
promptBox.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
setVal('input_negativeprompt', params.negative != null ? String(params.negative) : '');
|
||||
if (document.getElementById('alt_negativeprompt_textbox')) {
|
||||
setVal('alt_negativeprompt_textbox', params.negative != null ? String(params.negative) : '');
|
||||
}
|
||||
// Force-write numerics when present so prior chat values cannot stick.
|
||||
if (params.width != null) {
|
||||
setVal('input_width', String(params.width));
|
||||
@@ -3965,9 +4156,6 @@
|
||||
box.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
box.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
if (patch.negative != null) {
|
||||
setVal('input_negativeprompt', patch.negative);
|
||||
}
|
||||
if (Array.isArray(patch.loras)) {
|
||||
for (const l of patch.loras) {
|
||||
const triggers = l.triggers || (l.trigger_phrase ? [l.trigger_phrase] : []);
|
||||
@@ -3980,6 +4168,13 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
if (doPrompt) {
|
||||
if (patch.negative != null) {
|
||||
setNegativePrompt(patch.negative);
|
||||
} else if (patchHasGenTrigger(patch)) {
|
||||
ensureNegativeForGenerate(patch);
|
||||
}
|
||||
}
|
||||
|
||||
if (doLoras && Array.isArray(patch.loras) && typeof loraHelper !== 'undefined' && loraHelper) {
|
||||
try {
|
||||
@@ -4611,6 +4806,7 @@
|
||||
await applyPatch(job.patch, 'all');
|
||||
syncLiveParamsBar();
|
||||
}
|
||||
ensureNegativeForGenerate(job.patch);
|
||||
|
||||
if (epoch !== state.chatEpoch) {
|
||||
break;
|
||||
@@ -5332,6 +5528,18 @@
|
||||
}
|
||||
localStorage.setItem(LS_PACK_ORDINARY_MIG, '1');
|
||||
}
|
||||
// One-shot: stop staring at every Generate. Vision = button / /look / model look_at.
|
||||
if (!localStorage.getItem(LS_VISION_OPTIN_MIG)) {
|
||||
localStorage.setItem(LS_AUTO_VISION, '0');
|
||||
localStorage.setItem(LS_AUTO_CRITIQUE, '0');
|
||||
localStorage.setItem(LS_VISION_OPTIN_MIG, '1');
|
||||
if ($('sa_auto_vision')) {
|
||||
$('sa_auto_vision').checked = false;
|
||||
}
|
||||
if ($('sa_auto_critique')) {
|
||||
$('sa_auto_critique').checked = false;
|
||||
}
|
||||
}
|
||||
const base = localStorage.getItem(LS_BASE);
|
||||
const model = localStorage.getItem(LS_MODEL);
|
||||
const pack = localStorage.getItem(LS_PACK);
|
||||
@@ -7865,19 +8073,18 @@
|
||||
}
|
||||
if (!effective && !fromVisionHop && !fromAutoCritique && !fromDebug
|
||||
&& !opts.fromEmptyPatchRetry
|
||||
&& replyMissingJsonPatch(reply)
|
||||
&& (opts.userWantsGenerate || state.pendingSilentGen || userAsksContinue(opts.userText || ''))) {
|
||||
appendSystemNote('Патч пустой — прошу модель дописать JSON.');
|
||||
&& userImpliesGenerate(opts.userText || '')) {
|
||||
appendSystemNote('Нужен кадр — прошу JSON с prompt + generate.');
|
||||
await sendChat({
|
||||
skipSlash: true,
|
||||
skipAutoPack: true,
|
||||
fromEmptyPatchRetry: true,
|
||||
userWantsGenerate: true,
|
||||
forcedUserText:
|
||||
'Ты написал «JSON Patch» без fenced ```json```. '
|
||||
+ 'Сейчас ответь ТОЛЬКО одним fenced JSON объектом: '
|
||||
+ '{"prompt":"<English Krea prompt for Qwen3-VL>","actions":["generate"]}. '
|
||||
+ 'prompt — только английский (skill prompting). Без прозы, без ### заголовков.',
|
||||
'Пользователь уже просит кадр (это следует из сообщения, даже без слова «генерируй»). '
|
||||
+ 'Ответь ТОЛЬКО одним fenced JSON: '
|
||||
+ '{"prompt":"<English Krea prompt>","negative":"<echo or short quality neg>","actions":["generate"]}. '
|
||||
+ 'prompt — английский. Без прозы, без «скажи сгенерируй».',
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -7905,7 +8112,17 @@
|
||||
rememberLastPatch(effective);
|
||||
}
|
||||
}
|
||||
if (effective && !fromVisionHop && !fromAutoCritique && !suppressGen) {
|
||||
if (effective && !fromDebug && !suppressGen) {
|
||||
effective = ensureGenerateAction(effective, opts.userText || '');
|
||||
rememberLastPatch(effective);
|
||||
}
|
||||
if (effective && !fromVisionHop && !fromAutoCritique && !fromDebug && !shouldHonorLookAt(effective, opts)) {
|
||||
effective = stripLookAt(effective);
|
||||
if (effective) {
|
||||
rememberLastPatch(effective);
|
||||
}
|
||||
}
|
||||
if (effective && !fromVisionHop && !fromAutoCritique && !suppressGen && shouldHonorLookAt(effective, opts)) {
|
||||
const hopped = await maybeVisionHop(effective, opts.attachedSlotIds || []);
|
||||
if (hopped) {
|
||||
return;
|
||||
@@ -7917,6 +8134,7 @@
|
||||
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));
|
||||
@@ -8385,7 +8603,7 @@
|
||||
if (!opts.fromAutoCritique && !opts.fromDownload && !opts.fromVisionHop && !opts.fromDebug) {
|
||||
state.lastUserParamIntent = userTextMentionsParams(text);
|
||||
state.lastUserControlIntent = userTextMentionsControls(text);
|
||||
state.pendingSilentGen = userAsksGenerate(text) || userAsksContinue(text) || isSameButAspectRequest(text);
|
||||
state.pendingSilentGen = userImpliesGenerate(text);
|
||||
if (userAsksNoGenerate(text)) {
|
||||
state.pendingSilentGen = false;
|
||||
}
|
||||
@@ -8602,12 +8820,12 @@
|
||||
const prose = extractPatch(reply).prose || reply;
|
||||
state.history.push({ role: 'assistant', content: prose, persona, pack });
|
||||
persistHistory();
|
||||
setBusyPhase(state.pendingSilentGen || userAsksGenerate(text) || userAsksContinue(text) ? 'silent_gen' : 'thinking');
|
||||
setBusyPhase(state.pendingSilentGen || userImpliesGenerate(text) ? 'silent_gen' : 'thinking');
|
||||
try {
|
||||
await handleReplySideEffects(reply, civitaiResults, {
|
||||
...opts,
|
||||
userText: text,
|
||||
userWantsGenerate: !!state.pendingSilentGen || userAsksGenerate(text) || userAsksContinue(text),
|
||||
userWantsGenerate: !!state.pendingSilentGen || userImpliesGenerate(text),
|
||||
attachedSlotIds: visionSlots.map((s) => s.id),
|
||||
});
|
||||
} finally {
|
||||
|
||||
@@ -6,7 +6,7 @@ window.SA = window.SA || {};
|
||||
|
||||
(function () {
|
||||
const PATCH_KEYS = [
|
||||
'prompt', 'loras', 'width', 'height', 'steps', 'cfg', 'seed', 'sigma_shift', 'sampler',
|
||||
'prompt', 'negative', 'loras', 'width', 'height', 'steps', 'cfg', 'seed', 'sigma_shift', 'sampler',
|
||||
'actions', 'search_query', 'civitai_query',
|
||||
'use_init_image', 'clear_init_image', 'init_creativity', 'denoise',
|
||||
'use_mask_image', 'clear_mask_image', 'mask_blur', 'mask_grow',
|
||||
@@ -31,8 +31,8 @@ window.SA = window.SA || {};
|
||||
return false;
|
||||
}
|
||||
const cardish = !!(obj.kind || obj.triggers || obj.when || obj.prompt_hint);
|
||||
const genish = !!(obj.prompt != null || obj.loras || obj.actions || obj.width || obj.height
|
||||
|| obj.steps || obj.cfg || obj.aspect || obj.seed != null
|
||||
const genish = !!(obj.prompt != null || obj.negative != null || obj.loras || obj.actions
|
||||
|| obj.width || obj.height || obj.steps || obj.cfg || obj.aspect || obj.seed != null
|
||||
|| obj.search_query || obj.civitai_query || obj.look_at || obj.controls);
|
||||
if (cardish && !genish && (obj.name || obj.triggers || obj.when)) {
|
||||
return true;
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ public partial class SwarmAssistentExtension
|
||||
|
||||
static readonly string[] PatchKeys =
|
||||
[
|
||||
"prompt", "loras", "width", "height", "steps", "cfg", "seed", "sigma_shift", "sampler",
|
||||
"prompt", "negative", "loras", "width", "height", "steps", "cfg", "seed", "sigma_shift", "sampler",
|
||||
"actions", "search_query", "civitai_query",
|
||||
"use_init_image", "clear_init_image", "init_creativity", "denoise",
|
||||
"use_mask_image", "clear_mask_image", "mask_blur", "mask_grow",
|
||||
@@ -66,7 +66,7 @@ public partial class SwarmAssistentExtension
|
||||
return false;
|
||||
}
|
||||
bool cardish = HasValue(obj, "kind") || HasValue(obj, "triggers") || HasValue(obj, "when") || HasValue(obj, "prompt_hint");
|
||||
bool genish = HasValue(obj, "prompt") || HasValue(obj, "loras") || HasValue(obj, "actions")
|
||||
bool genish = HasValue(obj, "prompt") || HasValue(obj, "negative") || HasValue(obj, "loras") || HasValue(obj, "actions")
|
||||
|| HasValue(obj, "width") || HasValue(obj, "height") || HasValue(obj, "steps") || HasValue(obj, "cfg")
|
||||
|| HasValue(obj, "aspect") || HasValue(obj, "seed") || HasValue(obj, "search_query")
|
||||
|| HasValue(obj, "civitai_query") || HasValue(obj, "look_at") || HasValue(obj, "controls");
|
||||
|
||||
@@ -27,7 +27,7 @@ Never write a “JSON Patch” section in prose without an actual fenced ```json
|
||||
- Use only LoRA/checkpoint **names** from `selected_loras` / `available_loras` (or Civitai hop results). `selected_loras` = currently enabled. Prefer listed `triggers` / `trigger_phrase` / `blurb` — **never invent**.
|
||||
- Rich entries (blurbs/triggers) are selected + top krea-likely. Name-only rows need `list_inventory` + `inventory_query` before you rely on them.
|
||||
- `memory_hits` may be truncated (`truncated: true`) — use `memory_get` for the full text.
|
||||
- `has_vision_image` true means a real board frame exists. `images_in_request` true means JPEG bytes are in **this** request. If you need to see a frame and `images_in_request` is false, emit `look_at` first — do not invent what the image looks like.
|
||||
- `has_vision_image` true means a real board frame exists. `images_in_request` true means JPEG bytes are in **this** request. **Do not** `look_at` just because a frame exists. Emit `look_at` only when you cannot continue without pixels (user asked to look/critique/describe/compare, or a defect you cannot infer from the prompt). Never invent what the image looks like. A new Generate / «ещё» / prompt edit does **not** need vision.
|
||||
- Prefer `krea_likely` / Krea architecture; ignore FLUX/SDXL. Respect current params unless asked or pack is `form_params`.
|
||||
- Init/inpaint flags and `image_slots` are in the JSON. Extra pack fields are documented in the active pack.
|
||||
|
||||
@@ -45,6 +45,7 @@ Never write a “JSON Patch” section in prose without an actual fenced ```json
|
||||
```json
|
||||
{
|
||||
"prompt": "A fluffy red fox in fresh powder snow, soft morning light, 85mm f/2.8…",
|
||||
"negative": "bad quality, worst quality",
|
||||
"loras": [{"name": "exact_name_from_list", "weight": 0.8, "triggers": ["listed_trigger"]}],
|
||||
"aspect": "16:9",
|
||||
"actions": ["generate"],
|
||||
@@ -69,7 +70,7 @@ Several options in one ask (still one fence):
|
||||
|
||||
### Patch rules
|
||||
|
||||
- Omit unchanged keys. Prefer omitting Exact-matching `steps`/`cfg`/`sigma_shift`/`aspect`.
|
||||
- Omit unchanged **params** (`steps`/`cfg`/`sigma_shift`/`aspect`). For Generate, still include **`negative`**: create if live is empty, lightly supplement if the scene needs a specific omit, or echo the live/Exact box unchanged — never drop it.
|
||||
- Prefer omitting Exact-matching **`controls`** (e.g. do not re-emit `"horny": 35` / `"preference_bias": 0.35` when unchanged) — echoing defaults in a Generate patch resets the UI sliders.
|
||||
- `loras` replaces the full intended set for Apply. Prefer `aspect` over raw width/height.
|
||||
- Optional keys (seed, vary, init/mask, creativity/sliders, pack, **controls**, persona authoring, search/memory queries, **`variants`**) — use when needed; packs list the ones for that mode.
|
||||
@@ -78,7 +79,7 @@ Several options in one ask (still one fence):
|
||||
|
||||
### Actions / hops
|
||||
|
||||
- `"generate"` — Apply + start generation when the user wants a new image. Auto-Generate is a **UI checkbox** (Settings → Поведение); the model cannot toggle it — emit `actions:["generate"]` instead.
|
||||
- `"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.
|
||||
- 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).
|
||||
- `"interrupt"` — stop generation.
|
||||
@@ -88,5 +89,5 @@ Several options in one ask (still one fence):
|
||||
- `"persona_read"` — load lore shelves not in always-on identity (e.g. `roleplay` / `craft` / `humor` when NSFW tone or craft detail needs them).
|
||||
- `"memory_upsert"` / `"memory_forget"` / `"user_pref_upsert"` / `"user_pref_forget"` — writes.
|
||||
- `"persona_clone"` / `"persona_write"` / `"persona_switch"` — `author_persona` only. Never `"persona_delete"`.
|
||||
- `look_at: ["generate"|"ref1"|…]` — vision hop (JPEG arrives on the follow-up).
|
||||
- `look_at: ["generate"|"ref1"|…]` — vision hop (JPEG arrives on the follow-up). Opt-in: user asked, or you truly need pixels. Do not pair `look_at` with `actions:["generate"]` on a normal write turn (that stares at the *old* frame and delays the new one).
|
||||
- Pure Q&A: omit the JSON patch.
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"steps": 8,
|
||||
"cfg": 1,
|
||||
"sigma_shift": 1.15,
|
||||
"images": 1
|
||||
"images": 1,
|
||||
"negative": "bad quality, worst quality"
|
||||
},
|
||||
"profiles": {
|
||||
"turbo": {
|
||||
@@ -31,7 +32,7 @@
|
||||
"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.",
|
||||
"prompt_language": "Chat model always prep's Generate prompt for Krea: English natural prose for Qwen3-VL, structured (subject→pose→setting→camera→light). Chat may be RU; never leave Russian or thin drafts in patch.prompt.",
|
||||
"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.",
|
||||
"negatives": "Qwen3-VL negatives are weak — still keep a short Swarm negative box. Prefer positives in `prompt`; for `negative`: create if live is empty (Exact generation.negative), lightly supplement if the scene needs a specific omit, or echo live unchanged. Never drop/clear the box on Generate. 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."
|
||||
|
||||
@@ -4,8 +4,8 @@ Default all-rounder. Handle this turn from the user message + live context — d
|
||||
|
||||
## What you cover here
|
||||
|
||||
- **Write / improve prompt** → patch with `prompt` (+ `loras` when useful) and `actions: ["generate"]` when they want a new image.
|
||||
- **Light critique / improve last frame** → short notes + better `prompt`; use `look_at: ["generate"]` if `has_vision_image` and you have not seen the JPEG this turn.
|
||||
- **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.
|
||||
- **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).
|
||||
- **Params** → only when they ask (steps/CFG/aspect/seed); omit Exact-matching numbers otherwise.
|
||||
- **Inpaint / img2img** → set init/mask fields when they ask and flags allow; else say what is missing.
|
||||
@@ -26,6 +26,6 @@ Otherwise **stay in ordinary** and just do the work.
|
||||
|
||||
## Deliverable
|
||||
|
||||
Same as write_prompt: short reply + one fenced JSON patch; `actions: ["generate"]` when they want an image.
|
||||
«давай дальше» / next frame = new English `prompt` + `actions:["generate"]` in the **same** turn — never leave an empty `### JSON Patch` header. Chat may be RU; **Generate `prompt` is always EN** (skill `prompting`).
|
||||
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 «генерируй»).
|
||||
«давай дальше» / 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.
|
||||
|
||||
@@ -5,8 +5,8 @@ Goal: craft or improve a **Krea 2** prompt that will generate well on Turbo (loc
|
||||
## Deliverable
|
||||
|
||||
- Brief note of what you changed.
|
||||
- JSON patch with at least `prompt`, and `loras` when relevant.
|
||||
- `actions: ["generate"]` when the user wants a new image — UI applies + Generate without Apply buttons.
|
||||
- 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.
|
||||
- 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).
|
||||
- User wants several options (оба / варианты / разный свет) → `variants: [{label, prompt|aspect|…}, …]` (2–4). Base keys inherit; each item overrides only its diffs. Still one fence.
|
||||
|
||||
@@ -9,12 +9,13 @@ The **chat model** prepares the Generate-box text for **Krea 2** (Qwen3-VL). Do
|
||||
3. **Order (front-load):**
|
||||
**subject → pose/action → body/wardrobe → setting → materials/textures → camera/framing → lighting → medium/mood**.
|
||||
4. Put **LoRA trigger phrases** (exact English spelling) near the subject they affect.
|
||||
5. Prefer **positives** over negatives (Qwen negatives are weak).
|
||||
5. **`negative` on every Generate** — create if live is empty (Exact `generation.negative`), supplement if the scene needs a specific omit, or echo live unchanged. Put “no blur / no people” ideas as positives in `prompt` instead of stuffing the negative box. Never clear `negative`.
|
||||
6. Short ideas: expand into a full Krea paragraph. Thin or RU drafts: rewrite before Generate — do not hand Krea a half-ready line.
|
||||
|
||||
## Prep checklist (before `actions:["generate"]`)
|
||||
|
||||
- English only in `prompt`
|
||||
- `negative` present (new / supplemented / echoed — not omitted)
|
||||
- Subject and action clear in the first sentence
|
||||
- Wardrobe / body / setting concrete
|
||||
- Camera + lighting present
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
|
||||
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.3** — Stream no longer aborts on weak JSON fences (so prompts/skill_load can finish); `num_predict` 3072. Builds on 0.11.2 Krea prep.
|
||||
**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.4** — Vision is opt-in: look via «Посмотри результат» / `/look`, or when the model truly needs pixels. No auto look_at / auto-critique after every Generate (checkboxes stay, default off). Builds on 0.11.3.
|
||||
|
||||
**Version 0.11.2** — Before Krea Generate, chat model maximally preps the prompt (EN + structure); skip prep hop only if already Krea-ready English. Builds on 0.11.1.
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ public partial class SwarmAssistentExtension : Extension
|
||||
ExtensionAuthor = "mrleo1nid";
|
||||
Description = "Collaborative Krea 2 assistant: Ollama chat, persona presets, vector memory, model cards, Generate loop.";
|
||||
License = "MIT";
|
||||
Version = "0.11.3";
|
||||
Version = "0.11.6";
|
||||
Tags = ["tabs", "ui", "llm", "ollama", "krea", "inpaint", "memory"];
|
||||
}
|
||||
|
||||
|
||||
@@ -168,10 +168,10 @@
|
||||
<div class="sa-settings-panes">
|
||||
<div class="sa-spane" data-spane="behavior">
|
||||
<p class="sa-settings-hint">Автодействия после ответа модели и скилы текущей личности.</p>
|
||||
<label class="sa-check" title="После Generate шлёт look_at с JPEG кадра (не каждый чат). Если включена авто-критика — она уже подставляет кадр."><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_generate" checked /> Авто-Generate после патча</label>
|
||||
<label class="sa-check"><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 sa-danger" title="Опасно — скачивает без Confirm"><input type="checkbox" id="sa_auto_download" /> Авто-скачивание Civitai (выкл)</label>
|
||||
<div class="sa-skills-label">Скилы (процедуры)</div>
|
||||
|
||||
Reference in New Issue
Block a user