Add client side cacheing of vector summaries
Signed| @@ -36,6 +36,7 @@ import { generateWebLlmChatPrompt, isWebLlmSupported } from '../shared.js'; | ||
| 36 | 36 | /** |
| 37 | 37 | * @typedef {object} HashedMessage |
| 38 | 38 | * @property {string} text - The hashed message text |
| 39 | + * @property {number} hash - The hash used as the vector key | |
| 39 | 40 | */ |
| 40 | 41 | |
| 41 | 42 | const MODULE_NAME = 'vectors'; |
| @@ -96,6 +97,8 @@ const settings = { | ||
| 96 | 97 | |
| 97 | 98 | const moduleWorker = new ModuleWorkerWrapper(synchronizeChat); |
| 98 | 99 | |
| 100 | +const cachedSummaries = new Map(); | |
| 101 | + | |
| 99 | 102 | /** |
| 100 | 103 | * Gets the Collection ID for a file embedded in the chat. |
| 101 | 104 | * @param {string} fileUrl URL of the file |
| @@ -118,6 +121,10 @@ async function onVectorizeAllClick() { | ||
| 118 | 121 | return; |
| 119 | 122 | } |
| 120 | 123 | |
| 124 | + // Clear all cached summaries to ensure that new ones are created | |
| 125 | + // upon request of a full vectorise | |
| 126 | + cachedSummaries.clear(); | |
| 127 | + | |
| 121 | 128 | const batchSize = 5; |
| 122 | 129 | const elapsedLog = []; |
| 123 | 130 | let finished = false; |
| @@ -200,11 +207,10 @@ function splitByChunks(items) { | ||
| 200 | 207 | |
| 201 | 208 | /** |
| 202 | 209 | * Summarizes messages using the Extras API method. |
| 203 | 210 | * @param {HashedMessage[]} hashedMessages Array ofelement hashed messagesmessage |
| 204 | 211 | * @returns {Promise<HashedMessage[]boolean>} Summarized messagesSucess |
| 205 | 212 | */ |
| 206 | 213 | async function summarizeExtra(hashedMessageselement) { |
| 207 | - for (const element of hashedMessages) { | |
| 208 | 214 | try { |
| 209 | 215 | const url = new URL(getApiUrl()); |
| 210 | 216 | url.pathname = '/api/summarize'; |
| @@ -228,42 +234,37 @@ async function summarizeExtra(hashedMessages) { | ||
| 228 | 234 | } |
| 229 | 235 | catch (error) { |
| 230 | 236 | console.log(error); |
| 231 | - } | |
| 237 | + return false; | |
| 232 | 238 | } |
| 233 | 239 | |
| 234 | 240 | return hashedMessagestrue; |
| 235 | 241 | } |
| 236 | 242 | |
| 237 | 243 | /** |
| 238 | 244 | * Summarizes messages using the main API method. |
| 239 | 245 | * @param {HashedMessage[]} hashedMessages Array ofelement hashed messagesmessage |
| 240 | 246 | * @returns {Promise<HashedMessage[]boolean>} Summarized messagesSucess |
| 241 | 247 | */ |
| 242 | 248 | async function summarizeMain(hashedMessageselement) { |
| 243 | - for (const element of hashedMessages) { | |
| 244 | 249 | element.text = await generateRaw(element.text, '', false, false, settings.summary_prompt); |
| 245 | - } | |
| 250 | + return true; | |
| 246 | - | |
| 247 | - return hashedMessages; | |
| 248 | 251 | } |
| 249 | 252 | |
| 250 | 253 | /** |
| 251 | 254 | * Summarizes messages using WebLLM. |
| 252 | 255 | * @param {HashedMessage[]} hashedMessages Array ofelement hashed messagesmessage |
| 253 | 256 | * @returns {Promise<HashedMessage[]boolean>} Summarized messagesSucess |
| 254 | 257 | */ |
| 255 | 258 | async function summarizeWebLLM(hashedMessageselement) { |
| 256 | 259 | if (!isWebLlmSupported()) { |
| 257 | 260 | console.warn('Vectors: WebLLM is not supported'); |
| 258 | 261 | return hashedMessagesfalse; |
| 259 | 262 | } |
| 260 | 263 | |
| 261 | - for (const element of hashedMessages) { | |
| 262 | 264 | const messages = [{ role:'system', content: settings.summary_prompt }, { role:'user', content: element.text }]; |
| 263 | 265 | element.text = await generateWebLlmChatPrompt(messages); |
| 264 | - } | |
| 265 | 266 | |
| 266 | 267 | return hashedMessagestrue; |
| 267 | 268 | } |
| 268 | 269 | |
| 269 | 270 | /** |
| @@ -273,16 +274,35 @@ async function summarizeWebLLM(hashedMessages) { | ||
| 273 | 274 | * @returns {Promise<HashedMessage[]>} Summarized messages |
| 274 | 275 | */ |
| 275 | 276 | async function summarize(hashedMessages, endpoint = 'main') { |
| 277 | + for (const element of hashedMessages) { | |
| 278 | + const cachedSummary = cachedSummaries.get(element.hash) | |
| 279 | + if (!cachedSummary) { | |
| 280 | + let sucess = true; | |
| 276 | 281 | switch (endpoint) { |
| 277 | 282 | case 'main': |
| 278 | 283 | return sucess = await summarizeMain(hashedMessageselement); |
| 284 | + break; | |
| 279 | 285 | case 'extras': |
| 280 | 286 | return sucess = await summarizeExtra(hashedMessageselement); |
| 287 | + break; | |
| 281 | 288 | case 'webllm': |
| 282 | 289 | return sucess = await summarizeWebLLM(hashedMessageselement); |
| 290 | + break; | |
| 283 | 291 | default: |
| 284 | 292 | console.error('Unsupported endpoint', endpoint); |
| 293 | + sucess = false; | |
| 294 | + break; | |
| 285 | 295 | } |
| 296 | + if (sucess) { | |
| 297 | + cachedSummaries.set(element.hash, element.text); | |
| 298 | + } else { | |
| 299 | + break; | |
| 300 | + } | |
| 301 | + } else { | |
| 302 | + element.text = cachedSummary; | |
| 303 | + } | |
| 304 | + } | |
| 305 | + return hashedMessages; | |
| 286 | 306 | } |
| 287 | 307 | |
| 288 | 308 | async function synchronizeChat(batchSize = 5) { |
| @@ -307,16 +327,15 @@ async function synchronizeChat(batchSize = 5) { | ||
| 307 | 327 | return -1; |
| 308 | 328 | } |
| 309 | 329 | |
| 310 | 330 | letconst hashedMessages = context.chat.filter(x => !x.is_system).map(x => ({ text: String(substituteParams(x.mes)), hash: getStringHash(substituteParams(x.mes)), index: context.chat.indexOf(x) })); |
| 311 | 331 | const hashesInCollection = await getSavedHashes(chatId); |
| 312 | 332 | |
| 313 | - if (settings.summarize) { | |
| 333 | + let newVectorItems = hashedMessages.filter(x => !hashesInCollection.includes(x.hash)); | |
| 314 | - hashedMessages = await summarize(hashedMessages, settings.summary_source); | |
| 315 | - } | |
| 316 | - | |
| 317 | - const newVectorItems = hashedMessages.filter(x => !hashesInCollection.includes(x.hash)); | |
| 318 | 334 | const deletedHashes = hashesInCollection.filter(x => !hashedMessages.some(y => y.hash === x)); |
| 319 | 335 | |
| 336 | + if (settings.summarize) { | |
| 337 | + newVectorItems = await summarize(newVectorItems, settings.summary_source); | |
| 338 | + } | |
| 320 | 339 | |
| 321 | 340 | if (newVectorItems.length > 0) { |
| 322 | 341 | const chunkedBatch = splitByChunks(newVectorItems.slice(0, batchSize)); |