daftari 1.10.0 → 1.11.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/CHANGELOG.md +26 -0
- package/README.md +1 -0
- package/dist/cli.js +0 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +8 -1
- package/dist/server.js.map +1 -1
- package/dist/themes/clustering.d.ts +30 -0
- package/dist/themes/clustering.d.ts.map +1 -0
- package/dist/themes/clustering.js +439 -0
- package/dist/themes/clustering.js.map +1 -0
- package/dist/tools/search.d.ts +3 -0
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/search.js +8 -2
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/themes.d.ts +21 -0
- package/dist/tools/themes.d.ts.map +1 -0
- package/dist/tools/themes.js +425 -0
- package/dist/tools/themes.js.map +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.11.0] - 2026-05-21
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`vault_themes` thematic clustering** (#56). New MCP tool surfaces
|
|
15
|
+
thematic clusters across the vault. For each document the tool mean-pools
|
|
16
|
+
its chunk embeddings into one vector, L2-normalises, and clusters the
|
|
17
|
+
resulting per-document set with hand-rolled k-means (k-means++ init,
|
|
18
|
+
Lloyd's iterations). Default behaviour sweeps k ∈ {10, 15, 20, 25} and
|
|
19
|
+
picks the k with the best mean silhouette; an explicit `k` argument
|
|
20
|
+
skips the sweep. Each theme returns a heuristic label (TF-IDF over
|
|
21
|
+
titles + tags — no LLM call), a coherence score (mean pairwise cosine
|
|
22
|
+
inside the cluster — `null` for singleton clusters, where there are no
|
|
23
|
+
pairs to average), representative documents nearest the centroid, the
|
|
24
|
+
most frequent tags, and `secondaryDocs`: documents whose primary
|
|
25
|
+
cluster is elsewhere but whose pooled vector also aligns with this
|
|
26
|
+
theme's centroid (surfaces cross-cutting documents that the hard
|
|
27
|
+
one-doc-one-theme partition would otherwise hide). Optional
|
|
28
|
+
`collection` and `tags` filters scope clustering; RBAC drops documents
|
|
29
|
+
the caller cannot read. Output is deterministic for the same vault
|
|
30
|
+
(fixed seed). No new storage — reads the existing `chunks` /
|
|
31
|
+
`embeddings` tables. v1 is one-doc-one-theme at the partition level
|
|
32
|
+
(`documentCount` still partitions by primary); true multi-theme
|
|
33
|
+
membership, HDBSCAN, seeded-search/coverage mode, and LLM labels are
|
|
34
|
+
deferred.
|
|
35
|
+
|
|
10
36
|
## [1.10.0] - 2026-05-21
|
|
11
37
|
|
|
12
38
|
### Added
|
package/README.md
CHANGED
|
@@ -87,6 +87,7 @@ Daftari exposes 13 tools, grouped by layer.
|
|
|
87
87
|
|------|-------------|
|
|
88
88
|
| `vault_search` | Hybrid BM25 + vector search across the vault, with tunable ranking weights; each hit carries an inline decay assessment. |
|
|
89
89
|
| `vault_search_related` | Find documents thematically related to a given document. |
|
|
90
|
+
| `vault_themes` | Surface thematic clusters across the vault via k-means over document-pooled embeddings. Heuristic labels (TF-IDF), per-theme coherence, representative docs, secondary docs (cross-cutting documents from other clusters that also align here), deterministic output. |
|
|
90
91
|
| `vault_reindex` | Rebuild the SQLite search index from the markdown files. |
|
|
91
92
|
|
|
92
93
|
**Write safety**
|
package/dist/cli.js
CHANGED
|
File without changes
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,EAAE,KAAK,aAAa,EAAe,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,EAAE,KAAK,aAAa,EAAe,MAAM,kBAAkB,CAAC;AAOnE,eAAO,MAAM,WAAW,YAAY,CAAC;AAQrC,eAAO,MAAM,cAAc,QAAmB,CAAC;AAK/C,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,GAAE,aAA6B,GAAG,MAAM,CA2D7F"}
|
package/dist/server.js
CHANGED
|
@@ -11,6 +11,7 @@ import { guestAccess } from "./access/rbac.js";
|
|
|
11
11
|
import { curationTools } from "./tools/curation.js";
|
|
12
12
|
import { readTools } from "./tools/read.js";
|
|
13
13
|
import { searchTools } from "./tools/search.js";
|
|
14
|
+
import { themesTools } from "./tools/themes.js";
|
|
14
15
|
import { writeTools } from "./tools/write.js";
|
|
15
16
|
export const SERVER_NAME = "daftari";
|
|
16
17
|
// The version is read from the package manifest so it never drifts from the
|
|
@@ -23,7 +24,13 @@ export const SERVER_VERSION = manifest.version;
|
|
|
23
24
|
// Absent an explicit context the server falls back to the deny-all guest.
|
|
24
25
|
export function createServer(vaultRoot, access = guestAccess()) {
|
|
25
26
|
const server = new Server({ name: SERVER_NAME, version: SERVER_VERSION }, { capabilities: { tools: {} } });
|
|
26
|
-
const tools = [
|
|
27
|
+
const tools = [
|
|
28
|
+
...readTools,
|
|
29
|
+
...searchTools,
|
|
30
|
+
...themesTools,
|
|
31
|
+
...writeTools,
|
|
32
|
+
...curationTools,
|
|
33
|
+
];
|
|
27
34
|
const byName = new Map(tools.map((t) => [t.name, t]));
|
|
28
35
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
29
36
|
tools: tools.map((t) => ({
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,8EAA8E;AAC9E,EAAE;AACF,8EAA8E;AAC9E,+EAA+E;AAC/E,6CAA6C;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AACnG,OAAO,EAAsB,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAuB,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,CAAC,MAAM,WAAW,GAAG,SAAS,CAAC;AAErC,4EAA4E;AAC5E,+EAA+E;AAC/E,8EAA8E;AAC9E,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAE7F,CAAC;AACF,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC,OAAO,CAAC;AAE/C,sEAAsE;AACtE,+EAA+E;AAC/E,0EAA0E;AAC1E,MAAM,UAAU,YAAY,CAAC,SAAiB,EAAE,SAAwB,WAAW,EAAE;IACnF,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,EAC9C,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAChC,CAAC;IAEF,MAAM,KAAK,GAAqB,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export declare function seededRng(seed: number): () => number;
|
|
2
|
+
export declare function l2Normalize(vec: Float32Array): Float32Array;
|
|
3
|
+
export declare function meanPoolL2(vectors: Float32Array[]): Float32Array | null;
|
|
4
|
+
export declare function kmeansPlusPlusInit(data: Float32Array[], k: number, rng: () => number): number[];
|
|
5
|
+
export interface KMeansResult {
|
|
6
|
+
assignments: number[];
|
|
7
|
+
centroids: Float32Array[];
|
|
8
|
+
iterations: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function kmeans(data: Float32Array[], k: number, rng: () => number, maxIter: number): KMeansResult;
|
|
11
|
+
export declare function silhouetteScore(data: Float32Array[], assignments: number[]): number;
|
|
12
|
+
export declare function clusterCoherence(vectors: Float32Array[]): number;
|
|
13
|
+
export interface PickKResult {
|
|
14
|
+
k: number;
|
|
15
|
+
silhouette: number;
|
|
16
|
+
assignments: number[];
|
|
17
|
+
centroids: Float32Array[];
|
|
18
|
+
}
|
|
19
|
+
export declare function pickK(data: Float32Array[], candidates: number[], rng: () => number, maxIter: number): PickKResult;
|
|
20
|
+
export interface SecondaryMembership {
|
|
21
|
+
docIndex: number;
|
|
22
|
+
sim: number;
|
|
23
|
+
}
|
|
24
|
+
export interface SecondaryOptions {
|
|
25
|
+
delta: number;
|
|
26
|
+
minSimilarity: number;
|
|
27
|
+
maxPerDoc: number;
|
|
28
|
+
}
|
|
29
|
+
export declare function selectSecondaryMemberships(vectors: Float32Array[], assignments: number[], centroids: Float32Array[], options: SecondaryOptions): Map<number, SecondaryMembership[]>;
|
|
30
|
+
//# sourceMappingURL=clustering.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clustering.d.ts","sourceRoot":"","sources":["../../src/themes/clustering.ts"],"names":[],"mappings":"AAwBA,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,MAAM,CASpD;AAMD,wBAAgB,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,YAAY,CAa3D;AAKD,wBAAgB,UAAU,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,YAAY,GAAG,IAAI,CAsBvE;AAoCD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,MAAM,GAAG,MAAM,EAAE,CAoE/F;AAID,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAKD,wBAAgB,MAAM,CACpB,IAAI,EAAE,YAAY,EAAE,EACpB,CAAC,EAAE,MAAM,EACT,GAAG,EAAE,MAAM,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,YAAY,CAkFd;AAUD,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,CA6CnF;AAQD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAahE;AAID,MAAM,WAAW,WAAW;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,YAAY,EAAE,CAAC;CAC3B;AAOD,wBAAgB,KAAK,CACnB,IAAI,EAAE,YAAY,EAAE,EACpB,UAAU,EAAE,MAAM,EAAE,EACpB,GAAG,EAAE,MAAM,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,WAAW,CAuBb;AAQD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,gBAAgB;IAG/B,KAAK,EAAE,MAAM,CAAC;IAId,aAAa,EAAE,MAAM,CAAC;IAItB,SAAS,EAAE,MAAM,CAAC;CACnB;AAUD,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,YAAY,EAAE,EACvB,WAAW,EAAE,MAAM,EAAE,EACrB,SAAS,EAAE,YAAY,EAAE,EACzB,OAAO,EAAE,gBAAgB,GACxB,GAAG,CAAC,MAAM,EAAE,mBAAmB,EAAE,CAAC,CA2CpC"}
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
// Clustering primitives for vault_themes.
|
|
2
|
+
//
|
|
3
|
+
// All functions are pure. The k-means implementation is hand-rolled (no
|
|
4
|
+
// new dependency) and seeded by a caller-supplied RNG so the same vault
|
|
5
|
+
// produces the same themes across runs.
|
|
6
|
+
//
|
|
7
|
+
// Input vectors are L2-normalised by the caller, so Euclidean distance on
|
|
8
|
+
// the unit sphere is equivalent to cosine distance (1 - cos θ). We use
|
|
9
|
+
// squared Euclidean inside k-means because it is monotonic with Euclidean
|
|
10
|
+
// distance and avoids per-iteration sqrt work.
|
|
11
|
+
//
|
|
12
|
+
// Two distinct quality scores live here and must not be confused:
|
|
13
|
+
// - silhouetteScore: the internal k-picker. Mean over all points of
|
|
14
|
+
// (b - a) / max(a, b) where a is mean intra-cluster distance and b is
|
|
15
|
+
// mean nearest-other-cluster distance. Higher = better separation.
|
|
16
|
+
// - clusterCoherence: the per-theme number returned in the tool output.
|
|
17
|
+
// Mean pairwise cosine similarity inside one cluster. Higher = tighter.
|
|
18
|
+
// --- Seeded RNG ------------------------------------------------------------
|
|
19
|
+
// Mulberry32: a 32-bit, single-state PRNG. Cheap, deterministic, sufficient
|
|
20
|
+
// for clustering's stochastic init. Returns numbers in [0, 1). Seed 0 still
|
|
21
|
+
// produces a valid (constant-free) sequence because the increment 0x6D2B79F5
|
|
22
|
+
// keeps the state moving.
|
|
23
|
+
export function seededRng(seed) {
|
|
24
|
+
let a = seed | 0;
|
|
25
|
+
return () => {
|
|
26
|
+
a = (a + 0x6d2b79f5) | 0;
|
|
27
|
+
let t = a;
|
|
28
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
29
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
30
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
// --- Vector math -----------------------------------------------------------
|
|
34
|
+
// In-place is tempting but every caller wants a fresh array; allocating
|
|
35
|
+
// here avoids surprise mutations.
|
|
36
|
+
export function l2Normalize(vec) {
|
|
37
|
+
let norm = 0;
|
|
38
|
+
for (let i = 0; i < vec.length; i++) {
|
|
39
|
+
const x = vec[i];
|
|
40
|
+
norm += x * x;
|
|
41
|
+
}
|
|
42
|
+
if (norm === 0)
|
|
43
|
+
return new Float32Array(vec);
|
|
44
|
+
const inv = 1 / Math.sqrt(norm);
|
|
45
|
+
const out = new Float32Array(vec.length);
|
|
46
|
+
for (let i = 0; i < vec.length; i++) {
|
|
47
|
+
out[i] = vec[i] * inv;
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
// Mean-pool a document's chunk vectors into one vector, then L2-normalise so
|
|
52
|
+
// the result lives on the unit sphere (cosine semantics).
|
|
53
|
+
// Returns null when there is nothing to pool or the pooled vector is zero.
|
|
54
|
+
export function meanPoolL2(vectors) {
|
|
55
|
+
if (vectors.length === 0)
|
|
56
|
+
return null;
|
|
57
|
+
const dim = vectors[0]?.length ?? 0;
|
|
58
|
+
if (dim === 0)
|
|
59
|
+
return null;
|
|
60
|
+
const sum = new Float32Array(dim);
|
|
61
|
+
let kept = 0;
|
|
62
|
+
for (const v of vectors) {
|
|
63
|
+
if (v.length !== dim)
|
|
64
|
+
continue;
|
|
65
|
+
kept += 1;
|
|
66
|
+
for (let i = 0; i < dim; i++)
|
|
67
|
+
sum[i] = sum[i] + v[i];
|
|
68
|
+
}
|
|
69
|
+
if (kept === 0)
|
|
70
|
+
return null;
|
|
71
|
+
for (let i = 0; i < dim; i++)
|
|
72
|
+
sum[i] = sum[i] / kept;
|
|
73
|
+
// If the mean is the zero vector, l2Normalize returns zeros — surface that
|
|
74
|
+
// as null so downstream callers skip the document.
|
|
75
|
+
let norm = 0;
|
|
76
|
+
for (let i = 0; i < dim; i++) {
|
|
77
|
+
const x = sum[i];
|
|
78
|
+
norm += x * x;
|
|
79
|
+
}
|
|
80
|
+
if (norm === 0)
|
|
81
|
+
return null;
|
|
82
|
+
return l2Normalize(sum);
|
|
83
|
+
}
|
|
84
|
+
function squaredEuclidean(a, b) {
|
|
85
|
+
let acc = 0;
|
|
86
|
+
const n = Math.min(a.length, b.length);
|
|
87
|
+
for (let i = 0; i < n; i++) {
|
|
88
|
+
const d = a[i] - b[i];
|
|
89
|
+
acc += d * d;
|
|
90
|
+
}
|
|
91
|
+
return acc;
|
|
92
|
+
}
|
|
93
|
+
function cosineSimilarityNormalized(a, b) {
|
|
94
|
+
let dot = 0;
|
|
95
|
+
const n = Math.min(a.length, b.length);
|
|
96
|
+
for (let i = 0; i < n; i++)
|
|
97
|
+
dot += a[i] * b[i];
|
|
98
|
+
return dot;
|
|
99
|
+
}
|
|
100
|
+
// Euclidean distance on the unit sphere is monotonic with cosine distance.
|
|
101
|
+
// We expose it as the public distance for silhouette so the score is on the
|
|
102
|
+
// same scale callers expect.
|
|
103
|
+
function euclideanDistance(a, b) {
|
|
104
|
+
return Math.sqrt(squaredEuclidean(a, b));
|
|
105
|
+
}
|
|
106
|
+
// --- k-means++ initialisation ---------------------------------------------
|
|
107
|
+
// Picks k initial centroid INDICES from `data` using the k-means++ rule:
|
|
108
|
+
// the first centroid is uniform-random; each subsequent centroid is sampled
|
|
109
|
+
// with probability proportional to D(x)^2, where D(x) is the squared distance
|
|
110
|
+
// from x to the nearest already-chosen centroid.
|
|
111
|
+
//
|
|
112
|
+
// Falls back to a uniform draw if the weighted total is zero (degenerate
|
|
113
|
+
// data with all duplicates) so we always return k distinct indices when
|
|
114
|
+
// k ≤ data.length.
|
|
115
|
+
export function kmeansPlusPlusInit(data, k, rng) {
|
|
116
|
+
const n = data.length;
|
|
117
|
+
if (k <= 0 || n === 0)
|
|
118
|
+
return [];
|
|
119
|
+
const seeds = [];
|
|
120
|
+
const taken = new Set();
|
|
121
|
+
// First centroid: uniform-random.
|
|
122
|
+
const firstIdx = Math.floor(rng() * n);
|
|
123
|
+
const first = firstIdx >= n ? n - 1 : firstIdx;
|
|
124
|
+
seeds.push(first);
|
|
125
|
+
taken.add(first);
|
|
126
|
+
// For each remaining slot, compute D(x)^2 for every point against the
|
|
127
|
+
// closest already-picked centroid and sample one index weighted by D^2.
|
|
128
|
+
const dSquared = new Float64Array(n);
|
|
129
|
+
for (let i = 0; i < n; i++) {
|
|
130
|
+
dSquared[i] = squaredEuclidean(data[i], data[first]);
|
|
131
|
+
}
|
|
132
|
+
while (seeds.length < Math.min(k, n)) {
|
|
133
|
+
let total = 0;
|
|
134
|
+
for (let i = 0; i < n; i++) {
|
|
135
|
+
if (!taken.has(i))
|
|
136
|
+
total += dSquared[i];
|
|
137
|
+
}
|
|
138
|
+
let chosen = -1;
|
|
139
|
+
if (total === 0) {
|
|
140
|
+
// All untaken points coincide with an existing centroid (or there are
|
|
141
|
+
// duplicates). Pick the first untaken index uniformly so we still
|
|
142
|
+
// return k distinct seeds.
|
|
143
|
+
for (let i = 0; i < n; i++) {
|
|
144
|
+
if (!taken.has(i)) {
|
|
145
|
+
chosen = i;
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
let target = rng() * total;
|
|
152
|
+
for (let i = 0; i < n; i++) {
|
|
153
|
+
if (taken.has(i))
|
|
154
|
+
continue;
|
|
155
|
+
target -= dSquared[i];
|
|
156
|
+
if (target <= 0) {
|
|
157
|
+
chosen = i;
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (chosen === -1) {
|
|
162
|
+
// Numeric drift across the loop; pick the last untaken candidate.
|
|
163
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
164
|
+
if (!taken.has(i)) {
|
|
165
|
+
chosen = i;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (chosen === -1)
|
|
172
|
+
break;
|
|
173
|
+
seeds.push(chosen);
|
|
174
|
+
taken.add(chosen);
|
|
175
|
+
// Update each point's D^2 to the nearest of the (now larger) centroid set.
|
|
176
|
+
const chosenVec = data[chosen];
|
|
177
|
+
for (let i = 0; i < n; i++) {
|
|
178
|
+
const d = squaredEuclidean(data[i], chosenVec);
|
|
179
|
+
if (d < dSquared[i])
|
|
180
|
+
dSquared[i] = d;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return seeds;
|
|
184
|
+
}
|
|
185
|
+
// Standard Lloyd's iterations until assignments stabilise or `maxIter` is
|
|
186
|
+
// reached. k is clamped to data.length: clustering 4 documents into 10
|
|
187
|
+
// groups is undefined; instead we just return at most data.length clusters.
|
|
188
|
+
export function kmeans(data, k, rng, maxIter) {
|
|
189
|
+
const n = data.length;
|
|
190
|
+
if (n === 0)
|
|
191
|
+
return { assignments: [], centroids: [], iterations: 0 };
|
|
192
|
+
const effectiveK = Math.max(1, Math.min(k, n));
|
|
193
|
+
const dim = data[0].length;
|
|
194
|
+
const seedIdxs = kmeansPlusPlusInit(data, effectiveK, rng);
|
|
195
|
+
let centroids = seedIdxs.map((i) => {
|
|
196
|
+
const src = data[i];
|
|
197
|
+
const copy = new Float32Array(src.length);
|
|
198
|
+
for (let j = 0; j < src.length; j++)
|
|
199
|
+
copy[j] = src[j];
|
|
200
|
+
return copy;
|
|
201
|
+
});
|
|
202
|
+
const assignments = new Array(n).fill(0);
|
|
203
|
+
let iterations = 0;
|
|
204
|
+
for (let iter = 0; iter < maxIter; iter++) {
|
|
205
|
+
iterations = iter + 1;
|
|
206
|
+
let changed = false;
|
|
207
|
+
// Assign step.
|
|
208
|
+
for (let i = 0; i < n; i++) {
|
|
209
|
+
const point = data[i];
|
|
210
|
+
let bestC = 0;
|
|
211
|
+
let bestD = Number.POSITIVE_INFINITY;
|
|
212
|
+
for (let c = 0; c < centroids.length; c++) {
|
|
213
|
+
const d = squaredEuclidean(point, centroids[c]);
|
|
214
|
+
if (d < bestD) {
|
|
215
|
+
bestD = d;
|
|
216
|
+
bestC = c;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (assignments[i] !== bestC) {
|
|
220
|
+
assignments[i] = bestC;
|
|
221
|
+
changed = true;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// Update step.
|
|
225
|
+
const sums = Array.from({ length: centroids.length }, () => new Float32Array(dim));
|
|
226
|
+
const counts = new Array(centroids.length).fill(0);
|
|
227
|
+
for (let i = 0; i < n; i++) {
|
|
228
|
+
const c = assignments[i];
|
|
229
|
+
counts[c] = counts[c] + 1;
|
|
230
|
+
const sum = sums[c];
|
|
231
|
+
const point = data[i];
|
|
232
|
+
for (let d = 0; d < dim; d++)
|
|
233
|
+
sum[d] = sum[d] + point[d];
|
|
234
|
+
}
|
|
235
|
+
const newCentroids = [];
|
|
236
|
+
for (let c = 0; c < centroids.length; c++) {
|
|
237
|
+
const count = counts[c];
|
|
238
|
+
if (count === 0) {
|
|
239
|
+
// Empty cluster: keep the previous centroid in place rather than
|
|
240
|
+
// re-seeding. Re-seeding mid-run would defeat determinism.
|
|
241
|
+
newCentroids.push(centroids[c]);
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
const sum = sums[c];
|
|
245
|
+
const next = new Float32Array(dim);
|
|
246
|
+
for (let d = 0; d < dim; d++)
|
|
247
|
+
next[d] = sum[d] / count;
|
|
248
|
+
newCentroids.push(next);
|
|
249
|
+
}
|
|
250
|
+
centroids = newCentroids;
|
|
251
|
+
if (!changed)
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
// Final assignment pass on the converged centroids so callers don't see
|
|
255
|
+
// a stale assignment vector from before the last update step.
|
|
256
|
+
for (let i = 0; i < n; i++) {
|
|
257
|
+
const point = data[i];
|
|
258
|
+
let bestC = 0;
|
|
259
|
+
let bestD = Number.POSITIVE_INFINITY;
|
|
260
|
+
for (let c = 0; c < centroids.length; c++) {
|
|
261
|
+
const d = squaredEuclidean(point, centroids[c]);
|
|
262
|
+
if (d < bestD) {
|
|
263
|
+
bestD = d;
|
|
264
|
+
bestC = c;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
assignments[i] = bestC;
|
|
268
|
+
}
|
|
269
|
+
return { assignments, centroids, iterations };
|
|
270
|
+
}
|
|
271
|
+
// --- Silhouette (k-picker) ------------------------------------------------
|
|
272
|
+
// Mean silhouette over all points. For each point i:
|
|
273
|
+
// a(i) = mean distance from i to other points in the same cluster
|
|
274
|
+
// b(i) = min over other clusters C of (mean distance from i to points in C)
|
|
275
|
+
// s(i) = (b - a) / max(a, b)
|
|
276
|
+
// Singleton clusters contribute 0. With only one cluster the score is 0
|
|
277
|
+
// (no contrast).
|
|
278
|
+
export function silhouetteScore(data, assignments) {
|
|
279
|
+
const n = data.length;
|
|
280
|
+
if (n === 0 || n !== assignments.length)
|
|
281
|
+
return 0;
|
|
282
|
+
const clusters = new Map();
|
|
283
|
+
for (let i = 0; i < n; i++) {
|
|
284
|
+
const c = assignments[i];
|
|
285
|
+
const list = clusters.get(c);
|
|
286
|
+
if (list)
|
|
287
|
+
list.push(i);
|
|
288
|
+
else
|
|
289
|
+
clusters.set(c, [i]);
|
|
290
|
+
}
|
|
291
|
+
if (clusters.size < 2)
|
|
292
|
+
return 0;
|
|
293
|
+
let total = 0;
|
|
294
|
+
for (let i = 0; i < n; i++) {
|
|
295
|
+
const ci = assignments[i];
|
|
296
|
+
const own = clusters.get(ci);
|
|
297
|
+
if (!own || own.length <= 1) {
|
|
298
|
+
// Singleton: silhouette defined as 0.
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
// a(i): mean distance to other own-cluster members.
|
|
302
|
+
let aSum = 0;
|
|
303
|
+
let aCount = 0;
|
|
304
|
+
for (const j of own) {
|
|
305
|
+
if (j === i)
|
|
306
|
+
continue;
|
|
307
|
+
aSum += euclideanDistance(data[i], data[j]);
|
|
308
|
+
aCount += 1;
|
|
309
|
+
}
|
|
310
|
+
const a = aSum / aCount;
|
|
311
|
+
// b(i): min over other clusters of mean distance.
|
|
312
|
+
let b = Number.POSITIVE_INFINITY;
|
|
313
|
+
for (const [cj, members] of clusters) {
|
|
314
|
+
if (cj === ci)
|
|
315
|
+
continue;
|
|
316
|
+
let s = 0;
|
|
317
|
+
for (const j of members) {
|
|
318
|
+
s += euclideanDistance(data[i], data[j]);
|
|
319
|
+
}
|
|
320
|
+
const mean = s / members.length;
|
|
321
|
+
if (mean < b)
|
|
322
|
+
b = mean;
|
|
323
|
+
}
|
|
324
|
+
const denom = Math.max(a, b);
|
|
325
|
+
if (denom === 0)
|
|
326
|
+
continue;
|
|
327
|
+
total += (b - a) / denom;
|
|
328
|
+
}
|
|
329
|
+
return total / n;
|
|
330
|
+
}
|
|
331
|
+
// --- Coherence (per-theme score) ------------------------------------------
|
|
332
|
+
// Mean pairwise cosine similarity inside one cluster. Vectors are assumed
|
|
333
|
+
// L2-normalised, so cosine reduces to a dot product. Range is [-1, 1] in
|
|
334
|
+
// general; for unit-sphere embeddings produced by sentence-transformers it
|
|
335
|
+
// tends to fall in [0, 1].
|
|
336
|
+
export function clusterCoherence(vectors) {
|
|
337
|
+
if (vectors.length === 0)
|
|
338
|
+
return 0;
|
|
339
|
+
if (vectors.length === 1)
|
|
340
|
+
return 1;
|
|
341
|
+
let total = 0;
|
|
342
|
+
let pairs = 0;
|
|
343
|
+
for (let i = 0; i < vectors.length; i++) {
|
|
344
|
+
for (let j = i + 1; j < vectors.length; j++) {
|
|
345
|
+
total += cosineSimilarityNormalized(vectors[i], vectors[j]);
|
|
346
|
+
pairs += 1;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (pairs === 0)
|
|
350
|
+
return 0;
|
|
351
|
+
return total / pairs;
|
|
352
|
+
}
|
|
353
|
+
// Runs k-means for each k in `candidates`, computes silhouette, and returns
|
|
354
|
+
// the run with the highest score. Candidates are clamped to data.length and
|
|
355
|
+
// deduped; this is what handles the "tiny vault" edge case — if the user
|
|
356
|
+
// requested k ∈ {10, 15, 20, 25} but only has 8 documents, we end up running
|
|
357
|
+
// k-means at k=8 and stop.
|
|
358
|
+
export function pickK(data, candidates, rng, maxIter) {
|
|
359
|
+
const n = data.length;
|
|
360
|
+
const seen = new Set();
|
|
361
|
+
const ks = [];
|
|
362
|
+
for (const c of candidates) {
|
|
363
|
+
const clamped = Math.max(1, Math.min(c, n));
|
|
364
|
+
if (!seen.has(clamped)) {
|
|
365
|
+
seen.add(clamped);
|
|
366
|
+
ks.push(clamped);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (ks.length === 0)
|
|
370
|
+
ks.push(Math.min(1, n));
|
|
371
|
+
let best = null;
|
|
372
|
+
for (const k of ks) {
|
|
373
|
+
const { assignments, centroids } = kmeans(data, k, rng, maxIter);
|
|
374
|
+
const score = silhouetteScore(data, assignments);
|
|
375
|
+
if (!best || score > best.silhouette) {
|
|
376
|
+
best = { k, silhouette: score, assignments, centroids };
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
// `best` is non-null because ks has at least one entry.
|
|
380
|
+
return best;
|
|
381
|
+
}
|
|
382
|
+
// For each doc, finds the clusters (other than its primary) whose centroid
|
|
383
|
+
// is close enough that the doc plausibly belongs there too. Returns a map
|
|
384
|
+
// from cluster index → ordered (by sim desc) list of secondary docs.
|
|
385
|
+
//
|
|
386
|
+
// This implements the "soft reporting" path on top of the hard partition
|
|
387
|
+
// produced by k-means: `documentCount` still partitions by primary, but
|
|
388
|
+
// the secondary list surfaces the cross-cutting documents the partition
|
|
389
|
+
// hides. See `docs/architecture.md` for the rationale.
|
|
390
|
+
export function selectSecondaryMemberships(vectors, assignments, centroids, options) {
|
|
391
|
+
const out = new Map();
|
|
392
|
+
const { delta, minSimilarity, maxPerDoc } = options;
|
|
393
|
+
if (vectors.length === 0 || centroids.length < 2 || maxPerDoc <= 0)
|
|
394
|
+
return out;
|
|
395
|
+
for (let i = 0; i < vectors.length; i++) {
|
|
396
|
+
const vec = vectors[i];
|
|
397
|
+
const primary = assignments[i];
|
|
398
|
+
const primaryCentroid = centroids[primary];
|
|
399
|
+
if (!primaryCentroid)
|
|
400
|
+
continue;
|
|
401
|
+
const primarySim = cosineSimilarityNormalized(vec, primaryCentroid);
|
|
402
|
+
// Score every non-primary cluster; collect those passing both bars.
|
|
403
|
+
const candidates = [];
|
|
404
|
+
for (let c = 0; c < centroids.length; c++) {
|
|
405
|
+
if (c === primary)
|
|
406
|
+
continue;
|
|
407
|
+
const cv = centroids[c];
|
|
408
|
+
if (!cv)
|
|
409
|
+
continue;
|
|
410
|
+
const sim = cosineSimilarityNormalized(vec, cv);
|
|
411
|
+
if (sim < minSimilarity)
|
|
412
|
+
continue;
|
|
413
|
+
if (sim < primarySim - delta)
|
|
414
|
+
continue;
|
|
415
|
+
candidates.push({ cluster: c, sim });
|
|
416
|
+
}
|
|
417
|
+
if (candidates.length === 0)
|
|
418
|
+
continue;
|
|
419
|
+
// Take the doc's top `maxPerDoc` secondaries (highest-sim wins ties
|
|
420
|
+
// first; stable cluster index for a deterministic ordering otherwise).
|
|
421
|
+
candidates.sort((a, b) => b.sim - a.sim || a.cluster - b.cluster);
|
|
422
|
+
const top = candidates.slice(0, maxPerDoc);
|
|
423
|
+
for (const { cluster, sim } of top) {
|
|
424
|
+
const list = out.get(cluster);
|
|
425
|
+
const entry = { docIndex: i, sim };
|
|
426
|
+
if (list)
|
|
427
|
+
list.push(entry);
|
|
428
|
+
else
|
|
429
|
+
out.set(cluster, [entry]);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
// Order each cluster's secondaries by sim desc; stable on docIndex so the
|
|
433
|
+
// map serialises deterministically across runs.
|
|
434
|
+
for (const list of out.values()) {
|
|
435
|
+
list.sort((a, b) => b.sim - a.sim || a.docIndex - b.docIndex);
|
|
436
|
+
}
|
|
437
|
+
return out;
|
|
438
|
+
}
|
|
439
|
+
//# sourceMappingURL=clustering.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clustering.js","sourceRoot":"","sources":["../../src/themes/clustering.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/search.d.ts
CHANGED
|
@@ -2,7 +2,10 @@ import { type AccessContext } from "../access/rbac.js";
|
|
|
2
2
|
import { type Result } from "../frontmatter/types.js";
|
|
3
3
|
import { type HybridSearchResult, type RelatedSearchResult } from "../search/hybrid.js";
|
|
4
4
|
import { type ReindexResult } from "../search/reindex.js";
|
|
5
|
+
import { type IndexDb } from "../storage/index-db.js";
|
|
5
6
|
import type { ToolDefinition } from "./read.js";
|
|
7
|
+
export declare function openIndexForActiveProvider(vaultRoot: string): Result<IndexDb, Error>;
|
|
8
|
+
export declare function ensureIndexReady(vaultRoot: string): Promise<Result<void, Error>>;
|
|
6
9
|
export declare function vaultSearch(vaultRoot: string, args: Record<string, unknown>, access?: AccessContext): Promise<Result<HybridSearchResult, Error>>;
|
|
7
10
|
export declare function vaultSearchRelated(vaultRoot: string, args: Record<string, unknown>, access?: AccessContext): Promise<Result<RelatedSearchResult, Error>>;
|
|
8
11
|
export interface VaultReindexResult extends ReindexResult {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,aAAa,EAAW,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAEL,KAAK,kBAAkB,EAGvB,KAAK,mBAAmB,EAEzB,MAAM,qBAAqB,CAAC;AAQ7B,OAAO,EAAE,KAAK,aAAa,EAAgB,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,aAAa,EAAW,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAEL,KAAK,kBAAkB,EAGvB,KAAK,mBAAmB,EAEzB,MAAM,qBAAqB,CAAC;AAQ7B,OAAO,EAAE,KAAK,aAAa,EAAgB,MAAM,sBAAsB,CAAC;AAExE,OAAO,EAAiB,KAAK,OAAO,EAAe,MAAM,wBAAwB,CAAC;AAClF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAShD,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAEpF;AAgBD,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAsBtF;AA+BD,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,CAAC,EAAE,aAAa,GACrB,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC,CA2B5C;AAMD,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,CAAC,EAAE,aAAa,GACrB,OAAO,CAAC,MAAM,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC,CA2B7C;AAMD,MAAM,WAAW,kBAAmB,SAAQ,aAAa;IACvD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC,CAahG;AAgBD,eAAO,MAAM,WAAW,EAAE,cAAc,EA2DvC,CAAC"}
|
package/dist/tools/search.js
CHANGED
|
@@ -16,7 +16,10 @@ import { documentCount, openIndexDb } from "../storage/index-db.js";
|
|
|
16
16
|
// table matches the embeddings the search will query. A read-only tool
|
|
17
17
|
// that opens after a provider switch would otherwise face a vec table
|
|
18
18
|
// sized for the *previous* provider's vectors.
|
|
19
|
-
|
|
19
|
+
//
|
|
20
|
+
// Exported so other index-backed tools (vault_themes) reuse the same
|
|
21
|
+
// dim-aware open path.
|
|
22
|
+
export function openIndexForActiveProvider(vaultRoot) {
|
|
20
23
|
return openIndexDb(vaultRoot, getProvider().dim);
|
|
21
24
|
}
|
|
22
25
|
// Gate every index-backed tool on the current indexing state.
|
|
@@ -30,7 +33,10 @@ function openIndexForActiveProvider(vaultRoot) {
|
|
|
30
33
|
// through main(), or a vault whose .daftari directory was wiped) trigger
|
|
31
34
|
// a synchronous reindex so search still works without an explicit
|
|
32
35
|
// --reindex step.
|
|
33
|
-
|
|
36
|
+
//
|
|
37
|
+
// Exported so other index-backed tools (vault_themes) reuse the same
|
|
38
|
+
// readiness gate.
|
|
39
|
+
export async function ensureIndexReady(vaultRoot) {
|
|
34
40
|
const status = getIndexStatus();
|
|
35
41
|
if (status.status === "indexing") {
|
|
36
42
|
return err(new Error(indexingBusyMessage(status)));
|
package/dist/tools/search.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,EAAE;AACF,wEAAwE;AACxE,4EAA4E;AAC5E,+EAA+E;AAC/E,2EAA2E;AAC3E,iEAAiE;AAEjE,OAAO,EAAsB,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAe,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EACL,eAAe,EAGf,YAAY,EAEZ,aAAa,GACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,YAAY,EACZ,cAAc,GACf,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAsB,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type AccessContext } from "../access/rbac.js";
|
|
2
|
+
import { type Result } from "../frontmatter/types.js";
|
|
3
|
+
import type { ToolDefinition } from "./read.js";
|
|
4
|
+
export interface VaultTheme {
|
|
5
|
+
label: string;
|
|
6
|
+
documentCount: number;
|
|
7
|
+
coherence: number | null;
|
|
8
|
+
representativeDocs: string[];
|
|
9
|
+
secondaryDocs: string[];
|
|
10
|
+
relatedTags: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface VaultThemesResult {
|
|
13
|
+
themes: VaultTheme[];
|
|
14
|
+
totalDocuments: number;
|
|
15
|
+
skippedDocuments: number;
|
|
16
|
+
selectedK: number;
|
|
17
|
+
clusteredAt: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function vaultThemes(vaultRoot: string, args: Record<string, unknown>, access?: AccessContext): Promise<Result<VaultThemesResult, Error>>;
|
|
20
|
+
export declare const themesTools: ToolDefinition[];
|
|
21
|
+
//# sourceMappingURL=themes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"themes.d.ts","sourceRoot":"","sources":["../../src/tools/themes.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,KAAK,aAAa,EAAW,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAW/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAuChD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IAKtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAM7B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAwPD,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,CAAC,EAAE,aAAa,GACrB,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC,CA8H3C;AAMD,eAAO,MAAM,WAAW,EAAE,cAAc,EAqCvC,CAAC"}
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
// vault_themes — thematic clustering over document-pooled embeddings.
|
|
2
|
+
//
|
|
3
|
+
// Cluster DOCUMENTS, not chunks: for each document we mean-pool its chunk
|
|
4
|
+
// embeddings into one vector, L2-normalise it, and cluster the resulting
|
|
5
|
+
// ~N-doc set. ~3.5k document vectors instead of ~44k chunk vectors makes
|
|
6
|
+
// every algorithm cheap (silhouette becomes tractable on the full set; no
|
|
7
|
+
// sampling needed at this scale).
|
|
8
|
+
//
|
|
9
|
+
// v1 constraints (locked):
|
|
10
|
+
// - One-doc-one-theme. Each document lives in exactly one cluster.
|
|
11
|
+
// - Heuristic labels from TF-IDF over titles + tags. No LLM call.
|
|
12
|
+
// - No new storage — the tool is read-only against the existing
|
|
13
|
+
// embeddings / chunks / documents tables.
|
|
14
|
+
// - Default k-sweep over {10, 15, 20, 25}; an explicit `k` argument
|
|
15
|
+
// skips the sweep.
|
|
16
|
+
// - Deterministic via a fixed seed for the k-means RNG.
|
|
17
|
+
import { canRead } from "../access/rbac.js";
|
|
18
|
+
import { err, ok } from "../frontmatter/types.js";
|
|
19
|
+
import { getProvider } from "../search/vector.js";
|
|
20
|
+
import { blobToEmbedding } from "../storage/index-db.js";
|
|
21
|
+
import { clusterCoherence, kmeans, meanPoolL2, pickK, seededRng, selectSecondaryMemberships, } from "../themes/clustering.js";
|
|
22
|
+
import { ensureIndexReady, openIndexForActiveProvider } from "./search.js";
|
|
23
|
+
// Fixed seed for the k-means RNG. The whole point of vault_themes is that
|
|
24
|
+
// the same vault produces the same themes — a random seed would give the
|
|
25
|
+
// user different themes every run and erode trust in the output.
|
|
26
|
+
const THEMES_RNG_SEED = 0x7da17f1; // arbitrary; constant is what matters
|
|
27
|
+
// Default k-sweep range (from the issue dialogue). Each is clamped to the
|
|
28
|
+
// number of clusterable documents inside pickK.
|
|
29
|
+
const DEFAULT_K_CANDIDATES = [10, 15, 20, 25];
|
|
30
|
+
// k-means iteration cap. Documents on the unit sphere converge fast in
|
|
31
|
+
// practice; this is a safety bound for pathological inputs.
|
|
32
|
+
const KMEANS_MAX_ITER = 50;
|
|
33
|
+
// Representative-doc count returned per theme. 5 is enough to be useful in
|
|
34
|
+
// the UI without dragging in marginal members.
|
|
35
|
+
const REPRESENTATIVE_DOCS_PER_THEME = 5;
|
|
36
|
+
// Tag-frequency cutoff per theme. Five most-common tags is enough signal
|
|
37
|
+
// without burying the user in noise.
|
|
38
|
+
const RELATED_TAGS_PER_THEME = 5;
|
|
39
|
+
// Secondary-membership tuning. A doc qualifies as a secondary member of a
|
|
40
|
+
// non-primary cluster when its centroid similarity is BOTH within DELTA of
|
|
41
|
+
// the primary AND above MIN_ABS_SIMILARITY. The cap prevents a centroid-
|
|
42
|
+
// of-mass doc from showing up in every cluster. Defaults are deliberately
|
|
43
|
+
// conservative — secondaries should surface cross-cutting docs, not bury
|
|
44
|
+
// the primary signal.
|
|
45
|
+
const SECONDARY_DELTA = 0.1;
|
|
46
|
+
const SECONDARY_MIN_SIMILARITY = 0.5;
|
|
47
|
+
const SECONDARY_MAX_PER_DOC = 2;
|
|
48
|
+
// Hard cap on secondaries reported per theme. Same UX rationale as
|
|
49
|
+
// REPRESENTATIVE_DOCS_PER_THEME — enough to be useful, bounded enough to
|
|
50
|
+
// not bury the user.
|
|
51
|
+
const SECONDARY_DOCS_PER_THEME = 5;
|
|
52
|
+
// Tokenisation for TF-IDF labels: lowercase, split on non-alphanumeric,
|
|
53
|
+
// drop stopwords. This is deliberately a smaller list than bm25's — the
|
|
54
|
+
// label step doesn't need FTS5 alignment, and we keep "tag-like" tokens.
|
|
55
|
+
const LABEL_STOPWORDS = new Set([
|
|
56
|
+
"a",
|
|
57
|
+
"an",
|
|
58
|
+
"and",
|
|
59
|
+
"are",
|
|
60
|
+
"as",
|
|
61
|
+
"at",
|
|
62
|
+
"be",
|
|
63
|
+
"but",
|
|
64
|
+
"by",
|
|
65
|
+
"for",
|
|
66
|
+
"from",
|
|
67
|
+
"has",
|
|
68
|
+
"have",
|
|
69
|
+
"in",
|
|
70
|
+
"is",
|
|
71
|
+
"it",
|
|
72
|
+
"its",
|
|
73
|
+
"of",
|
|
74
|
+
"on",
|
|
75
|
+
"or",
|
|
76
|
+
"that",
|
|
77
|
+
"the",
|
|
78
|
+
"their",
|
|
79
|
+
"them",
|
|
80
|
+
"they",
|
|
81
|
+
"this",
|
|
82
|
+
"to",
|
|
83
|
+
"was",
|
|
84
|
+
"were",
|
|
85
|
+
"will",
|
|
86
|
+
"with",
|
|
87
|
+
"you",
|
|
88
|
+
"your",
|
|
89
|
+
]);
|
|
90
|
+
function labelTokens(text) {
|
|
91
|
+
return text
|
|
92
|
+
.toLowerCase()
|
|
93
|
+
.split(/[^a-z0-9]+/)
|
|
94
|
+
.filter((t) => t.length > 1 && !LABEL_STOPWORDS.has(t));
|
|
95
|
+
}
|
|
96
|
+
// Loads every (path, content_hash, embedding) row for the active model, in
|
|
97
|
+
// path-then-chunk-index order, and groups embeddings by document path.
|
|
98
|
+
function loadEmbeddingsByPath(db, model) {
|
|
99
|
+
const rows = db
|
|
100
|
+
.prepare(`SELECT c.path AS path, e.embedding AS embedding, e.dim AS dim
|
|
101
|
+
FROM chunks c
|
|
102
|
+
LEFT JOIN embeddings e
|
|
103
|
+
ON e.content_hash = c.content_hash AND e.model = ?
|
|
104
|
+
ORDER BY c.path, c.chunk_index`)
|
|
105
|
+
.all(model);
|
|
106
|
+
const provider = getProvider();
|
|
107
|
+
const expectedDim = provider.dim;
|
|
108
|
+
const out = new Map();
|
|
109
|
+
for (const row of rows) {
|
|
110
|
+
if (!row.embedding)
|
|
111
|
+
continue;
|
|
112
|
+
// Defense-in-depth: skip rows whose stored dim disagrees with the
|
|
113
|
+
// provider's expected dim — same guard `getAllChunks` applies.
|
|
114
|
+
const blobOk = row.embedding.length === expectedDim * 4;
|
|
115
|
+
const dimOk = row.dim === expectedDim;
|
|
116
|
+
if (!blobOk || !dimOk)
|
|
117
|
+
continue;
|
|
118
|
+
const vec = blobToEmbedding(row.embedding);
|
|
119
|
+
const list = out.get(row.path);
|
|
120
|
+
if (list)
|
|
121
|
+
list.push(vec);
|
|
122
|
+
else
|
|
123
|
+
out.set(row.path, [vec]);
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
// Applies the scope filter (collection, tags), RBAC, and the doc-has-an-
|
|
128
|
+
// embedded-chunk requirement; returns the clusterable doc set and the count
|
|
129
|
+
// of docs the embedding requirement skipped.
|
|
130
|
+
function buildScopedDocs(documents, embeddingsByPath, filters, access) {
|
|
131
|
+
const scoped = [];
|
|
132
|
+
let skipped = 0;
|
|
133
|
+
for (const doc of documents) {
|
|
134
|
+
if (filters.collection && doc.collection !== filters.collection)
|
|
135
|
+
continue;
|
|
136
|
+
if (filters.tags && filters.tags.length > 0) {
|
|
137
|
+
const hasAll = filters.tags.every((t) => doc.tags.includes(t));
|
|
138
|
+
if (!hasAll)
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (access && !canRead(access.role, doc.collection))
|
|
142
|
+
continue;
|
|
143
|
+
const chunkVecs = embeddingsByPath.get(doc.path) ?? [];
|
|
144
|
+
const pooled = meanPoolL2(chunkVecs);
|
|
145
|
+
if (!pooled) {
|
|
146
|
+
// No embedded chunks (or an all-zero pool): count as skipped per
|
|
147
|
+
// the v1 contract.
|
|
148
|
+
skipped += 1;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
scoped.push({
|
|
152
|
+
path: doc.path,
|
|
153
|
+
title: doc.title,
|
|
154
|
+
collection: doc.collection,
|
|
155
|
+
tags: doc.tags,
|
|
156
|
+
vector: pooled,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
return { scoped, skipped };
|
|
160
|
+
}
|
|
161
|
+
// Cosine similarity for L2-normalised vectors == dot product. Used for
|
|
162
|
+
// representative-doc ranking against a cluster centroid.
|
|
163
|
+
function dotNormalized(a, b) {
|
|
164
|
+
let s = 0;
|
|
165
|
+
const n = Math.min(a.length, b.length);
|
|
166
|
+
for (let i = 0; i < n; i++)
|
|
167
|
+
s += a[i] * b[i];
|
|
168
|
+
return s;
|
|
169
|
+
}
|
|
170
|
+
// TF-IDF over the cluster: term frequency among the cluster's titles +
|
|
171
|
+
// tags, divided by the document-frequency across ALL clusters (so a term
|
|
172
|
+
// that names every theme — e.g. "doc" — doesn't dominate any one of them).
|
|
173
|
+
// Falls back to the most common tags if TF-IDF yields nothing useful, and
|
|
174
|
+
// finally to a generic "theme N" placeholder.
|
|
175
|
+
function buildLabel(clusterDocs, globalDocCount, globalDf, themeIndex) {
|
|
176
|
+
const tf = new Map();
|
|
177
|
+
for (const doc of clusterDocs) {
|
|
178
|
+
const seen = new Set();
|
|
179
|
+
for (const tok of labelTokens(doc.title))
|
|
180
|
+
seen.add(tok);
|
|
181
|
+
for (const tag of doc.tags) {
|
|
182
|
+
for (const tok of labelTokens(tag))
|
|
183
|
+
seen.add(tok);
|
|
184
|
+
}
|
|
185
|
+
for (const tok of seen)
|
|
186
|
+
tf.set(tok, (tf.get(tok) ?? 0) + 1);
|
|
187
|
+
}
|
|
188
|
+
const scored = [];
|
|
189
|
+
for (const [term, freq] of tf) {
|
|
190
|
+
const df = globalDf.get(term) ?? 1;
|
|
191
|
+
// Standard smoothed IDF.
|
|
192
|
+
const idf = Math.log((globalDocCount + 1) / (df + 1)) + 1;
|
|
193
|
+
if (idf <= 0)
|
|
194
|
+
continue;
|
|
195
|
+
scored.push({ term, score: freq * idf });
|
|
196
|
+
}
|
|
197
|
+
scored.sort((a, b) => b.score - a.score);
|
|
198
|
+
const top = scored.slice(0, 3).map((s) => s.term);
|
|
199
|
+
if (top.length > 0)
|
|
200
|
+
return top.join(" / ");
|
|
201
|
+
// Fallback: most common tags.
|
|
202
|
+
const tagFreq = new Map();
|
|
203
|
+
for (const doc of clusterDocs) {
|
|
204
|
+
for (const tag of doc.tags)
|
|
205
|
+
tagFreq.set(tag, (tagFreq.get(tag) ?? 0) + 1);
|
|
206
|
+
}
|
|
207
|
+
const topTags = [...tagFreq.entries()]
|
|
208
|
+
.sort((a, b) => b[1] - a[1])
|
|
209
|
+
.slice(0, 3)
|
|
210
|
+
.map(([t]) => t);
|
|
211
|
+
if (topTags.length > 0)
|
|
212
|
+
return topTags.join(" / ");
|
|
213
|
+
return `theme ${themeIndex + 1}`;
|
|
214
|
+
}
|
|
215
|
+
// Builds the global document-frequency map across the clustered set. Used
|
|
216
|
+
// by buildLabel; computed once and shared because every cluster's IDF must
|
|
217
|
+
// agree on the document base.
|
|
218
|
+
function buildGlobalDf(docs) {
|
|
219
|
+
const df = new Map();
|
|
220
|
+
for (const doc of docs) {
|
|
221
|
+
const seen = new Set();
|
|
222
|
+
for (const tok of labelTokens(doc.title))
|
|
223
|
+
seen.add(tok);
|
|
224
|
+
for (const tag of doc.tags) {
|
|
225
|
+
for (const tok of labelTokens(tag))
|
|
226
|
+
seen.add(tok);
|
|
227
|
+
}
|
|
228
|
+
for (const tok of seen)
|
|
229
|
+
df.set(tok, (df.get(tok) ?? 0) + 1);
|
|
230
|
+
}
|
|
231
|
+
return df;
|
|
232
|
+
}
|
|
233
|
+
function topTagsForCluster(clusterDocs) {
|
|
234
|
+
const freq = new Map();
|
|
235
|
+
for (const doc of clusterDocs) {
|
|
236
|
+
for (const tag of doc.tags)
|
|
237
|
+
freq.set(tag, (freq.get(tag) ?? 0) + 1);
|
|
238
|
+
}
|
|
239
|
+
return [...freq.entries()]
|
|
240
|
+
.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
|
|
241
|
+
.slice(0, RELATED_TAGS_PER_THEME)
|
|
242
|
+
.map(([t]) => t);
|
|
243
|
+
}
|
|
244
|
+
function representativesForCluster(clusterDocs, centroid) {
|
|
245
|
+
return [...clusterDocs]
|
|
246
|
+
.map((d) => ({ path: d.path, sim: dotNormalized(d.vector, centroid) }))
|
|
247
|
+
.sort((a, b) => b.sim - a.sim || a.path.localeCompare(b.path))
|
|
248
|
+
.slice(0, REPRESENTATIVE_DOCS_PER_THEME)
|
|
249
|
+
.map((d) => d.path);
|
|
250
|
+
}
|
|
251
|
+
function parseArgs(args) {
|
|
252
|
+
let k;
|
|
253
|
+
if (args.k !== undefined) {
|
|
254
|
+
if (typeof args.k !== "number" || !Number.isInteger(args.k) || args.k <= 0) {
|
|
255
|
+
return err(new Error("vault_themes: 'k' must be a positive integer when provided"));
|
|
256
|
+
}
|
|
257
|
+
k = args.k;
|
|
258
|
+
}
|
|
259
|
+
const collection = typeof args.collection === "string" && args.collection.length > 0 ? args.collection : undefined;
|
|
260
|
+
let tags;
|
|
261
|
+
if (Array.isArray(args.tags)) {
|
|
262
|
+
const filtered = args.tags.filter((t) => typeof t === "string" && t.length > 0);
|
|
263
|
+
if (filtered.length > 0)
|
|
264
|
+
tags = filtered;
|
|
265
|
+
}
|
|
266
|
+
return ok({ k, collection, tags });
|
|
267
|
+
}
|
|
268
|
+
export async function vaultThemes(vaultRoot, args, access) {
|
|
269
|
+
const parsed = parseArgs(args);
|
|
270
|
+
if (!parsed.ok)
|
|
271
|
+
return parsed;
|
|
272
|
+
const ready = await ensureIndexReady(vaultRoot);
|
|
273
|
+
if (!ready.ok)
|
|
274
|
+
return ready;
|
|
275
|
+
const dbResult = openIndexForActiveProvider(vaultRoot);
|
|
276
|
+
if (!dbResult.ok)
|
|
277
|
+
return dbResult;
|
|
278
|
+
const db = dbResult.value;
|
|
279
|
+
try {
|
|
280
|
+
const provider = getProvider();
|
|
281
|
+
const docRows = db
|
|
282
|
+
.prepare("SELECT path, title, collection, tags FROM documents ORDER BY path")
|
|
283
|
+
.all();
|
|
284
|
+
const documents = docRows.map((r) => ({
|
|
285
|
+
path: r.path,
|
|
286
|
+
title: r.title,
|
|
287
|
+
collection: r.collection,
|
|
288
|
+
domain: "",
|
|
289
|
+
status: "",
|
|
290
|
+
confidence: "",
|
|
291
|
+
updated: "",
|
|
292
|
+
tags: JSON.parse(r.tags),
|
|
293
|
+
content: "",
|
|
294
|
+
tokens: [],
|
|
295
|
+
ttlDays: null,
|
|
296
|
+
created: "",
|
|
297
|
+
supersededBy: null,
|
|
298
|
+
}));
|
|
299
|
+
const embeddingsByPath = loadEmbeddingsByPath(db, provider.id);
|
|
300
|
+
const { scoped, skipped } = buildScopedDocs(documents, embeddingsByPath, parsed.value, access);
|
|
301
|
+
if (scoped.length === 0) {
|
|
302
|
+
return ok({
|
|
303
|
+
themes: [],
|
|
304
|
+
totalDocuments: 0,
|
|
305
|
+
skippedDocuments: skipped,
|
|
306
|
+
selectedK: 0,
|
|
307
|
+
clusteredAt: new Date().toISOString(),
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
const vectors = scoped.map((s) => s.vector);
|
|
311
|
+
const rng = seededRng(THEMES_RNG_SEED);
|
|
312
|
+
let selectedK;
|
|
313
|
+
let assignments;
|
|
314
|
+
let centroids;
|
|
315
|
+
if (parsed.value.k !== undefined) {
|
|
316
|
+
const result = kmeans(vectors, parsed.value.k, rng, KMEANS_MAX_ITER);
|
|
317
|
+
selectedK = result.centroids.length;
|
|
318
|
+
assignments = result.assignments;
|
|
319
|
+
centroids = result.centroids;
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
const result = pickK(vectors, DEFAULT_K_CANDIDATES, rng, KMEANS_MAX_ITER);
|
|
323
|
+
selectedK = result.centroids.length;
|
|
324
|
+
assignments = result.assignments;
|
|
325
|
+
centroids = result.centroids;
|
|
326
|
+
}
|
|
327
|
+
// Group docs by cluster.
|
|
328
|
+
const byCluster = new Map();
|
|
329
|
+
for (let i = 0; i < scoped.length; i++) {
|
|
330
|
+
const c = assignments[i];
|
|
331
|
+
const doc = scoped[i];
|
|
332
|
+
const list = byCluster.get(c);
|
|
333
|
+
if (list)
|
|
334
|
+
list.push(doc);
|
|
335
|
+
else
|
|
336
|
+
byCluster.set(c, [doc]);
|
|
337
|
+
}
|
|
338
|
+
const globalDf = buildGlobalDf(scoped);
|
|
339
|
+
// Secondary memberships: for each doc, find non-primary clusters its
|
|
340
|
+
// pooled vector also aligns with. This is the soft-reporting layer on
|
|
341
|
+
// top of the hard partition — it does NOT change documentCount, which
|
|
342
|
+
// still reflects primary membership only.
|
|
343
|
+
const secondaryByCluster = selectSecondaryMemberships(vectors, assignments, centroids, {
|
|
344
|
+
delta: SECONDARY_DELTA,
|
|
345
|
+
minSimilarity: SECONDARY_MIN_SIMILARITY,
|
|
346
|
+
maxPerDoc: SECONDARY_MAX_PER_DOC,
|
|
347
|
+
});
|
|
348
|
+
const themes = [];
|
|
349
|
+
let themeIndex = 0;
|
|
350
|
+
for (const [clusterIdx, clusterDocs] of byCluster) {
|
|
351
|
+
if (clusterDocs.length === 0)
|
|
352
|
+
continue;
|
|
353
|
+
const centroid = centroids[clusterIdx];
|
|
354
|
+
if (!centroid)
|
|
355
|
+
continue;
|
|
356
|
+
// Singleton clusters have no pairs to average — return null rather
|
|
357
|
+
// than the mathematically-trivial 1.0, which would falsely imply
|
|
358
|
+
// tightness.
|
|
359
|
+
const coherence = clusterDocs.length < 2 ? null : clusterCoherence(clusterDocs.map((d) => d.vector));
|
|
360
|
+
const secondaries = (secondaryByCluster.get(clusterIdx) ?? [])
|
|
361
|
+
.slice(0, SECONDARY_DOCS_PER_THEME)
|
|
362
|
+
.map((s) => scoped[s.docIndex].path);
|
|
363
|
+
themes.push({
|
|
364
|
+
label: buildLabel(clusterDocs, scoped.length, globalDf, themeIndex),
|
|
365
|
+
documentCount: clusterDocs.length,
|
|
366
|
+
coherence,
|
|
367
|
+
representativeDocs: representativesForCluster(clusterDocs, centroid),
|
|
368
|
+
secondaryDocs: secondaries,
|
|
369
|
+
relatedTags: topTagsForCluster(clusterDocs),
|
|
370
|
+
});
|
|
371
|
+
themeIndex += 1;
|
|
372
|
+
}
|
|
373
|
+
themes.sort((a, b) => b.documentCount - a.documentCount || a.label.localeCompare(b.label));
|
|
374
|
+
return ok({
|
|
375
|
+
themes,
|
|
376
|
+
totalDocuments: scoped.length,
|
|
377
|
+
skippedDocuments: skipped,
|
|
378
|
+
selectedK,
|
|
379
|
+
clusteredAt: new Date().toISOString(),
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
finally {
|
|
383
|
+
db.close();
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
// ---------------------------------------------------------------------------
|
|
387
|
+
// MCP tool definition
|
|
388
|
+
// ---------------------------------------------------------------------------
|
|
389
|
+
export const themesTools = [
|
|
390
|
+
{
|
|
391
|
+
name: "vault_themes",
|
|
392
|
+
description: "Surface thematic clusters across the vault using k-means over " +
|
|
393
|
+
"document-pooled embeddings. Each document's chunk vectors are " +
|
|
394
|
+
"mean-pooled into one vector, L2-normalised, and clustered. By " +
|
|
395
|
+
"default the tool sweeps k ∈ {10, 15, 20, 25} and picks the k with " +
|
|
396
|
+
"the best mean silhouette; pass `k` to skip the sweep. Each theme " +
|
|
397
|
+
"reports a heuristic label (TF-IDF over titles + tags), a coherence " +
|
|
398
|
+
"score (mean pairwise cosine inside the cluster), representative " +
|
|
399
|
+
"documents nearest the centroid, and the most frequent tags. " +
|
|
400
|
+
"Output is deterministic for the same vault.",
|
|
401
|
+
inputSchema: {
|
|
402
|
+
type: "object",
|
|
403
|
+
properties: {
|
|
404
|
+
k: {
|
|
405
|
+
type: "integer",
|
|
406
|
+
description: "Optional explicit cluster count. When omitted, the tool sweeps " +
|
|
407
|
+
"k ∈ {10, 15, 20, 25} and picks the k with the best silhouette.",
|
|
408
|
+
minimum: 1,
|
|
409
|
+
},
|
|
410
|
+
collection: {
|
|
411
|
+
type: "string",
|
|
412
|
+
description: "Restrict clustering to documents in this collection.",
|
|
413
|
+
},
|
|
414
|
+
tags: {
|
|
415
|
+
type: "array",
|
|
416
|
+
items: { type: "string" },
|
|
417
|
+
description: "Restrict to documents that have all of these tags.",
|
|
418
|
+
},
|
|
419
|
+
},
|
|
420
|
+
additionalProperties: false,
|
|
421
|
+
},
|
|
422
|
+
handler: (vaultRoot, args, access) => vaultThemes(vaultRoot, args, access),
|
|
423
|
+
},
|
|
424
|
+
];
|
|
425
|
+
//# sourceMappingURL=themes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"themes.js","sourceRoot":"","sources":["../../src/tools/themes.ts"],"names":[],"mappings":"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"}
|