fix(8.0): neural.clusters()/similarity must request vectors from get()
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.)
This commit is contained in:
parent
73a7d8291b
commit
cc1a4317b1
1 changed files with 15 additions and 9 deletions
|
|
@ -1076,6 +1076,12 @@ export class ImprovedNeuralAPI {
|
||||||
// Get vectors for all items using existing infrastructure
|
// Get vectors for all items using existing infrastructure
|
||||||
const itemsWithVectors = await this._getItemsWithVectors(itemIds)
|
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
|
// Determine optimal k
|
||||||
const k = options.maxClusters || Math.min(
|
const k = options.maxClusters || Math.min(
|
||||||
Math.floor(Math.sqrt(itemsWithVectors.length / 2)),
|
Math.floor(Math.sqrt(itemsWithVectors.length / 2)),
|
||||||
|
|
@ -1448,8 +1454,8 @@ export class ImprovedNeuralAPI {
|
||||||
// Similarity implementation methods
|
// Similarity implementation methods
|
||||||
private async _similarityById(id1: string, id2: string, options: SimilarityOptions): Promise<number | SimilarityResult> {
|
private async _similarityById(id1: string, id2: string, options: SimilarityOptions): Promise<number | SimilarityResult> {
|
||||||
// Get vectors for both items
|
// Get vectors for both items
|
||||||
const item1 = await this.brain.get(id1)
|
const item1 = await this.brain.get(id1, { includeVectors: true })
|
||||||
const item2 = await this.brain.get(id2)
|
const item2 = await this.brain.get(id2, { includeVectors: true })
|
||||||
|
|
||||||
if (!item1 || !item2) {
|
if (!item1 || !item2) {
|
||||||
return 0
|
return 0
|
||||||
|
|
@ -1520,7 +1526,7 @@ export class ImprovedNeuralAPI {
|
||||||
if (this._isVector(input)) {
|
if (this._isVector(input)) {
|
||||||
return input
|
return input
|
||||||
} else if (this._isId(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 || []
|
return item?.vector || []
|
||||||
} else if (typeof input === 'string') {
|
} else if (typeof input === 'string') {
|
||||||
return await this.brain.embed(input)
|
return await this.brain.embed(input)
|
||||||
|
|
@ -1795,7 +1801,7 @@ export class ImprovedNeuralAPI {
|
||||||
private async _getItemsWithMetadata(itemIds: string[]): Promise<ItemWithMetadata[]> {
|
private async _getItemsWithMetadata(itemIds: string[]): Promise<ItemWithMetadata[]> {
|
||||||
const items = await Promise.all(
|
const items = await Promise.all(
|
||||||
itemIds.map(async id => {
|
itemIds.map(async id => {
|
||||||
const noun = await this.brain.get(id)
|
const noun = await this.brain.get(id, { includeVectors: true })
|
||||||
if (!noun) {
|
if (!noun) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
@ -2087,7 +2093,7 @@ export class ImprovedNeuralAPI {
|
||||||
private async _getItemsWithVectors(itemIds: string[]): Promise<Array<{id: string, vector: number[]}>> {
|
private async _getItemsWithVectors(itemIds: string[]): Promise<Array<{id: string, vector: number[]}>> {
|
||||||
const items = await Promise.all(
|
const items = await Promise.all(
|
||||||
itemIds.map(async id => {
|
itemIds.map(async id => {
|
||||||
const noun = await this.brain.get(id)
|
const noun = await this.brain.get(id, { includeVectors: true })
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
vector: noun?.vector || []
|
vector: noun?.vector || []
|
||||||
|
|
@ -3208,7 +3214,7 @@ export class ImprovedNeuralAPI {
|
||||||
|
|
||||||
private async _calculateItemToClusterSimilarity(itemId: string, cluster: SemanticCluster): Promise<number> {
|
private async _calculateItemToClusterSimilarity(itemId: string, cluster: SemanticCluster): Promise<number> {
|
||||||
// Calculate similarity between an item and a cluster centroid
|
// 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) {
|
if (!item || !item.vector || !cluster.centroid) {
|
||||||
return 0 // No similarity if vectors missing
|
return 0 // No similarity if vectors missing
|
||||||
}
|
}
|
||||||
|
|
@ -3234,7 +3240,7 @@ export class ImprovedNeuralAPI {
|
||||||
// Get all member vectors
|
// Get all member vectors
|
||||||
const memberVectors: Vector[] = []
|
const memberVectors: Vector[] = []
|
||||||
for (const memberId of cluster.members) {
|
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) {
|
if (member && member.vector) {
|
||||||
memberVectors.push(member.vector)
|
memberVectors.push(member.vector)
|
||||||
}
|
}
|
||||||
|
|
@ -3630,7 +3636,7 @@ export class ImprovedNeuralAPI {
|
||||||
if (clusters.length === 0) break
|
if (clusters.length === 0) break
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const noun = await this.brain.get(itemId)
|
const noun = await this.brain.get(itemId, { includeVectors: true })
|
||||||
const itemVector = noun?.vector || []
|
const itemVector = noun?.vector || []
|
||||||
if (itemVector.length === 0) continue
|
if (itemVector.length === 0) continue
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue