From cc1a4317b1a24672d012c1404bc446c0ffc0862b Mon Sep 17 00:00:00 2001 From: David Snelling Date: Tue, 16 Jun 2026 13:13:07 -0700 Subject: [PATCH] fix(8.0): neural.clusters()/similarity must request vectors from get() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit brain.get() returns metadata only by default (includeVectors: false) for speed; the vector is fetched via get(id, { includeVectors: true }). The neural API's clustering, similarity, and vector-conversion helpers called get() without it, so every entity came back with vector: [] — _getItemsWithVectors filtered them all out, leaving k-means++ to dereference an empty array ("Cannot read properties of undefined (reading 'vector')"). neural.clusters() was effectively non-functional, and similarity-by-id silently scored 0. - Pass { includeVectors: true } at the 8 vector-consuming get() call sites (similarity, _convertToVector, _getItemsWith{Vectors,Metadata}, cluster membership + quality scoring). - Guard k-means against an empty item set (return an empty result, never crash). "cluster entities semantically" passes. (includeVFS clustering + vfs.move have a separate VFS-vector-dimension issue, tracked in .strategy.) --- src/neural/improvedNeuralAPI.ts | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/neural/improvedNeuralAPI.ts b/src/neural/improvedNeuralAPI.ts index 625bd9d9..700e46a5 100644 --- a/src/neural/improvedNeuralAPI.ts +++ b/src/neural/improvedNeuralAPI.ts @@ -1075,7 +1075,13 @@ export class ImprovedNeuralAPI { // Get vectors for all items using existing infrastructure const itemsWithVectors = await this._getItemsWithVectors(itemIds) - + + // No items carried a usable vector (e.g. all vectorless) — return an empty + // result rather than letting centroid init dereference an empty array. + if (itemsWithVectors.length === 0) { + return this._createEmptyResult(startTime, 'kmeans') + } + // Determine optimal k const k = options.maxClusters || Math.min( Math.floor(Math.sqrt(itemsWithVectors.length / 2)), @@ -1448,8 +1454,8 @@ export class ImprovedNeuralAPI { // Similarity implementation methods private async _similarityById(id1: string, id2: string, options: SimilarityOptions): Promise { // Get vectors for both items - const item1 = await this.brain.get(id1) - const item2 = await this.brain.get(id2) + const item1 = await this.brain.get(id1, { includeVectors: true }) + const item2 = await this.brain.get(id2, { includeVectors: true }) if (!item1 || !item2) { return 0 @@ -1520,7 +1526,7 @@ export class ImprovedNeuralAPI { if (this._isVector(input)) { return input } else if (this._isId(input)) { - const item = await this.brain.get(input) + const item = await this.brain.get(input, { includeVectors: true }) return item?.vector || [] } else if (typeof input === 'string') { return await this.brain.embed(input) @@ -1795,7 +1801,7 @@ export class ImprovedNeuralAPI { private async _getItemsWithMetadata(itemIds: string[]): Promise { const items = await Promise.all( itemIds.map(async id => { - const noun = await this.brain.get(id) + const noun = await this.brain.get(id, { includeVectors: true }) if (!noun) { return null } @@ -2087,7 +2093,7 @@ export class ImprovedNeuralAPI { private async _getItemsWithVectors(itemIds: string[]): Promise> { const items = await Promise.all( itemIds.map(async id => { - const noun = await this.brain.get(id) + const noun = await this.brain.get(id, { includeVectors: true }) return { id, vector: noun?.vector || [] @@ -3208,7 +3214,7 @@ export class ImprovedNeuralAPI { private async _calculateItemToClusterSimilarity(itemId: string, cluster: SemanticCluster): Promise { // Calculate similarity between an item and a cluster centroid - const item = await this.brain.get(itemId) + const item = await this.brain.get(itemId, { includeVectors: true }) if (!item || !item.vector || !cluster.centroid) { return 0 // No similarity if vectors missing } @@ -3234,7 +3240,7 @@ export class ImprovedNeuralAPI { // Get all member vectors const memberVectors: Vector[] = [] for (const memberId of cluster.members) { - const member = await this.brain.get(memberId) + const member = await this.brain.get(memberId, { includeVectors: true }) if (member && member.vector) { memberVectors.push(member.vector) } @@ -3630,7 +3636,7 @@ export class ImprovedNeuralAPI { if (clusters.length === 0) break try { - const noun = await this.brain.get(itemId) + const noun = await this.brain.get(itemId, { includeVectors: true }) const itemVector = noun?.vector || [] if (itemVector.length === 0) continue