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:
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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)
|
||||||
|
|||||||
@@ -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,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 2–4 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.
|
||||||
|
|||||||
@@ -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 (128–4096 OK).",
|
"turbo": "Krea 2 Turbo defaults live in exact.generation / exact.profiles.turbo. Never use CFG 0 — broken output. Side ~1024 (128–4096 OK).",
|
||||||
|
|||||||
@@ -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` (2–4 partial patches with `label`); still one fence, still STOP after it.
|
||||||
|
|||||||
@@ -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 user’s 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|…}, …]` (2–4). Base keys inherit; each item overrides only its diffs. Still one fence.
|
||||||
|
|
||||||
### Bad → good
|
### Bad → good
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -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 user’s language.
|
||||||
|
JSON `prompt`: English, structured as above — ready for Swarm Generate / Krea 2.
|
||||||
|
|||||||
@@ -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" },
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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)",
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user