Bump Assistent to 0.5: denser Krea packs, slash commands, and chips.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-21 21:15:46 +03:00
co-authored by Cursor
parent bb8cb990aa
commit b8145c6455
12 changed files with 741 additions and 88 deletions
+47
View File
@@ -691,6 +691,53 @@
line-height: 1.35; line-height: 1.35;
} }
.sa-chips {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
align-items: center;
}
.sa-chip {
appearance: none;
border: 1px solid color-mix(in srgb, currentColor 28%, transparent);
background: color-mix(in srgb, currentColor 6%, transparent);
color: inherit;
border-radius: 999px;
padding: 0.18rem 0.55rem;
font-size: 0.78rem;
font-weight: 600;
letter-spacing: 0.02em;
cursor: pointer;
line-height: 1.3;
}
.sa-chip:hover {
background: color-mix(in srgb, currentColor 12%, transparent);
}
.sa-chip.sa-chip-active {
border-color: color-mix(in srgb, #6cf 55%, currentColor);
background: color-mix(in srgb, #6cf 22%, transparent);
}
.sa-chip-sep {
width: 1px;
height: 1.1rem;
background: color-mix(in srgb, currentColor 28%, transparent);
margin: 0 0.15rem;
}
.sa-pack-flash {
animation: sa-flash 0.85s ease;
}
.sa-system-note {
white-space: pre-wrap;
opacity: 0.92;
font-size: 0.92rem;
}
.sa-composer-actions { .sa-composer-actions {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
+456 -10
View File
@@ -1,6 +1,6 @@
/** /**
* Swarm Assistent — Krea 2 collaborative chat (Ollama via Swarm API). * Swarm Assistent — Krea 2 collaborative chat (Ollama via Swarm API).
* v0.4.0: multi-window board, look_at slots, latest-patch buttons, first-run welcome. * v0.5.0: Krea knowledge packs, aspect/vary/lock_seed patches, slash commands, chips.
*/ */
(function () { (function () {
const LS_BASE = 'swarm_assistent_base_url'; const LS_BASE = 'swarm_assistent_base_url';
@@ -17,16 +17,57 @@
const GEN_ID = 'generate'; const GEN_ID = 'generate';
const MAX_REF_SLOTS = 4; const MAX_REF_SLOTS = 4;
const ASPECT_TABLE = {
'1:1': [1024, 1024],
'4:3': [1184, 896],
'3:2': [1248, 832],
'16:9': [1376, 768],
'2.35:1': [1568, 672],
'4:5': [928, 1152],
'2:3': [832, 1248],
'9:16': [768, 1376],
};
const PACK_ALIASES = {
write: 'write_prompt',
write_prompt: 'write_prompt',
critique: 'critique_image',
critique_image: 'critique_image',
compose: 'compose_scene',
compose_scene: 'compose_scene',
params: 'fix_params',
fix_params: 'fix_params',
inpaint: 'inpaint_edit',
inpaint_edit: 'inpaint_edit',
describe: 'describe_ref',
describe_ref: 'describe_ref',
};
const WELCOME_HTML = ` const WELCOME_HTML = `
<div class="sa-welcome-title">Assistent · Krea 2</div> <div class="sa-welcome-title">Assistent · Krea 2</div>
<ul> <ul>
<li><strong>Generate</strong> слева — живой просмотр текущей генерации. В чат сам не уходит.</li> <li><strong>Generate</strong> слева — живой просмотр текущей генерации. В чат сам не уходит.</li>
<li><strong>Ref</strong> — референсы: drop / paste / Snapshot gen / Send to Assistent.</li> <li><strong>Ref</strong> — референсы: drop / paste / Snapshot gen / Send to Assistent.</li>
<li>Глаз на окне — отправить это изображение мне в vision.</li> <li>Глаз на окне — отправить это изображение мне в vision.</li>
<li>Чипсы aspect / seed / Vary — быстрые патчи. В чате: <code>/help</code>.</li>
<li>Кнопки патча только у последнего предложения. Пока идёт генерация, Apply + Generate крутит спиннер.</li> <li>Кнопки патча только у последнего предложения. Пока идёт генерация, Apply + Generate крутит спиннер.</li>
</ul> </ul>
Напиши, что сгенерировать — или кинь референс и попроси правку.`; Напиши, что сгенерировать — или кинь референс и попроси правку.`;
const HELP_TEXT = `Slash-команды (без LLM):
/help — этот список
/gen — Generate сейчас
/look generate|refN — прикрепить окно к vision
/init /mask /clear — Init / Mask / Clear Init
/interrupt — остановить генерацию
/aspect 16:9 — размер из таблицы 1K
/seed lock|random — зафиксировать или рандомизировать seed
/vary — новый seed, тот же промпт
/pack write|critique|compose|params|inpaint|describe
/civitai <query> — поиск LoRA (Confirm в чате)
Чипсы над полем ввода делают то же для aspect / seed / vary.`;
const state = { const state = {
history: [], history: [],
packsLoaded: false, packsLoaded: false,
@@ -46,6 +87,7 @@
slots: [], slots: [],
selectedSlotId: 'ref1', selectedSlotId: 'ref1',
refSeq: 1, refSeq: 1,
packUserTouched: false,
}; };
function $(id) { function $(id) {
@@ -1151,7 +1193,19 @@
obj.slot_to_init != null || obj.slot_to_init != null ||
obj.slot_to_mask != null || obj.slot_to_mask != null ||
obj.snapshot_generate != null || obj.snapshot_generate != null ||
obj.select_slot != null obj.select_slot != null ||
obj.aspect != null ||
obj.images != null ||
obj.batch != null ||
obj.vary != null ||
obj.lock_seed != null ||
obj.creativity != null ||
obj.intensity != null ||
obj.complexity != null ||
obj.movement != null ||
obj.clear_prompt_images != null ||
obj.slot_to_prompt_image != null ||
obj.pack != null
); );
} }
@@ -1176,6 +1230,120 @@
return { prose, patch: lastPatch }; return { prose, patch: lastPatch };
} }
function normalizeAspect(raw) {
if (raw == null) {
return null;
}
let s = String(raw).trim().toLowerCase().replace(/\s+/g, '');
if (!s) {
return null;
}
if (s === 'square') {
s = '1:1';
} else if (s === 'portrait' || s === 'vert') {
s = '2:3';
} else if (s === 'landscape' || s === 'horiz') {
s = '16:9';
} else if (s === 'cinematic' || s === 'ultrawide') {
s = '2.35:1';
}
return ASPECT_TABLE[s] ? s : null;
}
function sizeFromAspect(aspect) {
const key = normalizeAspect(aspect);
return key ? ASPECT_TABLE[key] : null;
}
function guessAspectFromSize(w, h) {
const width = parseInt(w, 10);
const height = parseInt(h, 10);
if (!width || !height) {
return null;
}
let best = null;
let bestDist = Infinity;
for (const [key, [aw, ah]] of Object.entries(ASPECT_TABLE)) {
const dist = Math.abs(width / height - aw / ah) + Math.abs(width - aw) / 4000 + Math.abs(height - ah) / 4000;
if (dist < bestDist) {
bestDist = dist;
best = key;
}
}
return bestDist < 0.12 ? best : null;
}
function clearPromptImagesInBox() {
const box = document.getElementById('alt_prompt_textbox') || document.getElementById('input_prompt');
if (!box) {
return false;
}
const before = box.value || '';
const next = before
.replace(/<image\b[^>]*>[\s\S]*?<\/image>/gi, '')
.replace(/<image\b[^>]*\/?>/gi, '')
.replace(/data:image\/[a-zA-Z0-9.+-]+;base64,[A-Za-z0-9+/=]+/g, '')
.replace(/\n{3,}/g, '\n\n')
.trim();
if (next === before.trim()) {
return false;
}
box.value = next;
box.dispatchEvent(new Event('input', { bubbles: true }));
box.dispatchEvent(new Event('change', { bubbles: true }));
return true;
}
function setPackValue(packName, { flash, user } = {}) {
const pack = $('sa_pack');
if (!pack || !packName) {
return false;
}
const resolved = PACK_ALIASES[String(packName).trim()] || String(packName).trim();
if (![...pack.options].some((o) => o.value === resolved)) {
return false;
}
if (pack.value !== resolved) {
pack.value = resolved;
saveSettings();
}
if (user) {
state.packUserTouched = true;
}
if (flash) {
pack.classList.add('sa-pack-flash');
setTimeout(() => pack.classList.remove('sa-pack-flash'), 900);
}
return true;
}
function autoSelectPack(text) {
if (state.packUserTouched) {
return null;
}
const t = String(text || '').toLowerCase();
if (!t.trim()) {
return null;
}
if (/\b(опиши\s+реф|опиши\s+изображ|prompt\s+from\s+image|describe\s+(this|the|ref|image)|reverse\s*prompt)\b/i.test(t)
|| /опиши\s+(этот|эту|картинк|референс)/i.test(t)) {
return 'describe_ref';
}
if (/\b(critique|что\s+не\s+так|посмотри|смотри|разбери|критик)\b/i.test(t)) {
return 'critique_image';
}
if (/\b(inpaint|замажь|закрась|руки|лицо|маск|mask|img2img|init\s*image)\b/i.test(t)) {
return 'inpaint_edit';
}
if (/\b(steps?|cfg|seed|sigma|размер|aspect|ширин|высот|resolution|batch)\b/i.test(t)) {
return 'fix_params';
}
if (/\b(сцен|moodboard|атмосфер|compose|scene|мизансцен)\b/i.test(t)) {
return 'compose_scene';
}
return 'write_prompt';
}
function patchHasGenTrigger(patch) { function patchHasGenTrigger(patch) {
if (!patch) { if (!patch) {
return false; return false;
@@ -1188,16 +1356,21 @@
patch.loras || patch.loras ||
patch.width != null || patch.width != null ||
patch.height != null || patch.height != null ||
patch.aspect != null ||
patch.steps != null || patch.steps != null ||
patch.cfg != null || patch.cfg != null ||
patch.seed != null || patch.seed != null ||
patch.sigma_shift != null || patch.sigma_shift != null ||
patch.images != null ||
patch.batch != null ||
patch.vary === true ||
patch.use_init_image || patch.use_init_image ||
patch.clear_init_image || patch.clear_init_image ||
patch.init_creativity != null || patch.init_creativity != null ||
patch.denoise != null || patch.denoise != null ||
patch.use_mask_image || patch.use_mask_image ||
patch.clear_mask_image patch.clear_mask_image ||
patch.clear_prompt_images
); );
} }
@@ -1210,6 +1383,14 @@
const doParams = !which || which === 'all' || which === 'size' || which === 'params'; const doParams = !which || which === 'all' || which === 'size' || which === 'params';
const doInit = !which || which === 'all' || which === 'params' || which === 'init'; const doInit = !which || which === 'all' || which === 'params' || which === 'init';
if (patch.pack) {
setPackValue(patch.pack, { flash: true });
}
if (doPrompt && patch.clear_prompt_images) {
clearPromptImagesInBox();
}
if (doPrompt && patch.prompt != null) { if (doPrompt && patch.prompt != null) {
const box = document.getElementById('alt_prompt_textbox') || document.getElementById('input_prompt'); const box = document.getElementById('alt_prompt_textbox') || document.getElementById('input_prompt');
if (box) { if (box) {
@@ -1263,12 +1444,18 @@
} }
if (doParams) { if (doParams) {
const aspectSize = sizeFromAspect(patch.aspect);
if (aspectSize) {
setVal('input_width', String(aspectSize[0]));
setVal('input_height', String(aspectSize[1]));
} else {
if (patch.width != null) { if (patch.width != null) {
setVal('input_width', String(patch.width)); setVal('input_width', String(patch.width));
} }
if (patch.height != null) { if (patch.height != null) {
setVal('input_height', String(patch.height)); setVal('input_height', String(patch.height));
} }
}
if (patch.steps != null) { if (patch.steps != null) {
setVal('input_steps', String(patch.steps)); setVal('input_steps', String(patch.steps));
} }
@@ -1279,7 +1466,14 @@
setVal('input_cfg', String(patch.cfg)); setVal('input_cfg', String(patch.cfg));
} }
} }
if (patch.seed != null) { if (patch.vary === true) {
setVal('input_seed', '-1');
} else if (patch.lock_seed === true) {
const cur = val('input_seed');
if (cur && String(cur) !== '-1') {
setVal('input_seed', cur);
}
} else if (patch.seed != null) {
setVal('input_seed', String(patch.seed)); setVal('input_seed', String(patch.seed));
} }
if (patch.sigma_shift != null) { if (patch.sigma_shift != null) {
@@ -1293,6 +1487,14 @@
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));
} }
const batch = patch.images != null ? patch.images : patch.batch;
if (batch != null) {
if (document.getElementById('input_images')) {
setVal('input_images', String(batch));
} else if (document.getElementById('input_batchsize')) {
setVal('input_batchsize', String(batch));
}
}
} }
if (doInit) { if (doInit) {
@@ -1361,7 +1563,11 @@
setStatus('No image for Mask — drop a mask (white=edit) first'); setStatus('No image for Mask — drop a mask (white=edit) first');
} }
} }
if (patch.slot_to_prompt_image) {
setStatus('Prompt Images: drop the ref into the Swarm prompt box (no auto helper yet)');
} }
}
syncChipHighlight();
setStatus('Applied patch'); setStatus('Applied patch');
} }
@@ -2019,6 +2225,199 @@
} }
} }
async function applyQuickPatch(patch, note) {
const withActions = { ...patch };
if (!Array.isArray(withActions.actions) && patchHasGenTrigger(withActions)) {
withActions.actions = ['generate'];
}
await applyPatch(withActions, 'all');
setStatus(note || 'Applied');
if ($('sa_auto_generate')?.checked) {
await runGenerateFromPatch(withActions);
}
syncChipHighlight();
}
function syncChipHighlight() {
const bar = $('sa_chips');
if (!bar) {
return;
}
const cur = guessAspectFromSize(val('input_width'), val('input_height'));
const seed = val('input_seed');
bar.querySelectorAll('[data-aspect]').forEach((btn) => {
btn.classList.toggle('sa-chip-active', btn.getAttribute('data-aspect') === cur);
});
bar.querySelectorAll('[data-seed]').forEach((btn) => {
const mode = btn.getAttribute('data-seed');
const active = (mode === 'lock' && seed && seed !== '-1') || (mode === 'random' && (!seed || seed === '-1'));
btn.classList.toggle('sa-chip-active', active);
});
}
function appendSystemNote(text) {
const box = $('sa_messages');
if (!box) {
return;
}
hideChatEmpty();
const div = document.createElement('div');
div.className = 'sa-msg assistant sa-system-note';
div.textContent = text;
box.appendChild(div);
box.scrollTop = box.scrollHeight;
}
async function handleSlashCommand(raw) {
const text = String(raw || '').trim();
if (!text.startsWith('/')) {
return false;
}
const parts = text.slice(1).split(/\s+/);
const cmd = (parts[0] || '').toLowerCase();
const arg = parts.slice(1).join(' ').trim();
if (cmd === 'help' || cmd === '?') {
appendSystemNote(HELP_TEXT);
setStatus('/help');
return true;
}
if (cmd === 'gen' || cmd === 'generate') {
const prev = findCurrentGenerateSrc();
startBusyUi('generating');
state.generating = true;
setInterruptVisible(true);
if (!triggerGenerate()) {
state.generating = false;
stopBusyUi('Could not start Generate');
return true;
}
const src = await waitForNewImage(prev);
state.generating = false;
setInterruptVisible(state.busy);
if (src) {
const gen = generateSlot();
if (gen) {
gen.src = src;
renderBoard();
}
stopBusyUi('Generate done');
} else {
stopBusyUi('Generate finished');
}
return true;
}
if (cmd === 'look') {
const id = normalizeSlotId(arg || GEN_ID) || GEN_ID;
const slot = slotById(id);
if (!slot) {
setStatus(`Unknown slot: ${arg || GEN_ID}`);
return true;
}
if (!slot.src && id === GEN_ID) {
const src = findCurrentGenerateSrc();
if (src) {
slot.src = src;
}
}
if (!slot.src) {
setStatus(`Slot ${id} is empty`);
return true;
}
slot.attach = true;
renderBoard();
if ($('sa_input')) {
$('sa_input').value = `Look at ${id} and describe what you see.`;
}
setPackValue('critique_image', { flash: true });
await sendChat({ forceSlotIds: [id], skipAutoPack: true });
return true;
}
if (cmd === 'init') {
const src = selectedSrc() || findCurrentGenerateSrc();
if (!src) {
setStatus('No image for Init');
return true;
}
await setInitFromSrc(src);
setPackValue('inpaint_edit', { flash: true });
return true;
}
if (cmd === 'mask') {
const src = selectedSrc();
if (!src) {
setStatus('Select a window with a mask image');
return true;
}
await setMaskFromSrc(src);
setPackValue('inpaint_edit', { flash: true });
return true;
}
if (cmd === 'clear') {
clearInitAndMask();
return true;
}
if (cmd === 'interrupt' || cmd === 'stop') {
doInterruptNow();
state.busy = false;
state.generating = false;
setInterruptVisible(false);
syncGenerateBusy();
syncPatchActionAvailability();
stopBusyUi('Interrupted');
return true;
}
if (cmd === 'aspect') {
const key = normalizeAspect(arg);
if (!key) {
setStatus(`Unknown aspect. Try: ${Object.keys(ASPECT_TABLE).join(', ')}`);
return true;
}
await applyQuickPatch({ aspect: key, actions: ['generate'] }, `Aspect ${key}`);
return true;
}
if (cmd === 'seed') {
const mode = (arg || 'random').toLowerCase();
if (mode === 'lock' || mode === 'keep') {
await applyQuickPatch({ lock_seed: true }, 'Seed locked');
} else {
await applyQuickPatch({ seed: -1, vary: true, actions: ['generate'] }, 'Seed random');
}
return true;
}
if (cmd === 'vary') {
await applyQuickPatch({ vary: true, seed: -1, actions: ['generate'] }, 'Vary (new seed)');
return true;
}
if (cmd === 'pack') {
if (!setPackValue(arg, { flash: true, user: true })) {
setStatus('Pack: write|critique|compose|params|inpaint|describe');
} else {
setStatus(`Pack → ${$('sa_pack')?.value}`);
}
return true;
}
if (cmd === 'civitai') {
if (!arg) {
setStatus('/civitai <query>');
return true;
}
if ($('sa_input')) {
$('sa_input').value = `Find a Krea 2 LoRA for: ${arg}`;
}
setPackValue('write_prompt', { flash: true });
await sendChat({
skipAutoPack: true,
forcedUserText: `Search Civitai for Krea-compatible LoRA: ${arg}. Prefer actions search_civitai.`,
});
return true;
}
appendSystemNote(`Unknown command /${cmd}.\n\n${HELP_TEXT}`);
setStatus(`Unknown /${cmd}`);
return true;
}
async function maybeVisionHop(patch, attachedSlotIds) { async function maybeVisionHop(patch, attachedSlotIds) {
const ids = lookAtIdsFromPatch(patch); const ids = lookAtIdsFromPatch(patch);
if (!ids.length || state.visionHopUsed) { if (!ids.length || state.visionHopUsed) {
@@ -2058,17 +2457,39 @@
} }
async function sendChat(opts = {}) { async function sendChat(opts = {}) {
if (state.busy) { if (state.busy && !opts.fromVisionHop && !opts.fromAutoCritique) {
return; return;
} }
const rawInput = ($('sa_input')?.value || '').trim();
const text = (opts.forcedUserText || rawInput).trim();
if (!text) {
return;
}
if (!opts.fromAutoCritique && !opts.fromDownload && !opts.fromVisionHop && !opts.skipSlash) {
if (rawInput.startsWith('/')) {
if ($('sa_input')) {
$('sa_input').value = '';
}
const handled = await handleSlashCommand(rawInput);
if (handled) {
return;
}
}
}
if (!updateGate()) { if (!updateGate()) {
setStatus('Select a Krea 2 model'); setStatus('Select a Krea 2 model');
return; return;
} }
const text = ($('sa_input')?.value || '').trim();
if (!text) { if (!opts.skipAutoPack && !opts.fromAutoCritique && !opts.fromDownload && !opts.fromVisionHop) {
return; const guessed = autoSelectPack(text);
if (guessed) {
setPackValue(guessed, { flash: true });
} }
}
const pack = $('sa_pack')?.value || 'write_prompt'; const pack = $('sa_pack')?.value || 'write_prompt';
const model = $('sa_model')?.value; const model = $('sa_model')?.value;
if (!model) { if (!model) {
@@ -2467,13 +2888,14 @@
state.history = []; state.history = [];
state.critiqueHopUsed = false; state.critiqueHopUsed = false;
state.visionHopUsed = false; state.visionHopUsed = false;
state.packUserTouched = false;
const box = $('sa_messages'); const box = $('sa_messages');
if (box) { if (box) {
box.innerHTML = ''; box.innerHTML = '';
const empty = document.createElement('div'); const empty = document.createElement('div');
empty.className = 'sa-chat-empty'; empty.className = 'sa-chat-empty';
empty.id = 'sa_chat_empty'; empty.id = 'sa_chat_empty';
empty.innerHTML = '<div class="sa-chat-empty-title">Collaborative Krea 2</div><div class="sa-chat-empty-hint">Write a prompt, drop refs on the board, or send Generate via Send to Assistent.</div>'; empty.innerHTML = '<div class="sa-chat-empty-title">Collaborative Krea 2</div><div class="sa-chat-empty-hint">Write a prompt, drop refs, use aspect chips, or type <code>/help</code>.</div>';
box.appendChild(empty); box.appendChild(empty);
} }
stopBusyUi(''); stopBusyUi('');
@@ -2487,7 +2909,28 @@
}); });
$('sa_base_url')?.addEventListener('change', saveSettings); $('sa_base_url')?.addEventListener('change', saveSettings);
$('sa_model')?.addEventListener('change', saveSettings); $('sa_model')?.addEventListener('change', saveSettings);
$('sa_pack')?.addEventListener('change', saveSettings); $('sa_pack')?.addEventListener('change', () => {
state.packUserTouched = true;
saveSettings();
});
$('sa_chips')?.addEventListener('click', async (e) => {
const btn = e.target.closest('.sa-chip');
if (!btn || state.busy || state.generating) {
return;
}
const aspect = btn.getAttribute('data-aspect');
const seed = btn.getAttribute('data-seed');
const vary = btn.getAttribute('data-vary');
if (aspect) {
await applyQuickPatch({ aspect, actions: ['generate'] }, `Aspect ${aspect}`);
} else if (seed === 'lock') {
await applyQuickPatch({ lock_seed: true }, 'Seed locked');
} else if (seed === 'random') {
await applyQuickPatch({ seed: -1, actions: ['generate'] }, 'Seed random');
} else if (vary) {
await applyQuickPatch({ vary: true, seed: -1, actions: ['generate'] }, 'Vary');
}
});
$('sa_auto_vision')?.addEventListener('change', () => { $('sa_auto_vision')?.addEventListener('change', () => {
saveSettings(); saveSettings();
const gen = generateSlot(); const gen = generateSlot();
@@ -2501,6 +2944,9 @@
$('sa_auto_critique')?.addEventListener('change', saveSettings); $('sa_auto_critique')?.addEventListener('change', saveSettings);
$('sa_auto_download')?.addEventListener('change', saveSettings); $('sa_auto_download')?.addEventListener('change', saveSettings);
syncChipHighlight();
setInterval(syncChipHighlight, 2500);
setInterval(updateGate, 2000); setInterval(updateGate, 2000);
setInterval(syncGenerateSlot, 700); setInterval(syncGenerateSlot, 700);
setInterval(() => { setInterval(() => {
+71 -20
View File
@@ -6,11 +6,46 @@ You are **Swarm Assistent**, a collaborative art director for **Krea 2** image g
- Architecture: Krea 2 (12B DiT). Not FLUX, not SDXL, not FLUX.1-Krea. - Architecture: Krea 2 (12B DiT). Not FLUX, not SDXL, not FLUX.1-Krea.
- Text encoder: Qwen3-VL 4B. VAE: Qwen Image VAE. - Text encoder: Qwen3-VL 4B. VAE: Qwen Image VAE.
- **Turbo** defaults: steps **8**, CFG **1**, sigma shift **1.15**, side length ~**1024**. - **Turbo** defaults: steps **8** (min 4), CFG **1** (never CFG 0 — broken output), sigma shift **1.15**, side ~**1024** (1284096 OK).
- **Prompt Images** (refs in the prompt box) often **overpower** the text prompt — suggest them sparingly and warn the user. - **RAW / Base:** steps ~2052, CFG ~44.5. Community tip: RAW + turbo LoRA ~0.6 often beats pure Turbo for photoreal — mention only if checkpoint looks RAW; do not invent workflows Swarm cannot run.
- Built-in NSFW text-refiner may strip risque words; LoRAs may change that — do not lecture; stay practical.
- LoRAs: **only Krea2-trained**. Never suggest FLUX/SDXL LoRAs. - LoRAs: **only Krea2-trained**. Never suggest FLUX/SDXL LoRAs.
## How to prompt (local Swarm, not krea.ai cloud)
- Write **natural prose** for a photographer/director — not Danbooru tags, not `(word:1.5)`, not `masterpiece / best quality / 8k`.
- Order (front-load importance): **subject → pose/action → setting → materials → camera/framing → lighting → medium/mood**.
- Short user ideas: expand. Finished Flux/Krea-style paragraphs: keep wording; only fix anti-patterns.
- **Negative prompts are nearly useless** (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, do not lecture.
- **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, Creativity UI) are **not** in Swarm. Emulate with prompt language + board refs.
### Aspect → pixels (official 1K table)
| aspect | size |
| --- | --- |
| `1:1` | 1024×1024 |
| `4:3` | 1184×896 |
| `3:2` | 1248×832 |
| `16:9` | 1376×768 |
| `2.35:1` | 1568×672 |
| `4:5` | 928×1152 |
| `2:3` | 832×1248 |
| `9:16` | 768×1376 |
Prefer `aspect` in the patch; UI maps it to width/height.
### Known pitfalls
- **Dead eyes / weak emotion:** prefer an expressiveness/bypass LoRA from `available_loras` if present; describe eyes/expression vividly in prose.
- **3D / concept-art bias:** for photos say `photograph`, `real skin texture`, `film grain`, camera/lens — not only “photorealistic”.
- **Qwen VAE halftone** on sand/hair/fine weave: prefer **inpaint** that region at low denoise — do not rewrite the whole scene prompt.
### Creativity & “sliders” (LLM-only)
- `creativity`: `raw` | `low` | `medium` | `high` — how much **you** expand the users wording into the prompt. Not a SwarmUI field.
- Optional `intensity` / `complexity` / `movement` (100..100): weave into prompt lexicon (muted↔stylized, minimal↔dense, static↔kinetic camera). Do not invent UI sliders.
## Live context ## Live context
A JSON block named "Live SwarmUI context" is attached. Treat it as ground truth: A JSON block named "Live SwarmUI context" is attached. Treat it as ground truth:
@@ -18,11 +53,11 @@ A JSON block named "Live SwarmUI context" is attached. Treat it as ground truth:
- Use only LoRAs listed in `available_loras` (by exact `name`), or candidates from a Civitai search round. - Use only LoRAs listed in `available_loras` (by exact `name`), or candidates from a Civitai search round.
- Prefer listed `trigger_phrase` / `triggers`**never invent** trigger words. - Prefer listed `trigger_phrase` / `triggers`**never invent** trigger words.
- When enabling a LoRA, include its triggers in `prompt` if missing. - When enabling a LoRA, include its triggers in `prompt` if missing.
- Respect current width/height/steps/cfg/seed/sigma_shift/sampler unless the user asks to change them or the pack is `fix_params`. - Respect current width/height/steps/cfg/seed/sigma_shift/sampler unless the user asks or the pack is `fix_params`.
- `wildcards` lists installed wildcard names (`__name__` syntax in prompts). - `wildcards` lists installed wildcard names (`__name__` syntax in prompts).
- `prompt_image_count` > 0 means Prompt Images are attached — warn if they may dominate. - `prompt_image_count` > 0 means Prompt Images are attached — warn if they may dominate.
- **Init / inpaint:** `has_init_image`, `has_mask_image`, `init_creativity` (aka denoise, 01), `mask_blur`, `mask_grow`. - **Init / inpaint:** `has_init_image`, `has_mask_image`, `init_creativity` (aka denoise, 01), `mask_blur`, `mask_grow`.
- **Board (image windows):** `image_slots` lists windows. `generate` is the live current generation (view only unless attached). `ref1`… are user references. `attached_slot_ids` / `has_vision_image` = which windows are actually sent as vision this turn. To look at a window that was not attached, emit `look_at`. - **Board:** `image_slots`. `generate` = live gen. `ref1`… = refs. `attached_slot_ids` / `has_vision_image` = vision this turn. Emit `look_at` to see an unattached window.
## Output contract (mandatory) ## Output contract (mandatory)
@@ -34,13 +69,21 @@ A JSON block named "Live SwarmUI context" is attached. Treat it as ground truth:
"prompt": "...", "prompt": "...",
"negative": null, "negative": null,
"loras": [{"name": "exact_name_from_list", "weight": 0.8, "triggers": ["..."]}], "loras": [{"name": "exact_name_from_list", "weight": 0.8, "triggers": ["..."]}],
"width": 1024, "aspect": "16:9",
"height": 1280, "width": 1376,
"height": 768,
"steps": 8, "steps": 8,
"cfg": 1, "cfg": 1,
"seed": -1, "seed": -1,
"images": 1,
"sigma_shift": 1.15, "sigma_shift": 1.15,
"sampler": null, "sampler": null,
"creativity": "medium",
"intensity": 0,
"complexity": 0,
"movement": 0,
"vary": false,
"lock_seed": false,
"use_init_image": false, "use_init_image": false,
"clear_init_image": false, "clear_init_image": false,
"init_creativity": 0.45, "init_creativity": 0.45,
@@ -48,11 +91,14 @@ A JSON block named "Live SwarmUI context" is attached. Treat it as ground truth:
"clear_mask_image": false, "clear_mask_image": false,
"mask_blur": null, "mask_blur": null,
"mask_grow": null, "mask_grow": null,
"clear_prompt_images": false,
"slot_to_prompt_image": null,
"look_at": ["generate"], "look_at": ["generate"],
"slot_to_init": null, "slot_to_init": null,
"slot_to_mask": null, "slot_to_mask": null,
"snapshot_generate": false, "snapshot_generate": false,
"select_slot": null, "select_slot": null,
"pack": null,
"actions": ["generate"], "actions": ["generate"],
"search_query": null, "search_query": null,
"notes": "one-line why" "notes": "one-line why"
@@ -63,28 +109,33 @@ A JSON block named "Live SwarmUI context" is attached. Treat it as ground truth:
- Omit keys you are not changing. - Omit keys you are not changing.
- `loras` replaces the intended LoRA set for Apply (list all that should be on). - `loras` replaces the intended LoRA set for Apply (list all that should be on).
- width/height between 128 and 4096; prefer multiples near 1024 for Turbo. - Prefer `aspect` over raw width/height when framing changes; else width/height 1284096 near the table.
- `vary: true` — new random seed, keep prompt. `lock_seed: true` — reuse current seed (not 1).
- `images` / `batch` — batch size.
- `creativity` / slider ints — guide your prompt writing only (UI ignores them except weaving into `prompt`).
- `clear_prompt_images: true` — strip image embeds from the prompt box.
- `pack` — switch active prompt pack for a follow-up hop (`write_prompt`, `critique_image`, `compose_scene`, `fix_params`, `inpaint_edit`, `describe_ref`).
- Do not invent model or LoRA filenames. - Do not invent model or LoRA filenames.
- If you cannot help (wrong architecture / no Krea 2), say so and omit the JSON patch. - If you cannot help (wrong architecture / no Krea 2), say so and omit the JSON patch.
### Init image / inpaint ### Init image / inpaint
- **img2img:** set `use_init_image: true` and optionally `slot_to_init: "generate"` or `"ref1"` (uses that board window) plus `init_creativity` (0 = almost copy, 1 = almost new). Typical edits: **0.250.45**; restyle: **0.50.7**. Alias `denoise` is accepted. - **img2img:** `use_init_image: true` + optional `slot_to_init` + `init_creativity` (0≈copy, 1≈new). Edits **0.250.45**; restyle **0.50.7**. Alias `denoise` OK.
- **Inpaint:** needs Init + Mask. Set `use_init_image: true` and `use_mask_image: true` only when a board window holds a proper mask (white = edit, black = keep). Point to it with `slot_to_mask`. If the user has not painted a mask, tell them to use Swarm **Edit Image** / paint a mask, or press **As Mask**do not invent pixel masks. - **Inpaint:** Init + Mask. White = edit, black = keep. `slot_to_mask` when a board window is a mask. If no mask yet, tell user to paint one / **As Mask**never invent pixels.
- `clear_init_image` / `clear_mask_image` to leave img2img mode. - `clear_init_image` / `clear_mask_image` to leave img2img.
- Prompt Images ≠ Init Image. Prefer Init for structural edits; Prompt Images for style refs (and warn they can dominate). - Prompt Images ≠ Init. Prefer Init for structure; Prompt Images for style (warn they dominate).
### Actions (auto-safe) ### Actions (auto-safe)
- `"generate"` — after Apply, start generation (UI auto-generate is on by default). - `"generate"` — after Apply, start generation (UI auto-generate on by default).
- `"use_init"` / `"use_mask"` — same as the boolean flags (optional). - `"use_init"` / `"use_mask"` — same as boolean flags.
- `"search_civitai"` — Civitai search; user must **Confirm** downloads. - `"search_civitai"` — Civitai search; user **Confirm**s downloads.
- `"interrupt"` — stop generation. - `"interrupt"` — stop generation.
- `look_at: ["generate", "ref1"]`ask the UI to send those board windows as vision (a follow-up hop). Use when you need to see a slot that was not attached. - `look_at: ["generate", "ref1"]`vision hop for those board windows.
- `slot_to_init` / `slot_to_mask`which board id to copy into Swarm Init / Mask. - `slot_to_init` / `slot_to_mask`copy board id into Swarm Init / Mask.
- `snapshot_generate: true` — copy the live Generate window into a new/empty Ref. - `snapshot_generate: true` — copy live Generate into a Ref.
- Pure Q&A with no prompt/param change: omit the JSON patch entirely (do not burn GPU). - Pure Q&A with no change: omit the JSON patch (do not burn GPU).
### Auto-apply note ### Auto-apply note
The UI may auto-apply your patch and auto-generate when `actions` contains `generate` or when you change prompt/loras/size/init. Keep patches intentional. The UI may auto-apply and auto-generate when `actions` contains `generate` or when you change prompt/loras/size/init. Keep patches intentional.
+8 -6
View File
@@ -1,16 +1,18 @@
# Mode: compose_scene # Mode: compose_scene
Goal: co-create a scene / moodboard direction for **Krea 2**. Goal: co-create a scene / moodboard direction for **Krea 2** (local Swarm).
## Approach ## Approach
- Clarify subject, setting, time of day, camera distance, style. - Clarify subject, setting, time of day, camera distance, style.
- Propose one strong prompt (not five weak ones). - Propose **one** strong prompt (not five weak ones).
- Optionally suggest which available LoRAs fit — only from the live list, with triggers. - **Moodboard via board:** if refs exist, `look_at` several refs, extract palette/texture/mood into **text**, then write the prompt. Prefer text distillation over dumping refs as Prompt Images.
- Mention Prompt Images only if a reference would help, and warn that refs can overpower text. - If using Prompt Images / `slot_to_prompt_image`, warn they often **overpower** the text prompt.
- Suggest available LoRAs only from the live list, with triggers.
- Missing style LoRA → `search_civitai` + `search_query` (Krea-compatible). - Missing style LoRA → `search_civitai` + `search_query` (Krea-compatible).
- Optional intensity/complexity/movement → bake into prose (stylized, dense, kinetic…).
## Deliverable ## Deliverable
- Scene brief + JSON patch (`prompt`, optional `loras`, optional aspect). - Scene brief + JSON patch (`prompt`, optional `loras`, optional `aspect`).
- Add `actions: ["generate"]` when ready to try the scene. - `actions: ["generate"]` when ready to try the scene.
+16 -9
View File
@@ -4,18 +4,25 @@ Goal: look at the attached / requested board image(s) and improve the next gener
If vision is missing but `image_slots` shows `generate` or a ref with `has_image: true`, emit `look_at: ["generate"]` (or the ref id) and **omit** `actions: ["generate"]` this turn so the UI can hop vision first. If vision is missing but `image_slots` shows `generate` or a ref with `has_image: true`, emit `look_at: ["generate"]` (or the ref id) and **omit** `actions: ["generate"]` this turn so the UI can hop vision first.
## How to critique ## Critique checklist
- Describe what you see: subject, composition, lighting, defects (anatomy, blur, wrong style). - Subject, composition, lighting — what works / what fails.
- Tie feedback to **actionable** prompt / LoRA / size changes. - **Anatomy** (hands, limbs, face).
- If a LoRA trigger was missing or too strong, adjust weight or prompt placement. - **Dead eyes / flat expression** — stronger facial prose; enable bypass/expressiveness LoRA from inventory if available.
- If the frame needs a different aspect (too tight / too wide), change width/height. - **3D / concept-art bias** when the user wanted a photo — add photograph / real skin / film language.
- Prompt Images overpower text on Krea 2 — if the user relied on a ref, suggest weaker reliance or clearer text. - **Qwen VAE grid** on sand, hair, fine fabric — prefer **inpaint** that region, not a full rewrite.
- For **local fixes** (hands, face, object): prefer **inpaint** (`use_init_image` + mask) over rewriting the whole prompt; if no mask yet, say so and suggest painting one / pack `inpaint_edit`. - **Prompt Images dominate** (`prompt_image_count` > 0) — weaken reliance or clear via `clear_prompt_images`.
- For **global restyle**: img2img with moderate `init_creativity` (≈0.40.6) can be better than from-scratch. - Aspect too tight/wide — set `aspect` or width/height.
- Missing / wrong LoRA triggers or weights.
## Fix strategy
- **Local defect** (hands, face, object): inpaint (`use_init_image` + mask). No mask → ask to paint / pack `inpaint_edit`.
- **Global restyle:** img2img with `init_creativity` ≈ 0.40.6.
- **From-scratch rewrite:** only when composition is wrong.
## Deliverable ## Deliverable
- Short critique in the user's language. - Short critique in the user's language.
- JSON patch with improved `prompt` and any `loras` / size / init tweaks. - JSON patch with improved `prompt` and any `loras` / size / init tweaks.
- Include `actions: ["generate"]` when proposing a revised generation (default for this mode). - `actions: ["generate"]` when proposing a revised generation (default for this mode).
+28
View File
@@ -0,0 +1,28 @@
# Mode: describe_ref
Goal: turn an attached / requested board image into a **Krea 2ready** natural-language prompt (reverse prompt).
If vision is missing, emit `look_at` for the relevant slot and omit `actions: ["generate"]` this turn.
## How to describe
Write vivid flowing prose a photographer would give an artist. Start with the subject — never “In this image…”.
Cover in order:
1. Subject, pose, gaze, expression
2. Fashion, hair, materials, how light hits surfaces
3. Props and environment textures
4. Shot type, camera angle, DoF, framing
5. Lighting direction/quality, palette, mood
6. Medium / aesthetic (photo, editorial, illustration…)
- Quote on-image text in `"double quotes"`.
- No tag soup, no quality spam, no negatives.
- Default: emit `prompt` in the JSON patch **without** `actions: ["generate"]` — the user asked for a prompt, not a re-roll (unless they explicitly want to generate).
## Deliverable
- Brief note in the user's language (optional one line).
- JSON patch with `prompt` (and `aspect` if the frame is clearly non-square).
- Include `actions: ["generate"]` only if the user asked to regenerate from the description.
+12 -11
View File
@@ -1,20 +1,21 @@
# Mode: fix_params # Mode: fix_params
Goal: adjust **generation parameters** for Krea 2 Turbo (or Raw if context says so). Goal: adjust **generation parameters** for Krea 2 Turbo (or RAW if context says so).
## Guidelines ## Guidelines
- Turbo: prefer steps 412 (default 8), CFG ~1, sigma shift ~1.15. - **Turbo:** steps 412 (default **8**), CFG **1** (never 0), sigma shift ~**1.15**.
- Raw/base: higher steps (20+) and higher CFG may apply — only if context indicates Raw. - **RAW/base:** steps 20+, CFG ~44.5 — only if checkpoint/context indicates Raw.
- Aspect: change width/height for framing (portrait/landscape/square); keep near 1024 unless asked for higher res. - **Aspect:** prefer patch field `aspect` (`1:1`, `4:5`, `2:3`, `16:9`, `9:16`, `4:3`, `3:2`, `2.35:1`) — UI maps to official 1K sizes. Else set width/height near 1024.
- Seed: set `seed` when the user wants reproducibility; `-1` for random. - **Batch:** `images` or `batch` (14 typical).
- Sampler: only change if the form exposes it and the user asks. - **Seed:** `lock_seed: true` to reuse current; `vary: true` or `seed: -1` for a new roll; set numeric `seed` for exact reproducibility.
- **Init creativity** (`init_creativity` / denoise 01): only when `has_init_image` or enabling img2img — see pack `inpaint_edit`. - **Sampler/scheduler:** leave alone unless the user asks (Swarm default is fine; community Turbo often Euler + Simple).
- Do not change the prompt unless needed to match the new framing. - **Init creativity** only when `has_init_image` or enabling img2img — see `inpaint_edit`.
- Keep LoRAs unless the user asks to drop them. - Do not change the prompt unless needed for the new framing.
- Keep LoRAs unless asked to drop them.
## Deliverable ## Deliverable
- Explain the param change. - Explain the param change.
- JSON patch focusing on `width`, `height`, `steps`, `cfg`, `seed`, `sigma_shift`, optional `init_creativity` (and `prompt` only if necessary). - JSON patch focusing on `aspect` / `width` / `height` / `steps` / `cfg` / `seed` / `sigma_shift` / `images` / `vary` / `lock_seed` (and `prompt` only if necessary).
- Include `actions: ["generate"]` if the user wants to re-roll with the new params. - `actions: ["generate"]` if the user wants to re-roll with the new params.
+8 -6
View File
@@ -8,27 +8,29 @@ Goal: guide **img2img** (Init Image) and **inpainting** (Init + Mask) on **Krea
| --- | --- | | --- | --- |
| Soft edit / restyle whole frame | Init only + `init_creativity` | | Soft edit / restyle whole frame | Init only + `init_creativity` |
| Change one region (face, hand, logo) | Init + Mask (white = edit) | | Change one region (face, hand, logo) | Init + Mask (white = edit) |
| Hair / sand / VAE halftone grain | Mask bad region + **low** denoise (~0.20.35) |
| Fresh image from text | Clear init/mask; normal t2i | | Fresh image from text | Clear init/mask; normal t2i |
## Creativity (denoise) ## Creativity (denoise)
- **0.20.35** — small fixes, keep composition - **0.20.35** — small fixes, keep composition (also VAE grid fixes)
- **0.40.55** — noticeable edit, still related - **0.40.55** — noticeable edit, still related
- **0.60.8** — strong restyle; structure may drift - **0.60.8** — strong restyle; structure may drift
- Always set `use_init_image: true` when enabling img2img from the Assistent vision / current image. - Always set `use_init_image: true` when enabling img2img (`slot_to_init` to point at a board window).
## Mask rules ## Mask rules
- White = regenerate, black = preserve. Gray = partial. - White = regenerate, black = preserve. Gray = partial.
- Only set `use_mask_image: true` if context shows a vision image that is meant as a mask, or the user said they prepared one. - Only set `use_mask_image: true` if a proper mask exists or the user prepared one.
- If `has_mask_image` is false and the user wants regional edit: ask them to paint a mask in Swarm Image Editor (or Assistent **As Mask**), then continue. - If `has_mask_image` is false and they want a regional edit: ask them to paint a mask (Swarm Edit Image / Assistent **As Mask**), then continue.
- Optional: `mask_blur` / `mask_grow` for softer edges. - Optional: `mask_blur` / `mask_grow` for softer edges.
## Prompting ## Prompting
- Describe **what should appear in the edited region**, not the whole scene dump. - Describe **what should appear in the edited region**, not a whole-scene dump.
- Keep LoRA triggers if the subject depends on them. - Keep LoRA triggers if the subject depends on them.
- Match width/height to the init image when possible. - Match width/height (or `aspect`) to the init image when possible.
- **Prompt Images ≠ Init** — do not use Prompt Images as a substitute for Init/Mask.
## Deliverable ## Deliverable
+24 -10
View File
@@ -1,19 +1,33 @@
# Mode: write_prompt # Mode: write_prompt
Goal: craft or improve a **Krea 2** prompt that will generate well on Turbo. Goal: craft or improve a **Krea 2** prompt that will generate well on Turbo (local Swarm).
## How to write the prompt ## How to write the prompt
- Natural language + concrete visual details (subject, lighting, lens/mood, composition). Structure as flowing prose (not tag soup):
- Put **LoRA trigger phrases** near the subject they affect; do not dump unrelated tags.
- Prefer clarity over keyword stuffing. Krea 2 understands sentences. 1. **Subject + pose/action** (gaze, expression, body language)
- If the user wants a style covered by an available LoRA, enable that LoRA and weave its triggers in. 2. **Clothes / hair / materials** (fabric, fit, how light hits surfaces)
- Keep Turbo defaults unless the user asks otherwise (steps 8, cfg 1). 3. **Props & environment**
- If a needed LoRA is missing from `available_loras`, use `actions: ["search_civitai"]` with a clear `search_query` (and prefer Krea base). 4. **Composition** (shot type, angle, DoF, framing)
5. **Lighting, palette, mood**
6. **Medium** (photograph, editorial, illustration, film still…)
- Put **LoRA trigger phrases** near the subject they affect.
- Short user ideas → expand. User already wrote a polished paragraph → keep it; only fix tags/weights/negatives-as-positives.
- Keep Turbo defaults unless asked (steps 8, cfg 1). Prefer `aspect` for framing.
- Missing style LoRA → `actions: ["search_civitai"]` + clear `search_query` (Krea-compatible).
- Optional `creativity` / intensity/complexity/movement: expand or restrain wording accordingly; bake slider intent into the prose.
### Bad → good
Bad: `cute fox, snow, masterpiece, best quality, 8k, detailed, no blur`
Good: `A fluffy red fox sitting alert in fresh powder snow, ears forward, breath faintly visible in the cold air, soft morning light from the left catching orange fur and casting long blue shadows, shot on an 85mm lens at f/2.8 with creamy bokeh, calm winter atmosphere, sharp eyes and whiskers.`
## Deliverable ## Deliverable
- Explain briefly what you changed. - Explain briefly what you changed.
- Emit a JSON patch with at least `prompt`, and `loras` when relevant. - JSON patch with at least `prompt`, and `loras` when relevant.
- Include `actions: ["generate"]` when the user wants to see a new image. - `actions: ["generate"]` when the user wants a new image.
- Include `width`/`height` only if aspect should change for the scene (e.g. portrait → taller). - `aspect` (or width/height) only if framing should change.
+42 -6
View File
@@ -1,12 +1,15 @@
# Swarm Assistent # Swarm Assistent
SwarmUI extension for **collaborative Krea 2** prompting via **Ollama**: chat + multi-window board (live Generate + refs), LoRA/trigger awareness, applyable patches, **img2img / inpaint**, auto Generate, and Civitai search with Confirm. SwarmUI extension for **collaborative Krea 2** prompting via **Ollama**: chat + multi-window board (live Generate + refs), LoRA/trigger awareness, applyable patches, **img2img / inpaint**, slash commands, aspect/seed chips, auto Generate, and Civitai search with Confirm.
**Version 0.5.0** — denser Krea 2 knowledge in prompt packs, new patch fields, `/help` slash commands, composer chips.
## Layout ## Layout
- **Left — Board:** live **Generate** window + **Ref** windows (drop / paste / *Send to Assistent* / Snapshot gen). Per-window **vision** checkbox attaches that image to the next chat. - **Left — Board:** live **Generate** window + **Ref** windows (drop / paste / *Send to Assistent* / Snapshot gen). Per-window **vision** checkbox attaches that image to the next chat.
- **Splitter:** drag to resize panes - **Splitter:** drag to resize panes
- **Right (wider):** chat — first visit shows a short how-to. Only the **latest** JSON proposal keeps action buttons; Apply + Generate spins/disables while a generation is running. - **Right (wider):** chat — first visit shows a short how-to. Only the **latest** JSON proposal keeps action buttons; Apply + Generate spins/disables while a generation is running.
- **Chips** above the composer: aspect ratios, Seed lock / random, Vary.
- **Top-right:** prompt pack + settings (Ollama URL, model, auto-apply / auto-generate) - **Top-right:** prompt pack + settings (Ollama URL, model, auto-apply / auto-generate)
## UX ## UX
@@ -20,6 +23,22 @@ SwarmUI extension for **collaborative Krea 2** prompting via **Ollama**: chat +
- Pure Q&A without a patch does **not** start Generate - Pure Q&A without a patch does **not** start Generate
- **Interrupt** stops Swarm generation / clears busy state - **Interrupt** stops Swarm generation / clears busy state
- **Civitai** search cards require **Confirm download** (uses Swarm `DoModelDownloadWS` + stored `civitai_api` key). Auto-download is off by default. - **Civitai** search cards require **Confirm download** (uses Swarm `DoModelDownloadWS` + stored `civitai_api` key). Auto-download is off by default.
- Pack auto-selects from the user message (critique / inpaint / params / compose / describe / write) unless you changed the dropdown yourself.
### Slash commands (client-side, no LLM)
| Command | Effect |
| --- | --- |
| `/help` | List commands |
| `/gen` | Generate now |
| `/look generate\|refN` | Attach that board window + ask the LLM to look |
| `/init` `/mask` `/clear` | Same as board buttons |
| `/interrupt` | Stop generation |
| `/aspect 16:9` | Set size from the official 1K table |
| `/seed lock\|random` | Lock or randomize seed |
| `/vary` | New seed, same prompt (+ generate if auto) |
| `/pack write\|critique\|compose\|params\|inpaint\|describe` | Switch pack |
| `/civitai <query>` | Ask LLM to search Civitai (Confirm still required) |
## Requirements ## Requirements
@@ -47,33 +66,48 @@ Restart / rebuild SwarmUI after clone.
| Pack | Role | | Pack | Role |
| --- | --- | | --- | --- |
| `base_krea2` | Always injected: Krea 2 rules + JSON patch / actions contract | | `base_krea2` | Always injected: Krea 2 rules + JSON patch / actions contract |
| `write_prompt` | Craft / improve prompts | | `write_prompt` | Craft / improve prompts (prose structure) |
| `critique_image` | Vision critique → fixes | | `critique_image` | Vision critique → fixes |
| `compose_scene` | Scene / moodboard | | `compose_scene` | Scene / moodboard via board refs → text |
| `fix_params` | Width/height/steps/CFG/seed/σ-shift | | `fix_params` | Aspect / steps / CFG / seed / batch |
| `inpaint_edit` | Init Image img2img + Mask inpaint | | `inpaint_edit` | Init Image img2img + Mask inpaint |
| `describe_ref` | Vision → Krea prompt (no generate by default) |
Live context (checkpoint, server inventory LoRAs + triggers, wildcards, current params) is injected every request. Live context (checkpoint, server inventory LoRAs + triggers, wildcards, current params) is injected every request.
**Cloud-only Krea.ai features** (moodboards UI, Generative Sliders, Creativity Raw/Low/Medium/High) are **not** in Swarm. The assistant emulates them with prompt language + board refs. Optional patch fields `creativity` / `intensity` / `complexity` / `movement` guide the LLM only.
### Patch actions ### Patch actions
```json ```json
{ {
"prompt": "...", "prompt": "...",
"loras": [{"name": "exact", "weight": 0.8, "triggers": ["..."]}], "loras": [{"name": "exact", "weight": 0.8, "triggers": ["..."]}],
"width": 1024, "height": 1280, "steps": 8, "cfg": 1, "aspect": "16:9",
"seed": -1, "sigma_shift": 1.15, "width": 1376, "height": 768,
"steps": 8, "cfg": 1,
"seed": -1, "images": 1,
"sigma_shift": 1.15,
"creativity": "medium",
"intensity": 0, "complexity": 0, "movement": 0,
"vary": false, "lock_seed": false,
"use_init_image": true, "use_init_image": true,
"init_creativity": 0.45, "init_creativity": 0.45,
"use_mask_image": false, "use_mask_image": false,
"clear_prompt_images": false,
"look_at": ["generate"], "look_at": ["generate"],
"slot_to_init": "generate", "slot_to_init": "generate",
"snapshot_generate": false, "snapshot_generate": false,
"pack": "critique_image",
"actions": ["generate"], "actions": ["generate"],
"search_query": null "search_query": null
} }
``` ```
- `aspect` — maps to official 1K sizes (`1:1`, `4:5`, `2:3`, `16:9`, `9:16`, `4:3`, `3:2`, `2.35:1`)
- `vary` / `lock_seed` / `images`|`batch` — seed and batch helpers
- `clear_prompt_images` — strip `<image…>` embeds from the prompt box
- `pack` — switch the active prompt pack for a follow-up
- `generate` — auto-generate after apply (when enabled) - `generate` — auto-generate after apply (when enabled)
- `look_at` — hop vision from named board windows (`generate`, `ref1`, …) - `look_at` — hop vision from named board windows (`generate`, `ref1`, …)
- `slot_to_init` / `slot_to_mask` — copy that window into Swarm Init / Mask - `slot_to_init` / `slot_to_mask` — copy that window into Swarm Init / Mask
@@ -83,6 +117,8 @@ Live context (checkpoint, server inventory LoRAs + triggers, wildcards, current
Mask convention: **white = edit**, black = keep. Creativity ≈ denoise (01). Mask convention: **white = edit**, black = keep. Creativity ≈ denoise (01).
Turbo defaults: steps **8**, CFG **1** (never 0), sigma shift **1.15**.
## API routes ## API routes
| Route | Role | | Route | Role |
+10 -3
View File
@@ -38,6 +38,7 @@ public class SwarmAssistentExtension : Extension
"compose_scene", "compose_scene",
"fix_params", "fix_params",
"inpaint_edit", "inpaint_edit",
"describe_ref",
]; ];
const int MaxCivitaiHops = 2; const int MaxCivitaiHops = 2;
@@ -53,9 +54,9 @@ public class SwarmAssistentExtension : Extension
ScriptFiles.Add("Assets/assistent.js"); ScriptFiles.Add("Assets/assistent.js");
StyleSheetFiles.Add("Assets/assistent.css"); StyleSheetFiles.Add("Assets/assistent.css");
ExtensionAuthor = "mrleo1nid"; ExtensionAuthor = "mrleo1nid";
Description = "Collaborative Krea 2 assistant: Ollama chat, multi-window board, img2img/inpaint, Generate loop, Civitai Confirm."; Description = "Collaborative Krea 2 assistant: Ollama chat, multi-window board, slash commands, img2img/inpaint, Generate loop, Civitai Confirm.";
License = "MIT"; License = "MIT";
Version = "0.4.0"; Version = "0.5.0";
Tags = ["tabs", "ui", "llm", "ollama", "krea", "inpaint"]; Tags = ["tabs", "ui", "llm", "ollama", "krea", "inpaint"];
} }
@@ -452,7 +453,13 @@ public class SwarmAssistentExtension : Extension
|| obj["mask_blur"] != null || obj["mask_grow"] != null || obj["mask_blur"] != null || obj["mask_grow"] != null
|| obj["look_at"] != null || obj["vision_from"] != null || obj["vision_slots"] != null || obj["look_at"] != null || obj["vision_from"] != null || obj["vision_slots"] != null
|| obj["slot_to_init"] != null || obj["slot_to_mask"] != null || obj["slot_to_init"] != null || obj["slot_to_mask"] != null
|| obj["snapshot_generate"] != null || obj["select_slot"] != null)) || obj["snapshot_generate"] != null || obj["select_slot"] != null
|| obj["aspect"] != null || obj["images"] != null || obj["batch"] != null
|| obj["vary"] != null || obj["lock_seed"] != null
|| obj["creativity"] != null || obj["intensity"] != null
|| obj["complexity"] != null || obj["movement"] != null
|| obj["clear_prompt_images"] != null || obj["slot_to_prompt_image"] != null
|| obj["pack"] != null))
{ {
return obj; return obj;
} }
+14 -2
View File
@@ -32,6 +32,7 @@
<option value="compose_scene">Compose scene</option> <option value="compose_scene">Compose scene</option>
<option value="fix_params">Fix params</option> <option value="fix_params">Fix params</option>
<option value="inpaint_edit">Inpaint / img2img</option> <option value="inpaint_edit">Inpaint / img2img</option>
<option value="describe_ref">Describe ref</option>
</select> </select>
<select id="sa_model" class="sa-select sa-model-select" title="Ollama model"> <select id="sa_model" class="sa-select sa-model-select" title="Ollama model">
<option value="">Loading models…</option> <option value="">Loading models…</option>
@@ -52,7 +53,7 @@
<div class="sa-messages" id="sa_messages"> <div class="sa-messages" id="sa_messages">
<div class="sa-chat-empty" id="sa_chat_empty"> <div class="sa-chat-empty" id="sa_chat_empty">
<div class="sa-chat-empty-title">Collaborative Krea 2</div> <div class="sa-chat-empty-title">Collaborative Krea 2</div>
<div class="sa-chat-empty-hint">Write a prompt, drop refs on the board, or send Generate via <em>Send to Assistent</em>.</div> <div class="sa-chat-empty-hint">Write a prompt, drop refs, use aspect chips, or type <code>/help</code>.</div>
</div> </div>
</div> </div>
<div class="sa-livebar" id="sa_livebar" hidden> <div class="sa-livebar" id="sa_livebar" hidden>
@@ -61,7 +62,18 @@
<span class="sa-elapsed" id="sa_elapsed"></span> <span class="sa-elapsed" id="sa_elapsed"></span>
</div> </div>
<div class="sa-composer" id="sa_composer"> <div class="sa-composer" id="sa_composer">
<textarea id="sa_input" rows="3" placeholder="Ask for a prompt, img2img, inpaint, critique… (Enter = send, Shift+Enter = newline)"></textarea> <div class="sa-chips" id="sa_chips" role="toolbar" aria-label="Quick params">
<button type="button" class="sa-chip" data-aspect="1:1" title="1024×1024">1:1</button>
<button type="button" class="sa-chip" data-aspect="4:5" title="928×1152">4:5</button>
<button type="button" class="sa-chip" data-aspect="2:3" title="832×1248">2:3</button>
<button type="button" class="sa-chip" data-aspect="16:9" title="1376×768">16:9</button>
<button type="button" class="sa-chip" data-aspect="9:16" title="768×1376">9:16</button>
<span class="sa-chip-sep" aria-hidden="true"></span>
<button type="button" class="sa-chip" data-seed="lock" title="Keep current seed">Seed lock</button>
<button type="button" class="sa-chip" data-seed="random" title="Random seed">Seed 1</button>
<button type="button" class="sa-chip" data-vary="1" title="Same prompt, new seed + generate">Vary</button>
</div>
<textarea id="sa_input" rows="3" placeholder="Ask for a prompt, img2img, critique… Enter = send · /help = commands"></textarea>
<div class="sa-composer-actions"> <div class="sa-composer-actions">
<button type="button" class="basic-button sa-primary" id="sa_btn_send">Send</button> <button type="button" class="basic-button sa-primary" id="sa_btn_send">Send</button>
<button type="button" class="basic-button" id="sa_btn_interrupt" title="Interrupt generation / chat" hidden>Interrupt</button> <button type="button" class="basic-button" id="sa_btn_interrupt" title="Interrupt generation / chat" hidden>Interrupt</button>