Cache Keepalive: stay dormant until the first message/generation of the tab session
| @@ -47,6 +47,11 @@ const defaultSettings = { | ||
| 47 | 47 | |
| 48 | 48 | let idleTimer = null; |
| 49 | 49 | let keepaliveActive = false; |
| 50 | +// Whether keepalive has been "armed" this browser-tab session. It stays dormant until the | |
| 51 | +// first real message/generation happens in the tab, because on a fresh load (even with chat | |
| 52 | +// history already present) we have no way to know whether anything is still cached | |
| 53 | +// server-side — so pinging before then would be pointless. Resets on page reload. | |
| 54 | +let armed = false; | |
| 50 | 55 | // Per-profile "time since last message", keyed by connection profile id (or NONE_KEY). |
| 51 | 56 | // In-memory only: on reload every profile is treated as freshly active. |
| 52 | 57 | const lastActivity = {}; |
| @@ -89,13 +94,14 @@ function clearIdleTimer() { | ||
| 89 | 94 | |
| 90 | 95 | /** |
| 91 | 96 | * (Re)schedules the keepalive for the ACTIVE profile based on how long it has been since |
| 92 | 97 | * that profile's last message. Does nothing (and leaves the timer cleared) whenuntil keepalive is |
| 93 | 98 | * disabledhas been armed for thethis activetab connection.session, Calledor onwhen activityit ANDis wheneverdisabled for the active profileconnection. |
| 94 | 99 | * Called on activity AND whenever the active profile changes, so each profile keeps its own independent idle clock. |
| 100 | + * independent idle clock. | |
| 95 | 101 | */ |
| 96 | 102 | function scheduleIdleTimer() { |
| 97 | 103 | clearIdleTimer(); |
| 98 | 104 | if (!armed || !isKeepaliveEnabledForActive()) { |
| 99 | 105 | return; |
| 100 | 106 | } |
| 101 | 107 | const timeoutMs = Math.max(60, Number(extension_settings[MODULE].timeoutSeconds) || 295) * 1000; |
| @@ -113,6 +119,15 @@ function markActivity() { | ||
| 113 | 119 | } |
| 114 | 120 | |
| 115 | 121 | /** |
| 122 | + * Real message/generation activity: arms keepalive for this tab session (if it wasn't already) | |
| 123 | + * and records the activity. Loading a chat's history does NOT call this. | |
| 124 | + */ | |
| 125 | +function onChatActivity() { | |
| 126 | + armed = true; | |
| 127 | + markActivity(); | |
| 128 | +} | |
| 129 | + | |
| 130 | +/** | |
| 116 | 131 | * Fires a background ("quiet") generation against the active model to keep the |
| 117 | 132 | * server-side prompt cache warm. The full prompt prefix (current chat context + |
| 118 | 133 | * the keepalive message) is sent — which is what refreshes the cache — while the |
| @@ -132,7 +147,7 @@ function markActivity() { | ||
| 132 | 147 | * deactivateSendButtons() reflects the busy state — and release both in `finally`. |
| 133 | 148 | */ |
| 134 | 149 | async function fireKeepalive() { |
| 135 | 150 | if (!armed || !isKeepaliveEnabledForActive()) { |
| 136 | 151 | return; |
| 137 | 152 | } |
| 138 | 153 | |
| @@ -280,18 +295,22 @@ async function init() { | ||
| 280 | 295 | loadSettings(); |
| 281 | 296 | setupListeners(); |
| 282 | 297 | |
| 283 | 298 | // Any chatA orreal message/generation activityarms keepalive for this tab session and counts as "using" the active profile. |
| 284 | - const activityEvents = [ | |
| 299 | + // active profile. (Keepalive stays dormant until the first one — see `armed`.) | |
| 300 | + const activationEvents = [ | |
| 285 | 301 | event_types.MESSAGE_SENT, |
| 286 | 302 | event_types.MESSAGE_RECEIVED, |
| 287 | 303 | event_types.GENERATION_STARTED, |
| 288 | 304 | event_types.GENERATION_ENDED, |
| 289 | - event_types.CHAT_CHANGED, | |
| 290 | 305 | ]; |
| 291 | 306 | for (const event of activityEventsactivationEvents) { |
| 292 | 307 | eventSource.on(event, markActivityonChatActivity); |
| 293 | 308 | } |
| 294 | 309 | |
| 310 | + // Switching chats re-targets the per-profile idle clock but does NOT arm keepalive on its | |
| 311 | + // own — opening/loading a chat is not proof that anything is cached server-side. | |
| 312 | + eventSource.on(event_types.CHAT_CHANGED, markActivity); | |
| 313 | + | |
| 295 | 314 | // When the active profile changes, re-target the idle timer at the new profile using |
| 296 | 315 | // ITS own last-activity time (do NOT mark activity — switching is not using it). |
| 297 | 316 | eventSource.on(event_types.CONNECTION_PROFILE_LOADED, scheduleIdleTimer); |
| @@ -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 at least one message since opening the tab (a freshly loaded chat isn't proof anything is still cached).</small> |
| 20 | 20 | |
| 21 | 21 | <hr> |
| 22 | 22 | |