sensemaking 0.15.3 → 0.16.0
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/README.md +33 -5
- package/dist/cjs/cli/download.js +15 -19
- package/dist/cjs/cli/download.js.map +1 -1
- package/dist/cjs/cli/index.d.cts +1 -1
- package/dist/cjs/cli/index.d.ts +1 -1
- package/dist/cjs/cli/index.js +9 -20
- package/dist/cjs/cli/index.js.map +1 -1
- package/dist/cjs/cli/init.js +31 -5
- package/dist/cjs/cli/init.js.map +1 -1
- package/dist/cjs/cli/map.js +1 -3
- package/dist/cjs/cli/map.js.map +1 -1
- package/dist/cjs/cli/named.js +3 -8
- package/dist/cjs/cli/named.js.map +1 -1
- package/dist/cjs/cli/path.js +4 -7
- package/dist/cjs/cli/path.js.map +1 -1
- package/dist/cjs/cli/peek.js +2 -4
- package/dist/cjs/cli/peek.js.map +1 -1
- package/dist/cjs/cli/related.js +14 -20
- package/dist/cjs/cli/related.js.map +1 -1
- package/dist/cjs/cli/search.js +12 -17
- package/dist/cjs/cli/search.js.map +1 -1
- package/dist/cjs/cli/shared.js +17 -21
- package/dist/cjs/cli/shared.js.map +1 -1
- package/dist/cjs/cli/sql.js +7 -8
- package/dist/cjs/cli/sql.js.map +1 -1
- package/dist/cjs/cli/status.js +344 -102
- package/dist/cjs/cli/status.js.map +1 -1
- package/dist/cjs/cli/watch.js +3 -8
- package/dist/cjs/cli/watch.js.map +1 -1
- package/dist/cjs/cli.js +22 -34
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/column-hint.js +4 -2
- package/dist/cjs/column-hint.js.map +1 -1
- package/dist/cjs/commands/peek.js +5 -3
- package/dist/cjs/commands/peek.js.map +1 -1
- package/dist/cjs/commands/related.js +31 -28
- package/dist/cjs/commands/related.js.map +1 -1
- package/dist/cjs/commands/scope.js +4 -2
- package/dist/cjs/commands/scope.js.map +1 -1
- package/dist/cjs/commands/search.js +36 -108
- package/dist/cjs/commands/search.js.map +1 -1
- package/dist/cjs/commands/signals.d.cts +18 -0
- package/dist/cjs/commands/signals.d.ts +18 -0
- package/dist/cjs/commands/signals.js +299 -0
- package/dist/cjs/commands/signals.js.map +1 -0
- package/dist/cjs/commands/status.d.cts +2 -2
- package/dist/cjs/commands/status.d.ts +2 -2
- package/dist/cjs/commands/status.js +1 -1
- package/dist/cjs/commands/status.js.map +1 -1
- package/dist/cjs/config/access.d.cts +5 -2
- package/dist/cjs/config/access.d.ts +5 -2
- package/dist/cjs/config/access.js +34 -14
- package/dist/cjs/config/access.js.map +1 -1
- package/dist/cjs/config/index.d.cts +4 -1
- package/dist/cjs/config/index.d.ts +4 -1
- package/dist/cjs/config/index.js +12 -2
- package/dist/cjs/config/index.js.map +1 -1
- package/dist/cjs/config/load.d.cts +6 -1
- package/dist/cjs/config/load.d.ts +6 -1
- package/dist/cjs/config/load.js +87 -15
- package/dist/cjs/config/load.js.map +1 -1
- package/dist/cjs/config/resolve.js +1 -1
- package/dist/cjs/config/resolve.js.map +1 -1
- package/dist/cjs/config/signals.d.cts +4 -0
- package/dist/cjs/config/signals.d.ts +4 -0
- package/dist/cjs/config/signals.js +30 -0
- package/dist/cjs/config/signals.js.map +1 -0
- package/dist/cjs/config/types.d.cts +6 -4
- package/dist/cjs/config/types.d.ts +6 -4
- package/dist/cjs/config/types.js +1 -1
- package/dist/cjs/config/types.js.map +1 -1
- package/dist/cjs/config/validate.js +121 -12
- package/dist/cjs/config/validate.js.map +1 -1
- package/dist/cjs/db/open.d.cts +1 -1
- package/dist/cjs/db/open.d.ts +1 -1
- package/dist/cjs/db/open.js +10 -3
- package/dist/cjs/db/open.js.map +1 -1
- package/dist/cjs/db/reconcile.d.cts +1 -0
- package/dist/cjs/db/reconcile.d.ts +1 -0
- package/dist/cjs/db/reconcile.js +20 -3
- package/dist/cjs/db/reconcile.js.map +1 -1
- package/dist/cjs/embed/cohere.d.cts +2 -0
- package/dist/cjs/embed/cohere.d.ts +2 -0
- package/dist/cjs/embed/cohere.js +238 -0
- package/dist/cjs/embed/cohere.js.map +1 -0
- package/dist/cjs/embed/http.d.cts +2 -0
- package/dist/cjs/embed/http.d.ts +2 -0
- package/dist/cjs/embed/http.js +258 -0
- package/dist/cjs/embed/http.js.map +1 -0
- package/dist/cjs/embed/identity.d.cts +13 -0
- package/dist/cjs/embed/identity.d.ts +13 -0
- package/dist/cjs/embed/identity.js +194 -0
- package/dist/cjs/embed/identity.js.map +1 -0
- package/dist/cjs/embed/langfit.d.cts +6 -0
- package/dist/cjs/embed/langfit.d.ts +6 -0
- package/dist/cjs/embed/langfit.js +326 -0
- package/dist/cjs/embed/langfit.js.map +1 -0
- package/dist/cjs/embed/languages.d.cts +3 -0
- package/dist/cjs/embed/languages.d.ts +3 -0
- package/dist/cjs/embed/languages.js +98 -0
- package/dist/cjs/embed/languages.js.map +1 -0
- package/dist/cjs/embed/openai.d.cts +2 -0
- package/dist/cjs/embed/openai.d.ts +2 -0
- package/dist/cjs/embed/openai.js +268 -0
- package/dist/cjs/embed/openai.js.map +1 -0
- package/dist/cjs/embed/query.d.cts +21 -0
- package/dist/cjs/embed/query.d.ts +21 -0
- package/dist/cjs/embed/query.js +610 -0
- package/dist/cjs/embed/query.js.map +1 -0
- package/dist/cjs/embed/registry.d.cts +3 -0
- package/dist/cjs/embed/registry.d.ts +3 -0
- package/dist/cjs/embed/registry.js +114 -0
- package/dist/cjs/embed/registry.js.map +1 -0
- package/dist/cjs/embed/static.d.cts +2 -0
- package/dist/cjs/embed/static.d.ts +2 -0
- package/dist/cjs/embed/static.js +367 -0
- package/dist/cjs/embed/static.js.map +1 -0
- package/dist/cjs/embed/store.d.cts +8 -0
- package/dist/cjs/embed/store.d.ts +8 -0
- package/dist/cjs/embed/store.js +498 -0
- package/dist/cjs/embed/store.js.map +1 -0
- package/dist/cjs/embed/types.d.cts +8 -0
- package/dist/cjs/embed/types.d.ts +8 -0
- package/dist/cjs/embed/types.js +7 -0
- package/dist/cjs/embed/types.js.map +1 -0
- package/dist/cjs/errors.d.cts +1 -1
- package/dist/cjs/errors.d.ts +1 -1
- package/dist/cjs/errors.js +15 -24
- package/dist/cjs/errors.js.map +1 -1
- package/dist/cjs/features/embed.d.cts +4 -27
- package/dist/cjs/features/embed.d.ts +4 -27
- package/dist/cjs/features/embed.js +6 -978
- package/dist/cjs/features/embed.js.map +1 -1
- package/dist/cjs/features/links.js +4 -2
- package/dist/cjs/features/links.js.map +1 -1
- package/dist/cjs/features/rank.js +1 -1
- package/dist/cjs/features/tags.js +4 -2
- package/dist/cjs/features/tags.js.map +1 -1
- package/dist/cjs/fences.js.map +1 -1
- package/dist/cjs/graph.js +5 -3
- package/dist/cjs/graph.js.map +1 -1
- package/dist/cjs/index.d.cts +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/output.d.cts +2 -2
- package/dist/cjs/output.d.ts +2 -2
- package/dist/cjs/output.js +15 -8
- package/dist/cjs/output.js.map +1 -1
- package/dist/cjs/scan.js +13 -8
- package/dist/cjs/scan.js.map +1 -1
- package/dist/cjs/segment.js +67 -14
- package/dist/cjs/segment.js.map +1 -1
- package/dist/cjs/traverse.js +4 -2
- package/dist/cjs/traverse.js.map +1 -1
- package/dist/cjs/watch.js +2 -5
- package/dist/cjs/watch.js.map +1 -1
- package/dist/esm/cli/download.js +9 -8
- package/dist/esm/cli/download.js.map +1 -1
- package/dist/esm/cli/index.d.ts +1 -1
- package/dist/esm/cli/index.js +1 -1
- package/dist/esm/cli/index.js.map +1 -1
- package/dist/esm/cli/init.js +32 -6
- package/dist/esm/cli/init.js.map +1 -1
- package/dist/esm/cli/status.js +36 -8
- package/dist/esm/cli/status.js.map +1 -1
- package/dist/esm/commands/related.js +14 -10
- package/dist/esm/commands/related.js.map +1 -1
- package/dist/esm/commands/search.js +27 -78
- package/dist/esm/commands/search.js.map +1 -1
- package/dist/esm/commands/signals.d.ts +18 -0
- package/dist/esm/commands/signals.js +78 -0
- package/dist/esm/commands/signals.js.map +1 -0
- package/dist/esm/commands/status.d.ts +2 -2
- package/dist/esm/commands/status.js +2 -2
- package/dist/esm/commands/status.js.map +1 -1
- package/dist/esm/config/access.d.ts +5 -2
- package/dist/esm/config/access.js +31 -12
- package/dist/esm/config/access.js.map +1 -1
- package/dist/esm/config/index.d.ts +4 -1
- package/dist/esm/config/index.js +2 -1
- package/dist/esm/config/index.js.map +1 -1
- package/dist/esm/config/load.d.ts +6 -1
- package/dist/esm/config/load.js +67 -9
- package/dist/esm/config/load.js.map +1 -1
- package/dist/esm/config/resolve.js +2 -2
- package/dist/esm/config/resolve.js.map +1 -1
- package/dist/esm/config/signals.d.ts +4 -0
- package/dist/esm/config/signals.js +14 -0
- package/dist/esm/config/signals.js.map +1 -0
- package/dist/esm/config/types.d.ts +6 -4
- package/dist/esm/config/types.js +1 -1
- package/dist/esm/config/types.js.map +1 -1
- package/dist/esm/config/validate.js +58 -11
- package/dist/esm/config/validate.js.map +1 -1
- package/dist/esm/db/open.d.ts +1 -1
- package/dist/esm/db/open.js +7 -2
- package/dist/esm/db/open.js.map +1 -1
- package/dist/esm/db/reconcile.d.ts +1 -0
- package/dist/esm/db/reconcile.js +10 -0
- package/dist/esm/db/reconcile.js.map +1 -1
- package/dist/esm/embed/cohere.d.ts +2 -0
- package/dist/esm/embed/cohere.js +46 -0
- package/dist/esm/embed/cohere.js.map +1 -0
- package/dist/esm/embed/http.d.ts +2 -0
- package/dist/esm/embed/http.js +33 -0
- package/dist/esm/embed/http.js.map +1 -0
- package/dist/esm/embed/identity.d.ts +13 -0
- package/dist/esm/embed/identity.js +92 -0
- package/dist/esm/embed/identity.js.map +1 -0
- package/dist/esm/embed/langfit.d.ts +6 -0
- package/dist/esm/embed/langfit.js +44 -0
- package/dist/esm/embed/langfit.js.map +1 -0
- package/dist/esm/embed/languages.d.ts +3 -0
- package/dist/esm/embed/languages.js +80 -0
- package/dist/esm/embed/languages.js.map +1 -0
- package/dist/esm/embed/openai.d.ts +2 -0
- package/dist/esm/embed/openai.js +49 -0
- package/dist/esm/embed/openai.js.map +1 -0
- package/dist/esm/embed/query.d.ts +21 -0
- package/dist/esm/embed/query.js +181 -0
- package/dist/esm/embed/query.js.map +1 -0
- package/dist/esm/embed/registry.d.ts +3 -0
- package/dist/esm/embed/registry.js +45 -0
- package/dist/esm/embed/registry.js.map +1 -0
- package/dist/esm/embed/static.d.ts +2 -0
- package/dist/esm/embed/static.js +72 -0
- package/dist/esm/embed/static.js.map +1 -0
- package/dist/esm/embed/store.d.ts +8 -0
- package/dist/esm/embed/store.js +123 -0
- package/dist/esm/embed/store.js.map +1 -0
- package/dist/esm/embed/types.d.ts +8 -0
- package/dist/esm/embed/types.js +3 -0
- package/dist/esm/embed/types.js.map +1 -0
- package/dist/esm/errors.d.ts +1 -1
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/features/embed.d.ts +4 -27
- package/dist/esm/features/embed.js +0 -346
- package/dist/esm/features/embed.js.map +1 -1
- package/dist/esm/fences.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/output.d.ts +2 -2
- package/dist/esm/output.js +6 -2
- package/dist/esm/output.js.map +1 -1
- package/dist/esm/scan.js +7 -4
- package/dist/esm/scan.js.map +1 -1
- package/dist/esm/segment.js +49 -19
- package/dist/esm/segment.js.map +1 -1
- package/package.json +9 -1
- package/schema.json +14 -7
- package/skills/sense/SKILL.md +21 -5
- package/skills/sense-setup/EXAMPLES.md +6 -6
- package/skills/sense-setup/SKILL.md +6 -4
package/dist/esm/cli/download.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { basename } from 'node:path';
|
|
1
2
|
import { embedConfig } from '../config/index.js';
|
|
3
|
+
import { downloadModel, isDownloadable, modelDir, modelPresent } from '../embed/store.js';
|
|
2
4
|
import { SenseError } from '../errors.js';
|
|
3
|
-
import { downloadModel, modelDir, modelPresent } from '../features/embed.js';
|
|
4
5
|
import { USAGE } from './index.js';
|
|
5
6
|
import { CONFIG, parse } from './shared.js';
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// is no prompt for CI to bypass; running it twice is a no-op.
|
|
7
|
+
// A prefetch over the store's own fetch path: a search would fetch the same weights
|
|
8
|
+
// lazily, so this only moves the wait earlier. Idempotent; asks nothing, so CI can run it unattended.
|
|
9
9
|
const download = async (ctx)=>{
|
|
10
10
|
const { values } = parse(ctx.argv, `usage: ${ctx.name} ${USAGE.download}`, {
|
|
11
11
|
...CONFIG
|
|
@@ -13,15 +13,16 @@ const download = async (ctx)=>{
|
|
|
13
13
|
const cfg = ctx.resolveConfig(values.config);
|
|
14
14
|
const e = embedConfig(cfg);
|
|
15
15
|
if (!e) throw new SenseError('EMBED_DISABLED', 'no preset asks for vectors, so there is no model to download');
|
|
16
|
-
if (e.
|
|
17
|
-
console.log(`embed
|
|
16
|
+
if (e.provider !== 'static') {
|
|
17
|
+
console.log(`embed provider is "${e.provider}" (${e.url}), so there is nothing to download; ${ctx.name} search reaches it over the network`);
|
|
18
18
|
return;
|
|
19
19
|
}
|
|
20
20
|
if (modelPresent(cfg)) {
|
|
21
|
-
|
|
21
|
+
const at = isDownloadable(e.model) ? `@${basename(modelDir(e.model))} ` : ' ';
|
|
22
|
+
console.log(`${e.model}${at}is already available in ${modelDir(e.model)}`);
|
|
22
23
|
return;
|
|
23
24
|
}
|
|
24
25
|
const dir = await downloadModel(e.model, (file, into)=>console.error(`fetching ${e.model}/${file} into ${into}`));
|
|
25
|
-
console.log(`downloaded ${e.model} into ${dir}`);
|
|
26
|
+
console.log(`downloaded ${e.model}@${basename(dir)} into ${dir}`);
|
|
26
27
|
};
|
|
27
28
|
export default download;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/download.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/download.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport { embedConfig } from '../config/index.ts';\nimport { downloadModel, isDownloadable, modelDir, modelPresent } from '../embed/store.ts';\nimport { SenseError } from '../errors.ts';\nimport { USAGE } from './index.ts';\nimport { CONFIG, parse } from './shared.ts';\nimport type { Command } from './types.ts';\n\n// A prefetch over the store's own fetch path: a search would fetch the same weights\n// lazily, so this only moves the wait earlier. Idempotent; asks nothing, so CI can run it unattended.\nconst download: Command = async (ctx) => {\n const { values } = parse(ctx.argv, `usage: ${ctx.name} ${USAGE.download}`, { ...CONFIG });\n const cfg = ctx.resolveConfig(values.config as string | undefined);\n const e = embedConfig(cfg);\n if (!e) throw new SenseError('EMBED_DISABLED', 'no preset asks for vectors, so there is no model to download');\n\n if (e.provider !== 'static') {\n console.log(`embed provider is \"${e.provider}\" (${e.url}), so there is nothing to download; ${ctx.name} search reaches it over the network`);\n return;\n }\n if (modelPresent(cfg)) {\n const at = isDownloadable(e.model) ? `@${basename(modelDir(e.model))} ` : ' ';\n console.log(`${e.model}${at}is already available in ${modelDir(e.model)}`);\n return;\n }\n const dir = await downloadModel(e.model, (file, into) => console.error(`fetching ${e.model}/${file} into ${into}`));\n console.log(`downloaded ${e.model}@${basename(dir)} into ${dir}`);\n};\nexport default download;\n"],"names":["basename","embedConfig","downloadModel","isDownloadable","modelDir","modelPresent","SenseError","USAGE","CONFIG","parse","download","ctx","values","argv","name","cfg","resolveConfig","config","e","provider","console","log","url","at","model","dir","file","into","error"],"mappings":"AAAA,SAASA,QAAQ,QAAQ,YAAY;AACrC,SAASC,WAAW,QAAQ,qBAAqB;AACjD,SAASC,aAAa,EAAEC,cAAc,EAAEC,QAAQ,EAAEC,YAAY,QAAQ,oBAAoB;AAC1F,SAASC,UAAU,QAAQ,eAAe;AAC1C,SAASC,KAAK,QAAQ,aAAa;AACnC,SAASC,MAAM,EAAEC,KAAK,QAAQ,cAAc;AAG5C,oFAAoF;AACpF,sGAAsG;AACtG,MAAMC,WAAoB,OAAOC;IAC/B,MAAM,EAAEC,MAAM,EAAE,GAAGH,MAAME,IAAIE,IAAI,EAAE,CAAC,OAAO,EAAEF,IAAIG,IAAI,CAAC,CAAC,EAAEP,MAAMG,QAAQ,EAAE,EAAE;QAAE,GAAGF,MAAM;IAAC;IACvF,MAAMO,MAAMJ,IAAIK,aAAa,CAACJ,OAAOK,MAAM;IAC3C,MAAMC,IAAIjB,YAAYc;IACtB,IAAI,CAACG,GAAG,MAAM,IAAIZ,WAAW,kBAAkB;IAE/C,IAAIY,EAAEC,QAAQ,KAAK,UAAU;QAC3BC,QAAQC,GAAG,CAAC,CAAC,mBAAmB,EAAEH,EAAEC,QAAQ,CAAC,GAAG,EAAED,EAAEI,GAAG,CAAC,oCAAoC,EAAEX,IAAIG,IAAI,CAAC,mCAAmC,CAAC;QAC3I;IACF;IACA,IAAIT,aAAaU,MAAM;QACrB,MAAMQ,KAAKpB,eAAee,EAAEM,KAAK,IAAI,CAAC,CAAC,EAAExB,SAASI,SAASc,EAAEM,KAAK,GAAG,CAAC,CAAC,GAAG;QAC1EJ,QAAQC,GAAG,CAAC,GAAGH,EAAEM,KAAK,GAAGD,GAAG,wBAAwB,EAAEnB,SAASc,EAAEM,KAAK,GAAG;QACzE;IACF;IACA,MAAMC,MAAM,MAAMvB,cAAcgB,EAAEM,KAAK,EAAE,CAACE,MAAMC,OAASP,QAAQQ,KAAK,CAAC,CAAC,SAAS,EAAEV,EAAEM,KAAK,CAAC,CAAC,EAAEE,KAAK,MAAM,EAAEC,MAAM;IACjHP,QAAQC,GAAG,CAAC,CAAC,WAAW,EAAEH,EAAEM,KAAK,CAAC,CAAC,EAAExB,SAASyB,KAAK,MAAM,EAAEA,KAAK;AAClE;AACA,eAAef,SAAS"}
|
package/dist/esm/cli/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Command } from './types.js';
|
|
2
2
|
export declare const USAGE: {
|
|
3
|
-
readonly init: "init";
|
|
3
|
+
readonly init: "init [--model id-or-path] [--provider static|openai|cohere] [--url url]";
|
|
4
4
|
readonly watch: "watch [--force] [--config path]";
|
|
5
5
|
readonly status: "status [--format table|json] [--config path]";
|
|
6
6
|
readonly download: "download [--config path]";
|
package/dist/esm/cli/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// top-level usage() and the command's own parse() error/--help output, so there is one
|
|
3
3
|
// copy of each line, not two that can drift.
|
|
4
4
|
export const USAGE = {
|
|
5
|
-
init: 'init',
|
|
5
|
+
init: 'init [--model id-or-path] [--provider static|openai|cohere] [--url url]',
|
|
6
6
|
watch: 'watch [--force] [--config path]',
|
|
7
7
|
status: 'status [--format table|json] [--config path]',
|
|
8
8
|
download: 'download [--config path]',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/index.ts"],"sourcesContent":["import type { Command } from './types.ts';\n\n// Each command's usage line, minus the leading \"usage: <name> \" -- shared by cli.ts's\n// top-level usage() and the command's own parse() error/--help output, so there is one\n// copy of each line, not two that can drift.\nexport const USAGE = {\n init: 'init',\n watch: 'watch [--force] [--config path]',\n status: 'status [--format table|json] [--config path]',\n download: 'download [--config path]',\n sql: 'sql \"<statement>\" [params...] [--preset name] [--format table|json|csv] [--config path]',\n search: 'search \"<terms>\" [--preset name] [--include glob ...] [--exclude glob ...] [--no-exclude] [--where \"<sql>\"] [--k n] [--format table|json|csv] [--config path]',\n map: 'map [--format table|json] [--config path]',\n peek: 'peek <path> [--preset name] [--include glob ...] [--exclude glob ...] [--no-exclude] [--where \"<sql>\"] [--format table|json] [--config path]',\n path: 'path <a> <b> [--preset name] [--include glob ...] [--exclude glob ...] [--no-exclude] [--where \"<sql>\"] [--max-depth n] [--format table|json] [--config path]',\n related: 'related <note> [--preset name] [--include glob ...] [--exclude glob ...] [--no-exclude] [--where \"<sql>\"] [--k n] [--format table|json|csv] [--config path]',\n} as const;\n\n// Lazy registry: a command's imports load only when it runs, so a heavy dependency in one\n// command never taxes the others (or --version/--help). These names are reserved: a saved\n// entry in config can never shadow them.\nexport const COMMANDS: Record<string, () => Promise<{ default: Command }>> = {\n init: () => import('./init.ts'),\n watch: () => import('./watch.ts'),\n status: () => import('./status.ts'),\n download: () => import('./download.ts'),\n sql: () => import('./sql.ts'),\n search: () => import('./search.ts'),\n map: () => import('./map.ts'),\n peek: () => import('./peek.ts'),\n path: () => import('./path.ts'),\n related: () => import('./related.ts'),\n};\n\nexport type { Command, Ctx } from './types.ts';\n"],"names":["USAGE","init","watch","status","download","sql","search","map","peek","path","related","COMMANDS"],"mappings":"AAEA,sFAAsF;AACtF,uFAAuF;AACvF,6CAA6C;AAC7C,OAAO,MAAMA,QAAQ;IACnBC,MAAM;IACNC,OAAO;IACPC,QAAQ;IACRC,UAAU;IACVC,KAAK;IACLC,QAAQ;IACRC,KAAK;IACLC,MAAM;IACNC,MAAM;IACNC,SAAS;AACX,EAAW;AAEX,0FAA0F;AAC1F,0FAA0F;AAC1F,yCAAyC;AACzC,OAAO,MAAMC,WAAgE;IAC3EV,MAAM,IAAM,MAAM,CAAC;IACnBC,OAAO,IAAM,MAAM,CAAC;IACpBC,QAAQ,IAAM,MAAM,CAAC;IACrBC,UAAU,IAAM,MAAM,CAAC;IACvBC,KAAK,IAAM,MAAM,CAAC;IAClBC,QAAQ,IAAM,MAAM,CAAC;IACrBC,KAAK,IAAM,MAAM,CAAC;IAClBC,MAAM,IAAM,MAAM,CAAC;IACnBC,MAAM,IAAM,MAAM,CAAC;IACnBC,SAAS,IAAM,MAAM,CAAC;AACxB,EAAE"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/index.ts"],"sourcesContent":["import type { Command } from './types.ts';\n\n// Each command's usage line, minus the leading \"usage: <name> \" -- shared by cli.ts's\n// top-level usage() and the command's own parse() error/--help output, so there is one\n// copy of each line, not two that can drift.\nexport const USAGE = {\n init: 'init [--model id-or-path] [--provider static|openai|cohere] [--url url]',\n watch: 'watch [--force] [--config path]',\n status: 'status [--format table|json] [--config path]',\n download: 'download [--config path]',\n sql: 'sql \"<statement>\" [params...] [--preset name] [--format table|json|csv] [--config path]',\n search: 'search \"<terms>\" [--preset name] [--include glob ...] [--exclude glob ...] [--no-exclude] [--where \"<sql>\"] [--k n] [--format table|json|csv] [--config path]',\n map: 'map [--format table|json] [--config path]',\n peek: 'peek <path> [--preset name] [--include glob ...] [--exclude glob ...] [--no-exclude] [--where \"<sql>\"] [--format table|json] [--config path]',\n path: 'path <a> <b> [--preset name] [--include glob ...] [--exclude glob ...] [--no-exclude] [--where \"<sql>\"] [--max-depth n] [--format table|json] [--config path]',\n related: 'related <note> [--preset name] [--include glob ...] [--exclude glob ...] [--no-exclude] [--where \"<sql>\"] [--k n] [--format table|json|csv] [--config path]',\n} as const;\n\n// Lazy registry: a command's imports load only when it runs, so a heavy dependency in one\n// command never taxes the others (or --version/--help). These names are reserved: a saved\n// entry in config can never shadow them.\nexport const COMMANDS: Record<string, () => Promise<{ default: Command }>> = {\n init: () => import('./init.ts'),\n watch: () => import('./watch.ts'),\n status: () => import('./status.ts'),\n download: () => import('./download.ts'),\n sql: () => import('./sql.ts'),\n search: () => import('./search.ts'),\n map: () => import('./map.ts'),\n peek: () => import('./peek.ts'),\n path: () => import('./path.ts'),\n related: () => import('./related.ts'),\n};\n\nexport type { Command, Ctx } from './types.ts';\n"],"names":["USAGE","init","watch","status","download","sql","search","map","peek","path","related","COMMANDS"],"mappings":"AAEA,sFAAsF;AACtF,uFAAuF;AACvF,6CAA6C;AAC7C,OAAO,MAAMA,QAAQ;IACnBC,MAAM;IACNC,OAAO;IACPC,QAAQ;IACRC,UAAU;IACVC,KAAK;IACLC,QAAQ;IACRC,KAAK;IACLC,MAAM;IACNC,MAAM;IACNC,SAAS;AACX,EAAW;AAEX,0FAA0F;AAC1F,0FAA0F;AAC1F,yCAAyC;AACzC,OAAO,MAAMC,WAAgE;IAC3EV,MAAM,IAAM,MAAM,CAAC;IACnBC,OAAO,IAAM,MAAM,CAAC;IACpBC,QAAQ,IAAM,MAAM,CAAC;IACrBC,UAAU,IAAM,MAAM,CAAC;IACvBC,KAAK,IAAM,MAAM,CAAC;IAClBC,QAAQ,IAAM,MAAM,CAAC;IACrBC,KAAK,IAAM,MAAM,CAAC;IAClBC,MAAM,IAAM,MAAM,CAAC;IACnBC,MAAM,IAAM,MAAM,CAAC;IACnBC,SAAS,IAAM,MAAM,CAAC;AACxB,EAAE"}
|
package/dist/esm/cli/init.js
CHANGED
|
@@ -1,14 +1,40 @@
|
|
|
1
|
-
import { initConfig } from '../config/index.js';
|
|
1
|
+
import { DEFAULT_EMBED_MODEL, initConfig } from '../config/index.js';
|
|
2
2
|
import { USAGE } from './index.js';
|
|
3
3
|
import { parse } from './shared.js';
|
|
4
4
|
const init = (ctx)=>{
|
|
5
|
-
parse(ctx.argv, `usage: ${ctx.name} ${USAGE.init}`, {
|
|
6
|
-
|
|
5
|
+
const { values } = parse(ctx.argv, `usage: ${ctx.name} ${USAGE.init}`, {
|
|
6
|
+
model: {
|
|
7
|
+
type: 'string'
|
|
8
|
+
},
|
|
9
|
+
provider: {
|
|
10
|
+
type: 'string'
|
|
11
|
+
},
|
|
12
|
+
url: {
|
|
13
|
+
type: 'string'
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
const modelFlag = values.model;
|
|
17
|
+
const providerFlag = values.provider;
|
|
18
|
+
const model = modelFlag !== null && modelFlag !== void 0 ? modelFlag : DEFAULT_EMBED_MODEL;
|
|
19
|
+
const provider = providerFlag !== null && providerFlag !== void 0 ? providerFlag : 'static';
|
|
20
|
+
const url = values.url;
|
|
21
|
+
// An invalid --provider throws here (validateConfig's own shape error), before anything is
|
|
22
|
+
// written -- one validator, not a second copy of its rules.
|
|
23
|
+
const configPath = initConfig(process.cwd(), {
|
|
24
|
+
model: modelFlag,
|
|
25
|
+
provider: providerFlag,
|
|
26
|
+
url
|
|
27
|
+
});
|
|
7
28
|
console.log(`created ${configPath}`);
|
|
8
29
|
console.log(`query away: ${ctx.name} sql "SELECT path FROM frontmatter LIMIT 10"`);
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
|
|
30
|
+
// Naming a model is consent to fetch it, so writing one into the config says so loudly,
|
|
31
|
+
// with the download consequence and the prefetch command, right where it was just written.
|
|
32
|
+
if (provider === 'static') {
|
|
33
|
+
console.log(`embed model: ${model} (static); nothing downloads until the first vector search -- ${ctx.name} download fetches it now instead`);
|
|
34
|
+
} else {
|
|
35
|
+
console.log(`embed model: ${model} (${provider}${url ? ` at ${url}` : ''}); reached over the network at search time, nothing to download`);
|
|
36
|
+
}
|
|
37
|
+
console.log('a non-English tree should pick a model for its languages: see the sense-setup skill or INTEGRATIONS.md');
|
|
12
38
|
// The decisions a new tree faces -- features, frontmatter conventions, note size -- are not
|
|
13
39
|
// in this output; name both places that hold them, since an agent may have neither.
|
|
14
40
|
console.log('features and tree design: the sense-setup skill, or schema.json');
|
package/dist/esm/cli/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/init.ts"],"sourcesContent":["import { initConfig } from '../config/index.ts';\nimport { USAGE } from './index.ts';\nimport { parse } from './shared.ts';\nimport type { Command } from './types.ts';\n\nconst init: Command = (ctx) => {\n parse(ctx.argv, `usage: ${ctx.name} ${USAGE.init}`, {});\n const configPath = initConfig(process.cwd());\n console.log(`created ${configPath}`);\n console.log(`query away: ${ctx.name} sql \"SELECT path FROM frontmatter LIMIT 10\"`);\n //
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/init.ts"],"sourcesContent":["import { DEFAULT_EMBED_MODEL, initConfig } from '../config/index.ts';\nimport { USAGE } from './index.ts';\nimport { parse } from './shared.ts';\nimport type { Command } from './types.ts';\n\nconst init: Command = (ctx) => {\n const { values } = parse(ctx.argv, `usage: ${ctx.name} ${USAGE.init}`, {\n model: { type: 'string' },\n provider: { type: 'string' },\n url: { type: 'string' },\n });\n const modelFlag = values.model as string | undefined;\n const providerFlag = values.provider as string | undefined;\n const model = modelFlag ?? DEFAULT_EMBED_MODEL;\n const provider = providerFlag ?? 'static';\n const url = values.url as string | undefined;\n // An invalid --provider throws here (validateConfig's own shape error), before anything is\n // written -- one validator, not a second copy of its rules.\n const configPath = initConfig(process.cwd(), { model: modelFlag, provider: providerFlag, url });\n console.log(`created ${configPath}`);\n console.log(`query away: ${ctx.name} sql \"SELECT path FROM frontmatter LIMIT 10\"`);\n // Naming a model is consent to fetch it, so writing one into the config says so loudly,\n // with the download consequence and the prefetch command, right where it was just written.\n if (provider === 'static') {\n console.log(`embed model: ${model} (static); nothing downloads until the first vector search -- ${ctx.name} download fetches it now instead`);\n } else {\n console.log(`embed model: ${model} (${provider}${url ? ` at ${url}` : ''}); reached over the network at search time, nothing to download`);\n }\n console.log('a non-English tree should pick a model for its languages: see the sense-setup skill or INTEGRATIONS.md');\n // The decisions a new tree faces -- features, frontmatter conventions, note size -- are not\n // in this output; name both places that hold them, since an agent may have neither.\n console.log('features and tree design: the sense-setup skill, or schema.json');\n};\nexport default init;\n"],"names":["DEFAULT_EMBED_MODEL","initConfig","USAGE","parse","init","ctx","values","argv","name","model","type","provider","url","modelFlag","providerFlag","configPath","process","cwd","console","log"],"mappings":"AAAA,SAASA,mBAAmB,EAAEC,UAAU,QAAQ,qBAAqB;AACrE,SAASC,KAAK,QAAQ,aAAa;AACnC,SAASC,KAAK,QAAQ,cAAc;AAGpC,MAAMC,OAAgB,CAACC;IACrB,MAAM,EAAEC,MAAM,EAAE,GAAGH,MAAME,IAAIE,IAAI,EAAE,CAAC,OAAO,EAAEF,IAAIG,IAAI,CAAC,CAAC,EAAEN,MAAME,IAAI,EAAE,EAAE;QACrEK,OAAO;YAAEC,MAAM;QAAS;QACxBC,UAAU;YAAED,MAAM;QAAS;QAC3BE,KAAK;YAAEF,MAAM;QAAS;IACxB;IACA,MAAMG,YAAYP,OAAOG,KAAK;IAC9B,MAAMK,eAAeR,OAAOK,QAAQ;IACpC,MAAMF,QAAQI,sBAAAA,uBAAAA,YAAab;IAC3B,MAAMW,WAAWG,yBAAAA,0BAAAA,eAAgB;IACjC,MAAMF,MAAMN,OAAOM,GAAG;IACtB,2FAA2F;IAC3F,4DAA4D;IAC5D,MAAMG,aAAad,WAAWe,QAAQC,GAAG,IAAI;QAAER,OAAOI;QAAWF,UAAUG;QAAcF;IAAI;IAC7FM,QAAQC,GAAG,CAAC,CAAC,QAAQ,EAAEJ,YAAY;IACnCG,QAAQC,GAAG,CAAC,CAAC,YAAY,EAAEd,IAAIG,IAAI,CAAC,4CAA4C,CAAC;IACjF,wFAAwF;IACxF,2FAA2F;IAC3F,IAAIG,aAAa,UAAU;QACzBO,QAAQC,GAAG,CAAC,CAAC,aAAa,EAAEV,MAAM,8DAA8D,EAAEJ,IAAIG,IAAI,CAAC,gCAAgC,CAAC;IAC9I,OAAO;QACLU,QAAQC,GAAG,CAAC,CAAC,aAAa,EAAEV,MAAM,EAAE,EAAEE,WAAWC,MAAM,CAAC,IAAI,EAAEA,KAAK,GAAG,GAAG,+DAA+D,CAAC;IAC3I;IACAM,QAAQC,GAAG,CAAC;IACZ,4FAA4F;IAC5F,oFAAoF;IACpFD,QAAQC,GAAG,CAAC;AACd;AACA,eAAef,KAAK"}
|
package/dist/esm/cli/status.js
CHANGED
|
@@ -2,7 +2,9 @@ import { dirname } from 'node:path';
|
|
|
2
2
|
import { presetCoverage } from '../commands/index.js';
|
|
3
3
|
import { embedConfig, featureStates, SUPPORTED_CONFIG_VERSION } from '../config/index.js';
|
|
4
4
|
import { docCount, getMeta, open, SCHEMA_VERSION } from '../db/index.js';
|
|
5
|
-
import {
|
|
5
|
+
import { probeReachable } from '../embed/http.js';
|
|
6
|
+
import { languageDistribution } from '../embed/langfit.js';
|
|
7
|
+
import { isDownloadable, MODEL_FILENAMES, modelDir, modelPresent, readLanguages } from '../embed/store.js';
|
|
6
8
|
import { featuresLine, presetsLines } from '../output.js';
|
|
7
9
|
import { USAGE } from './index.js';
|
|
8
10
|
import { CONFIG, FORMAT, formatOf, parse, printWarnings } from './shared.js';
|
|
@@ -38,8 +40,16 @@ export function redactUrl(url) {
|
|
|
38
40
|
return url;
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
// A stopped Ollama/LM Studio/Cohere endpoint prints identically to a running one otherwise; any
|
|
44
|
+
// HTTP response counts as reachable (probeReachable), so this only distinguishes up from down.
|
|
45
|
+
async function embedReachable(e) {
|
|
46
|
+
var _e_url;
|
|
47
|
+
if (e.provider === 'static' || e.provider === 'openai' && !e.url) return null;
|
|
48
|
+
const base = ((_e_url = e.url) !== null && _e_url !== void 0 ? _e_url : 'https://api.cohere.com').replace(/\/+$/, '');
|
|
49
|
+
return probeReachable(e.provider === 'openai' ? `${base}/models` : base);
|
|
50
|
+
}
|
|
51
|
+
const status = async (ctx)=>{
|
|
52
|
+
var _languageDistribution, _process_env_e_key, _cfg_migratedFrom, _cfg_unknownKeys, _cfg_queries;
|
|
43
53
|
const { values } = parse(ctx.argv, `usage: ${ctx.name} ${USAGE.status}`, {
|
|
44
54
|
...FORMAT,
|
|
45
55
|
...CONFIG
|
|
@@ -51,8 +61,12 @@ const status = (ctx)=>{
|
|
|
51
61
|
const features = featureStates(cfg);
|
|
52
62
|
const e = embedConfig(cfg);
|
|
53
63
|
const hasModel = e ? modelPresent(cfg) : false;
|
|
64
|
+
const reachable = e ? await embedReachable(e) : null;
|
|
54
65
|
const vectors = e ? vectorState(db) : null;
|
|
55
66
|
const presets = presetCoverage(db, cfg);
|
|
67
|
+
const staticLanguages = e && e.provider === 'static' ? readLanguages(e.model) : undefined;
|
|
68
|
+
const declaredLanguages = staticLanguages && staticLanguages.length > 0 ? staticLanguages : null;
|
|
69
|
+
const detectedLanguages = (_languageDistribution = languageDistribution(db)) !== null && _languageDistribution !== void 0 ? _languageDistribution : null;
|
|
56
70
|
const heartbeat = getMeta(db, 'watch_heartbeat');
|
|
57
71
|
// Derived at open() (F): 3x the largest reconcile this cache has ever held its write
|
|
58
72
|
// transaction for, floored at 30s -- read back from the connection rather than
|
|
@@ -77,12 +91,15 @@ const status = (ctx)=>{
|
|
|
77
91
|
features: features.on,
|
|
78
92
|
featuresOff: features.off,
|
|
79
93
|
embed: e ? {
|
|
80
|
-
|
|
94
|
+
provider: e.provider,
|
|
81
95
|
model: e.model,
|
|
82
|
-
dir: e.
|
|
96
|
+
dir: e.provider === 'static' ? modelDir(e.model) : null,
|
|
83
97
|
url: e.url ? redactUrl(e.url) : null,
|
|
98
|
+
reachable,
|
|
84
99
|
keyEnv,
|
|
85
100
|
downloaded: hasModel,
|
|
101
|
+
languages: declaredLanguages,
|
|
102
|
+
detectedLanguages,
|
|
86
103
|
...vectors !== null && vectors !== void 0 ? vectors : {}
|
|
87
104
|
} : null,
|
|
88
105
|
presets,
|
|
@@ -107,12 +124,23 @@ const status = (ctx)=>{
|
|
|
107
124
|
// not as an empty corpus.
|
|
108
125
|
if (!e) console.log('embed: off (no preset asks for vectors)');
|
|
109
126
|
else {
|
|
110
|
-
console.log(`embed: ${e.
|
|
111
|
-
if (e.
|
|
112
|
-
if (
|
|
127
|
+
console.log(`embed: ${e.provider} ${e.model}`);
|
|
128
|
+
if (e.provider === 'static') console.log(` model: ${modelDir(e.model)} (${hasModel ? 'present' : `missing, ${isDownloadable(e.model) ? `fetches automatically at first search, or run \`${ctx.name} download\` to prefetch` : `no ${MODEL_FILENAMES} in that directory`}`})`);
|
|
129
|
+
if (declaredLanguages) console.log(` languages: ${declaredLanguages.join(', ')} (declared by the model card)`);
|
|
130
|
+
// Cohere defaults to api.cohere.com with no url in the config; show it anyway so the
|
|
131
|
+
// reachability probe below has something to name.
|
|
132
|
+
if (e.url || reachable !== null) {
|
|
133
|
+
const shown = e.url ? redactUrl(e.url) : 'https://api.cohere.com (default)';
|
|
134
|
+
console.log(` url: ${shown}${reachable === null ? '' : reachable ? ' (reachable)' : ' (unreachable)'}`);
|
|
135
|
+
}
|
|
113
136
|
if (keyEnv) console.log(` key: env ${keyEnv.name} (${keyEnv.set ? 'set' : 'NOT SET'})`);
|
|
114
137
|
if (!hasModel) console.log(' vectors: off until the model is present; search runs on words and links');
|
|
115
138
|
else if (vectors) console.log(` vectors: ${vectors.embedded} embedded, ${vectors.pending} pending (embedded on the first semantic search)`);
|
|
139
|
+
if (detectedLanguages) {
|
|
140
|
+
const total = Object.values(detectedLanguages).reduce((a, b)=>a + b, 0);
|
|
141
|
+
const breakdown = Object.entries(detectedLanguages).sort((a, b)=>b[1] - a[1]).map(([code, n])=>`${code} ${Math.round(n / total * 100)}%`).join(', ');
|
|
142
|
+
console.log(` detected: ${breakdown} (${total} chunks classified)`);
|
|
143
|
+
}
|
|
116
144
|
}
|
|
117
145
|
console.log('');
|
|
118
146
|
for (const line of presetsLines(presets))console.log(line);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/status.ts"],"sourcesContent":["import { dirname } from 'node:path';\nimport { presetCoverage } from '../commands/index.ts';\nimport { embedConfig, featureStates, SUPPORTED_CONFIG_VERSION } from '../config/index.ts';\nimport { docCount, getMeta, open, SCHEMA_VERSION } from '../db/index.ts';\nimport { isDownloadable, MODEL_FILENAMES, modelDir, modelPresent } from '../features/embed.ts';\nimport { featuresLine, presetsLines } from '../output.ts';\nimport { USAGE } from './index.ts';\nimport { CONFIG, FORMAT, formatOf, parse, printWarnings } from './shared.ts';\nimport type { Command } from './types.ts';\n\n// status names what you cannot read off the config file: locations sense chose, values it\n// derived, state it stored. Config content is not echoed -- the config line says which file to\n// read, and duplicating its globs here would create two places that disagree after an edit.\n// The exception is the embed block's url and key, whose effect is remote and otherwise invisible.\n\n// Vectors are computed on the first semantic query, not at reconcile, so a tree can have a\n// semantic-on preset with nothing embedded yet -- reported here because a fast reconcile on\n// such a tree otherwise reads as \"the feature isn't on\".\nfunction vectorState(db: ReturnType<typeof open>['db']): { embedded: number; pending: number } | null {\n try {\n const row = db.prepare('SELECT COUNT(vector) AS embedded, COUNT(*) - COUNT(vector) AS pending FROM embeddings').get() as { embedded: number; pending: number };\n return row;\n } catch {\n return null;\n }\n}\n\nfunction parseErrorCount(db: ReturnType<typeof open>['db']): number {\n const row = db.prepare(`SELECT COUNT(*) AS n FROM frontmatter WHERE \"_parse_error\" IS NOT NULL`).get() as { n: number };\n return row.n;\n}\n\n// A URL can carry credentials in its userinfo (https://user:pass@host/v1). status output gets\n// pasted into issues and transcripts, so it is always safe to paste.\nexport function redactUrl(url: string): string {\n try {\n const parsed = new URL(url);\n if (!parsed.username && !parsed.password) return url;\n parsed.username = '';\n parsed.password = '';\n return `${parsed.toString().replace(/\\/$/, '')} (credentials redacted)`;\n } catch {\n return url;\n }\n}\n\nconst status: Command = (ctx) => {\n const { values } = parse(ctx.argv, `usage: ${ctx.name} ${USAGE.status}`, { ...FORMAT, ...CONFIG });\n const format = formatOf(values);\n const cfg = ctx.resolveConfig(values.config as string | undefined);\n const { db, dbPath, warnings } = open(cfg);\n printWarnings(warnings);\n\n const features = featureStates(cfg);\n const e = embedConfig(cfg);\n const hasModel = e ? modelPresent(cfg) : false;\n const vectors = e ? vectorState(db) : null;\n const presets = presetCoverage(db, cfg);\n const heartbeat = getMeta(db, 'watch_heartbeat');\n // Derived at open() (F): 3x the largest reconcile this cache has ever held its write\n // transaction for, floored at 30s -- read back from the connection rather than\n // recomputed here, so this always reports what open() actually set.\n const busyTimeoutMs = (db.prepare('PRAGMA busy_timeout').get() as { timeout: number }).timeout;\n // The env var holds the token; only its name and whether it is set are ever reported.\n const keyEnv = e?.key ? { name: e.key, set: (process.env[e.key] ?? '') !== '' } : null;\n const result = {\n config: cfg.configPath,\n configVersion: SUPPORTED_CONFIG_VERSION,\n migratedFrom: cfg.migratedFrom ?? null,\n unknownConfigKeys: cfg.unknownKeys ?? null,\n tree: cfg.baseDir,\n cache: dirname(dbPath),\n db: dbPath,\n cacheSchema: SCHEMA_VERSION,\n docs: docCount(db),\n unparseableFrontmatter: parseErrorCount(db),\n features: features.on,\n featuresOff: features.off,\n embed: e ? { type: e.type, model: e.model, dir: e.type === 'static' ? modelDir(e.model) : null, url: e.url ? redactUrl(e.url) : null, keyEnv, downloaded: hasModel, ...(vectors ?? {}) } : null,\n presets,\n queries: Object.keys(cfg.queries ?? {}).length,\n watcherPid: getMeta(db, 'watch_pid'),\n watcherHeartbeatSecondsAgo: heartbeat ? Math.round((Date.now() - Date.parse(heartbeat)) / 1000) : null,\n busyTimeoutMs,\n };\n\n if (format === 'json') {\n console.log(JSON.stringify(result, null, 2));\n } else {\n const migrated = cfg.migratedFrom !== undefined ? `, migrated from v${cfg.migratedFrom} on this run` : '';\n console.log(`config: ${result.config} (v${SUPPORTED_CONFIG_VERSION}${migrated})`);\n // A load-time stderr warning has scrolled away by the time anyone looks here.\n if (cfg.unknownKeys) console.log(` unknown keys, ignored: ${cfg.unknownKeys.join(', ')}`);\n console.log(`tree: ${result.tree}`);\n console.log(`cache: ${result.cache} (schema ${SCHEMA_VERSION}; delete this directory to reset)`);\n console.log(`docs: ${result.docs}${result.unparseableFrontmatter > 0 ? ` (${result.unparseableFrontmatter} with unparseable frontmatter: WHERE _parse_error IS NOT NULL)` : ''}`);\n console.log('');\n console.log(featuresLine(features));\n // One authoritative block, off states included: an absent model reads as a degraded search,\n // not as an empty corpus.\n if (!e) console.log('embed: off (no preset asks for vectors)');\n else {\n console.log(`embed: ${e.type} ${e.model}`);\n if (e.type === 'static') console.log(` model: ${modelDir(e.model)} (${hasModel ? 'present' : `missing, ${isDownloadable(e.model) ? `run \\`${ctx.name} download\\`` : `no ${MODEL_FILENAMES} in that directory`}`})`);\n if (e.url) console.log(` url: ${redactUrl(e.url)}`);\n if (keyEnv) console.log(` key: env ${keyEnv.name} (${keyEnv.set ? 'set' : 'NOT SET'})`);\n if (!hasModel) console.log(' vectors: off until the model is present; search runs on words and links');\n else if (vectors) console.log(` vectors: ${vectors.embedded} embedded, ${vectors.pending} pending (embedded on the first semantic search)`);\n }\n console.log('');\n for (const line of presetsLines(presets)) console.log(line);\n console.log('');\n console.log(`queries: ${result.queries} saved (${ctx.name} --list)`);\n const pid = result.watcherPid ? `pid ${result.watcherPid}, ` : '';\n console.log(result.watcherHeartbeatSecondsAgo === null ? 'watcher: none' : `watcher: ${pid}last heartbeat ${result.watcherHeartbeatSecondsAgo}s ago`);\n console.log(`sqlite: busy_timeout ${result.busyTimeoutMs}ms (derived: 3x the largest reconcile this cache has recorded, floored at 30000ms)`);\n }\n db.close();\n};\nexport default status;\n"],"names":["dirname","presetCoverage","embedConfig","featureStates","SUPPORTED_CONFIG_VERSION","docCount","getMeta","open","SCHEMA_VERSION","isDownloadable","MODEL_FILENAMES","modelDir","modelPresent","featuresLine","presetsLines","USAGE","CONFIG","FORMAT","formatOf","parse","printWarnings","vectorState","db","row","prepare","get","parseErrorCount","n","redactUrl","url","parsed","URL","username","password","toString","replace","status","ctx","process","cfg","values","argv","name","format","resolveConfig","config","dbPath","warnings","features","e","hasModel","vectors","presets","heartbeat","busyTimeoutMs","timeout","keyEnv","key","set","env","result","configPath","configVersion","migratedFrom","unknownConfigKeys","unknownKeys","tree","baseDir","cache","cacheSchema","docs","unparseableFrontmatter","on","featuresOff","off","embed","type","model","dir","downloaded","queries","Object","keys","length","watcherPid","watcherHeartbeatSecondsAgo","Math","round","Date","now","console","log","JSON","stringify","migrated","undefined","join","embedded","pending","line","pid","close"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/cli/status.ts"],"sourcesContent":["import { dirname } from 'node:path';\nimport { presetCoverage } from '../commands/index.ts';\nimport { embedConfig, featureStates, SUPPORTED_CONFIG_VERSION } from '../config/index.ts';\nimport { docCount, getMeta, open, SCHEMA_VERSION } from '../db/index.ts';\nimport { probeReachable } from '../embed/http.ts';\nimport { languageDistribution } from '../embed/langfit.ts';\nimport { isDownloadable, MODEL_FILENAMES, modelDir, modelPresent, readLanguages } from '../embed/store.ts';\nimport { featuresLine, presetsLines } from '../output.ts';\nimport { USAGE } from './index.ts';\nimport { CONFIG, FORMAT, formatOf, parse, printWarnings } from './shared.ts';\nimport type { Command } from './types.ts';\n\n// status names what you cannot read off the config file: locations sense chose, values it\n// derived, state it stored. Config content is not echoed -- the config line says which file to\n// read, and duplicating its globs here would create two places that disagree after an edit.\n// The exception is the embed block's url and key, whose effect is remote and otherwise invisible.\n\n// Vectors are computed on the first semantic query, not at reconcile, so a tree can have a\n// semantic-on preset with nothing embedded yet -- reported here because a fast reconcile on\n// such a tree otherwise reads as \"the feature isn't on\".\nfunction vectorState(db: ReturnType<typeof open>['db']): { embedded: number; pending: number } | null {\n try {\n const row = db.prepare('SELECT COUNT(vector) AS embedded, COUNT(*) - COUNT(vector) AS pending FROM embeddings').get() as { embedded: number; pending: number };\n return row;\n } catch {\n return null;\n }\n}\n\nfunction parseErrorCount(db: ReturnType<typeof open>['db']): number {\n const row = db.prepare(`SELECT COUNT(*) AS n FROM frontmatter WHERE \"_parse_error\" IS NOT NULL`).get() as { n: number };\n return row.n;\n}\n\n// A URL can carry credentials in its userinfo (https://user:pass@host/v1). status output gets\n// pasted into issues and transcripts, so it is always safe to paste.\nexport function redactUrl(url: string): string {\n try {\n const parsed = new URL(url);\n if (!parsed.username && !parsed.password) return url;\n parsed.username = '';\n parsed.password = '';\n return `${parsed.toString().replace(/\\/$/, '')} (credentials redacted)`;\n } catch {\n return url;\n }\n}\n\n// A stopped Ollama/LM Studio/Cohere endpoint prints identically to a running one otherwise; any\n// HTTP response counts as reachable (probeReachable), so this only distinguishes up from down.\nasync function embedReachable(e: { provider: string; url?: string }): Promise<boolean | null> {\n if (e.provider === 'static' || (e.provider === 'openai' && !e.url)) return null;\n const base = (e.url ?? 'https://api.cohere.com').replace(/\\/+$/, '');\n return probeReachable(e.provider === 'openai' ? `${base}/models` : base);\n}\n\nconst status: Command = async (ctx) => {\n const { values } = parse(ctx.argv, `usage: ${ctx.name} ${USAGE.status}`, { ...FORMAT, ...CONFIG });\n const format = formatOf(values);\n const cfg = ctx.resolveConfig(values.config as string | undefined);\n const { db, dbPath, warnings } = open(cfg);\n printWarnings(warnings);\n\n const features = featureStates(cfg);\n const e = embedConfig(cfg);\n const hasModel = e ? modelPresent(cfg) : false;\n const reachable = e ? await embedReachable(e) : null;\n const vectors = e ? vectorState(db) : null;\n const presets = presetCoverage(db, cfg);\n const staticLanguages = e && e.provider === 'static' ? readLanguages(e.model) : undefined;\n const declaredLanguages = staticLanguages && staticLanguages.length > 0 ? staticLanguages : null;\n const detectedLanguages = languageDistribution(db) ?? null;\n const heartbeat = getMeta(db, 'watch_heartbeat');\n // Derived at open() (F): 3x the largest reconcile this cache has ever held its write\n // transaction for, floored at 30s -- read back from the connection rather than\n // recomputed here, so this always reports what open() actually set.\n const busyTimeoutMs = (db.prepare('PRAGMA busy_timeout').get() as { timeout: number }).timeout;\n // The env var holds the token; only its name and whether it is set are ever reported.\n const keyEnv = e?.key ? { name: e.key, set: (process.env[e.key] ?? '') !== '' } : null;\n const result = {\n config: cfg.configPath,\n configVersion: SUPPORTED_CONFIG_VERSION,\n migratedFrom: cfg.migratedFrom ?? null,\n unknownConfigKeys: cfg.unknownKeys ?? null,\n tree: cfg.baseDir,\n cache: dirname(dbPath),\n db: dbPath,\n cacheSchema: SCHEMA_VERSION,\n docs: docCount(db),\n unparseableFrontmatter: parseErrorCount(db),\n features: features.on,\n featuresOff: features.off,\n embed: e ? { provider: e.provider, model: e.model, dir: e.provider === 'static' ? modelDir(e.model) : null, url: e.url ? redactUrl(e.url) : null, reachable, keyEnv, downloaded: hasModel, languages: declaredLanguages, detectedLanguages, ...(vectors ?? {}) } : null,\n presets,\n queries: Object.keys(cfg.queries ?? {}).length,\n watcherPid: getMeta(db, 'watch_pid'),\n watcherHeartbeatSecondsAgo: heartbeat ? Math.round((Date.now() - Date.parse(heartbeat)) / 1000) : null,\n busyTimeoutMs,\n };\n\n if (format === 'json') {\n console.log(JSON.stringify(result, null, 2));\n } else {\n const migrated = cfg.migratedFrom !== undefined ? `, migrated from v${cfg.migratedFrom} on this run` : '';\n console.log(`config: ${result.config} (v${SUPPORTED_CONFIG_VERSION}${migrated})`);\n // A load-time stderr warning has scrolled away by the time anyone looks here.\n if (cfg.unknownKeys) console.log(` unknown keys, ignored: ${cfg.unknownKeys.join(', ')}`);\n console.log(`tree: ${result.tree}`);\n console.log(`cache: ${result.cache} (schema ${SCHEMA_VERSION}; delete this directory to reset)`);\n console.log(`docs: ${result.docs}${result.unparseableFrontmatter > 0 ? ` (${result.unparseableFrontmatter} with unparseable frontmatter: WHERE _parse_error IS NOT NULL)` : ''}`);\n console.log('');\n console.log(featuresLine(features));\n // One authoritative block, off states included: an absent model reads as a degraded search,\n // not as an empty corpus.\n if (!e) console.log('embed: off (no preset asks for vectors)');\n else {\n console.log(`embed: ${e.provider} ${e.model}`);\n if (e.provider === 'static') console.log(` model: ${modelDir(e.model)} (${hasModel ? 'present' : `missing, ${isDownloadable(e.model) ? `fetches automatically at first search, or run \\`${ctx.name} download\\` to prefetch` : `no ${MODEL_FILENAMES} in that directory`}`})`);\n if (declaredLanguages) console.log(` languages: ${declaredLanguages.join(', ')} (declared by the model card)`);\n // Cohere defaults to api.cohere.com with no url in the config; show it anyway so the\n // reachability probe below has something to name.\n if (e.url || reachable !== null) {\n const shown = e.url ? redactUrl(e.url) : 'https://api.cohere.com (default)';\n console.log(` url: ${shown}${reachable === null ? '' : reachable ? ' (reachable)' : ' (unreachable)'}`);\n }\n if (keyEnv) console.log(` key: env ${keyEnv.name} (${keyEnv.set ? 'set' : 'NOT SET'})`);\n if (!hasModel) console.log(' vectors: off until the model is present; search runs on words and links');\n else if (vectors) console.log(` vectors: ${vectors.embedded} embedded, ${vectors.pending} pending (embedded on the first semantic search)`);\n if (detectedLanguages) {\n const total = Object.values(detectedLanguages).reduce((a, b) => a + b, 0);\n const breakdown = Object.entries(detectedLanguages)\n .sort((a, b) => b[1] - a[1])\n .map(([code, n]) => `${code} ${Math.round((n / total) * 100)}%`)\n .join(', ');\n console.log(` detected: ${breakdown} (${total} chunks classified)`);\n }\n }\n console.log('');\n for (const line of presetsLines(presets)) console.log(line);\n console.log('');\n console.log(`queries: ${result.queries} saved (${ctx.name} --list)`);\n const pid = result.watcherPid ? `pid ${result.watcherPid}, ` : '';\n console.log(result.watcherHeartbeatSecondsAgo === null ? 'watcher: none' : `watcher: ${pid}last heartbeat ${result.watcherHeartbeatSecondsAgo}s ago`);\n console.log(`sqlite: busy_timeout ${result.busyTimeoutMs}ms (derived: 3x the largest reconcile this cache has recorded, floored at 30000ms)`);\n }\n db.close();\n};\nexport default status;\n"],"names":["dirname","presetCoverage","embedConfig","featureStates","SUPPORTED_CONFIG_VERSION","docCount","getMeta","open","SCHEMA_VERSION","probeReachable","languageDistribution","isDownloadable","MODEL_FILENAMES","modelDir","modelPresent","readLanguages","featuresLine","presetsLines","USAGE","CONFIG","FORMAT","formatOf","parse","printWarnings","vectorState","db","row","prepare","get","parseErrorCount","n","redactUrl","url","parsed","URL","username","password","toString","replace","embedReachable","e","provider","base","status","ctx","process","cfg","values","argv","name","format","resolveConfig","config","dbPath","warnings","features","hasModel","reachable","vectors","presets","staticLanguages","model","undefined","declaredLanguages","length","detectedLanguages","heartbeat","busyTimeoutMs","timeout","keyEnv","key","set","env","result","configPath","configVersion","migratedFrom","unknownConfigKeys","unknownKeys","tree","baseDir","cache","cacheSchema","docs","unparseableFrontmatter","on","featuresOff","off","embed","dir","downloaded","languages","queries","Object","keys","watcherPid","watcherHeartbeatSecondsAgo","Math","round","Date","now","console","log","JSON","stringify","migrated","join","shown","embedded","pending","total","reduce","a","b","breakdown","entries","sort","map","code","line","pid","close"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { embedEnabled, resolveSearch } from '../config/index.js';
|
|
1
|
+
import { embedConfig, embedEnabled, resolveSearch } from '../config/index.js';
|
|
2
|
+
import { embedPending, hasEmbedding, similarNotes } from '../embed/query.js';
|
|
3
|
+
import { localModelMissing, MODEL_FILENAMES } from '../embed/store.js';
|
|
2
4
|
import { SenseError } from '../errors.js';
|
|
3
|
-
import { embedPending, hasEmbedding, modelPresent, similarNotes } from '../features/embed.js';
|
|
4
5
|
import { resolveNote } from './peek.js';
|
|
5
6
|
import { scopedPaths, scopeHasEmbeddings } from './scope.js';
|
|
6
7
|
// Notes most similar by cosine, excluding self and everything already linked either way.
|
|
@@ -20,16 +21,19 @@ export async function relatedNotes(db, cfg, pathArg, overrides, k) {
|
|
|
20
21
|
// note does not already link to, which is a real answer.
|
|
21
22
|
const effective = resolveSearch(cfg, overrides);
|
|
22
23
|
if (!embedEnabled(cfg)) {
|
|
23
|
-
throw new SenseError('EMBED_DISABLED', 'related ranks notes by meaning, and this tree has no embedding model; add an "embed" block naming one to sense.config.json
|
|
24
|
+
throw new SenseError('EMBED_DISABLED', 'related ranks notes by meaning, and this tree has no embedding model; add an "embed" block naming one to sense.config.json (a Hugging Face id fetches automatically at first use; `sense download` prefetches it) -- search works without it, on words and links');
|
|
24
25
|
}
|
|
25
|
-
// search gates on the same
|
|
26
|
-
//
|
|
27
|
-
// vectors would answer for a scope that declined them.
|
|
28
|
-
if (
|
|
29
|
-
throw new SenseError('PRESET_NOT_SEMANTIC', `preset "${effective.presetName}"
|
|
26
|
+
// search gates on the same signal (see wantsVectors above); reading it here too keeps a
|
|
27
|
+
// preset's declared signals meaning one thing. Without this, an overlapping vectors-on
|
|
28
|
+
// preset's vectors would answer for a scope that declined them.
|
|
29
|
+
if (effective.signals.vectors === undefined) {
|
|
30
|
+
throw new SenseError('PRESET_NOT_SEMANTIC', `preset "${effective.presetName}" has no "vectors" signal, so this scope has no vectors and related has no other signal; search it instead (words and links), or add "vectors" to that preset's signals`);
|
|
30
31
|
}
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
// A downloadable HF id proceeds -- embedPending's getProvider call fetches it lazily on
|
|
33
|
+
// consent. Only a local path with missing files can never fetch itself.
|
|
34
|
+
const e = embedConfig(cfg); // embedEnabled(cfg) above guarantees this is set
|
|
35
|
+
if (localModelMissing(e)) {
|
|
36
|
+
throw new SenseError('EMBED_MODEL_MISSING', `related ranks notes by meaning, so it needs the embedding model, but the local model path "${e.model}" is missing ${MODEL_FILENAMES}; point embed.model at a directory containing them (search still works without it, on words and links)`);
|
|
33
37
|
}
|
|
34
38
|
const allowed = scopedPaths(db, cfg, overrides);
|
|
35
39
|
// Top up pending rows before the seed check, or a fresh index reports every note as
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/related.ts"],"sourcesContent":["import type { DatabaseSync } from 'node:sqlite';\nimport type { ResolvedConfig, SearchOverrides } from '../config/index.ts';\nimport { embedEnabled, resolveSearch } from '../config/index.ts';\nimport {
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/commands/related.ts"],"sourcesContent":["import type { DatabaseSync } from 'node:sqlite';\nimport type { ResolvedConfig, SearchOverrides } from '../config/index.ts';\nimport { embedConfig, embedEnabled, resolveSearch } from '../config/index.ts';\nimport { embedPending, hasEmbedding, similarNotes } from '../embed/query.ts';\nimport { localModelMissing, MODEL_FILENAMES } from '../embed/store.ts';\nimport { SenseError } from '../errors.ts';\nimport { resolveNote } from './peek.ts';\nimport { scopedPaths, scopeHasEmbeddings } from './scope.ts';\n\n// Notes most similar by cosine, excluding self and everything already linked either way.\n// Its own command, not a peek section: a full embeddings scan is ~480ms at 26k notes.\nexport async function relatedNotes(db: DatabaseSync, cfg: ResolvedConfig, pathArg: string, overrides: SearchOverrides, k: number): Promise<Array<{ path: string; similarity: number }>> {\n const paths = (db.prepare('SELECT \"path\" FROM frontmatter').all() as Array<{ path: string }>).map((r) => r.path);\n const path = resolveNote(paths, pathArg);\n\n const outbound = (db.prepare('SELECT DISTINCT dst FROM links WHERE src = ? AND dst IS NOT NULL AND dst != src').all(path) as Array<{ dst: string }>).map((r) => r.dst);\n const backlinks = (db.prepare('SELECT DISTINCT src FROM links WHERE dst = ? AND src != dst').all(path) as Array<{ src: string }>).map((r) => r.src);\n const exclude = new Set([path, ...outbound, ...backlinks]);\n\n // Vectors are the only signal `related` has, so every way of not having them is an error\n // naming the cause. An empty table then means one thing: nothing near in meaning that this\n // note does not already link to, which is a real answer.\n const effective = resolveSearch(cfg, overrides);\n if (!embedEnabled(cfg)) {\n throw new SenseError('EMBED_DISABLED', 'related ranks notes by meaning, and this tree has no embedding model; add an \"embed\" block naming one to sense.config.json (a Hugging Face id fetches automatically at first use; `sense download` prefetches it) -- search works without it, on words and links');\n }\n // search gates on the same signal (see wantsVectors above); reading it here too keeps a\n // preset's declared signals meaning one thing. Without this, an overlapping vectors-on\n // preset's vectors would answer for a scope that declined them.\n if (effective.signals.vectors === undefined) {\n throw new SenseError('PRESET_NOT_SEMANTIC', `preset \"${effective.presetName}\" has no \"vectors\" signal, so this scope has no vectors and related has no other signal; search it instead (words and links), or add \"vectors\" to that preset's signals`);\n }\n // A downloadable HF id proceeds -- embedPending's getProvider call fetches it lazily on\n // consent. Only a local path with missing files can never fetch itself.\n const e = embedConfig(cfg); // embedEnabled(cfg) above guarantees this is set\n if (localModelMissing(e)) {\n throw new SenseError('EMBED_MODEL_MISSING', `related ranks notes by meaning, so it needs the embedding model, but the local model path \"${e.model}\" is missing ${MODEL_FILENAMES}; point embed.model at a directory containing them (search still works without it, on words and links)`);\n }\n const allowed = scopedPaths(db, cfg, overrides);\n // Top up pending rows before the seed check, or a fresh index reports every note as\n // having no indexed text until some search has run.\n await embedPending(db, cfg, cfg.baseDir);\n if (!hasEmbedding(db, path)) {\n throw new SenseError('NOTE_NOT_EMBEDDED', `${path} has no indexed text to compare -- a note that is frontmatter only, or empty, has nothing to rank by meaning`);\n }\n if (!scopeHasEmbeddings(db, cfg, allowed)) return [];\n return similarNotes(db, cfg, path, { exclude, allowed, k });\n}\n"],"names":["embedConfig","embedEnabled","resolveSearch","embedPending","hasEmbedding","similarNotes","localModelMissing","MODEL_FILENAMES","SenseError","resolveNote","scopedPaths","scopeHasEmbeddings","relatedNotes","db","cfg","pathArg","overrides","k","paths","prepare","all","map","r","path","outbound","dst","backlinks","src","exclude","Set","effective","signals","vectors","undefined","presetName","e","model","allowed","baseDir"],"mappings":"AAEA,SAASA,WAAW,EAAEC,YAAY,EAAEC,aAAa,QAAQ,qBAAqB;AAC9E,SAASC,YAAY,EAAEC,YAAY,EAAEC,YAAY,QAAQ,oBAAoB;AAC7E,SAASC,iBAAiB,EAAEC,eAAe,QAAQ,oBAAoB;AACvE,SAASC,UAAU,QAAQ,eAAe;AAC1C,SAASC,WAAW,QAAQ,YAAY;AACxC,SAASC,WAAW,EAAEC,kBAAkB,QAAQ,aAAa;AAE7D,yFAAyF;AACzF,sFAAsF;AACtF,OAAO,eAAeC,aAAaC,EAAgB,EAAEC,GAAmB,EAAEC,OAAe,EAAEC,SAA0B,EAAEC,CAAS;IAC9H,MAAMC,QAAQ,AAACL,GAAGM,OAAO,CAAC,kCAAkCC,GAAG,GAA+BC,GAAG,CAAC,CAACC,IAAMA,EAAEC,IAAI;IAC/G,MAAMA,OAAOd,YAAYS,OAAOH;IAEhC,MAAMS,WAAW,AAACX,GAAGM,OAAO,CAAC,mFAAmFC,GAAG,CAACG,MAAiCF,GAAG,CAAC,CAACC,IAAMA,EAAEG,GAAG;IACrK,MAAMC,YAAY,AAACb,GAAGM,OAAO,CAAC,+DAA+DC,GAAG,CAACG,MAAiCF,GAAG,CAAC,CAACC,IAAMA,EAAEK,GAAG;IAClJ,MAAMC,UAAU,IAAIC,IAAI;QAACN;WAASC;WAAaE;KAAU;IAEzD,yFAAyF;IACzF,2FAA2F;IAC3F,yDAAyD;IACzD,MAAMI,YAAY5B,cAAcY,KAAKE;IACrC,IAAI,CAACf,aAAaa,MAAM;QACtB,MAAM,IAAIN,WAAW,kBAAkB;IACzC;IACA,wFAAwF;IACxF,uFAAuF;IACvF,gEAAgE;IAChE,IAAIsB,UAAUC,OAAO,CAACC,OAAO,KAAKC,WAAW;QAC3C,MAAM,IAAIzB,WAAW,uBAAuB,CAAC,QAAQ,EAAEsB,UAAUI,UAAU,CAAC,uKAAuK,CAAC;IACtP;IACA,wFAAwF;IACxF,wEAAwE;IACxE,MAAMC,IAAInC,YAAYc,MAAM,iDAAiD;IAC7E,IAAIR,kBAAkB6B,IAAI;QACxB,MAAM,IAAI3B,WAAW,uBAAuB,CAAC,2FAA2F,EAAE2B,EAAEC,KAAK,CAAC,aAAa,EAAE7B,gBAAgB,sGAAsG,CAAC;IAC1R;IACA,MAAM8B,UAAU3B,YAAYG,IAAIC,KAAKE;IACrC,oFAAoF;IACpF,oDAAoD;IACpD,MAAMb,aAAaU,IAAIC,KAAKA,IAAIwB,OAAO;IACvC,IAAI,CAAClC,aAAaS,IAAIU,OAAO;QAC3B,MAAM,IAAIf,WAAW,qBAAqB,GAAGe,KAAK,4GAA4G,CAAC;IACjK;IACA,IAAI,CAACZ,mBAAmBE,IAAIC,KAAKuB,UAAU,OAAO,EAAE;IACpD,OAAOhC,aAAaQ,IAAIC,KAAKS,MAAM;QAAEK;QAASS;QAASpB;IAAE;AAC3D"}
|
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { contentTokenize, embedConfig, featureEnabled, resolveSearch } from '../config/index.js';
|
|
4
|
+
import { localModelMissing, MODEL_FILENAMES } from '../embed/store.js';
|
|
4
5
|
import { SenseError } from '../errors.js';
|
|
5
|
-
import { modelPresent, semanticCandidates } from '../features/embed.js';
|
|
6
|
-
import { linkEdges } from '../features/index.js';
|
|
7
|
-
import { personalizedRank } from '../graph.js';
|
|
8
6
|
import { searchError } from '../search-error.js';
|
|
9
7
|
import { segmentMatch } from '../segment.js';
|
|
10
8
|
import { materializeScope, narrowByWhere, rawScope, scopeHasEmbeddings } from './scope.js';
|
|
11
|
-
|
|
12
|
-
// like a title match found through title, not like a body match.
|
|
13
|
-
const WEIGHTED_BM25 = 'bm25(content, 10.0, 5.0, 1.0, 0, 10.0, 5.0, 1.0)';
|
|
14
|
-
const RRF_K = 60;
|
|
9
|
+
import { linksCandidates, vectorsCandidates, wordsCandidates } from './signals.js';
|
|
15
10
|
// snippet() re-tokenizes each candidate doc, superlinearly: ~10s for one 1MB doc
|
|
16
11
|
// (BENCHMARKING.md). Past this bound, rows get the linear JS excerpt instead.
|
|
17
|
-
const SNIPPET_BOUND = 16384;
|
|
18
12
|
const EXCERPT_WINDOW = 160;
|
|
19
13
|
// Bare terms from an FTS5 query string: strips operators/quoting so the oversized-doc excerpt
|
|
20
14
|
// scan matches the same words the query matched on, not FTS5 syntax.
|
|
@@ -107,12 +101,12 @@ function lineRangeFor(db, path, line) {
|
|
|
107
101
|
const row = db.prepare('SELECT start_line, end_line FROM sections WHERE "path" = ? AND start_line <= ? AND end_line >= ? ORDER BY start_line DESC LIMIT 1').get(path, line, line);
|
|
108
102
|
return row ? `L${row.start_line}-${row.end_line}` : null;
|
|
109
103
|
}
|
|
110
|
-
//
|
|
104
|
+
// The declared or defaulted signals compose via RRF; `via` names which ones produced each row.
|
|
111
105
|
// `opts` arrives already resolved (config.ts:resolveSearch).
|
|
112
106
|
export async function search(db, cfg, terms, opts = {}) {
|
|
113
107
|
var _hits_get, _chunkLines_get, _chunkSimilarity_get;
|
|
114
108
|
const effective = resolveSearch(cfg, opts);
|
|
115
|
-
const { k } = effective;
|
|
109
|
+
const { k, signals } = effective;
|
|
116
110
|
const allPaths = db.prepare('SELECT "path" FROM frontmatter').all().map((r)=>r.path);
|
|
117
111
|
const scopePaths = rawScope(db, cfg, opts, allPaths);
|
|
118
112
|
const scopeActive = scopePaths.size < allPaths.length;
|
|
@@ -126,11 +120,14 @@ export async function search(db, cfg, terms, opts = {}) {
|
|
|
126
120
|
throw searchError(err, terms, effective.where);
|
|
127
121
|
}
|
|
128
122
|
const fetch = Math.max(k * 3, 30);
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
const wantsVectors =
|
|
132
|
-
if (wantsVectors
|
|
133
|
-
|
|
123
|
+
// A downloadable HF id proceeds -- getProvider fetches it lazily on consent. Only a
|
|
124
|
+
// local path with missing files errors here, since nothing will ever fetch it for itself.
|
|
125
|
+
const wantsVectors = signals.vectors !== undefined;
|
|
126
|
+
if (wantsVectors) {
|
|
127
|
+
const e = embedConfig(cfg); // validate.ts guarantees this is set whenever "vectors" is declared
|
|
128
|
+
if (localModelMissing(e)) {
|
|
129
|
+
throw new SenseError('EMBED_MODEL_MISSING', `preset "${effective.presetName}" searches with vectors, but the local model path "${e.model}" is missing ${MODEL_FILENAMES}; point embed.model at a directory containing them, or drop "vectors" from that preset's signals to search without them`);
|
|
130
|
+
}
|
|
134
131
|
}
|
|
135
132
|
const semanticEnabled = wantsVectors && scopeHasEmbeddings(db, cfg, allowedPaths);
|
|
136
133
|
// Terms pass to MATCH as written, with one transform: a run of text whose language marks no
|
|
@@ -152,18 +149,17 @@ export async function search(db, cfg, terms, opts = {}) {
|
|
|
152
149
|
// thousands of paths, past SQLITE_MAX_VARIABLE_NUMBER on older builds.
|
|
153
150
|
if (scopeActive) materializeScope(db, '_search_scope', scopePaths);
|
|
154
151
|
const scopeCond = scopeActive ? `AND content.path IN (SELECT "path" FROM _search_scope)` : '';
|
|
155
|
-
// Docs past SNIPPET_BOUND skip snippet() entirely -- SQLite
|
|
156
|
-
// short-circuits the untaken CASE branch, so it's never invoked on them.
|
|
157
|
-
// Column 2 (text), not -1 (best column): -1 could surface a _seg sidecar as the excerpt,
|
|
158
|
-
// which is machine-spaced and not what the author wrote. A row that matches only in a
|
|
159
|
-
// sidecar gets an unhighlighted text excerpt below instead -- a stated cost.
|
|
160
|
-
const matchSql = `SELECT content.path AS path, CASE WHEN length(content.text) <= ${SNIPPET_BOUND} THEN snippet(content, 2, '«', '»', '…', 10) ELSE NULL END AS hit FROM content ${whereJoin} WHERE content MATCH ? ${whereCond} ${scopeCond} ORDER BY ${WEIGHTED_BM25} LIMIT ${fetch}`;
|
|
161
152
|
const query = contentTokenize(cfg) === undefined ? segmentMatch(terms) : terms;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
153
|
+
const candidates = new Map();
|
|
154
|
+
let matchRows = [];
|
|
155
|
+
// A query that rewrites to nothing (e.g. only unspaced-script punctuation) has no
|
|
156
|
+
// searchable words: zero lexical rows, and the other signals still compose.
|
|
157
|
+
if (signals.words !== undefined && query.trim() !== '') {
|
|
158
|
+
try {
|
|
159
|
+
matchRows = wordsCandidates(db, candidates, query, whereJoin, whereCond, scopeCond, fetch, signals.words);
|
|
160
|
+
} catch (err) {
|
|
161
|
+
throw searchError(err, terms, scope);
|
|
162
|
+
}
|
|
167
163
|
}
|
|
168
164
|
const hits = new Map(matchRows.map((r)=>[
|
|
169
165
|
r.path,
|
|
@@ -172,58 +168,11 @@ export async function search(db, cfg, terms, opts = {}) {
|
|
|
172
168
|
// hit === null here means the bound suppressed snippet(), not "no match" -- distinguish
|
|
173
169
|
// from via='link' rows (never in matchRows, so absent from this set) below.
|
|
174
170
|
const oversized = new Set(matchRows.filter((r)=>r.hit === null).map((r)=>r.path));
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
score: 1 / (RRF_K + i),
|
|
179
|
-
via: 'match'
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
const edges = featureEnabled(cfg, 'links') && matchRows.length > 0 ? linkEdges(db) : [];
|
|
183
|
-
if (edges.length > 0) {
|
|
184
|
-
// Gates the label only, not the score: restart mass ranks every seed without an incident
|
|
185
|
-
// edge, but dropping it from the score reweights fusion (FEVER hit@10 0.997 -> 0.907).
|
|
186
|
-
const linked = new Set(edges.flat());
|
|
187
|
-
const seeds = new Map(matchRows.map((r, i)=>[
|
|
188
|
-
r.path,
|
|
189
|
-
1 / (i + 1)
|
|
190
|
-
]));
|
|
191
|
-
const ranked = [
|
|
192
|
-
...personalizedRank(allPaths, edges, seeds)
|
|
193
|
-
].filter(([path, score])=>score > 1e-9 && allowedPaths.has(path)).sort((a, b)=>b[1] - a[1]).slice(0, fetch);
|
|
194
|
-
ranked.forEach(([path], i)=>{
|
|
195
|
-
const existing = candidates.get(path);
|
|
196
|
-
if (existing) {
|
|
197
|
-
existing.score += 1 / (RRF_K + i);
|
|
198
|
-
if (linked.has(path)) existing.via = 'match+link';
|
|
199
|
-
} else {
|
|
200
|
-
candidates.set(path, {
|
|
201
|
-
score: 1 / (RRF_K + i),
|
|
202
|
-
via: 'link'
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
// Vector expansion, invoked only: a third RRF list at the swept flat-region constants
|
|
208
|
-
// (weight 1, pool = fetch). Each row carries its best chunk's line range.
|
|
209
|
-
const chunkLines = new Map();
|
|
210
|
-
const chunkSimilarity = new Map();
|
|
171
|
+
if (signals.links !== undefined) linksCandidates(db, candidates, matchRows, allPaths, allowedPaths, fetch, signals.links);
|
|
172
|
+
let chunkLines = new Map();
|
|
173
|
+
let chunkSimilarity = new Map();
|
|
211
174
|
if (semanticEnabled) {
|
|
212
|
-
|
|
213
|
-
vec.forEach(({ path, lines, similarity }, i)=>{
|
|
214
|
-
chunkLines.set(path, lines);
|
|
215
|
-
chunkSimilarity.set(path, similarity);
|
|
216
|
-
const existing = candidates.get(path);
|
|
217
|
-
if (existing) {
|
|
218
|
-
existing.score += 1 / (RRF_K + i);
|
|
219
|
-
existing.via = `${existing.via}+vector`;
|
|
220
|
-
} else {
|
|
221
|
-
candidates.set(path, {
|
|
222
|
-
score: 1 / (RRF_K + i),
|
|
223
|
-
via: 'vector'
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
});
|
|
175
|
+
({ chunkLines, chunkSimilarity } = await vectorsCandidates(db, cfg, candidates, terms, fetch, allowedPaths, signals.vectors));
|
|
227
176
|
}
|
|
228
177
|
db.exec('DROP TABLE IF EXISTS _search');
|
|
229
178
|
db.exec('CREATE TEMP TABLE _search ("path" TEXT PRIMARY KEY, score REAL, via TEXT, hit TEXT, lines TEXT, similarity REAL)');
|