@tobilu/qmd 1.1.6 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/README.md +198 -39
- package/bin/qmd +23 -0
- package/dist/{formatter.d.ts → cli/formatter.d.ts} +1 -1
- package/dist/{formatter.js → cli/formatter.js} +1 -1
- package/dist/{qmd.js → cli/qmd.js} +266 -154
- package/dist/embedded-skills.d.ts +6 -0
- package/dist/embedded-skills.js +14 -0
- package/dist/index.d.ts +129 -38
- package/dist/index.js +175 -41
- package/dist/llm.d.ts +6 -0
- package/dist/llm.js +24 -1
- package/dist/maintenance.d.ts +23 -0
- package/dist/maintenance.js +37 -0
- package/dist/{mcp.js → mcp/server.js} +41 -61
- package/dist/store.d.ts +83 -19
- package/dist/store.js +561 -84
- package/package.json +12 -11
- /package/dist/{qmd.d.ts → cli/qmd.d.ts} +0 -0
- /package/dist/{mcp.d.ts → mcp/server.d.ts} +0 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Generated from skills/qmd source files. Keep this in sync when updating the packaged skill.
|
|
2
|
+
const EMBEDDED_QMD_SKILL_BASE64 = {
|
|
3
|
+
"SKILL.md": "LS0tCm5hbWU6IHFtZApkZXNjcmlwdGlvbjogU2VhcmNoIG1hcmtkb3duIGtub3dsZWRnZSBiYXNlcywgbm90ZXMsIGFuZCBkb2N1bWVudGF0aW9uIHVzaW5nIFFNRC4gVXNlIHdoZW4gdXNlcnMgYXNrIHRvIHNlYXJjaCBub3RlcywgZmluZCBkb2N1bWVudHMsIG9yIGxvb2sgdXAgaW5mb3JtYXRpb24uCmxpY2Vuc2U6IE1JVApjb21wYXRpYmlsaXR5OiBSZXF1aXJlcyBxbWQgQ0xJIG9yIE1DUCBzZXJ2ZXIuIEluc3RhbGwgdmlhIGBucG0gaW5zdGFsbCAtZyBAdG9iaWx1L3FtZGAuCm1ldGFkYXRhOgogIGF1dGhvcjogdG9iaQogIHZlcnNpb246ICIyLjAuMCIKYWxsb3dlZC10b29sczogQmFzaChxbWQ6KiksIG1jcF9fcW1kX18qCi0tLQoKIyBRTUQgLSBRdWljayBNYXJrZG93biBTZWFyY2gKCkxvY2FsIHNlYXJjaCBlbmdpbmUgZm9yIG1hcmtkb3duIGNvbnRlbnQuCgojIyBTdGF0dXMKCiFgcW1kIHN0YXR1cyAyPi9kZXYvbnVsbCB8fCBlY2hvICJOb3QgaW5zdGFsbGVkOiBucG0gaW5zdGFsbCAtZyBAdG9iaWx1L3FtZCJgCgojIyBNQ1A6IGBxdWVyeWAKCmBgYGpzb24KewogICJzZWFyY2hlcyI6IFsKICAgIHsgInR5cGUiOiAibGV4IiwgInF1ZXJ5IjogIkNBUCB0aGVvcmVtIGNvbnNpc3RlbmN5IiB9LAogICAgeyAidHlwZSI6ICJ2ZWMiLCAicXVlcnkiOiAidHJhZGVvZmYgYmV0d2VlbiBjb25zaXN0ZW5jeSBhbmQgYXZhaWxhYmlsaXR5IiB9CiAgXSwKICAiY29sbGVjdGlvbnMiOiBbImRvY3MiXSwKICAibGltaXQiOiAxMAp9CmBgYAoKIyMjIFF1ZXJ5IFR5cGVzCgp8IFR5cGUgfCBNZXRob2QgfCBJbnB1dCB8CnwtLS0tLS18LS0tLS0tLS18LS0tLS0tLXwKfCBgbGV4YCB8IEJNMjUgfCBLZXl3b3JkcyDigJQgZXhhY3QgdGVybXMsIG5hbWVzLCBjb2RlIHwKfCBgdmVjYCB8IFZlY3RvciB8IFF1ZXN0aW9uIOKAlCBuYXR1cmFsIGxhbmd1YWdlIHwKfCBgaHlkZWAgfCBWZWN0b3IgfCBBbnN3ZXIg4oCUIGh5cG90aGV0aWNhbCByZXN1bHQgKDUwLTEwMCB3b3JkcykgfAoKIyMjIFdyaXRpbmcgR29vZCBRdWVyaWVzCgoqKmxleCAoa2V5d29yZCkqKgotIDItNSB0ZXJtcywgbm8gZmlsbGVyIHdvcmRzCi0gRXhhY3QgcGhyYXNlOiBgImNvbm5lY3Rpb24gcG9vbCJgIChxdW90ZWQpCi0gRXhjbHVkZSB0ZXJtczogYHBlcmZvcm1hbmNlIC1zcG9ydHNgIChtaW51cyBwcmVmaXgpCi0gQ29kZSBpZGVudGlmaWVycyB3b3JrOiBgaGFuZGxlRXJyb3IgYXN5bmNgCgoqKnZlYyAoc2VtYW50aWMpKioKLSBGdWxsIG5hdHVyYWwgbGFuZ3VhZ2UgcXVlc3Rpb24KLSBCZSBzcGVjaWZpYzogYCJob3cgZG9lcyB0aGUgcmF0ZSBsaW1pdGVyIGhhbmRsZSBidXJzdCB0cmFmZmljImAKLSBJbmNsdWRlIGNvbnRleHQ6IGAiaW4gdGhlIHBheW1lbnQgc2VydmljZSwgaG93IGFyZSByZWZ1bmRzIHByb2Nlc3NlZCJgCgoqKmh5ZGUgKGh5cG90aGV0aWNhbCBkb2N1bWVudCkqKgotIFdyaXRlIDUwLTEwMCB3b3JkcyBvZiB3aGF0IHRoZSAqYW5zd2VyKiBsb29rcyBsaWtlCi0gVXNlIHRoZSB2b2NhYnVsYXJ5IHlvdSBleHBlY3QgaW4gdGhlIHJlc3VsdAoKKipleHBhbmQgKGF1dG8tZXhwYW5kKSoqCi0gVXNlIGEgc2luZ2xlLWxpbmUgcXVlcnkgKGltcGxpY2l0KSBvciBgZXhwYW5kOiBxdWVzdGlvbmAgb24gaXRzIG93biBsaW5lCi0gTGV0cyB0aGUgbG9jYWwgTExNIGdlbmVyYXRlIGxleC92ZWMvaHlkZSB2YXJpYXRpb25zCi0gRG8gbm90IG1peCBgZXhwYW5kOmAgd2l0aCBvdGhlciB0eXBlZCBsaW5lcyDigJQgaXQncyBlaXRoZXIgYSBzdGFuZGFsb25lIGV4cGFuZCBxdWVyeSBvciBhIGZ1bGwgcXVlcnkgZG9jdW1lbnQKCiMjIyBJbnRlbnQgKERpc2FtYmlndWF0aW9uKQoKV2hlbiBhIHF1ZXJ5IHRlcm0gaXMgYW1iaWd1b3VzLCBhZGQgYGludGVudGAgdG8gc3RlZXIgcmVzdWx0czoKCmBgYGpzb24KewogICJzZWFyY2hlcyI6IFsKICAgIHsgInR5cGUiOiAibGV4IiwgInF1ZXJ5IjogInBlcmZvcm1hbmNlIiB9CiAgXSwKICAiaW50ZW50IjogIndlYiBwYWdlIGxvYWQgdGltZXMgYW5kIENvcmUgV2ViIFZpdGFscyIKfQpgYGAKCkludGVudCBhZmZlY3RzIGV4cGFuc2lvbiwgcmVyYW5raW5nLCBjaHVuayBzZWxlY3Rpb24sIGFuZCBzbmlwcGV0IGV4dHJhY3Rpb24uIEl0IGRvZXMgbm90IHNlYXJjaCBvbiBpdHMgb3duIOKAlCBpdCdzIGEgc3RlZXJpbmcgc2lnbmFsIHRoYXQgZGlzYW1iaWd1YXRlcyBxdWVyaWVzIGxpa2UgInBlcmZvcm1hbmNlIiAod2ViLXBlcmYgdnMgdGVhbSBoZWFsdGggdnMgZml0bmVzcykuCgojIyMgQ29tYmluaW5nIFR5cGVzCgp8IEdvYWwgfCBBcHByb2FjaCB8CnwtLS0tLS18LS0tLS0tLS0tLXwKfCBLbm93IGV4YWN0IHRlcm1zIHwgYGxleGAgb25seSB8CnwgRG9uJ3Qga25vdyB2b2NhYnVsYXJ5IHwgVXNlIGEgc2luZ2xlLWxpbmUgcXVlcnkgKGltcGxpY2l0IGBleHBhbmQ6YCkgb3IgYHZlY2AgfAp8IEJlc3QgcmVjYWxsIHwgYGxleGAgKyBgdmVjYCB8CnwgQ29tcGxleCB0b3BpYyB8IGBsZXhgICsgYHZlY2AgKyBgaHlkZWAgfAp8IEFtYmlndW91cyBxdWVyeSB8IEFkZCBgaW50ZW50YCB0byBhbnkgY29tYmluYXRpb24gYWJvdmUgfAoKRmlyc3QgcXVlcnkgZ2V0cyAyeCB3ZWlnaHQgaW4gZnVzaW9uIOKAlCBwdXQgeW91ciBiZXN0IGd1ZXNzIGZpcnN0LgoKIyMjIExleCBRdWVyeSBTeW50YXgKCnwgU3ludGF4IHwgTWVhbmluZyB8IEV4YW1wbGUgfAp8LS0tLS0tLS18LS0tLS0tLS0tfC0tLS0tLS0tLXwKfCBgdGVybWAgfCBQcmVmaXggbWF0Y2ggfCBgcGVyZmAgbWF0Y2hlcyAicGVyZm9ybWFuY2UiIHwKfCBgInBocmFzZSJgIHwgRXhhY3QgcGhyYXNlIHwgYCJyYXRlIGxpbWl0ZXIiYCB8CnwgYC10ZXJtYCB8IEV4Y2x1ZGUgfCBgcGVyZm9ybWFuY2UgLXNwb3J0c2AgfAoKTm90ZTogYC10ZXJtYCBvbmx5IHdvcmtzIGluIGxleCBxdWVyaWVzLCBub3QgdmVjL2h5ZGUuCgojIyMgQ29sbGVjdGlvbiBGaWx0ZXJpbmcKCmBgYGpzb24KeyAiY29sbGVjdGlvbnMiOiBbImRvY3MiXSB9ICAgICAgICAgICAgICAvLyBTaW5nbGUKeyAiY29sbGVjdGlvbnMiOiBbImRvY3MiLCAibm90ZXMiXSB9ICAgICAvLyBNdWx0aXBsZSAoT1IpCmBgYAoKT21pdCB0byBzZWFyY2ggYWxsIGNvbGxlY3Rpb25zLgoKIyMgT3RoZXIgTUNQIFRvb2xzCgp8IFRvb2wgfCBVc2UgfAp8LS0tLS0tfC0tLS0tfAp8IGBnZXRgIHwgUmV0cmlldmUgZG9jIGJ5IHBhdGggb3IgYCNkb2NpZGAgfAp8IGBtdWx0aV9nZXRgIHwgUmV0cmlldmUgbXVsdGlwbGUgYnkgZ2xvYi9saXN0IHwKfCBgc3RhdHVzYCB8IENvbGxlY3Rpb25zIGFuZCBoZWFsdGggfAoKIyMgQ0xJCgpgYGBiYXNoCnFtZCBxdWVyeSAicXVlc3Rpb24iICAgICAgICAgICAgICAjIEF1dG8tZXhwYW5kICsgcmVyYW5rCnFtZCBxdWVyeSAkJ2xleDogWFxudmVjOiBZJyAgICAgICAjIFN0cnVjdHVyZWQKcW1kIHF1ZXJ5ICQnZXhwYW5kOiBxdWVzdGlvbicgICAgICMgRXhwbGljaXQgZXhwYW5kCnFtZCBxdWVyeSAtLWpzb24gLS1leHBsYWluICJxIiAgICAjIFNob3cgc2NvcmUgdHJhY2VzIChSUkYgKyByZXJhbmsgYmxlbmQpCnFtZCBzZWFyY2ggImtleXdvcmRzIiAgICAgICAgICAgICAjIEJNMjUgb25seSAobm8gTExNKQpxbWQgZ2V0ICIjYWJjMTIzIiAgICAgICAgICAgICAgICAgIyBCeSBkb2NpZApxbWQgbXVsdGktZ2V0ICJqb3VybmFscy8yMDI2LSoubWQiIC1sIDQwICAjIEJhdGNoIHB1bGwgc25pcHBldHMgYnkgZ2xvYgpxbWQgbXVsdGktZ2V0IG5vdGVzL2Zvby5tZCxub3Rlcy9iYXIubWQgICAjIENvbW1hLXNlcGFyYXRlZCBsaXN0LCBwcmVzZXJ2ZXMgb3JkZXIKYGBgCgojIyBIVFRQIEFQSQoKYGBgYmFzaApjdXJsIC1YIFBPU1QgaHR0cDovL2xvY2FsaG9zdDo4MTgxL3F1ZXJ5IFwKICAtSCAiQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi9qc29uIiBcCiAgLWQgJ3sic2VhcmNoZXMiOiBbeyJ0eXBlIjogImxleCIsICJxdWVyeSI6ICJ0ZXN0In1dfScKYGBgCgojIyBTZXR1cAoKYGBgYmFzaApucG0gaW5zdGFsbCAtZyBAdG9iaWx1L3FtZApxbWQgY29sbGVjdGlvbiBhZGQgfi9ub3RlcyAtLW5hbWUgbm90ZXMKcW1kIGVtYmVkCmBgYAo=",
|
|
4
|
+
"references/mcp-setup.md": "IyBRTUQgTUNQIFNlcnZlciBTZXR1cAoKIyMgSW5zdGFsbAoKYGBgYmFzaApucG0gaW5zdGFsbCAtZyBAdG9iaWx1L3FtZApxbWQgY29sbGVjdGlvbiBhZGQgfi9wYXRoL3RvL21hcmtkb3duIC0tbmFtZSBteWtub3dsZWRnZQpxbWQgZW1iZWQKYGBgCgojIyBDb25maWd1cmUgTUNQIENsaWVudAoKKipDbGF1ZGUgQ29kZSoqIChgfi8uY2xhdWRlL3NldHRpbmdzLmpzb25gKToKYGBganNvbgp7CiAgIm1jcFNlcnZlcnMiOiB7CiAgICAicW1kIjogeyAiY29tbWFuZCI6ICJxbWQiLCAiYXJncyI6IFsibWNwIl0gfQogIH0KfQpgYGAKCioqQ2xhdWRlIERlc2t0b3AqKiAoYH4vTGlicmFyeS9BcHBsaWNhdGlvbiBTdXBwb3J0L0NsYXVkZS9jbGF1ZGVfZGVza3RvcF9jb25maWcuanNvbmApOgpgYGBqc29uCnsKICAibWNwU2VydmVycyI6IHsKICAgICJxbWQiOiB7ICJjb21tYW5kIjogInFtZCIsICJhcmdzIjogWyJtY3AiXSB9CiAgfQp9CmBgYAoKKipPcGVuQ2xhdyoqIChgfi8ub3BlbmNsYXcvb3BlbmNsYXcuanNvbmApOgpgYGBqc29uCnsKICAibWNwIjogewogICAgInNlcnZlcnMiOiB7CiAgICAgICJxbWQiOiB7ICJjb21tYW5kIjogInFtZCIsICJhcmdzIjogWyJtY3AiXSB9CiAgICB9CiAgfQp9CmBgYAoKIyMgSFRUUCBNb2RlCgpgYGBiYXNoCnFtZCBtY3AgLS1odHRwICAgICAgICAgICAgICAjIFBvcnQgODE4MQpxbWQgbWNwIC0taHR0cCAtLWRhZW1vbiAgICAgIyBCYWNrZ3JvdW5kCnFtZCBtY3Agc3RvcCAgICAgICAgICAgICAgICAjIFN0b3AgZGFlbW9uCmBgYAoKIyMgVG9vbHMKCiMjIyBzdHJ1Y3R1cmVkX3NlYXJjaAoKU2VhcmNoIHdpdGggcHJlLWV4cGFuZGVkIHF1ZXJpZXMuCgpgYGBqc29uCnsKICAic2VhcmNoZXMiOiBbCiAgICB7ICJ0eXBlIjogImxleCIsICJxdWVyeSI6ICJrZXl3b3JkIHBocmFzZXMiIH0sCiAgICB7ICJ0eXBlIjogInZlYyIsICJxdWVyeSI6ICJuYXR1cmFsIGxhbmd1YWdlIHF1ZXN0aW9uIiB9LAogICAgeyAidHlwZSI6ICJoeWRlIiwgInF1ZXJ5IjogImh5cG90aGV0aWNhbCBhbnN3ZXIgcGFzc2FnZS4uLiIgfQogIF0sCiAgImxpbWl0IjogMTAsCiAgImNvbGxlY3Rpb24iOiAib3B0aW9uYWwiLAogICJtaW5TY29yZSI6IDAuMAp9CmBgYAoKfCBUeXBlIHwgTWV0aG9kIHwgSW5wdXQgfAp8LS0tLS0tfC0tLS0tLS0tfC0tLS0tLS18CnwgYGxleGAgfCBCTTI1IHwgS2V5d29yZHMgKDItNSB0ZXJtcykgfAp8IGB2ZWNgIHwgVmVjdG9yIHwgUXVlc3Rpb24gfAp8IGBoeWRlYCB8IFZlY3RvciB8IEFuc3dlciBwYXNzYWdlICg1MC0xMDAgd29yZHMpIHwKCiMjIyBnZXQKClJldHJpZXZlIGRvY3VtZW50IGJ5IHBhdGggb3IgYCNkb2NpZGAuCgp8IFBhcmFtIHwgVHlwZSB8IERlc2NyaXB0aW9uIHwKfC0tLS0tLS18LS0tLS0tfC0tLS0tLS0tLS0tLS18CnwgYHBhdGhgIHwgc3RyaW5nIHwgRmlsZSBwYXRoIG9yIGAjZG9jaWRgIHwKfCBgZnVsbGAgfCBib29sPyB8IFJldHVybiBmdWxsIGNvbnRlbnQgfAp8IGBsaW5lTnVtYmVyc2AgfCBib29sPyB8IEFkZCBsaW5lIG51bWJlcnMgfAoKIyMjIG11bHRpX2dldAoKUmV0cmlldmUgbXVsdGlwbGUgZG9jdW1lbnRzLgoKfCBQYXJhbSB8IFR5cGUgfCBEZXNjcmlwdGlvbiB8CnwtLS0tLS0tfC0tLS0tLXwtLS0tLS0tLS0tLS0tfAp8IGBwYXR0ZXJuYCB8IHN0cmluZyB8IEdsb2Igb3IgY29tbWEtc2VwYXJhdGVkIGxpc3QgfAp8IGBtYXhCeXRlc2AgfCBudW1iZXI/IHwgU2tpcCBsYXJnZSBmaWxlcyAoZGVmYXVsdCAxMEtCKSB8CgojIyMgc3RhdHVzCgpJbmRleCBoZWFsdGggYW5kIGNvbGxlY3Rpb25zLiBObyBwYXJhbXMuCgojIyBUcm91Ymxlc2hvb3RpbmcKCi0gKipOb3Qgc3RhcnRpbmcqKjogYHdoaWNoIHFtZGAsIGBxbWQgbWNwYCBtYW51YWxseQotICoqTm8gcmVzdWx0cyoqOiBgcW1kIGNvbGxlY3Rpb24gbGlzdGAsIGBxbWQgZW1iZWRgCi0gKipTbG93IGZpcnN0IHNlYXJjaCoqOiBOb3JtYWwsIG1vZGVscyBsb2FkaW5nICh+M0dCKQo="
|
|
5
|
+
};
|
|
6
|
+
export function getEmbeddedQmdSkillFiles() {
|
|
7
|
+
return Object.entries(EMBEDDED_QMD_SKILL_BASE64).map(([relativePath, encoded]) => ({
|
|
8
|
+
relativePath,
|
|
9
|
+
content: Buffer.from(encoded, 'base64').toString('utf8'),
|
|
10
|
+
}));
|
|
11
|
+
}
|
|
12
|
+
export function getEmbeddedQmdSkillContent() {
|
|
13
|
+
return Buffer.from(EMBEDDED_QMD_SKILL_BASE64["SKILL.md"], "base64").toString("utf8");
|
|
14
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Usage:
|
|
5
5
|
* import { createStore } from '@tobilu/qmd'
|
|
6
6
|
*
|
|
7
|
-
* const store = createStore({
|
|
7
|
+
* const store = await createStore({
|
|
8
8
|
* dbPath: './my-index.sqlite',
|
|
9
9
|
* config: {
|
|
10
10
|
* collections: {
|
|
@@ -13,15 +13,85 @@
|
|
|
13
13
|
* }
|
|
14
14
|
* })
|
|
15
15
|
*
|
|
16
|
-
* const results = await store.query
|
|
17
|
-
* store.close()
|
|
16
|
+
* const results = await store.search({ query: "how does auth work?" })
|
|
17
|
+
* await store.close()
|
|
18
18
|
*/
|
|
19
|
-
import { type Store as InternalStore, type DocumentResult, type DocumentNotFound, type SearchResult, type HybridQueryResult, type HybridQueryOptions, type HybridQueryExplain, type
|
|
19
|
+
import { extractSnippet, addLineNumbers, DEFAULT_MULTI_GET_MAX_BYTES, type Store as InternalStore, type DocumentResult, type DocumentNotFound, type SearchResult, type HybridQueryResult, type HybridQueryOptions, type HybridQueryExplain, type ExpandedQuery, type StructuredSearchOptions, type MultiGetResult, type IndexStatus, type IndexHealthInfo, type SearchHooks, type ReindexProgress, type ReindexResult, type EmbedProgress, type EmbedResult } from "./store.js";
|
|
20
20
|
import { type Collection, type CollectionConfig, type NamedCollection, type ContextMap } from "./collections.js";
|
|
21
|
-
export type { DocumentResult, DocumentNotFound, SearchResult, HybridQueryResult, HybridQueryOptions, HybridQueryExplain,
|
|
21
|
+
export type { DocumentResult, DocumentNotFound, SearchResult, HybridQueryResult, HybridQueryOptions, HybridQueryExplain, ExpandedQuery, StructuredSearchOptions, MultiGetResult, IndexStatus, IndexHealthInfo, SearchHooks, ReindexProgress, ReindexResult, EmbedProgress, EmbedResult, Collection, CollectionConfig, NamedCollection, ContextMap, };
|
|
22
|
+
export type { InternalStore };
|
|
23
|
+
export { extractSnippet, addLineNumbers, DEFAULT_MULTI_GET_MAX_BYTES };
|
|
24
|
+
export { getDefaultDbPath } from "./store.js";
|
|
25
|
+
export { Maintenance } from "./maintenance.js";
|
|
26
|
+
/**
|
|
27
|
+
* Progress info emitted during update() for each file processed.
|
|
28
|
+
*/
|
|
29
|
+
export type UpdateProgress = {
|
|
30
|
+
collection: string;
|
|
31
|
+
file: string;
|
|
32
|
+
current: number;
|
|
33
|
+
total: number;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Aggregated result from update() across all collections.
|
|
37
|
+
*/
|
|
38
|
+
export type UpdateResult = {
|
|
39
|
+
collections: number;
|
|
40
|
+
indexed: number;
|
|
41
|
+
updated: number;
|
|
42
|
+
unchanged: number;
|
|
43
|
+
removed: number;
|
|
44
|
+
needsEmbedding: number;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Options for the unified search() method.
|
|
48
|
+
*/
|
|
49
|
+
export interface SearchOptions {
|
|
50
|
+
/** Simple query string — will be auto-expanded via LLM */
|
|
51
|
+
query?: string;
|
|
52
|
+
/** Pre-expanded queries (from expandQuery) — skips auto-expansion */
|
|
53
|
+
queries?: ExpandedQuery[];
|
|
54
|
+
/** Domain intent hint — steers expansion and reranking */
|
|
55
|
+
intent?: string;
|
|
56
|
+
/** Rerank results using LLM (default: true) */
|
|
57
|
+
rerank?: boolean;
|
|
58
|
+
/** Filter to a specific collection */
|
|
59
|
+
collection?: string;
|
|
60
|
+
/** Filter to specific collections */
|
|
61
|
+
collections?: string[];
|
|
62
|
+
/** Max results (default: 10) */
|
|
63
|
+
limit?: number;
|
|
64
|
+
/** Minimum score threshold */
|
|
65
|
+
minScore?: number;
|
|
66
|
+
/** Include explain traces */
|
|
67
|
+
explain?: boolean;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Options for searchLex() — BM25 keyword search.
|
|
71
|
+
*/
|
|
72
|
+
export interface LexSearchOptions {
|
|
73
|
+
limit?: number;
|
|
74
|
+
collection?: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Options for searchVector() — vector similarity search.
|
|
78
|
+
*/
|
|
79
|
+
export interface VectorSearchOptions {
|
|
80
|
+
limit?: number;
|
|
81
|
+
collection?: string;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Options for expandQuery() — manual query expansion.
|
|
85
|
+
*/
|
|
86
|
+
export interface ExpandQueryOptions {
|
|
87
|
+
intent?: string;
|
|
88
|
+
}
|
|
22
89
|
/**
|
|
23
90
|
* Options for creating a QMD store.
|
|
24
|
-
*
|
|
91
|
+
*
|
|
92
|
+
* Provide `dbPath` and optionally `configPath` (YAML file) or `config` (inline).
|
|
93
|
+
* If neither configPath nor config is provided, the store reads from existing
|
|
94
|
+
* DB state (useful for reopening a previously-configured store).
|
|
25
95
|
*/
|
|
26
96
|
export interface StoreOptions {
|
|
27
97
|
/** Path to the SQLite database file */
|
|
@@ -34,72 +104,93 @@ export interface StoreOptions {
|
|
|
34
104
|
/**
|
|
35
105
|
* The QMD SDK store — provides search, retrieval, collection management,
|
|
36
106
|
* context management, and indexing operations.
|
|
107
|
+
*
|
|
108
|
+
* All methods are async. The store manages its own LlamaCpp instance
|
|
109
|
+
* (lazy-loaded, auto-unloaded after inactivity) — no global singletons.
|
|
37
110
|
*/
|
|
38
111
|
export interface QMDStore {
|
|
39
112
|
/** The underlying internal store (for advanced use) */
|
|
40
113
|
readonly internal: InternalStore;
|
|
41
114
|
/** Path to the SQLite database */
|
|
42
115
|
readonly dbPath: string;
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
/** BM25
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
structuredSearch(searches: StructuredSubSearch[], options?: StructuredSearchOptions): Promise<HybridQueryResult[]>;
|
|
116
|
+
/** Full search: query expansion + multi-signal retrieval + LLM reranking */
|
|
117
|
+
search(options: SearchOptions): Promise<HybridQueryResult[]>;
|
|
118
|
+
/** BM25 keyword search (fast, no LLM) */
|
|
119
|
+
searchLex(query: string, options?: LexSearchOptions): Promise<SearchResult[]>;
|
|
120
|
+
/** Vector similarity search (embedding model, no reranking) */
|
|
121
|
+
searchVector(query: string, options?: VectorSearchOptions): Promise<SearchResult[]>;
|
|
122
|
+
/** Expand a query into typed sub-searches (lex/vec/hyde) for manual control */
|
|
123
|
+
expandQuery(query: string, options?: ExpandQueryOptions): Promise<ExpandedQuery[]>;
|
|
52
124
|
/** Get a single document by path or docid */
|
|
53
125
|
get(pathOrDocid: string, options?: {
|
|
54
126
|
includeBody?: boolean;
|
|
55
|
-
}): DocumentResult | DocumentNotFound
|
|
127
|
+
}): Promise<DocumentResult | DocumentNotFound>;
|
|
128
|
+
/** Get the body content of a document, optionally sliced by line range */
|
|
129
|
+
getDocumentBody(pathOrDocid: string, opts?: {
|
|
130
|
+
fromLine?: number;
|
|
131
|
+
maxLines?: number;
|
|
132
|
+
}): Promise<string | null>;
|
|
56
133
|
/** Get multiple documents by glob pattern or comma-separated list */
|
|
57
134
|
multiGet(pattern: string, options?: {
|
|
58
135
|
includeBody?: boolean;
|
|
59
136
|
maxBytes?: number;
|
|
60
|
-
}): {
|
|
137
|
+
}): Promise<{
|
|
61
138
|
docs: MultiGetResult[];
|
|
62
139
|
errors: string[];
|
|
63
|
-
}
|
|
140
|
+
}>;
|
|
64
141
|
/** Add or update a collection */
|
|
65
142
|
addCollection(name: string, opts: {
|
|
66
143
|
path: string;
|
|
67
144
|
pattern?: string;
|
|
68
145
|
ignore?: string[];
|
|
69
|
-
}): void
|
|
146
|
+
}): Promise<void>;
|
|
70
147
|
/** Remove a collection */
|
|
71
|
-
removeCollection(name: string): boolean
|
|
148
|
+
removeCollection(name: string): Promise<boolean>;
|
|
72
149
|
/** Rename a collection */
|
|
73
|
-
renameCollection(oldName: string, newName: string): boolean
|
|
150
|
+
renameCollection(oldName: string, newName: string): Promise<boolean>;
|
|
74
151
|
/** List all collections with document stats */
|
|
75
|
-
listCollections(): {
|
|
152
|
+
listCollections(): Promise<{
|
|
76
153
|
name: string;
|
|
77
154
|
pwd: string;
|
|
78
155
|
glob_pattern: string;
|
|
79
156
|
doc_count: number;
|
|
80
157
|
active_count: number;
|
|
81
158
|
last_modified: string | null;
|
|
82
|
-
|
|
159
|
+
includeByDefault: boolean;
|
|
160
|
+
}[]>;
|
|
161
|
+
/** Get names of collections included by default in queries */
|
|
162
|
+
getDefaultCollectionNames(): Promise<string[]>;
|
|
83
163
|
/** Add context for a path within a collection */
|
|
84
|
-
addContext(collectionName: string, pathPrefix: string, contextText: string): boolean
|
|
164
|
+
addContext(collectionName: string, pathPrefix: string, contextText: string): Promise<boolean>;
|
|
85
165
|
/** Remove context from a collection path */
|
|
86
|
-
removeContext(collectionName: string, pathPrefix: string): boolean
|
|
166
|
+
removeContext(collectionName: string, pathPrefix: string): Promise<boolean>;
|
|
87
167
|
/** Set global context (applies to all collections) */
|
|
88
|
-
setGlobalContext(context: string | undefined): void
|
|
168
|
+
setGlobalContext(context: string | undefined): Promise<void>;
|
|
89
169
|
/** Get global context */
|
|
90
|
-
getGlobalContext(): string | undefined
|
|
170
|
+
getGlobalContext(): Promise<string | undefined>;
|
|
91
171
|
/** List all contexts across all collections */
|
|
92
|
-
listContexts(): Array<{
|
|
172
|
+
listContexts(): Promise<Array<{
|
|
93
173
|
collection: string;
|
|
94
174
|
path: string;
|
|
95
175
|
context: string;
|
|
96
|
-
}
|
|
176
|
+
}>>;
|
|
177
|
+
/** Re-index collections by scanning the filesystem */
|
|
178
|
+
update(options?: {
|
|
179
|
+
collections?: string[];
|
|
180
|
+
onProgress?: (info: UpdateProgress) => void;
|
|
181
|
+
}): Promise<UpdateResult>;
|
|
182
|
+
/** Generate vector embeddings for documents that need them */
|
|
183
|
+
embed(options?: {
|
|
184
|
+
force?: boolean;
|
|
185
|
+
model?: string;
|
|
186
|
+
onProgress?: (info: EmbedProgress) => void;
|
|
187
|
+
}): Promise<EmbedResult>;
|
|
97
188
|
/** Get index status (document counts, collections, embedding state) */
|
|
98
|
-
getStatus(): IndexStatus
|
|
189
|
+
getStatus(): Promise<IndexStatus>;
|
|
99
190
|
/** Get index health info (stale embeddings, etc.) */
|
|
100
|
-
getIndexHealth(): IndexHealthInfo
|
|
101
|
-
/** Close the
|
|
102
|
-
close(): void
|
|
191
|
+
getIndexHealth(): Promise<IndexHealthInfo>;
|
|
192
|
+
/** Close the store and release all resources (LLM models, DB connection) */
|
|
193
|
+
close(): Promise<void>;
|
|
103
194
|
}
|
|
104
195
|
/**
|
|
105
196
|
* Create a QMD store for programmatic access to search and indexing.
|
|
@@ -107,13 +198,13 @@ export interface QMDStore {
|
|
|
107
198
|
* @example
|
|
108
199
|
* ```typescript
|
|
109
200
|
* // With a YAML config file
|
|
110
|
-
* const store = createStore({
|
|
201
|
+
* const store = await createStore({
|
|
111
202
|
* dbPath: './index.sqlite',
|
|
112
203
|
* configPath: './qmd.yml',
|
|
113
204
|
* })
|
|
114
205
|
*
|
|
115
206
|
* // With inline config (no files needed besides the DB)
|
|
116
|
-
* const store = createStore({
|
|
207
|
+
* const store = await createStore({
|
|
117
208
|
* dbPath: './index.sqlite',
|
|
118
209
|
* config: {
|
|
119
210
|
* collections: {
|
|
@@ -122,8 +213,8 @@ export interface QMDStore {
|
|
|
122
213
|
* }
|
|
123
214
|
* })
|
|
124
215
|
*
|
|
125
|
-
* const results = await store.query
|
|
126
|
-
* store.close()
|
|
216
|
+
* const results = await store.search({ query: "authentication flow" })
|
|
217
|
+
* await store.close()
|
|
127
218
|
* ```
|
|
128
219
|
*/
|
|
129
|
-
export declare function createStore(options: StoreOptions): QMDStore
|
|
220
|
+
export declare function createStore(options: StoreOptions): Promise<QMDStore>;
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Usage:
|
|
5
5
|
* import { createStore } from '@tobilu/qmd'
|
|
6
6
|
*
|
|
7
|
-
* const store = createStore({
|
|
7
|
+
* const store = await createStore({
|
|
8
8
|
* dbPath: './my-index.sqlite',
|
|
9
9
|
* config: {
|
|
10
10
|
* collections: {
|
|
@@ -13,24 +13,31 @@
|
|
|
13
13
|
* }
|
|
14
14
|
* })
|
|
15
15
|
*
|
|
16
|
-
* const results = await store.query
|
|
17
|
-
* store.close()
|
|
16
|
+
* const results = await store.search({ query: "how does auth work?" })
|
|
17
|
+
* await store.close()
|
|
18
18
|
*/
|
|
19
|
-
import { createStore as createStoreInternal, hybridQuery, structuredSearch, listCollections as storeListCollections, } from "./store.js";
|
|
20
|
-
import {
|
|
19
|
+
import { createStore as createStoreInternal, hybridQuery, structuredSearch, extractSnippet, addLineNumbers, DEFAULT_EMBED_MODEL, DEFAULT_MULTI_GET_MAX_BYTES, reindexCollection, generateEmbeddings, listCollections as storeListCollections, syncConfigToDb, getStoreCollections, getStoreCollection, getStoreGlobalContext, getStoreContexts, upsertStoreCollection, deleteStoreCollection, renameStoreCollection, updateStoreContext, removeStoreContext, setStoreGlobalContext, vacuumDatabase, cleanupOrphanedContent, cleanupOrphanedVectors, deleteLLMCache, deleteInactiveDocuments, clearAllEmbeddings, } from "./store.js";
|
|
20
|
+
import { LlamaCpp, } from "./llm.js";
|
|
21
|
+
import { setConfigSource, loadConfig, addCollection as collectionsAddCollection, removeCollection as collectionsRemoveCollection, renameCollection as collectionsRenameCollection, addContext as collectionsAddContext, removeContext as collectionsRemoveContext, setGlobalContext as collectionsSetGlobalContext, } from "./collections.js";
|
|
22
|
+
// Re-export utility functions used by frontends
|
|
23
|
+
export { extractSnippet, addLineNumbers, DEFAULT_MULTI_GET_MAX_BYTES };
|
|
24
|
+
// Re-export getDefaultDbPath for CLI/MCP that need the default database location
|
|
25
|
+
export { getDefaultDbPath } from "./store.js";
|
|
26
|
+
// Re-export Maintenance class for CLI housekeeping operations
|
|
27
|
+
export { Maintenance } from "./maintenance.js";
|
|
21
28
|
/**
|
|
22
29
|
* Create a QMD store for programmatic access to search and indexing.
|
|
23
30
|
*
|
|
24
31
|
* @example
|
|
25
32
|
* ```typescript
|
|
26
33
|
* // With a YAML config file
|
|
27
|
-
* const store = createStore({
|
|
34
|
+
* const store = await createStore({
|
|
28
35
|
* dbPath: './index.sqlite',
|
|
29
36
|
* configPath: './qmd.yml',
|
|
30
37
|
* })
|
|
31
38
|
*
|
|
32
39
|
* // With inline config (no files needed besides the DB)
|
|
33
|
-
* const store = createStore({
|
|
40
|
+
* const store = await createStore({
|
|
34
41
|
* dbPath: './index.sqlite',
|
|
35
42
|
* config: {
|
|
36
43
|
* collections: {
|
|
@@ -39,56 +46,183 @@ import { setConfigSource, loadConfig, addCollection as collectionsAddCollection,
|
|
|
39
46
|
* }
|
|
40
47
|
* })
|
|
41
48
|
*
|
|
42
|
-
* const results = await store.query
|
|
43
|
-
* store.close()
|
|
49
|
+
* const results = await store.search({ query: "authentication flow" })
|
|
50
|
+
* await store.close()
|
|
44
51
|
* ```
|
|
45
52
|
*/
|
|
46
|
-
export function createStore(options) {
|
|
53
|
+
export async function createStore(options) {
|
|
47
54
|
if (!options.dbPath) {
|
|
48
55
|
throw new Error("dbPath is required");
|
|
49
56
|
}
|
|
50
|
-
if (!options.configPath && !options.config) {
|
|
51
|
-
throw new Error("Either configPath or config is required");
|
|
52
|
-
}
|
|
53
57
|
if (options.configPath && options.config) {
|
|
54
58
|
throw new Error("Provide either configPath or config, not both");
|
|
55
59
|
}
|
|
56
|
-
//
|
|
57
|
-
setConfigSource({
|
|
58
|
-
configPath: options.configPath,
|
|
59
|
-
config: options.config,
|
|
60
|
-
});
|
|
61
|
-
// Create the internal store
|
|
60
|
+
// Create the internal store (opens DB, creates tables)
|
|
62
61
|
const internal = createStoreInternal(options.dbPath);
|
|
62
|
+
const db = internal.db;
|
|
63
|
+
// Track whether we have a YAML config path for write-through
|
|
64
|
+
const hasYamlConfig = !!options.configPath;
|
|
65
|
+
// Sync config into SQLite store_collections
|
|
66
|
+
if (options.configPath) {
|
|
67
|
+
// YAML mode: inject config source for write-through, sync to DB
|
|
68
|
+
setConfigSource({ configPath: options.configPath });
|
|
69
|
+
const config = loadConfig();
|
|
70
|
+
syncConfigToDb(db, config);
|
|
71
|
+
}
|
|
72
|
+
else if (options.config) {
|
|
73
|
+
// Inline config mode: inject config source for mutations, sync to DB
|
|
74
|
+
setConfigSource({ config: options.config });
|
|
75
|
+
syncConfigToDb(db, options.config);
|
|
76
|
+
}
|
|
77
|
+
// else: DB-only mode — no external config, use existing store_collections
|
|
78
|
+
// Create a per-store LlamaCpp instance — lazy-loads models on first use,
|
|
79
|
+
// auto-unloads after 5 min inactivity to free VRAM.
|
|
80
|
+
const llm = new LlamaCpp({
|
|
81
|
+
inactivityTimeoutMs: 5 * 60 * 1000,
|
|
82
|
+
disposeModelsOnInactivity: true,
|
|
83
|
+
});
|
|
84
|
+
internal.llm = llm;
|
|
63
85
|
const store = {
|
|
64
86
|
internal,
|
|
65
87
|
dbPath: internal.dbPath,
|
|
66
|
-
// Search
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
88
|
+
// Search
|
|
89
|
+
search: async (opts) => {
|
|
90
|
+
if (!opts.query && !opts.queries) {
|
|
91
|
+
throw new Error("search() requires either 'query' or 'queries'");
|
|
92
|
+
}
|
|
93
|
+
// Normalize collection/collections
|
|
94
|
+
const collections = [
|
|
95
|
+
...(opts.collection ? [opts.collection] : []),
|
|
96
|
+
...(opts.collections ?? []),
|
|
97
|
+
];
|
|
98
|
+
const skipRerank = opts.rerank === false;
|
|
99
|
+
if (opts.queries) {
|
|
100
|
+
// Pre-expanded queries — use structuredSearch
|
|
101
|
+
return structuredSearch(internal, opts.queries, {
|
|
102
|
+
collections: collections.length > 0 ? collections : undefined,
|
|
103
|
+
limit: opts.limit,
|
|
104
|
+
minScore: opts.minScore,
|
|
105
|
+
explain: opts.explain,
|
|
106
|
+
intent: opts.intent,
|
|
107
|
+
skipRerank,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
// Simple query string — use hybridQuery (expand + search + rerank)
|
|
111
|
+
return hybridQuery(internal, opts.query, {
|
|
112
|
+
collection: collections[0],
|
|
113
|
+
limit: opts.limit,
|
|
114
|
+
minScore: opts.minScore,
|
|
115
|
+
explain: opts.explain,
|
|
116
|
+
intent: opts.intent,
|
|
117
|
+
skipRerank,
|
|
118
|
+
});
|
|
119
|
+
},
|
|
120
|
+
searchLex: async (q, opts) => internal.searchFTS(q, opts?.limit, opts?.collection),
|
|
121
|
+
searchVector: async (q, opts) => internal.searchVec(q, DEFAULT_EMBED_MODEL, opts?.limit, opts?.collection),
|
|
122
|
+
expandQuery: async (q, opts) => internal.expandQuery(q, undefined, opts?.intent),
|
|
123
|
+
get: async (pathOrDocid, opts) => internal.findDocument(pathOrDocid, opts),
|
|
124
|
+
getDocumentBody: async (pathOrDocid, opts) => {
|
|
125
|
+
const result = internal.findDocument(pathOrDocid, { includeBody: false });
|
|
126
|
+
if ("error" in result)
|
|
127
|
+
return null;
|
|
128
|
+
return internal.getDocumentBody(result, opts?.fromLine, opts?.maxLines);
|
|
129
|
+
},
|
|
130
|
+
multiGet: async (pattern, opts) => internal.findDocuments(pattern, opts),
|
|
131
|
+
// Collection Management — write to SQLite + write-through to YAML/inline if configured
|
|
132
|
+
addCollection: async (name, opts) => {
|
|
133
|
+
upsertStoreCollection(db, name, { path: opts.path, pattern: opts.pattern, ignore: opts.ignore });
|
|
134
|
+
if (hasYamlConfig || options.config) {
|
|
135
|
+
collectionsAddCollection(name, opts.path, opts.pattern);
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
removeCollection: async (name) => {
|
|
139
|
+
const result = deleteStoreCollection(db, name);
|
|
140
|
+
if (hasYamlConfig || options.config) {
|
|
141
|
+
collectionsRemoveCollection(name);
|
|
142
|
+
}
|
|
143
|
+
return result;
|
|
144
|
+
},
|
|
145
|
+
renameCollection: async (oldName, newName) => {
|
|
146
|
+
const result = renameStoreCollection(db, oldName, newName);
|
|
147
|
+
if (hasYamlConfig || options.config) {
|
|
148
|
+
collectionsRenameCollection(oldName, newName);
|
|
149
|
+
}
|
|
150
|
+
return result;
|
|
151
|
+
},
|
|
152
|
+
listCollections: async () => storeListCollections(db),
|
|
153
|
+
getDefaultCollectionNames: async () => {
|
|
154
|
+
const collections = storeListCollections(db);
|
|
155
|
+
return collections.filter(c => c.includeByDefault).map(c => c.name);
|
|
156
|
+
},
|
|
157
|
+
// Context Management — write to SQLite + write-through to YAML/inline if configured
|
|
158
|
+
addContext: async (collectionName, pathPrefix, contextText) => {
|
|
159
|
+
const result = updateStoreContext(db, collectionName, pathPrefix, contextText);
|
|
160
|
+
if (hasYamlConfig || options.config) {
|
|
161
|
+
collectionsAddContext(collectionName, pathPrefix, contextText);
|
|
162
|
+
}
|
|
163
|
+
return result;
|
|
164
|
+
},
|
|
165
|
+
removeContext: async (collectionName, pathPrefix) => {
|
|
166
|
+
const result = removeStoreContext(db, collectionName, pathPrefix);
|
|
167
|
+
if (hasYamlConfig || options.config) {
|
|
168
|
+
collectionsRemoveContext(collectionName, pathPrefix);
|
|
169
|
+
}
|
|
170
|
+
return result;
|
|
171
|
+
},
|
|
172
|
+
setGlobalContext: async (context) => {
|
|
173
|
+
setStoreGlobalContext(db, context);
|
|
174
|
+
if (hasYamlConfig || options.config) {
|
|
175
|
+
collectionsSetGlobalContext(context);
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
getGlobalContext: async () => getStoreGlobalContext(db),
|
|
179
|
+
listContexts: async () => getStoreContexts(db),
|
|
180
|
+
// Indexing — reads collections from SQLite
|
|
181
|
+
update: async (updateOpts) => {
|
|
182
|
+
const collections = getStoreCollections(db);
|
|
183
|
+
const filtered = updateOpts?.collections
|
|
184
|
+
? collections.filter(c => updateOpts.collections.includes(c.name))
|
|
185
|
+
: collections;
|
|
186
|
+
internal.clearCache();
|
|
187
|
+
let totalIndexed = 0, totalUpdated = 0, totalUnchanged = 0, totalRemoved = 0;
|
|
188
|
+
for (const col of filtered) {
|
|
189
|
+
const result = await reindexCollection(internal, col.path, col.pattern || "**/*.md", col.name, {
|
|
190
|
+
ignorePatterns: col.ignore,
|
|
191
|
+
onProgress: updateOpts?.onProgress
|
|
192
|
+
? (info) => updateOpts.onProgress({ collection: col.name, ...info })
|
|
193
|
+
: undefined,
|
|
194
|
+
});
|
|
195
|
+
totalIndexed += result.indexed;
|
|
196
|
+
totalUpdated += result.updated;
|
|
197
|
+
totalUnchanged += result.unchanged;
|
|
198
|
+
totalRemoved += result.removed;
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
collections: filtered.length,
|
|
202
|
+
indexed: totalIndexed,
|
|
203
|
+
updated: totalUpdated,
|
|
204
|
+
unchanged: totalUnchanged,
|
|
205
|
+
removed: totalRemoved,
|
|
206
|
+
needsEmbedding: internal.getHashesNeedingEmbedding(),
|
|
207
|
+
};
|
|
208
|
+
},
|
|
209
|
+
embed: async (embedOpts) => {
|
|
210
|
+
return generateEmbeddings(internal, {
|
|
211
|
+
force: embedOpts?.force,
|
|
212
|
+
model: embedOpts?.model,
|
|
213
|
+
onProgress: embedOpts?.onProgress,
|
|
214
|
+
});
|
|
75
215
|
},
|
|
76
|
-
removeCollection: (name) => collectionsRemoveCollection(name),
|
|
77
|
-
renameCollection: (oldName, newName) => collectionsRenameCollection(oldName, newName),
|
|
78
|
-
listCollections: () => storeListCollections(internal.db),
|
|
79
|
-
// Context Management
|
|
80
|
-
addContext: (collectionName, pathPrefix, contextText) => collectionsAddContext(collectionName, pathPrefix, contextText),
|
|
81
|
-
removeContext: (collectionName, pathPrefix) => collectionsRemoveContext(collectionName, pathPrefix),
|
|
82
|
-
setGlobalContext: (context) => collectionsSetGlobalContext(context),
|
|
83
|
-
getGlobalContext: () => collectionsGetGlobalContext(),
|
|
84
|
-
listContexts: () => collectionsListAllContexts(),
|
|
85
216
|
// Index Health
|
|
86
|
-
getStatus: () => internal.getStatus(),
|
|
87
|
-
getIndexHealth: () => internal.getIndexHealth(),
|
|
217
|
+
getStatus: async () => internal.getStatus(),
|
|
218
|
+
getIndexHealth: async () => internal.getIndexHealth(),
|
|
88
219
|
// Lifecycle
|
|
89
|
-
close: () => {
|
|
220
|
+
close: async () => {
|
|
221
|
+
await llm.dispose();
|
|
90
222
|
internal.close();
|
|
91
|
-
|
|
223
|
+
if (hasYamlConfig || options.config) {
|
|
224
|
+
setConfigSource(undefined); // Reset config source
|
|
225
|
+
}
|
|
92
226
|
},
|
|
93
227
|
};
|
|
94
228
|
return store;
|
package/dist/llm.d.ts
CHANGED
|
@@ -212,6 +212,7 @@ export type LlamaCppConfig = {
|
|
|
212
212
|
disposeModelsOnInactivity?: boolean;
|
|
213
213
|
};
|
|
214
214
|
export declare class LlamaCpp implements LLM {
|
|
215
|
+
private readonly _ciMode;
|
|
215
216
|
private llama;
|
|
216
217
|
private embedModel;
|
|
217
218
|
private embedContexts;
|
|
@@ -373,6 +374,11 @@ export declare class SessionReleasedError extends Error {
|
|
|
373
374
|
* ```
|
|
374
375
|
*/
|
|
375
376
|
export declare function withLLMSession<T>(fn: (session: ILLMSession) => Promise<T>, options?: LLMSessionOptions): Promise<T>;
|
|
377
|
+
/**
|
|
378
|
+
* Execute a function with a scoped LLM session using a specific LlamaCpp instance.
|
|
379
|
+
* Unlike withLLMSession, this does not use the global singleton.
|
|
380
|
+
*/
|
|
381
|
+
export declare function withLLMSessionForLlm<T>(llm: LlamaCpp, fn: (session: ILLMSession) => Promise<T>, options?: LLMSessionOptions): Promise<T>;
|
|
376
382
|
/**
|
|
377
383
|
* Check if idle unload is safe (no active sessions or operations).
|
|
378
384
|
* Used internally by LlamaCpp idle timer.
|
package/dist/llm.js
CHANGED
|
@@ -47,7 +47,7 @@ export function formatDocForEmbedding(text, title, modelUri) {
|
|
|
47
47
|
// =============================================================================
|
|
48
48
|
// HuggingFace model URIs for node-llama-cpp
|
|
49
49
|
// Format: hf:<user>/<repo>/<file>
|
|
50
|
-
// Override via QMD_EMBED_MODEL env var (e.g. hf:Qwen/Qwen3-Embedding-0.6B-GGUF/
|
|
50
|
+
// Override via QMD_EMBED_MODEL env var (e.g. hf:Qwen/Qwen3-Embedding-0.6B-GGUF/Qwen3-Embedding-0.6B-Q8_0.gguf)
|
|
51
51
|
const DEFAULT_EMBED_MODEL = process.env.QMD_EMBED_MODEL ?? "hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf";
|
|
52
52
|
const DEFAULT_RERANK_MODEL = "hf:ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF/qwen3-reranker-0.6b-q8_0.gguf";
|
|
53
53
|
// const DEFAULT_GENERATE_MODEL = "hf:ggml-org/Qwen3-0.6B-GGUF/Qwen3-0.6B-Q8_0.gguf";
|
|
@@ -164,6 +164,7 @@ function resolveExpandContextSize(configValue) {
|
|
|
164
164
|
return parsed;
|
|
165
165
|
}
|
|
166
166
|
export class LlamaCpp {
|
|
167
|
+
_ciMode = !!process.env.CI;
|
|
167
168
|
llama = null;
|
|
168
169
|
embedModel = null;
|
|
169
170
|
embedContexts = [];
|
|
@@ -575,6 +576,8 @@ export class LlamaCpp {
|
|
|
575
576
|
* Uses Promise.all for parallel embedding - node-llama-cpp handles batching internally
|
|
576
577
|
*/
|
|
577
578
|
async embedBatch(texts) {
|
|
579
|
+
if (this._ciMode)
|
|
580
|
+
throw new Error("LLM operations are disabled in CI (set CI=true)");
|
|
578
581
|
// Ping activity at start to keep models alive during this operation
|
|
579
582
|
this.touchActivity();
|
|
580
583
|
if (texts.length === 0)
|
|
@@ -626,6 +629,8 @@ export class LlamaCpp {
|
|
|
626
629
|
}
|
|
627
630
|
}
|
|
628
631
|
async generate(prompt, options = {}) {
|
|
632
|
+
if (this._ciMode)
|
|
633
|
+
throw new Error("LLM operations are disabled in CI (set CI=true)");
|
|
629
634
|
// Ping activity at start to keep models alive during this operation
|
|
630
635
|
this.touchActivity();
|
|
631
636
|
// Ensure model is loaded
|
|
@@ -677,6 +682,8 @@ export class LlamaCpp {
|
|
|
677
682
|
// High-level abstractions
|
|
678
683
|
// ==========================================================================
|
|
679
684
|
async expandQuery(query, options = {}) {
|
|
685
|
+
if (this._ciMode)
|
|
686
|
+
throw new Error("LLM operations are disabled in CI (set CI=true)");
|
|
680
687
|
// Ping activity at start to keep models alive during this operation
|
|
681
688
|
this.touchActivity();
|
|
682
689
|
const llama = await this.ensureLlama();
|
|
@@ -764,6 +771,8 @@ export class LlamaCpp {
|
|
|
764
771
|
static RERANK_TEMPLATE_OVERHEAD = 200;
|
|
765
772
|
static RERANK_TARGET_DOCS_PER_CONTEXT = 10;
|
|
766
773
|
async rerank(query, documents, options = {}) {
|
|
774
|
+
if (this._ciMode)
|
|
775
|
+
throw new Error("LLM operations are disabled in CI (set CI=true)");
|
|
767
776
|
// Ping activity at start to keep models alive during this operation
|
|
768
777
|
this.touchActivity();
|
|
769
778
|
const contexts = await this.ensureRerankContexts();
|
|
@@ -1065,6 +1074,20 @@ export async function withLLMSession(fn, options) {
|
|
|
1065
1074
|
session.release();
|
|
1066
1075
|
}
|
|
1067
1076
|
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Execute a function with a scoped LLM session using a specific LlamaCpp instance.
|
|
1079
|
+
* Unlike withLLMSession, this does not use the global singleton.
|
|
1080
|
+
*/
|
|
1081
|
+
export async function withLLMSessionForLlm(llm, fn, options) {
|
|
1082
|
+
const manager = new LLMSessionManager(llm);
|
|
1083
|
+
const session = new LLMSession(manager, options);
|
|
1084
|
+
try {
|
|
1085
|
+
return await fn(session);
|
|
1086
|
+
}
|
|
1087
|
+
finally {
|
|
1088
|
+
session.release();
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1068
1091
|
/**
|
|
1069
1092
|
* Check if idle unload is safe (no active sessions or operations).
|
|
1070
1093
|
* Used internally by LlamaCpp idle timer.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maintenance - Database cleanup operations for QMD.
|
|
3
|
+
*
|
|
4
|
+
* Wraps low-level store operations that the CLI needs for housekeeping.
|
|
5
|
+
* Takes an internal Store in the constructor — allowed to access DB directly.
|
|
6
|
+
*/
|
|
7
|
+
import type { Store } from "./store.js";
|
|
8
|
+
export declare class Maintenance {
|
|
9
|
+
private store;
|
|
10
|
+
constructor(store: Store);
|
|
11
|
+
/** Run VACUUM on the SQLite database to reclaim space */
|
|
12
|
+
vacuum(): void;
|
|
13
|
+
/** Remove content rows that are no longer referenced by any document */
|
|
14
|
+
cleanupOrphanedContent(): number;
|
|
15
|
+
/** Remove vector embeddings for content that no longer exists */
|
|
16
|
+
cleanupOrphanedVectors(): number;
|
|
17
|
+
/** Clear the LLM response cache (query expansion, reranking) */
|
|
18
|
+
clearLLMCache(): number;
|
|
19
|
+
/** Delete documents marked as inactive (removed from filesystem) */
|
|
20
|
+
deleteInactiveDocs(): number;
|
|
21
|
+
/** Clear all vector embeddings (forces re-embedding) */
|
|
22
|
+
clearEmbeddings(): void;
|
|
23
|
+
}
|