Cache Keepalive: arm per-chat (only on MESSAGE_SENT, disarm on CHAT_CHANGED); fixes false-arming on chat open
| @@ -52,10 +52,10 @@ const defaultSettings = { | ||
| 52 | 52 | |
| 53 | 53 | let idleTimer = null; |
| 54 | 54 | let keepaliveActive = false; |
| 55 | 55 | // Whether keepalive has beenis "armed" thisfor browser-tabthe sessionCURRENT chat. It stays dormant until the user sends a new |
| 56 | 56 | // first real message/generation happens in the tab,open becausechat — on a freshfreshly loadloaded or switched-to chat (even with chathistory already |
| 57 | 57 | // history already present) we have no way to know whether anythingits prompt is still cached server-side, so pinging would |
| 58 | 58 | // server-side — so pinging before then would be pointless. ResetsReset on page reload and whenever the chat changes. |
| 59 | 59 | let armed = false; |
| 60 | 60 | // Wall-clock time (Date.now() + delay) the pending idle timer is expected to fire, or null when |
| 61 | 61 | // none is scheduled. Used to detect a timer that fired far too late (sleep/throttle). |
| @@ -103,7 +103,7 @@ function clearIdleTimer() { | ||
| 103 | 103 | /** |
| 104 | 104 | * (Re)schedules the keepalive for the ACTIVE profile based on how long it has been since |
| 105 | 105 | * that profile's last message. Does nothing (and leaves the timer cleared) until keepalive |
| 106 | 106 | * has been armed for thisthe tabcurrent sessionchat, or when it is disabled for the active connection. |
| 107 | 107 | * Called on activity AND whenever the active profile changes, so each profile keeps its own |
| 108 | 108 | * independent idle clock. |
| 109 | 109 | */ |
| @@ -130,15 +130,26 @@ function markActivity() { | ||
| 130 | 130 | } |
| 131 | 131 | |
| 132 | 132 | /** |
| 133 | - * Real message/generation activity: arms keepalive for this tab session (if it wasn't already) | |
| 133 | + * The user sent a new message in the current chat — the ONLY thing that arms keepalive. | |
| 134 | - * and records the activity. Loading a chat's history does NOT call this. | |
| 134 | + * Background/quiet generations, swipes, regenerations, and loading chat history deliberately do | |
| 135 | + * NOT arm it: none of those prove the user is actively continuing THIS chat from a known-cached | |
| 136 | + * prompt (GENERATION_STARTED in particular fires for every quiet/background generation too). | |
| 135 | 137 | */ |
| 136 | 138 | function onChatActivityonMessageSent() { |
| 137 | 139 | armed = true; |
| 138 | 140 | markActivity(); |
| 139 | 141 | } |
| 140 | 142 | |
| 141 | 143 | /** |
| 144 | + * Opening or switching chats disarms keepalive: a freshly loaded chat is not proof its prompt is | |
| 145 | + * still cached server-side, so we wait for a new sent message in that chat before running again. | |
| 146 | + */ | |
| 147 | +function onChatChanged() { | |
| 148 | + armed = false; | |
| 149 | + scheduleIdleTimer(); | |
| 150 | +} | |
| 151 | + | |
| 152 | +/** | |
| 142 | 153 | * Fires a background ("quiet") generation against the active model to keep the |
| 143 | 154 | * server-side prompt cache warm. The full prompt prefix (current chat context + |
| 144 | 155 | * the keepalive message) is sent — which is what refreshes the cache — while the |
| @@ -165,7 +176,7 @@ async function fireKeepalive() { | ||
| 165 | 176 | // If the timer fired much later than scheduled (machine sleep, background-tab throttling, |
| 166 | 177 | // etc.), far more wall-clock time has elapsed than the idle interval, so the cache has |
| 167 | 178 | // almost certainly expired. Don't ping on a stale assumption — go dormant until the next |
| 168 | 179 | // realsent message re-arms keepalive, exactly like a freshly opened tabchat. |
| 169 | 180 | if (scheduledFireTime !== null && (Date.now() - scheduledFireTime) > MAX_TIMER_DRIFT_MS) { |
| 170 | 181 | console.debug('[Keepalive] Idle timer fired', Date.now() - scheduledFireTime, 'ms late; skipping and disarming until next activity.'); |
| 171 | 182 | armed = false; |
| @@ -318,21 +329,23 @@ async function init() { | ||
| 318 | 329 | loadSettings(); |
| 319 | 330 | setupListeners(); |
| 320 | 331 | |
| 321 | - // A real message/generation arms keepalive for this tab session and counts as using the | |
| 332 | + // Only the user sending a new message arms keepalive (for the current chat). | |
| 322 | - // active profile. (Keepalive stays dormant until the first one — see `armed`.) | |
| 333 | + eventSource.on(event_types.MESSAGE_SENT, onMessageSent); | |
| 323 | - const activationEvents = [ | |
| 334 | + | |
| 324 | - event_types.MESSAGE_SENT, | |
| 335 | + // These reset the active profile's idle clock (so generation time isn't counted as idle) but | |
| 336 | + // never arm keepalive on their own — they also fire for background/quiet generations and while | |
| 337 | + // a chat is loading. They only re-arm the timer if a sent message already armed it. | |
| 338 | + const activityEvents = [ | |
| 325 | 339 | event_types.MESSAGE_RECEIVED, |
| 326 | 340 | event_types.GENERATION_STARTED, |
| 327 | 341 | event_types.GENERATION_ENDED, |
| 328 | 342 | ]; |
| 329 | 343 | for (const event of activationEventsactivityEvents) { |
| 330 | 344 | eventSource.on(event, onChatActivitymarkActivity); |
| 331 | 345 | } |
| 332 | 346 | |
| 333 | - // Switching chats re-targets the per-profile idle clock but does NOT arm keepalive on its | |
| 347 | + // Opening/switching a chat disarms keepalive — it must be re-armed by a new sent message. | |
| 334 | - // own — opening/loading a chat is not proof that anything is cached server-side. | |
| 348 | + eventSource.on(event_types.CHAT_CHANGED, onChatChanged); | |
| 335 | - eventSource.on(event_types.CHAT_CHANGED, markActivity); | |
| 336 | 349 | |
| 337 | 350 | // When the active profile changes, re-target the idle timer at the new profile using |
| 338 | 351 | // ITS own last-activity time (do NOT mark activity — switching is not using it). |
| @@ -16,7 +16,7 @@ | ||
| 16 | 16 | <label for="keepalive_message" data-i18n="ext_keepalive_message">Keepalive message</label> |
| 17 | 17 | <input id="keepalive_message" class="text_pole" type="text" /> |
| 18 | 18 | |
| 19 | 19 | <small data-i18n="ext_keepalive_help">After this many seconds without activity on the active connection profile, the current chat plus this message (sent as a hidden user message) is submitted to that profile to keep its server-side prompt cache warm. The completion is capped to a single token and is never saved to the chat. Sending is briefly locked while it runs. Only the profile you're currently using is pinged; each profile has its own idle timer. Keepalive stays off until you've sent atsend leasta onenew message since openingin the tabcurrent chat (aopening, freshlyswitching loadedto, or reloading a chat isn't proof anythingits prompt is still cached), and it also skips a ping if its timer fired far later than scheduled (e.g. after the machine slept).</small> |
| 20 | 20 | |
| 21 | 21 | <hr> |
| 22 | 22 | |