diff --git a/Assets/assistent.js b/Assets/assistent.js index 182e699..4e1e29b 100644 --- a/Assets/assistent.js +++ b/Assets/assistent.js @@ -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 || '', }; diff --git a/AssistentChatPipeline.cs b/AssistentChatPipeline.cs index 3ec6702..d8f378b 100644 --- a/AssistentChatPipeline.cs +++ b/AssistentChatPipeline.cs @@ -18,6 +18,9 @@ public partial class SwarmAssistentExtension const int MaxCivitaiHopsFallback = 2; const int MaxToolHopsFallback = 4; + static bool IsSlimDebugPack(string packName) => + string.Equals(packName, "debug_explain", StringComparison.OrdinalIgnoreCase); + (List messages, JObject systemLayers) BuildOllamaMessages(string packName, bool includeBase, string contextJson, JArray userMessages, string extraSystem = null, string personaId = null, IEnumerable skillIds = null) { List ollamaMessages = []; @@ -45,7 +48,9 @@ public partial class SwarmAssistentExtension AddLayer("core", Config.LoadCorePrompt(pid)); } - if (Memory is not null) + bool slimDebug = IsSlimDebugPack(packName); + + if (Memory is not null && !slimDebug) { try { @@ -72,23 +77,25 @@ public partial class SwarmAssistentExtension + exact.ToString(Newtonsoft.Json.Formatting.None) + "\n```"); } - StringBuilder skillsBlock = new(); - foreach (string skillId in skillIds ?? Config.ResolveEnabledSkills(pid, null)) + if (!slimDebug) { - string skillText = Config.LoadSkillPrompt(pid, skillId); - if (!string.IsNullOrWhiteSpace(skillText)) + StringBuilder skillsBlock = new(); + foreach (string skillId in skillIds ?? Config.ResolveEnabledSkills(pid, null)) { - if (skillsBlock.Length > 0) + string skillText = Config.LoadSkillPrompt(pid, skillId); + if (!string.IsNullOrWhiteSpace(skillText)) { - skillsBlock.AppendLine(); + if (skillsBlock.Length > 0) + { + skillsBlock.AppendLine(); + } + skillsBlock.AppendLine($"## Skill: {skillId}"); + skillsBlock.AppendLine(skillText.TrimEnd()); } - skillsBlock.AppendLine($"## Skill: {skillId}"); - skillsBlock.AppendLine(skillText.TrimEnd()); } + AddLayer("skills", skillsBlock.ToString()); + AddLayer("identity", Config.RenderIdentityBlock(pid)); } - AddLayer("skills", skillsBlock.ToString()); - - AddLayer("identity", Config.RenderIdentityBlock(pid)); if (!string.IsNullOrWhiteSpace(packName)) { @@ -169,20 +176,27 @@ public partial class SwarmAssistentExtension Logs.Debug($"Assistent memory seed: {ex.Message}"); } - string retrieveQuery = BuildRetrieveQuery(userMessages, contextJson, packName); + bool slimDebug = IsSlimDebugPack(packName); JArray hits = []; - try + if (!slimDebug) { - AssistentMemory.RetrieveOptions opt = MemoryRetrieveOptions(pid); - hits = await Memory.RetrieveAsync(root, retrieveQuery, opt.TopK, embed, Config.PersonaExtendsChain(pid), opt); - } - catch (Exception ex) - { - Logs.Debug($"Assistent memory retrieve: {ex.Message}"); + string retrieveQuery = BuildRetrieveQuery(userMessages, contextJson, packName); + try + { + AssistentMemory.RetrieveOptions opt = MemoryRetrieveOptions(pid); + hits = await Memory.RetrieveAsync(root, retrieveQuery, opt.TopK, embed, Config.PersonaExtendsChain(pid), opt); + } + catch (Exception ex) + { + Logs.Debug($"Assistent memory retrieve: {ex.Message}"); + } } string enrichedContext = InjectMemoryHits(contextJson, hits); - enrichedContext = EnrichPersonaContext(enrichedContext, pid, packName); + if (!slimDebug) + { + enrichedContext = EnrichPersonaContext(enrichedContext, pid, packName); + } (List messages, JObject systemLayers) = BuildOllamaMessages(packName, includeBase, enrichedContext, userMessages, personaId: pid, skillIds: skills); int systemChars = systemLayers["total"]?.Value() ?? messages.FirstOrDefault(m => string.Equals(m["role"]?.ToString(), "system", StringComparison.OrdinalIgnoreCase))?["content"]?.ToString()?.Length @@ -190,7 +204,9 @@ public partial class SwarmAssistentExtension JArray civitaiResults = []; string reply = ""; JObject lastRaw = null; - int maxHops = Math.Max(CfgInt("max_civitai_hops", MaxCivitaiHopsFallback), CfgInt("max_tool_hops", MaxToolHopsFallback)); + int maxHops = slimDebug + ? 1 + : Math.Max(CfgInt("max_civitai_hops", MaxCivitaiHopsFallback), CfgInt("max_tool_hops", MaxToolHopsFallback)); HashSet hopDone = new(StringComparer.OrdinalIgnoreCase); var chain = Config.PersonaExtendsChain(pid); for (int hop = 0; hop < maxHops; hop++) @@ -200,6 +216,10 @@ public partial class SwarmAssistentExtension await onHopStart(hop); } (reply, lastRaw) = await CallOllamaChat(root, modelName, messages, stream: onDelta is not null, onDelta, pid); + if (slimDebug) + { + break; + } JObject patch = TryParsePatch(reply); await ApplyMemoryActions(root, patch, embed, pid); ApplyUserPrefActions(patch, pid); diff --git a/AssistentConfig.cs b/AssistentConfig.cs index 3184bca..11aaf92 100644 --- a/AssistentConfig.cs +++ b/AssistentConfig.cs @@ -979,6 +979,12 @@ public sealed class AssistentConfig { continue; } + bool hidden = meta["hidden"]?.Value() == true; + if (hidden) + { + byId.Remove(id); + continue; + } bool enabled = meta["enabled"]?.Value() != false; string[] aliases = (meta["aliases"] as JArray)?.Select(t => t?.ToString()).Where(s => !string.IsNullOrWhiteSpace(s)).ToArray() ?? []; byId[id] = ( diff --git a/Config/_base/core/core.md b/Config/_base/core/core.md index a34b0e7..d6b0207 100644 --- a/Config/_base/core/core.md +++ b/Config/_base/core/core.md @@ -79,7 +79,7 @@ Several options in one ask (still one fence): ### Actions / hops -- `"generate"` — Apply + start generation. Emit this whenever the turn is a **new/updated frame** (scene brief, “her in the studio”, pose/light/wardrobe change, «ещё», variants) — not only if they typed «генерируй». Omit `generate` only for: Pure Q&A, remember/save prompt, look/critique without a redraw, describe_ref without a gen ask, Cards/authoring. +- `"generate"` — Apply + start generation when this turn is a **new/updated frame** (they described a shot, asked to draw/edit/«ещё», or clearly want to see a result). They do **not** have to type «генерируй». **Do not** emit `generate` for chat, opinions («нравится»), trivia, look/critique without a redraw, remember/save, describe_ref, Cards/authoring. Chat-only turns: prose, no JSON patch (or prompt-only without `actions`). - If the user only asks to **remember / save** a prompt as base/template («запомни», «как базовый промпт», «шаблон») and did **not** ask for a new image: **omit** `actions: ["generate"]`, do **not** `look_at`. Prefer `memory_upsert` (kind `note`, key like `base_prompt`) or a short ack; you may echo `prompt` in the patch only to sync the SwarmUI box — the UI will not Generate on remember turns. - `"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. diff --git a/Config/_base/packs/debug_explain.json b/Config/_base/packs/debug_explain.json new file mode 100644 index 0000000..09dba32 --- /dev/null +++ b/Config/_base/packs/debug_explain.json @@ -0,0 +1,9 @@ +{ + "id": "debug_explain", + "title": "Debug explain", + "order": 999, + "hidden": true, + "enabled": true, + "aliases": ["debug_explain"], + "prompt_file": "debug_explain.md" +} diff --git a/Config/_base/packs/debug_explain.md b/Config/_base/packs/debug_explain.md new file mode 100644 index 0000000..5019fc4 --- /dev/null +++ b/Config/_base/packs/debug_explain.md @@ -0,0 +1,20 @@ +# Mode: debug_explain (hidden) + +You are answering a **debug Q&A** about the current Assistent UI dump. This is not a generation turn. + +## Hard rules + +- Explain in the user's language, **5–10 short lines**. +- **No** fenced JSON. **No** `### JSON Patch`. **No** `actions`. **No** `generate`. **No** `look_at`. **No** LoRA/search hops. +- Do **not** roleplay a patch, and do **not** tell the user to type «сгенерируй». +- Ignore persona voice if it conflicts with this: be a concise technician. + +## What to cover + +Use the dump in the user message plus Exact / live JSON if present: + +1. What prompt / negative / LoRAs / params are live now. +2. What comes from Exact vs `session_exact` vs live Swarm fields vs last patch. +3. Why the last turn behaved that way (apply / generate / look / chat-only). + +If a field is missing from the dump, say so — do not invent numbers. diff --git a/Config/_base/packs/ordinary.md b/Config/_base/packs/ordinary.md index d09003e..537b8f0 100644 --- a/Config/_base/packs/ordinary.md +++ b/Config/_base/packs/ordinary.md @@ -4,7 +4,7 @@ Default all-rounder. Handle this turn from the user message + live context — d ## What you cover here -- **Write / improve prompt** → patch with `prompt` + `negative` (+ `loras` when useful) and `actions: ["generate"]` when this turn is a frame (scene, edit, «ещё») — **not** only if they typed «генерируй». Do **not** `look_at` the last frame first. `negative`: create / supplement / echo live — do not omit on Generate. +- **Write / improve prompt** → patch with `prompt` + `negative` (+ `loras`) and `actions: ["generate"]` only when they want a **new frame** (scene to draw, edit, «ещё») — context is enough, magic word is not required. Chat / «нравится» / Q&A → prose only, **no** generate. Do **not** `look_at` the last frame first. `negative` on Generate: create / supplement / echo live. - **Light critique / improve last frame** → only when the user asks to look / critique / describe the picture. Then `look_at: ["generate"]` if `images_in_request` is false. Otherwise edit the prompt from text; `has_vision_image` alone is not a reason to look. - **Scene / mood** → compose direction into the prompt (same patch rules). - **Params** → only when they ask (steps/CFG/aspect/seed); omit Exact-matching numbers otherwise. @@ -26,6 +26,6 @@ Otherwise **stay in ordinary** and just do the work. ## Deliverable -Same as write_prompt: short reply + one fenced JSON patch; `actions: ["generate"]` when the turn is a frame (context is enough — they do not have to say «генерируй»). +Same as write_prompt: short reply + fenced JSON **only if** this turn is a frame; then `actions: ["generate"]`. Chat/Q&A/opinion: no generate (prose is enough). «давай дальше» / next frame = new English `prompt` + `negative` (echo live if unchanged) + `actions:["generate"]` in the **same** turn — never leave an empty `### JSON Patch` header. Chat may be RU; **Generate `prompt` is always EN** (skill `prompting`). Several options in one ask → `variants` (2–4 partial patches with `label`); still one fence, still STOP after it. diff --git a/Config/_base/packs/write_prompt.md b/Config/_base/packs/write_prompt.md index ff12abb..ce8e8c7 100644 --- a/Config/_base/packs/write_prompt.md +++ b/Config/_base/packs/write_prompt.md @@ -6,7 +6,7 @@ Goal: craft or improve a **Krea 2** prompt that will generate well on Turbo (loc - Brief note of what you changed. - JSON patch with at least `prompt` and `negative` (create / supplement / echo live), and `loras` when relevant. -- `actions: ["generate"]` when this turn is a new/updated image (scene brief, edit, «ещё») — they do **not** have to type «генерируй». Skip generate only for Q&A / remember / look-only. Do **not** `look_at` unless they asked to see/critique the last frame. +- `actions: ["generate"]` only when they want a new/updated image (scene, edit, «ещё») — they do **not** have to type «генерируй». Skip generate for chat / Q&A / remember / look-only. Do **not** `look_at` unless they asked to see/critique the last frame. - Prefer Exact Turbo defaults / `recommended_params`. Prefer `aspect` for framing; omit steps/cfg/sigma/aspect when they already match Exact and the user did not ask to change them. - 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. diff --git a/README.md b/README.md index 1f16b1a..ed97ecb 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ SwarmUI extension for **collaborative Krea 2** prompting via **Ollama**: chat + board (Generate | Refs tabs), LoRA chips, **persona presets** (`Config/personas/`), **About the user** prefs + craft vector memory, model cards with Civitai fetch, img2img/inpaint, slash commands, auto Generate. +**Version 0.11.8** — `session_exact` remembers applied params that differ from Exact (not only when the user typed the knob). `/debug ask` uses a hidden Q&A pack: 5–10 line explain, no JSON/generate, dump stays a system note. Builds on 0.11.7. + +**Version 0.11.7** — Generate only for a real frame request: chat/opinions no longer auto-run Swarm. Context still counts («нарисуй», «ещё одну», «другая поза»), not only «генерируй». Builds on 0.11.6. + **Version 0.11.6** — Generate from context, not only «генерируй»: scene briefs / edits / «ещё» emit `actions:["generate"]`; client injects it if the model forgets. Builds on 0.11.5. **Version 0.11.5** — Generate always carries a negative: model creates / supplements / echoes live; client pass-through if omitted. Builds on 0.11.4. diff --git a/SwarmAssistentExtension.cs b/SwarmAssistentExtension.cs index f80a09e..f655cdc 100644 --- a/SwarmAssistentExtension.cs +++ b/SwarmAssistentExtension.cs @@ -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.6"; + Version = "0.11.8"; Tags = ["tabs", "ui", "llm", "ollama", "krea", "inpaint", "memory"]; } diff --git a/Tabs/Text2Image/Assistent.html b/Tabs/Text2Image/Assistent.html index 18cee22..5fa15b2 100644 --- a/Tabs/Text2Image/Assistent.html +++ b/Tabs/Text2Image/Assistent.html @@ -170,7 +170,7 @@

Автодействия после ответа модели и скилы текущей личности.

- +