From ced6d8f1192d3ecb0d0b6088b57d8ddd8335c5ff Mon Sep 17 00:00:00 2001 From: Julian lechner Date: Thu, 8 Oct 2026 12:42:05 +0200 Subject: [PATCH] fix(qmd): keep models loaded until the daemon's own idle exit QMD 2.8.3 createStore unloads the models after 5 idle minutes, which made the next search reload them. Co-Authored-By: Claude Opus 5.5 --- scripts/tests/test-qmd-daemon.mjs | 8 +++++++- shared/qmd/qmd-daemon.mjs | 9 +++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/scripts/tests/test-qmd-daemon.mjs b/scripts/tests/test-qmd-daemon.mjs index cbfee61..52366c8 100644 --- a/scripts/tests/test-qmd-daemon.mjs +++ b/scripts/tests/test-qmd-daemon.mjs @@ -5,7 +5,7 @@ import { execFileSync } from 'node:child_process'; import { mkdtempSync, rmSync, utimesSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join, resolve } from 'node:path'; -import { createServer, gitFingerprint } from '../../shared/qmd/qmd-daemon.mjs'; +import { createServer, gitFingerprint, keepModelsLoaded } from '../../shared/qmd/qmd-daemon.mjs'; import { collectionName } from '../../shared/qmd/qmd-lib.mjs'; const defaultHits = () => [{ displayPath: `${collectionName('/r')}/src/a.ts`, body: 'x\nretry here', bestChunkPos: 2, bestChunk: 'retry here', score: 0.91 }]; @@ -168,3 +168,9 @@ test('refresh, stop, 404 and 403 on POST routes', async (t) => { assert.deepEqual(await (await s.call('/stop', {})).json(), { stopping: true }); assert.equal(s.idle.length, 1); }); + +test('keepModelsLoaded disables QMD unloading models after 5 idle minutes', () => { + const llm = { inactivityTimeoutMs: 5 * 60 * 1000, disposeModelsOnInactivity: true }; + keepModelsLoaded({ internal: { llm } }); + assert.deepEqual(llm, { inactivityTimeoutMs: 0, disposeModelsOnInactivity: false }); +}); diff --git a/shared/qmd/qmd-daemon.mjs b/shared/qmd/qmd-daemon.mjs index 78ee4d9..3576fd3 100644 --- a/shared/qmd/qmd-daemon.mjs +++ b/shared/qmd/qmd-daemon.mjs @@ -27,6 +27,14 @@ export function gitFingerprint(repo) { } } +// QMD 2.8.3 createStore unloads models after 5 idle minutes (store.internal.llm); the daemon's own idle exit replaces that. +export function keepModelsLoaded(store) { + const llm = store.internal?.llm; + if (!llm) return; + llm.inactivityTimeoutMs = 0; + llm.disposeModelsOnInactivity = false; +} + const REPO_COLLECTION = /^repo-[0-9a-f]{12}$/; function tokenMatches(given, expected) { @@ -136,6 +144,7 @@ async function main() { } const { createStore, extractSnippet } = await importQmd(); const store = await createStore({ dbPath: qmdDbPath(), configPath: qmdConfigPath() }); + keepModelsLoaded(store); const collectionsFile = join(dir, 'collections.json'); const token = randomBytes(32).toString('hex'); let stopping = false;