kmeans.mjs
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd
feat(calendar): enforce agent context tiers in retrieval AP…
Human
minor
⚠ breaking
1 day ago
| 1 | /** |
| 2 | * Minimal k-means for embedding vectors (Issue #1 Phase C8). |
| 3 | */ |
| 4 | |
| 5 | function distSq(a, b) { |
| 6 | let s = 0; |
| 7 | const n = Math.min(a.length, b.length); |
| 8 | for (let i = 0; i < n; i++) { |
| 9 | const d = a[i] - b[i]; |
| 10 | s += d * d; |
| 11 | } |
| 12 | return s; |
| 13 | } |
| 14 | |
| 15 | function mean(vectors) { |
| 16 | if (!vectors.length) return []; |
| 17 | const dim = vectors[0].length; |
| 18 | const acc = new Array(dim).fill(0); |
| 19 | for (const v of vectors) { |
| 20 | for (let i = 0; i < dim; i++) acc[i] += v[i] || 0; |
| 21 | } |
| 22 | return acc.map((x) => x / vectors.length); |
| 23 | } |
| 24 | |
| 25 | /** |
| 26 | * @param {{ id: string, vector: number[] }[]} points |
| 27 | * @param {number} k |
| 28 | * @param {number} maxIter |
| 29 | * @returns {{ labels: number[], centroids: number[][] }} |
| 30 | */ |
| 31 | export function kmeans(points, k, maxIter = 25) { |
| 32 | if (!points.length || k < 1) return { labels: [], centroids: [] }; |
| 33 | k = Math.min(k, points.length); |
| 34 | const dim = points[0].vector.length; |
| 35 | const centroids = []; |
| 36 | const step = Math.max(1, Math.floor(points.length / k)); |
| 37 | for (let c = 0; c < k; c++) { |
| 38 | centroids.push([...points[(c * step) % points.length].vector]); |
| 39 | } |
| 40 | |
| 41 | const labels = new Array(points.length).fill(0); |
| 42 | |
| 43 | for (let it = 0; it < maxIter; it++) { |
| 44 | let changed = false; |
| 45 | for (let i = 0; i < points.length; i++) { |
| 46 | let best = 0; |
| 47 | let bestD = Infinity; |
| 48 | for (let c = 0; c < k; c++) { |
| 49 | const d = distSq(points[i].vector, centroids[c]); |
| 50 | if (d < bestD) { |
| 51 | bestD = d; |
| 52 | best = c; |
| 53 | } |
| 54 | } |
| 55 | if (labels[i] !== best) { |
| 56 | labels[i] = best; |
| 57 | changed = true; |
| 58 | } |
| 59 | } |
| 60 | const groups = Array.from({ length: k }, () => []); |
| 61 | for (let i = 0; i < points.length; i++) { |
| 62 | groups[labels[i]].push(points[i].vector); |
| 63 | } |
| 64 | for (let c = 0; c < k; c++) { |
| 65 | if (groups[c].length) centroids[c] = mean(groups[c]); |
| 66 | } |
| 67 | if (!changed) break; |
| 68 | } |
| 69 | |
| 70 | return { labels, centroids }; |
| 71 | } |
File History
2 commits
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd
feat(calendar): enforce agent context tiers in retrieval AP…
Human
minor
⚠
1 day ago
sha256:9103f98c89257ed2b01c237cea895dabb3e85ea337dccb1161c175e4422355b6
docs: accept Calendar Events v0 spec with Phase 0 security …
Human
2 days ago