Cache Keepalive: stay dormant until the first message/generation of the tab session

4b6db4ab8cabb9ea2efba3790631e37f4f6a1c64

permissionBRICK <permissionBRICK@gmail.com>

2 files changed, +30 -11Showing whitespace changes
public/scripts/extensions/keepalive/index.js+29 -10
@@ -47,6 +47,11 @@ const defaultSettings = {
4747
4848let idleTimer = null;
4949let 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;
5055// Per-profile "time since last message", keyed by connection profile id (or NONE_KEY).
5156// In-memory only: on reload every profile is treated as freshly active.
5257const lastActivity = {};
@@ -89,13 +94,14 @@ function clearIdleTimer() {
8994
9095/**
9196 * (Re)schedules the keepalive for the ACTIVE profile based on how long it has been since
9297 * that profile's last message. Does nothing (and leaves the timer cleared) whenuntil keepalive is
9398 * disabledhas been armed for thethis activetab connection.session, Calledor onwhen activityit ANDis wheneverdisabled for the active profileconnection.
9499 * Called on activity AND whenever the active profile changes, so each profile keeps its own independent idle clock.
100+ * independent idle clock.
95101 */
96102function scheduleIdleTimer() {
97103 clearIdleTimer();
98104 if (!armed || !isKeepaliveEnabledForActive()) {
99105 return;
100106 }
101107 const timeoutMs = Math.max(60, Number(extension_settings[MODULE].timeoutSeconds) || 295) * 1000;
@@ -113,6 +119,15 @@ function markActivity() {
113119}
114120
115121/**
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+/**
116131 * Fires a background ("quiet") generation against the active model to keep the
117132 * server-side prompt cache warm. The full prompt prefix (current chat context +
118133 * the keepalive message) is sent — which is what refreshes the cache — while the
@@ -132,7 +147,7 @@ function markActivity() {
132147 * deactivateSendButtons() reflects the busy state — and release both in `finally`.
133148 */
134149async function fireKeepalive() {
135150 if (!armed || !isKeepaliveEnabledForActive()) {
136151 return;
137152 }
138153
@@ -280,18 +295,22 @@ async function init() {
280295 loadSettings();
281296 setupListeners();
282297
283298 // 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 = [
285301 event_types.MESSAGE_SENT,
286302 event_types.MESSAGE_RECEIVED,
287303 event_types.GENERATION_STARTED,
288304 event_types.GENERATION_ENDED,
289- event_types.CHAT_CHANGED,
290305 ];
291306 for (const event of activityEventsactivationEvents) {
292307 eventSource.on(event, markActivityonChatActivity);
293308 }
294309
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+
295314 // When the active profile changes, re-target the idle timer at the new profile using
296315 // ITS own last-activity time (do NOT mark activity — switching is not using it).
297316 eventSource.on(event_types.CONNECTION_PROFILE_LOADED, scheduleIdleTimer);
public/scripts/extensions/keepalive/settings.html+1 -1
@@ -16,7 +16,7 @@
1616 <label for="keepalive_message" data-i18n="ext_keepalive_message">Keepalive message</label>
1717 <input id="keepalive_message" class="text_pole" type="text" />
1818
1919 <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>
2020
2121 <hr>
2222