Cache Keepalive: send ping as a user message (depth-0 injection) and update default message
| @@ -4,9 +4,12 @@ import { | |||
| 4 | deactivateSendButtons, | 4 | deactivateSendButtons, |
| 5 | eventSource, | 5 | eventSource, |
| 6 | event_types, | 6 | event_types, |
| 7 | extension_prompt_roles, | ||
| 8 | extension_prompt_types, | ||
| 7 | generateQuietPrompt, | 9 | generateQuietPrompt, |
| 8 | isGenerating, | 10 | isGenerating, |
| 9 | saveSettingsDebounced, | 11 | saveSettingsDebounced, |
| 12 | setExtensionPrompt, | ||
| 10 | setSendButtonState, | 13 | setSendButtonState, |
| 11 | streamingProcessor, | 14 | streamingProcessor, |
| 12 | } from '../../../script.js'; | 15 | } from '../../../script.js'; |
| @@ -20,6 +23,12 @@ const MODULE = 'keepalive'; | |||
| 20 | // cache), but we never pay for a full completion. | 23 | // cache), but we never pay for a full completion. |
| 21 | const KEEPALIVE_RESPONSE_LENGTH = 1; | 24 | const KEEPALIVE_RESPONSE_LENGTH = 1; |
| 22 | 25 | ||
| 26 | // Extension-prompt key used to inject the keepalive message as a USER message at depth 0. | ||
| 27 | const KEEPALIVE_INJECT_ID = 'keepalive_ping'; | ||
| 28 | |||
| 29 | // Original default message; used to migrate untouched installs to the current default wording. | ||
| 30 | const LEGACY_DEFAULT_MESSAGE = 'Keep the cache warm.'; | ||
| 31 | |||
| 23 | // Idle-tracking key used when no connection profile is active (manual API panel). | 32 | // Idle-tracking key used when no connection profile is active (manual API panel). |
| 24 | const NONE_KEY = '__none__'; | 33 | const NONE_KEY = '__none__'; |
| 25 | 34 | ||
| @@ -31,7 +40,7 @@ const EXPIRED_GRACE_MS = 3000; | |||
| 31 | const defaultSettings = { | 40 | const defaultSettings = { |
| 32 | enabled: false, | 41 | enabled: false, |
| 33 | timeoutSeconds: 295, | 42 | timeoutSeconds: 295, |
| 34 | message: 'Keep the cache warm.', | 43 | message: '[Note: just reply with an empty response to keep the session alive]', |
| 35 | // Per-profile enable map: { [profileId]: boolean }. Unlisted profiles default to enabled. | 44 | // Per-profile enable map: { [profileId]: boolean }. Unlisted profiles default to enabled. |
| 36 | profiles: {}, | 45 | profiles: {}, |
| 37 | }; | 46 | }; |
| @@ -139,7 +148,12 @@ async function fireKeepalive() { | |||
| 139 | deactivateSendButtons(); | 148 | deactivateSendButtons(); |
| 140 | 149 | ||
| 141 | try { | 150 | try { |
| 142 | await generateQuietPrompt({ quietPrompt: extension_settings[MODULE].message, responseLength: KEEPALIVE_RESPONSE_LENGTH }); | 151 | // Deliver the keepalive as a USER message at depth 0 (the quiet-prompt path forces a |
| 152 | // system role — openai.js hardcodes role:'system' for it), using the same depth+role | ||
| 153 | // injection Author's Note uses. The quiet prompt itself stays empty so the full chat | ||
| 154 | // context is still assembled (which is what keeps the cached prefix warm). | ||
| 155 | setExtensionPrompt(KEEPALIVE_INJECT_ID, extension_settings[MODULE].message, extension_prompt_types.IN_CHAT, 0, false, extension_prompt_roles.USER); | ||
| 156 | await generateQuietPrompt({ quietPrompt: '', responseLength: KEEPALIVE_RESPONSE_LENGTH }); | ||
| 143 | // The ping kept this profile warm; treat it as fresh activity so the next | 157 | // The ping kept this profile warm; treat it as fresh activity so the next |
| 144 | // keepalive for it is a full timeout away. | 158 | // keepalive for it is a full timeout away. |
| 145 | lastActivity[getActiveProfileKey()] = Date.now(); | 159 | lastActivity[getActiveProfileKey()] = Date.now(); |
| @@ -147,6 +161,7 @@ async function fireKeepalive() { | |||
| 147 | // Best-effort: any failure here is harmless and intentionally swallowed. | 161 | // Best-effort: any failure here is harmless and intentionally swallowed. |
| 148 | console.debug('[Keepalive] Background generation ended:', error); | 162 | console.debug('[Keepalive] Background generation ended:', error); |
| 149 | } finally { | 163 | } finally { |
| 164 | setExtensionPrompt(KEEPALIVE_INJECT_ID, '', extension_prompt_types.IN_CHAT, 0, false, extension_prompt_roles.USER); | ||
| 150 | setSendButtonState(false); | 165 | setSendButtonState(false); |
| 151 | activateSendButtons(); | 166 | activateSendButtons(); |
| 152 | keepaliveActive = false; | 167 | keepaliveActive = false; |
| @@ -209,6 +224,13 @@ function loadSettings() { | |||
| 209 | extension_settings[MODULE].profiles = {}; | 224 | extension_settings[MODULE].profiles = {}; |
| 210 | } | 225 | } |
| 211 | 226 | ||
| 227 | // Migrate the original default message to the current default so installs that never | ||
| 228 | // customized it pick up the improved wording (custom messages are left untouched). | ||
| 229 | if (extension_settings[MODULE].message === LEGACY_DEFAULT_MESSAGE) { | ||
| 230 | extension_settings[MODULE].message = defaultSettings.message; | ||
| 231 | saveSettingsDebounced(); | ||
| 232 | } | ||
| 233 | |||
| 212 | $('#keepalive_enabled').prop('checked', extension_settings[MODULE].enabled); | 234 | $('#keepalive_enabled').prop('checked', extension_settings[MODULE].enabled); |
| 213 | $('#keepalive_timeout').val(extension_settings[MODULE].timeoutSeconds); | 235 | $('#keepalive_timeout').val(extension_settings[MODULE].timeoutSeconds); |
| 214 | $('#keepalive_message').val(extension_settings[MODULE].message); | 236 | $('#keepalive_message').val(extension_settings[MODULE].message); |
| @@ -16,7 +16,7 @@ | |||
| 16 | <label for="keepalive_message" data-i18n="ext_keepalive_message">Keepalive message</label> | 16 | <label for="keepalive_message" data-i18n="ext_keepalive_message">Keepalive message</label> |
| 17 | <input id="keepalive_message" class="text_pole" type="text" /> | 17 | <input id="keepalive_message" class="text_pole" type="text" /> |
| 18 | 18 | ||
| 19 | <small data-i18n="ext_keepalive_help">After this many seconds without activity on the active connection profile, a hidden background prompt (the current chat plus this message) is sent 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.</small> | 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.</small> |
| 20 | 20 | ||
| 21 | <hr> | 21 | <hr> |
| 22 | 22 | ||