Ship Assistent 0.11.3: variants grid, EN Krea prep, and stream fence fix.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-22 04:58:03 +03:00
co-authored by Cursor
parent 036484c7b7
commit 56e089d8da
18 changed files with 10244 additions and 9218 deletions
+136
View File
@@ -5,6 +5,7 @@
min-height: 28rem; min-height: 28rem;
padding: 0.5rem 0.65rem 0.65rem; padding: 0.5rem 0.65rem 0.65rem;
box-sizing: border-box; box-sizing: border-box;
position: relative;
} }
.sa-gate { .sa-gate {
@@ -1847,6 +1848,137 @@
align-items: center; align-items: center;
} }
.sa-board.sa-board-variants {
grid-template-columns: 1fr 1fr;
grid-auto-rows: minmax(7.5rem, 1fr);
}
.sa-slot.sa-slot-gen-result .sa-slot-open {
appearance: none;
border: 1px solid color-mix(in srgb, currentColor 28%, transparent);
background: color-mix(in srgb, currentColor 10%, transparent);
color: inherit;
border-radius: 999px;
padding: 0.05rem 0.45rem;
font-size: 0.68rem;
cursor: pointer;
margin-left: auto;
opacity: 0.85;
}
.sa-slot.sa-slot-gen-result .sa-slot-open:hover {
opacity: 1;
}
.sa-lightbox {
position: absolute;
inset: 0;
z-index: 80;
display: flex;
align-items: center;
justify-content: center;
pointer-events: auto;
}
.sa-lightbox[hidden] {
display: none !important;
}
.sa-lightbox-backdrop {
position: absolute;
inset: 0;
background: color-mix(in srgb, #000 62%, transparent);
}
.sa-lightbox-panel {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
gap: 0.45rem;
width: min(96%, 52rem);
max-height: 94%;
padding: 0.55rem 0.65rem 0.65rem;
border-radius: 0.65rem;
border: 1px solid color-mix(in srgb, currentColor 28%, transparent);
background: color-mix(in srgb, Canvas 92%, transparent);
box-shadow: 0 12px 40px color-mix(in srgb, #000 35%, transparent);
}
.sa-lightbox-head {
display: flex;
align-items: center;
gap: 0.45rem;
}
.sa-lightbox-title {
font-weight: 650;
font-size: 0.9rem;
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.sa-lightbox-idx {
font-size: 0.75rem;
opacity: 0.7;
}
.sa-lightbox-body {
position: relative;
display: flex;
align-items: center;
justify-content: center;
min-height: 12rem;
flex: 1;
overflow: hidden;
}
.sa-lightbox-body img {
max-width: 100%;
max-height: min(70vh, 36rem);
object-fit: contain;
border-radius: 0.35rem;
}
.sa-lightbox-nav {
appearance: none;
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 2rem;
height: 2.4rem;
border: 1px solid color-mix(in srgb, currentColor 30%, transparent);
border-radius: 0.4rem;
background: color-mix(in srgb, Canvas 80%, transparent);
color: inherit;
font-size: 1.4rem;
line-height: 1;
cursor: pointer;
opacity: 0.85;
}
.sa-lightbox-nav:hover {
opacity: 1;
}
.sa-lightbox-prev {
left: 0.25rem;
}
.sa-lightbox-next {
right: 0.25rem;
}
.sa-lightbox-actions {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
justify-content: flex-end;
}
@media (max-width: 900px) { @media (max-width: 900px) {
.sa-layout { .sa-layout {
flex-direction: column; flex-direction: column;
@@ -1872,4 +2004,8 @@
border-right: none; border-right: none;
border-bottom: 1px solid color-mix(in srgb, currentColor 18%, transparent); border-bottom: 1px solid color-mix(in srgb, currentColor 18%, transparent);
} }
.sa-lightbox-panel {
width: 98%;
max-height: 96%;
}
} }
+753 -33
View File
File diff suppressed because it is too large Load Diff
+44
View File
@@ -16,6 +16,7 @@ window.SA = window.SA || {};
'clear_prompt_images', 'slot_to_prompt_image', 'pack', 'clear_prompt_images', 'slot_to_prompt_image', 'pack',
'memories', 'memory_query', 'memory_kind', 'tag_query', 'user_prefs', 'memories', 'memory_query', 'memory_kind', 'tag_query', 'user_prefs',
'controls', 'persona_clone', 'persona_shelves', 'persona', 'notes', 'controls', 'persona_clone', 'persona_shelves', 'persona', 'notes',
'variants',
]; ];
const FENCE_RE = /```(?:json)?\s*([\s\S]*?)```/gi; const FENCE_RE = /```(?:json)?\s*([\s\S]*?)```/gi;
@@ -92,9 +93,52 @@ window.SA = window.SA || {};
return { prose, patch: lastPatch }; return { prose, patch: lastPatch };
} }
/**
* Closed fence worth freezing the stream / stopping Ollama early.
* Weak fences (pack / creativity / empty) must NOT stop — model often continues with the real patch.
*/
function isTerminalStreamPatch(obj) {
if (!obj || typeof obj !== 'object') {
return false;
}
if (isCardObject(obj)) {
return true;
}
if (Array.isArray(obj.variants) && obj.variants.length) {
return true;
}
if (obj.look_at != null || obj.vision_from != null || obj.vision_slots != null) {
return true;
}
if (obj.search_query != null || obj.civitai_query != null
|| obj.memory_query != null || obj.tag_query != null || obj.inventory_query != null) {
return true;
}
const acts = Array.isArray(obj.actions) ? obj.actions.map(String) : [];
const hopOrGen = [
'skill_load', 'persona_read', 'memory_get', 'memory_search', 'lookup_tags',
'list_inventory', 'search_civitai', 'interrupt', 'generate',
'memory_upsert', 'user_pref_upsert',
];
if (acts.some((a) => hopOrGen.includes(a))) {
return true;
}
if (String(obj.prompt || '').trim().length >= 48) {
return true;
}
if (obj.loras != null || obj.aspect != null || obj.steps != null
|| obj.width != null || obj.height != null || obj.cfg != null
|| obj.seed != null || obj.controls != null
|| obj.memories != null || obj.user_prefs != null) {
return true;
}
return false;
}
SA.PATCH_KEYS = PATCH_KEYS; SA.PATCH_KEYS = PATCH_KEYS;
SA.isCardObject = isCardObject; SA.isCardObject = isCardObject;
SA.isPatchObject = isPatchObject; SA.isPatchObject = isPatchObject;
SA.isTerminalStreamPatch = isTerminalStreamPatch;
SA.normalizePatch = normalizePatch; SA.normalizePatch = normalizePatch;
SA.extractPatch = extractPatch; SA.extractPatch = extractPatch;
})(); })();
+2 -1
View File
@@ -530,7 +530,8 @@ public sealed class AssistentConfig
} }
if (patch["prompt"] != null || patch["loras"] != null || patch["aspect"] != null if (patch["prompt"] != null || patch["loras"] != null || patch["aspect"] != null
|| patch["width"] != null || patch["height"] != null || patch["steps"] != null || patch["width"] != null || patch["height"] != null || patch["steps"] != null
|| patch["cfg"] != null || patch["seed"] != null) || patch["cfg"] != null || patch["seed"] != null
|| patch["variants"] != null)
{ {
return true; return true;
} }
+8
View File
@@ -166,6 +166,12 @@ public partial class SwarmAssistentExtension
{ {
int numCtx = Config.LoadAssistant(AssistentConfig.SafeId(personaId) ?? Config.DefaultPersonaId())["num_ctx"]?.Value<int?>() int numCtx = Config.LoadAssistant(AssistentConfig.SafeId(personaId) ?? Config.DefaultPersonaId())["num_ctx"]?.Value<int?>()
?? DefaultNumCtxFallback; ?? DefaultNumCtxFallback;
int numPredict = Config.LoadAssistant(AssistentConfig.SafeId(personaId) ?? Config.DefaultPersonaId())["num_predict"]?.Value<int?>()
?? 3072;
if (numPredict < 512)
{
numPredict = 512;
}
JObject payload = new() JObject payload = new()
{ {
["model"] = modelName, ["model"] = modelName,
@@ -174,6 +180,8 @@ public partial class SwarmAssistentExtension
["options"] = new JObject ["options"] = new JObject
{ {
["num_ctx"] = numCtx, ["num_ctx"] = numCtx,
// Without this, Ollama defaults can cut mid-prompt / mid-skill_load fence.
["num_predict"] = numPredict,
}, },
["keep_alive"] = "15m", ["keep_alive"] = "15m",
}; };
+77 -10
View File
@@ -21,6 +21,7 @@ public partial class SwarmAssistentExtension
"clear_prompt_images", "slot_to_prompt_image", "pack", "memories", "memory", "clear_prompt_images", "slot_to_prompt_image", "pack", "memories", "memory",
"memory_query", "memory_kind", "tag_query", "user_prefs", "memory_query", "memory_kind", "tag_query", "user_prefs",
"inventory_query", "skills", "persona_shelves", "controls", "inventory_query", "skills", "persona_shelves", "controls",
"variants",
]; ];
static bool HasValue(JObject obj, string key) static bool HasValue(JObject obj, string key)
@@ -107,8 +108,10 @@ public partial class SwarmAssistentExtension
} }
/// <summary> /// <summary>
/// If the reply already contains a closed fenced patch/card, cut everything after it. /// If the reply already contains a closed fenced patch/card that is "done enough" to act on,
/// Models often keep writing («Готово!», second aspect, …) and the stream never feels done. /// cut everything after it. Do NOT stop on weak fences (pack/creativity/notes-only) — models
/// often emit a tiny JSON first then the real prompt fence; aborting early cuts the prompt
/// and blocks skill_load / generate.
/// </summary> /// </summary>
static bool TryTruncateAtCompleteFence(string reply, out string truncated) static bool TryTruncateAtCompleteFence(string reply, out string truncated)
{ {
@@ -129,18 +132,11 @@ public partial class SwarmAssistentExtension
try try
{ {
JObject obj = JObject.Parse(raw); JObject obj = JObject.Parse(raw);
if (obj is null) if (obj is null || !FenceIsTerminalPatch(obj))
{
continue;
}
bool usable = LooksLikeCardObject(obj)
|| Array.Exists(PatchKeys, k => obj[k] is not null);
if (!usable)
{ {
continue; continue;
} }
truncated = reply.Substring(0, match.Index + match.Length).TrimEnd(); truncated = reply.Substring(0, match.Index + match.Length).TrimEnd();
// Only treat as complete if the fence actually closed (regex requires ```).
return true; return true;
} }
catch catch
@@ -151,6 +147,77 @@ public partial class SwarmAssistentExtension
return false; return false;
} }
/// <summary>
/// True when a closed fence is worth aborting the Ollama stream (real deliverable or tool hop).
/// </summary>
static bool FenceIsTerminalPatch(JObject obj)
{
if (obj is null)
{
return false;
}
if (LooksLikeCardObject(obj))
{
return true;
}
if (obj["variants"] is JArray variants && variants.Count > 0)
{
return true;
}
if (HasValue(obj, "look_at") || HasValue(obj, "vision_from") || HasValue(obj, "vision_slots"))
{
return true;
}
if (HasValue(obj, "search_query") || HasValue(obj, "civitai_query"))
{
return true;
}
if (HasValue(obj, "memory_query") || HasValue(obj, "tag_query") || HasValue(obj, "inventory_query"))
{
return true;
}
if (obj["actions"] is JArray acts)
{
foreach (JToken a in acts)
{
string s = a?.ToString() ?? "";
if (string.IsNullOrWhiteSpace(s))
{
continue;
}
if (s.Equals("skill_load", StringComparison.OrdinalIgnoreCase)
|| s.Equals("persona_read", StringComparison.OrdinalIgnoreCase)
|| s.Equals("memory_get", StringComparison.OrdinalIgnoreCase)
|| s.Equals("memory_search", StringComparison.OrdinalIgnoreCase)
|| s.Equals("lookup_tags", StringComparison.OrdinalIgnoreCase)
|| s.Equals("list_inventory", StringComparison.OrdinalIgnoreCase)
|| s.Equals("search_civitai", StringComparison.OrdinalIgnoreCase)
|| s.Equals("interrupt", StringComparison.OrdinalIgnoreCase)
|| s.Equals("generate", StringComparison.OrdinalIgnoreCase)
|| s.Equals("memory_upsert", StringComparison.OrdinalIgnoreCase)
|| s.Equals("user_pref_upsert", StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
}
string prompt = obj["prompt"]?.ToString()?.Trim() ?? "";
if (prompt.Length >= 48)
{
return true;
}
// Real param change without prose notes
if (HasValue(obj, "loras") || HasValue(obj, "aspect") || HasValue(obj, "steps")
|| HasValue(obj, "width") || HasValue(obj, "height") || HasValue(obj, "cfg")
|| HasValue(obj, "seed") || HasValue(obj, "controls")
|| HasValue(obj, "memories") || HasValue(obj, "user_prefs"))
{
return true;
}
// Weak: pack / creativity / intensity / empty actions / notes-only → keep streaming
return false;
}
static string ExtractSearchQuery(JObject patch) static string ExtractSearchQuery(JObject patch)
{ {
if (patch is null) if (patch is null)
+2
View File
@@ -1,5 +1,6 @@
{ {
"num_ctx": 16384, "num_ctx": 16384,
"num_predict": 3072,
"max_civitai_hops": 2, "max_civitai_hops": 2,
"max_loras_inventory": 150, "max_loras_inventory": 150,
"max_checkpoints_inventory": 60, "max_checkpoints_inventory": 60,
@@ -9,6 +10,7 @@
"inventory_prompt_names": 24, "inventory_prompt_names": 24,
"inventory_hop_limit": 20, "inventory_hop_limit": 20,
"max_ref_slots": 4, "max_ref_slots": 4,
"max_gen_variants": 4,
"default_pack": "ordinary", "default_pack": "ordinary",
"default_persona": "neutral", "default_persona": "neutral",
"embed_model": "nomic-embed-text", "embed_model": "nomic-embed-text",
+18 -3
View File
@@ -18,7 +18,7 @@ When instructions conflict, apply this order (highest wins):
Exact = defaults encyclopedia. About the user = human taste. RAG = soft craft notes. Do **not** re-emit `steps` / `cfg` / `sigma_shift` / `aspect` when they already match Exact (or `session_exact`) and the user did not ask to change them. Exact = defaults encyclopedia. About the user = human taste. RAG = soft craft notes. Do **not** re-emit `steps` / `cfg` / `sigma_shift` / `aspect` when they already match Exact (or `session_exact`) and the user did not ask to change them.
Never write a “JSON Patch” section in prose without an actual fenced ```json``` object. An empty `### JSON Patch` header is a failure — omit the section or emit a real fence. Keep prose short (a few lines). **After the closing ``` of the JSON fence, STOP** — no «Готово!», no second aspect, no “сейчас сгенерирую оба”. One turn = one patch (one aspect). Prompt prose structure lives in skill `prompting` — do not invent a second recipe here. Never write a “JSON Patch” section in prose without an actual fenced ```json``` object. An empty `### JSON Patch` header is a failure — omit the section or emit a real fence. If you described the next frame / prompt in prose, the fence **must** include that `prompt` and usually `actions: ["generate"]` in the **same** turn — never stop after the header. **`prompt` must be English** (Krea 2 / Qwen3-VL) — translate + structure per skill `prompting`; chat prose may stay RU. Keep prose short (a few lines). **After the closing ``` of the JSON fence, STOP** — no «Готово!», no second fenced patch, no “сейчас сгенерирую оба” in prose. **One turn = one patch.** When the user asks for several options (разный свет / оба / варианты), put 24 items in **`variants`** (partial patches with optional `label`); the UI runs them sequentially and shows a grid. Do not emit two fences. Ordinary single-image requests stay one patch without `variants`. Prompt prose structure lives in skill `prompting` — do not invent a second recipe here.
## Live context ## Live context
@@ -52,18 +52,33 @@ Never write a “JSON Patch” section in prose without an actual fenced ```json
} }
``` ```
Several options in one ask (still one fence):
```json
{
"prompt": "same subject base…",
"aspect": "16:9",
"actions": ["generate"],
"variants": [
{ "label": "warm light", "prompt": "… warm window light …" },
{ "label": "cool light", "prompt": "… cool moonlight …" },
{ "label": "portrait 9:16", "aspect": "9:16" }
]
}
```
### Patch rules ### Patch rules
- Omit unchanged keys. Prefer omitting Exact-matching `steps`/`cfg`/`sigma_shift`/`aspect`. - Omit unchanged keys. Prefer omitting Exact-matching `steps`/`cfg`/`sigma_shift`/`aspect`.
- 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. - 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. - `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) — use when needed; packs list the ones for that mode. - 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.
- **`controls`** — only keys declared in this persona's `controls.json` (Exact). Clamp to min/max. Example: `"controls": { "horny": 55 }`. Do not invent control ids. - **`controls`** — only keys declared in this persona's `controls.json` (Exact). Clamp to min/max. Example: `"controls": { "horny": 55 }`. Do not invent control ids.
- Do not invent model or LoRA filenames. - Do not invent model or LoRA filenames.
### Actions / hops ### Actions / hops
- `"generate"` — Apply + start generation when the user wants a new image. - `"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.
- If the user only asks to **remember / save** a prompt as base/template («запомни», «как базовый промпт», «шаблон») and did **not** ask for a new image: **omit** `actions: ["generate"]`, do **not** `look_at`. Prefer `memory_upsert` (kind `note`, key like `base_prompt`) or a short ack; you may echo `prompt` in the patch only to sync the SwarmUI box — the UI will not Generate on remember turns. - If the user only asks to **remember / save** a prompt as base/template («запомни», «как базовый промпт», «шаблон») and did **not** ask for a new image: **omit** `actions: ["generate"]`, do **not** `look_at`. Prefer `memory_upsert` (kind `note`, key like `base_prompt`) or a short ack; you may echo `prompt` in the patch only to sync the SwarmUI box — the UI will not Generate on remember turns.
- `"search_civitai"` + **required** short `search_query` — Civitai hop (user Confirms downloads). Without `search_query` the hop is skipped (never search the whole user message). - `"search_civitai"` + **required** short `search_query` — Civitai hop (user Confirms downloads). Without `search_query` the hop is skipped (never search the whole user message).
- `"interrupt"` — stop generation. - `"interrupt"` — stop generation.
+1
View File
@@ -30,6 +30,7 @@
}, },
"facts": { "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.", "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": "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.",
"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.", "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 (1284096 OK).", "turbo": "Krea 2 Turbo defaults live in exact.generation / exact.profiles.turbo. Never use CFG 0 — broken output. Side ~1024 (1284096 OK).",
+2
View File
@@ -27,3 +27,5 @@ Otherwise **stay in ordinary** and just do the work.
## Deliverable ## Deliverable
Same as write_prompt: short reply + one fenced JSON patch; `actions: ["generate"]` when they want an image. 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`).
Several options in one ask → `variants` (24 partial patches with `label`); still one fence, still STOP after it.
+2 -1
View File
@@ -1,6 +1,6 @@
# Mode: write_prompt # Mode: write_prompt
Goal: craft or improve a **Krea 2** prompt that will generate well on Turbo (local Swarm). Prompt prose recipe is in skill `prompting` — follow it; do not invent a second structure. Goal: craft or improve a **Krea 2** prompt that will generate well on Turbo (local Swarm). Prompt prose recipe is in skill `prompting` — follow it; do not invent a second structure. The JSON **`prompt` field is always English** (translate + structure); user-facing notes may stay in the users language.
## Deliverable ## Deliverable
@@ -9,6 +9,7 @@ Goal: craft or improve a **Krea 2** prompt that will generate well on Turbo (loc
- `actions: ["generate"]` when the user wants a new image — UI applies + Generate without Apply buttons. - `actions: ["generate"]` when the user wants a new image — UI applies + Generate without Apply buttons.
- Prefer Exact Turbo defaults / `recommended_params`. Prefer `aspect` for framing; omit steps/cfg/sigma/aspect when they already match Exact and the user did not ask to change them. - Prefer Exact Turbo defaults / `recommended_params`. Prefer `aspect` for framing; omit steps/cfg/sigma/aspect when they already match Exact and the user did not ask to change them.
- Missing style LoRA → `actions: ["search_civitai"]` + short `search_query` (Krea-compatible). - Missing style LoRA → `actions: ["search_civitai"]` + short `search_query` (Krea-compatible).
- User wants several options (оба / варианты / разный свет) → `variants: [{label, prompt|aspect|…}, …]` (24). Base keys inherit; each item overrides only its diffs. Still one fence.
### Bad → good ### Bad → good
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"always": [ "always": [
"Match the user's language (RU or EN)", "Match the user's language (RU or EN) in chat — Generate prompt stays English",
"Use only inventory / memory_hits / cards for LoRA names and triggers", "Use only inventory / memory_hits / cards for LoRA names and triggers",
"Emit valid JSON patches when changing generation state" "Emit valid JSON patches when changing generation state"
], ],
+23 -5
View File
@@ -1,9 +1,27 @@
# Skill: prompting # Skill: prompting
Write **natural prose** for a photographer/director — not Danbooru tags, not `(word:1.5)`, not `masterpiece / best quality / 8k`. The **chat model** prepares the Generate-box text for **Krea 2** (Qwen3-VL). Do this prep as fully as you can in the same turn (or the dedicated prep hop). Chat with the user may be RU/EN; the JSON **`prompt` is always English**.
Order (front-load importance): **subject → pose/action → setting → materials → camera/framing → lighting → medium/mood**. ## Hard rules (Krea / Qwen3-VL)
- Short user ideas: expand. Finished Krea-style paragraphs: keep wording; only fix anti-patterns. 1. **`prompt` language = English only** — never put Russian into the JSON `prompt`. Translate the idea, then structure it.
- Put LoRA trigger phrases near the subject they affect. 2. **Natural photographer/director prose** — not Danbooru tag soup, not `(word:1.5)`, not `masterpiece / best quality / 8k`.
- Prefer positives over negatives. 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).
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`
- Subject and action clear in the first sentence
- Wardrobe / body / setting concrete
- Camera + lighting present
- One coherent scene; NSFW stated in plain English if needed
- Triggers placed next to what they modify
## Deliverable
User-facing prose: short, in the users language.
JSON `prompt`: English, structured as above — ready for Swarm Generate / Krea 2.
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"welcome_html": "<div class=\"sa-welcome-title\">Assistent · Krea 2</div><ul><li><strong>Generate</strong> слева — живой просмотр. В чат сам не уходит.</li><li><strong>Refs</strong> — референсы на отдельной вкладке: drop / paste / Снимок gen.</li><li>Галочка vision на окне — отправить кадр модели.</li><li>Чипсы aspect / seed / Vary / Turbo·RAW. В чате: <code>/help</code>.</li><li>Кнопки патча только у последнего предложения.</li></ul>Напиши, что сгенерировать — или кинь референс и попроси правку.", "welcome_html": "<div class=\"sa-welcome-title\">Assistent · Krea 2</div><ul><li><strong>Generate</strong> слева — живой просмотр. Несколько вариантов → сетка + клик для просмотра.</li><li><strong>Refs</strong> — референсы на отдельной вкладке: drop / paste / Снимок gen.</li><li>Галочка vision на окне — отправить кадр модели.</li><li>Чипсы aspect / seed / Vary / Turbo·RAW. В чате: <code>/help</code>.</li><li>Кнопки патча только у последнего предложения.</li></ul>Напиши, что сгенерировать — или кинь референс и попроси правку.",
"help_text": "Slash-команды (без LLM):\n/help — этот список\n/new — новый чат\n/history — список чатов\n/debug — сводка UI/Exact\n/debug ask · /why — сводка + короткий ответ модели\n/gen — Generate сейчас\n/look generate|refN — прикрепить окно к vision\n/init /mask /clear — Init / Mask / Clear Init\n/interrupt — остановить генерацию\n/aspect 16:9 — размер из таблицы 1K\n/seed lock|random — зафиксировать или рандомизировать seed\n/vary — новый seed, тот же промпт\n/pack write|ordinary|critique|compose|params|inpaint|describe|card|persona\n/persona new — интервью: клон текущей личности (overlay)\n/persona clone <id> — клон с указанной\n/persona save — записать согласованный черновик\n/civitai <query> — поиск LoRA (Confirm в чате)\n/inventory — rescan моделей + обновить список LoRA\n\nЧипсы над полем ввода делают то же для aspect / seed / vary / Turbo·RAW.\nПри старте всегда новый чат; смена чата восстанавливает параметры.\nOverlay-личности удаляет только кнопка ✕ рядом с селектом (не модель).", "help_text": "Slash-команды (без LLM):\n/help — этот список\n/new — новый чат\n/history — список чатов\n/debug — сводка UI/Exact\n/debug ask · /why — сводка + короткий ответ модели\n/gen — Generate сейчас\n/look generate|refN — прикрепить окно к vision\n/init /mask /clear — Init / Mask / Clear Init\n/interrupt — остановить генерацию\n/aspect 16:9 — размер из таблицы 1K\n/seed lock|random — зафиксировать или рандомизировать seed\n/vary — новый seed, тот же промпт\n/pack write|ordinary|critique|compose|params|inpaint|describe|card|persona\n/persona new — интервью: клон текущей личности (overlay)\n/persona clone <id> — клон с указанной\n/persona save — записать согласованный черновик\n/civitai <query> — поиск LoRA (Confirm в чате)\n/inventory — rescan моделей + обновить список LoRA\n\nНесколько вариантов в одном запросе («оба», разный свет) → патч с variants[] → сетка на Generate, клик / Открыть / Enter — просмотр.\nЧипсы над полем ввода делают то же для aspect / seed / vary / Turbo·RAW.\nПри старте всегда новый чат; смена чата восстанавливает параметры.\nOverlay-личности удаляет только кнопка ✕ рядом с селектом (не модель).",
"chips": [ "chips": [
{ "label": "1:1", "action": "aspect", "value": "1:1", "title": "1024×1024" }, { "label": "1:1", "action": "aspect", "value": "1:1", "title": "1024×1024" },
{ "label": "4:5", "action": "aspect", "value": "4:5", "title": "928×1152" }, { "label": "4:5", "action": "aspect", "value": "4:5", "title": "928×1152" },
+2 -1
View File
@@ -6,9 +6,10 @@
"detailed Krea prose" "detailed Krea prose"
], ],
"process": [ "process": [
"translate idea → English Krea prose before Generate",
"subject → pose → clothes/hair → setting → camera → light → mood", "subject → pose → clothes/hair → setting → camera → light → mood",
"front-load what matters", "front-load what matters",
"natural prose, not tag soup" "natural EN prose, not tag soup"
], ],
"defaults": [ "defaults": [
"photoreal / film unless user asks anime/hentai", "photoreal / film unless user asks anime/hentai",
+1 -1
View File
@@ -2,7 +2,7 @@
"always": [ "always": [
"You are Leonid; the user is not Leonid — never greet/address them as Леонид/Leonid unless About the user says their name is that", "You are Leonid; the user is not Leonid — never greet/address them as Леонид/Leonid unless About the user says their name is that",
"If asked your name, say you are Leonid (assistant)", "If asked your name, say you are Leonid (assistant)",
"Match user language (RU/EN)", "Match user language (RU/EN) in chat; Generate patch prompt is always English for Krea",
"Craft first: triggers, aspect, Turbo — horny never replaces technique", "Craft first: triggers, aspect, Turbo — horny never replaces technique",
"Scale appearance/outfits by controls.preference_bias (1…1)", "Scale appearance/outfits by controls.preference_bias (1…1)",
"Scale sexual tone + roleplay fetishes by controls.horny (0…100)", "Scale sexual tone + roleplay fetishes by controls.horny (0…100)",
+11 -1
View File
@@ -2,6 +2,16 @@
SwarmUI extension for **collaborative Krea 2** prompting via **Ollama**: chat + board (Generate | Refs tabs), LoRA chips, **persona presets** (`Config/personas/`), **About the user** prefs + craft vector memory, model cards with Civitai fetch, img2img/inpaint, slash commands, auto Generate. SwarmUI extension for **collaborative Krea 2** prompting via **Ollama**: chat + board (Generate | Refs tabs), LoRA chips, **persona presets** (`Config/personas/`), **About the user** prefs + craft vector memory, model cards with Civitai fetch, img2img/inpaint, slash commands, auto Generate.
**Version 0.11.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.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.
**Version 0.11.1** — Krea Generate `prompt` must be English (skill `prompting` + Exact + client rewrite hop if Cyrillic leaks). Builds on 0.11.0 variants.
**Version 0.11.0** — Пакет вариантов: JSON `variants[]` (2–4) → последовательные Generate → сетка на вкладке Generate + lightbox. Builds on 0.10.22 empty-patch fix.
**Version 0.10.22** — Empty `### JSON Patch` no longer dead-ends: synthesize prompt from prose / retry; «давай дальше» counts as Generate. Builds on 0.10.21 spinner fix.
**Version 0.10.21** — Board Generate spinner clears when Swarm finishes (`num_live_gens`), not only when the image URL string changes. Builds on 0.10.20 remember-without-gen. **Version 0.10.21** — Board Generate spinner clears when Swarm finishes (`num_live_gens`), not only when the image URL string changes. Builds on 0.10.20 remember-without-gen.
**Version 0.10.20** — «Запомни / базовый промпт» no longer triggers Auto-Generate or auto look_at (even if the model sneaks `actions:["generate"]`). Builds on 0.10.19 slider echo fix. **Version 0.10.20** — «Запомни / базовый промпт» no longer triggers Auto-Generate or auto look_at (even if the model sneaks `actions:["generate"]`). Builds on 0.10.19 slider echo fix.
@@ -12,7 +22,7 @@ SwarmUI extension for **collaborative Krea 2** prompting via **Ollama**: chat +
## Layout ## Layout
- **Left — Board tabs:** **Generate** (full-height live view) | **Refs** (reference grid + badge `N · vision M`); **Посмотри результат** attaches the finished frame and asks for a verdict - **Left — Board tabs:** **Generate** (live view, or a **variant grid** when the model emits `variants[]`) | **Refs** (reference grid + badge `N · vision M`); click a variant to select / open lightbox; **Посмотри результат** attaches the selected finished frame and asks for a verdict
- **Splitter:** drag to resize panes - **Splitter:** drag to resize panes
- **Right:** Chat | Cards | Settings; persona / pack / Ollama chat model; **Ollama health** badge - **Right:** Chat | Cards | Settings; persona / pack / Ollama chat model; **Ollama health** badge
- **Chips / slash:** loaded from `Config/_base/ui.json` (persona can override) - **Chips / slash:** loaded from `Config/_base/ui.json` (persona can override)
+1 -1
View File
@@ -36,7 +36,7 @@ public partial class SwarmAssistentExtension : Extension
ExtensionAuthor = "mrleo1nid"; ExtensionAuthor = "mrleo1nid";
Description = "Collaborative Krea 2 assistant: Ollama chat, persona presets, vector memory, model cards, Generate loop."; Description = "Collaborative Krea 2 assistant: Ollama chat, persona presets, vector memory, model cards, Generate loop.";
License = "MIT"; License = "MIT";
Version = "0.10.21"; Version = "0.11.3";
Tags = ["tabs", "ui", "llm", "ollama", "krea", "inpaint", "memory"]; Tags = ["tabs", "ui", "llm", "ollama", "krea", "inpaint", "memory"];
} }