Harden Exact reconcile and soften post-Generate warm.

Overwrite foreign steps/cfg in sparse patches when the user did not ask for params, and skip the cold-load path when Ollama already has the chat model resident.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-23 06:58:53 +03:00
co-authored by Cursor
parent 5f33130381
commit 7aadb40a11
7 changed files with 117 additions and 35 deletions
+34 -10
View File
@@ -1353,7 +1353,7 @@
if (want == null) {
return false;
}
if (patch?.[k] != null && String(patch[k]) !== String(want)) {
if (userIntent && patch?.[k] != null && String(patch[k]) !== String(want)) {
return false;
}
if (userIntent && state.sessionExact?.[k] != null) {
@@ -4751,20 +4751,25 @@
}
/** Re-load chat model into VRAM. force=true after Generate even without park — Krea often evicts Ollama. */
/**
* Re-load chat model into VRAM. force=true after Generate even without park
* Krea often evicts Ollama. Soft: AssistentWarmLlm skipped=already_resident
* clears expectColdLoad without a fake cold path. Resolves { ok, alreadyResident }.
*/
function warmLlm({ force = false } = {}) {
return new Promise((resolve) => {
const model = $('sa_model')?.value;
if (!model || typeof genericRequest !== 'function') {
resolve(false);
resolve({ ok: false, alreadyResident: false });
return;
}
if (!force && !state.llmParked) {
resolve(false);
resolve({ ok: false, alreadyResident: false });
return;
}
const baseUrl = $('sa_base_url')?.value || 'http://127.0.0.1:11434';
let settled = false;
const finish = (ok) => {
const finish = (ok, alreadyResident = false) => {
if (settled) {
return;
}
@@ -4773,11 +4778,20 @@
if (ok) {
state.expectColdLoad = false;
}
resolve(!!ok);
resolve({ ok: !!ok, alreadyResident: !!alreadyResident });
};
// VL cold-load can exceed a minute — don't time out the flag early.
setTimeout(() => finish(false), 180000);
genericRequest('AssistentWarmLlm', { baseUrl, model }, () => finish(true), 0, () => finish(false));
genericRequest(
'AssistentWarmLlm',
{ baseUrl, model },
(data) => {
const skipped = !!(data && (data.skipped === 'already_resident' || data.already_resident === true));
finish(true, skipped);
},
0,
() => finish(false),
);
});
}
@@ -5130,7 +5144,10 @@
state.generating = false;
setInterruptVisible(state.busy);
state.expectColdLoad = true;
// Soft cold flag: only assume eviction when we parked. Otherwise wait for
// warmLlm — already_resident means keep chatting without a cold path.
const parkedBeforeWarm = !!state.llmParked;
state.expectColdLoad = parkedBeforeWarm;
if (jobs && lastSrc && state.selectedGenResultId && epoch === state.chatEpoch) {
const row = state.genResults.find((r) => r.id === state.selectedGenResultId);
@@ -5144,9 +5161,16 @@
const multiDone = !!(jobs && finishedGenResultCount() > 1);
const willAutoCritique = !multiDone && !!$('sa_auto_critique')?.checked;
if (state.view === 'chat' && paneVisible && !willAutoCritique && epoch === state.chatEpoch) {
startBusyUi('warming');
setStatus('Возвращаю LLM в GPU…');
await warmLlm({ force: true });
if (parkedBeforeWarm || state.expectColdLoad) {
startBusyUi('warming');
setStatus('Возвращаю LLM в GPU…');
}
const warmResult = await warmLlm({ force: true });
if (warmResult?.alreadyResident) {
state.expectColdLoad = false;
} else if (!warmResult?.ok) {
state.expectColdLoad = true;
}
}
if (epoch !== state.chatEpoch) {
+10 -4
View File
@@ -81,10 +81,16 @@ export function exactKeysToForce(live, defaults, {
if (want == null) {
continue;
}
const eps = key === 'steps' ? 0.5 : 0.051;
// Patch asked for a non-Exact number — honor it.
if (patch && patch[key] != null && !numClose(patch[key], want, eps)) {
continue;
const eps = key === 'steps' ? 0.5 : 0.051;
// Honor intentional non-Exact patch only when the user asked for params.
// Sparse LLM echoes of live 20/7 must not block Exact (turbo/raw label -> numbers).
if (
userParamIntent
&& patch
&& patch[key] != null
&& !numClose(patch[key], want, eps)
) {
continue;
}
if (userParamIntent && sessionExact && sessionExact[key] != null) {
continue;
+15 -4
View File
@@ -372,7 +372,7 @@ export function resolveExactProfileDefaults({ exact, profiles, profileName } = {
}
/**
* On generate, inject Exact profile steps/cfg/sigma when the patch omitted them.
* On generate, inject Exact profile steps/cfg/sigma when omitted, and overwrite foreign leftovers when the user did not ask for params.
* Client is authoritative so sparse LLM deltas are safe. Honors explicit patch values
* and sessionExact when the user asked for params this turn.
* Returns clearSessionKeys so the UI apply path is not blocked by stale sessionExact.
@@ -390,9 +390,20 @@ export function mergeExactParamsForGenerate(patch, {
const defaults = resolveExactProfileDefaults({ exact, profiles, profileName });
const out = { ...patch };
const clearSessionKeys = [];
for (const key of EXACT_GENERATE_PARAM_KEYS) {
if (out[key] != null) {
continue;
for (const key of EXACT_GENERATE_PARAM_KEYS) {
if (out[key] != null) {
// Sparse LLM may echo live leftovers (20/7). Without user intent, force Exact.
if (
!userParamIntent
&& defaults[key] != null
&& String(out[key]) !== String(defaults[key])
) {
out[key] = defaults[key];
if (sessionExact?.[key] != null && String(sessionExact[key]) !== String(defaults[key])) {
clearSessionKeys.push(key);
}
}
continue;
}
if (userParamIntent && sessionExact?.[key] != null) {
out[key] = sessionExact[key];