Ship Assistent 0.10.2: lean context packing with quality-first vision flags.

Cut always-on system tokens (slim core, inventory, identity shelves, hops) and fix has_vision_image vs images_in_request so look_at gates correctly; enrich rich LoRAs and expose system_chars in debug.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-22 02:26:41 +03:00
co-authored by Cursor
parent f7f9f6e78d
commit 26e4ef76fb
12 changed files with 622 additions and 171 deletions
+169 -81
View File
@@ -27,6 +27,8 @@
let MAX_REF_SLOTS = 4;
let CONTEXT_PROMPT_MAX = 2000;
let HISTORY_KEEP_TURNS = 4;
let INVENTORY_PROMPT_RICH = 12;
let INVENTORY_PROMPT_NAMES = 24;
let ASPECT_TABLE = {
'1:1': [1024, 1024],
@@ -136,6 +138,8 @@
streamMeta: null,
critiqueHopUsed: false,
visionHopUsed: false,
lastSystemChars: 0,
lastContextChars: 0,
busyPhase: 'idle',
busyStarted: 0,
gotDelta: false,
@@ -784,6 +788,12 @@
return state.slots.filter((s) => s.attach && s.src);
}
/** Real board frames (not model previews). Used for has_vision_image even when JPEG is not sent. */
function visionReadySlots() {
ensureBoard();
return state.slots.filter((s) => s && s.src && !looksLikeModelPreview(s.src));
}
function setSlotSrc(id, src, { select = true, attach = null, note = null, switchTab = false, allowPreview = false } = {}) {
const slot = slotById(id);
if (!slot) {
@@ -2374,11 +2384,11 @@
batch: parseInt(val('input_images') || val('input_batchsize') || '0', 10) || null,
prompt_image_count: countPromptImages(),
selected_loras: [],
available_loras: slimInventoryLoras(inv.loras || [], 100),
available_checkpoints: slimInventoryCheckpoints(inv.checkpoints || [], 40),
wildcards: (inv.wildcards || []).map((w) => w.name || w).slice(0, 60),
available_loras: [],
available_checkpoints: slimInventoryCheckpoints(inv.checkpoints || [], 8),
wildcards: (inv.wildcards || []).map((w) => w.name || w).slice(0, 20),
inventory_at: inv.inventory_at || null,
has_vision_image: attachableSlots().length > 0,
has_vision_image: visionReadySlots().length > 0,
image_slots: slotCatalog(),
attached_slot_ids: attachableSlots().map((s) => s.id),
has_civitai_key: !!inv.has_civitai_key,
@@ -2389,6 +2399,14 @@
user_prefs_count: 0,
...initCtx,
};
{
const slim = slimInventoryLoras(inv.loras || [], INVENTORY_PROMPT_NAMES);
ctx.available_loras = slim;
if ((inv.loras || []).length > slim.length) {
ctx.available_loras_truncated = true;
ctx.available_loras_total = (inv.loras || []).length;
}
}
try {
const model = resolveCurrentCheckpoint();
@@ -2403,14 +2421,35 @@
try {
if (typeof loraHelper !== 'undefined' && loraHelper && Array.isArray(loraHelper.selected)) {
ctx.selected_loras = loraHelper.selected.map((l) => ({
name: l.name || l,
weight: (loraHelper.loraWeightPref && loraHelper.loraWeightPref[l.name || l]) || 1,
}));
const byName = new Map();
for (const l of inv.loras || []) {
if (l?.name) {
byName.set(String(l.name).toLowerCase(), l);
}
}
ctx.selected_loras = loraHelper.selected.map((l) => {
const name = l.name || l;
const invRow = byName.get(String(name).toLowerCase()) || {};
const out = {
name,
weight: (loraHelper.loraWeightPref && loraHelper.loraWeightPref[name]) || invRow.default_weight || 1,
};
if (invRow.trigger_phrase) {
out.trigger_phrase = invRow.trigger_phrase;
}
if (Array.isArray(invRow.triggers) && invRow.triggers.length) {
out.triggers = invRow.triggers.slice(0, 8);
}
if (invRow.blurb) {
out.blurb = invRow.blurb;
}
return out;
});
ctx.enabled_loras = ctx.selected_loras;
}
} catch (e) { /* ignore */ }
// Recommendation cards: checkpoint + selected LoRAs + other has_card entries (capped).
// Recommendation cards: checkpoint + selected LoRAs only (no has_card sweep).
const cardKeys = [];
const seenCard = new Set();
const addKey = (kind, name) => {
@@ -2432,22 +2471,6 @@
addKey('lora', l.name);
}
}
for (const l of inv.loras || []) {
if (l?.has_card && l?.name) {
addKey('lora', l.name);
}
if (cardKeys.length >= 14) {
break;
}
}
for (const c of inv.checkpoints || []) {
if (c?.has_card && c?.name) {
addKey('checkpoint', c.name);
}
if (cardKeys.length >= 16) {
break;
}
}
for (const k of cardKeys) {
const cached = state.modelCards[`${k.kind}:${k.name}`];
if (cached) {
@@ -2477,8 +2500,8 @@
architecture: m.architecture || null,
});
}
if (ctx.available_loras.length > 80) {
ctx.available_loras = ctx.available_loras.slice(0, 80);
if (ctx.available_loras.length > INVENTORY_PROMPT_NAMES) {
ctx.available_loras = ctx.available_loras.slice(0, INVENTORY_PROMPT_NAMES);
}
} catch (e) { /* ignore */ }
}
@@ -2491,28 +2514,33 @@
const profile = hasRaw && !hasTurbo ? 'raw' : 'turbo';
ctx.krea_profile = profile;
const defaults = mergedGenerationDefaults(profile);
ctx.recommended_params = {
// Only send recommended_params when live UI differs from Exact-backed defaults
// (Exact itself is already in the system prompt).
const rec = {
steps: defaults.steps ?? 8,
cfg: defaults.cfg ?? 1,
sigma_shift: defaults.sigma_shift ?? 1.15,
};
if (defaults.aspect) {
ctx.recommended_params.aspect = defaults.aspect;
rec.aspect = defaults.aspect;
}
const liveDiffers = (ctx.steps != null && ctx.steps !== rec.steps)
|| (ctx.cfg != null && ctx.cfg !== rec.cfg)
|| (ctx.sigma_shift != null && ctx.sigma_shift !== rec.sigma_shift);
if (liveDiffers) {
ctx.recommended_params = rec;
}
} catch (e) {
ctx.krea_profile = 'turbo';
const defaults = mergedGenerationDefaults('turbo');
ctx.recommended_params = {
steps: defaults.steps ?? 8,
cfg: defaults.cfg ?? 1,
sigma_shift: defaults.sigma_shift ?? 1.15,
};
}
ctx.session_exact = state.sessionExact && Object.keys(state.sessionExact).length
? { ...state.sessionExact }
: {};
: undefined;
// Exact KV is already in the system prompt — do not duplicate the full blob into live context.
if (!ctx.session_exact) {
delete ctx.session_exact;
}
return ctx;
}
@@ -2562,7 +2590,8 @@
}
}
} catch (e) { /* ignore */ }
const softCap = Math.min(limit || 100, 80);
const namesCap = Math.min(limit || INVENTORY_PROMPT_NAMES, INVENTORY_PROMPT_NAMES);
const richCap = Math.max(4, Math.min(INVENTORY_PROMPT_RICH, namesCap));
const rows = (list || []).map((l) => {
const sel = selected.has(String(l.name || '').toLowerCase());
const hasCard = !!l.has_card;
@@ -2584,58 +2613,72 @@
};
});
rows.sort((a, b) => b._score - a._score || String(a.name).localeCompare(String(b.name)));
// Full detail for top tier; name+trigger only for the rest within softCap.
const fullDetail = 36;
return rows.slice(0, softCap).map((row, idx) => {
const out = { name: row.name, title: row.title };
if (row.trigger_phrase) {
out.trigger_phrase = row.trigger_phrase;
// Rich: enabled + top krea/card (triggers/blurb). Rest: name (+ krea_likely) only.
let richUsed = 0;
const out = [];
for (const row of rows) {
if (out.length >= namesCap) {
break;
}
if (row.triggers) {
out.triggers = row.triggers;
const sel = selected.has(String(row.name || '').toLowerCase());
let wantRich = sel;
if (!wantRich && richUsed < richCap && (row.krea_likely || row.has_card || row.blurb)) {
wantRich = true;
}
if (row.krea_likely) {
out.krea_likely = true;
}
if (row.has_card) {
out.has_card = true;
}
const rich = idx < fullDetail || row._score >= 200;
if (rich) {
if (row.architecture) {
out.architecture = row.architecture;
if (wantRich) {
const rich = { name: row.name, title: row.title };
if (row.trigger_phrase) {
rich.trigger_phrase = row.trigger_phrase;
}
if (row.compat_class) {
out.compat_class = row.compat_class;
if (row.triggers) {
rich.triggers = row.triggers;
}
if (row.krea_likely) {
rich.krea_likely = true;
}
if (row.has_card) {
rich.has_card = true;
}
if (row.blurb) {
out.blurb = row.blurb;
rich.blurb = row.blurb;
}
if (row.default_weight) {
out.default_weight = row.default_weight;
rich.default_weight = row.default_weight;
}
if (row.tags) {
out.tags = row.tags;
if (row.architecture) {
rich.architecture = row.architecture;
}
out.push(rich);
if (!sel) {
richUsed++;
}
} else {
const nameOnly = { name: row.name };
if (row.krea_likely) {
nameOnly.krea_likely = true;
}
out.push(nameOnly);
}
return out;
});
}
return out;
}
function slimInventoryCheckpoints(list, limit) {
const rows = (list || []).slice();
rows.sort((a, b) => ((b.krea_likely ? 1 : 0) - (a.krea_likely ? 1 : 0)) || ((b.has_card ? 1 : 0) - (a.has_card ? 1 : 0)) || String(a.name).localeCompare(String(b.name)));
return rows.slice(0, limit || 40).map((c) => {
return rows.slice(0, limit || 8).map((c) => {
const out = {
name: c.name,
title: c.title || c.name,
architecture: c.architecture || null,
compat_class: c.compat_class || null,
has_card: !!c.has_card,
krea_likely: !!c.krea_likely,
};
if (c.blurb) {
out.blurb = c.blurb;
if (c.architecture) {
out.architecture = c.architecture;
}
if (c.krea_likely) {
out.krea_likely = true;
}
if (c.has_card) {
out.has_card = true;
}
return out;
});
@@ -3566,6 +3609,30 @@
await sendChat({ fromAutoCritique: true, forceSlotIds: [GEN_ID] });
}
/** After Generate: send look_at with JPEG when sa_auto_vision is on (skipped if auto-critique already attaches vision). */
async function maybeAutoVisionLook(imageSrc) {
if (!wantsAutoVision() || $('sa_auto_critique')?.checked || state.visionHopUsed || state.busy) {
return;
}
const src = await resolveFinishedGenerateSrc(imageSrc);
if (!src) {
return;
}
const gen = generateSlot();
if (gen) {
gen.src = src;
gen.attach = true;
renderBoard();
}
state.visionHopUsed = true;
setPackValue('critique_image', { flash: true });
if ($('sa_input')) {
$('sa_input').value = 'Look at the Generate result and briefly say what worked and what to fix next.';
}
setStatus('Auto look_at…');
await sendChat({ fromVisionHop: true, forceSlotIds: [GEN_ID], skipAutoPack: true });
}
/** Board action: attach the finished Generate frame and ask for a verdict. */
async function askLookAtResult() {
if (state.busy || state.generating) {
@@ -4309,6 +4376,12 @@
if (asst.context_prompt_max != null) {
CONTEXT_PROMPT_MAX = Math.max(200, Number(asst.context_prompt_max) || 2000);
}
if (asst.inventory_prompt_rich != null) {
INVENTORY_PROMPT_RICH = Math.max(4, Number(asst.inventory_prompt_rich) || 12);
}
if (asst.inventory_prompt_names != null) {
INVENTORY_PROMPT_NAMES = Math.max(INVENTORY_PROMPT_RICH, Number(asst.inventory_prompt_names) || 24);
}
fillKnobsFromConfig(data);
if (applyDefaults || data.exact) {
fillEmptyParamsFromExact();
@@ -6375,6 +6448,7 @@
);
if (src) {
await maybeAutoCritique(src);
await maybeAutoVisionLook(src);
}
} else if (!state.generating) {
stopBusyUi(wantsGen ? 'Применено' : '');
@@ -6494,9 +6568,12 @@
` ckpt=${ctx.checkpoint?.name || '—'} · krea_profile=${ctx.krea_profile || profile}`,
` ${ctx.width || '?'}×${ctx.height || '?'} (${aspect}) · steps=${ctx.steps ?? '—'} · cfg=${ctx.cfg ?? '—'} · sigma=${ctx.sigma_shift ?? '—'} · seed=${ctx.seed ?? '—'} · batch=${ctx.batch ?? '—'}`,
` loras: ${formatDebugLoras(ctx.selected_loras || ctx.enabled_loras)}`,
` available_loras=${(ctx.available_loras || []).length}${ctx.available_loras_truncated ? ` truncated/${ctx.available_loras_total || '?'}` : ''}`,
` prompt: ${clipDebug(ctx.prompt, 220)}`,
` negative: ${clipDebug(ctx.negative, 120)}`,
` init=${!!ctx.has_init_image} mask=${!!ctx.has_mask_image} prompt_images=${ctx.prompt_image_count || 0}`,
` has_vision_image=${!!ctx.has_vision_image} · images_in_request=${!!ctx.images_in_request} · vision_ready=${visionReadySlots().length}`,
` context_json_chars≈${JSON.stringify(ctx).length} · last_system_chars=${state.lastSystemChars || '—'} · last_context_chars=${state.lastContextChars || '—'}`,
'',
'Exact defaults (merged):',
` generation=${JSON.stringify(exactGen)}`,
@@ -6853,11 +6930,10 @@
}
let wantedIds = (opts.forceSlotIds || []).map(normalizeSlotId).filter(Boolean);
if (!wantedIds.length) {
wantedIds = attachableSlots().map((s) => s.id);
}
if (wantsAutoVision() && generateSlot()?.src && !looksLikeModelPreview(generateSlot().src) && !wantedIds.includes(GEN_ID)) {
wantedIds.push(GEN_ID);
// JPEG only on look_at / vision hop — never auto-attach every board ref to ordinary chat.
const sendVision = !!(opts.fromVisionHop || (wantedIds.length && opts.forceSlotIds));
if (!sendVision) {
wantedIds = [];
}
const visionSlots = wantedIds.map((id) => slotById(id)).filter((s) => s && s.src && !looksLikeModelPreview(s.src));
let images = null;
@@ -6897,8 +6973,12 @@
persistHistory();
const context = collectLiveContext();
context.has_vision_image = !!images;
context.attached_slot_ids = visionSlots.map((s) => s.id);
// has_vision_image = board has a real frame (even when JPEG is not in this request).
// images_in_request = JPEG bytes are attached to the last user message this turn.
context.has_vision_image = visionReadySlots().length > 0;
context.images_in_request = !!(images && images.length);
context.attached_slot_ids = attachableSlots().map((s) => s.id);
context.vision_slot_ids = visionSlots.map((s) => s.id);
context.persona = persona;
if (opts.cardTarget) {
context.card_target = opts.cardTarget;
@@ -6935,10 +7015,18 @@
embed_model: $('sa_embed_model')?.value || state.preferredEmbed || '',
};
const finishOk = async (reply, civitaiResults) => {
const finishOk = async (reply, civitaiResults, meta = {}) => {
if (chatEpoch !== state.chatEpoch) {
return;
}
if (meta.system_chars != null) {
state.lastSystemChars = Number(meta.system_chars) || 0;
}
try {
state.lastContextChars = (context && JSON.stringify(context).length) || 0;
} catch (e) {
state.lastContextChars = 0;
}
const prose = extractPatch(reply).prose || reply;
state.history.push({ role: 'assistant', content: prose, persona, pack });
persistHistory();
@@ -7018,7 +7106,7 @@
const reply = data.reply || (state.streamEl?.querySelector('.sa-msg-body')?.textContent) || '';
const civitai = data.civitai_results || [];
finalizeStreamMessage(reply, civitai);
finishOk(reply, civitai);
finishOk(reply, civitai, { system_chars: data.system_chars });
}
},
0,
@@ -7046,7 +7134,7 @@
}
const reply = data.reply || '';
appendMessage('assistant', reply, null, data.civitai_results || [], msgMeta);
finishOk(reply, data.civitai_results || []);
finishOk(reply, data.civitai_results || [], { system_chars: data.system_chars });
},
0,
(err2) => finishErr(String(err2 || err || 'Chat failed')),
@@ -7069,7 +7157,7 @@
}
const reply = data.reply || '';
appendMessage('assistant', reply, null, data.civitai_results || [], msgMeta);
finishOk(reply, data.civitai_results || []);
finishOk(reply, data.civitai_results || [], { system_chars: data.system_chars });
},
0,
(err) => finishErr(String(err || 'Chat failed')),