@sparkleideas/plugins 3.0.0-alpha.10

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.
Files changed (80) hide show
  1. package/README.md +401 -0
  2. package/__tests__/collection-manager.test.ts +332 -0
  3. package/__tests__/dependency-graph.test.ts +434 -0
  4. package/__tests__/enhanced-plugin-registry.test.ts +488 -0
  5. package/__tests__/plugin-registry.test.ts +368 -0
  6. package/__tests__/ruvector-bridge.test.ts +2429 -0
  7. package/__tests__/ruvector-integration.test.ts +1602 -0
  8. package/__tests__/ruvector-migrations.test.ts +1099 -0
  9. package/__tests__/ruvector-quantization.test.ts +846 -0
  10. package/__tests__/ruvector-streaming.test.ts +1088 -0
  11. package/__tests__/sdk.test.ts +325 -0
  12. package/__tests__/security.test.ts +348 -0
  13. package/__tests__/utils/ruvector-test-utils.ts +860 -0
  14. package/examples/plugin-creator/index.ts +636 -0
  15. package/examples/plugin-creator/plugin-creator.test.ts +312 -0
  16. package/examples/ruvector/README.md +288 -0
  17. package/examples/ruvector/attention-patterns.ts +394 -0
  18. package/examples/ruvector/basic-usage.ts +288 -0
  19. package/examples/ruvector/docker-compose.yml +75 -0
  20. package/examples/ruvector/gnn-analysis.ts +501 -0
  21. package/examples/ruvector/hyperbolic-hierarchies.ts +557 -0
  22. package/examples/ruvector/init-db.sql +119 -0
  23. package/examples/ruvector/quantization.ts +680 -0
  24. package/examples/ruvector/self-learning.ts +447 -0
  25. package/examples/ruvector/semantic-search.ts +576 -0
  26. package/examples/ruvector/streaming-large-data.ts +507 -0
  27. package/examples/ruvector/transactions.ts +594 -0
  28. package/examples/ruvector-plugins/hook-pattern-library.ts +486 -0
  29. package/examples/ruvector-plugins/index.ts +79 -0
  30. package/examples/ruvector-plugins/intent-router.ts +354 -0
  31. package/examples/ruvector-plugins/mcp-tool-optimizer.ts +424 -0
  32. package/examples/ruvector-plugins/reasoning-bank.ts +657 -0
  33. package/examples/ruvector-plugins/ruvector-plugins.test.ts +518 -0
  34. package/examples/ruvector-plugins/semantic-code-search.ts +498 -0
  35. package/examples/ruvector-plugins/shared/index.ts +20 -0
  36. package/examples/ruvector-plugins/shared/vector-utils.ts +257 -0
  37. package/examples/ruvector-plugins/sona-learning.ts +445 -0
  38. package/package.json +97 -0
  39. package/src/collections/collection-manager.ts +661 -0
  40. package/src/collections/index.ts +56 -0
  41. package/src/collections/official/index.ts +1040 -0
  42. package/src/core/base-plugin.ts +416 -0
  43. package/src/core/plugin-interface.ts +215 -0
  44. package/src/hooks/index.ts +685 -0
  45. package/src/index.ts +378 -0
  46. package/src/integrations/agentic-flow.ts +743 -0
  47. package/src/integrations/index.ts +88 -0
  48. package/src/integrations/ruvector/ARCHITECTURE.md +1245 -0
  49. package/src/integrations/ruvector/attention-advanced.ts +1040 -0
  50. package/src/integrations/ruvector/attention-executor.ts +782 -0
  51. package/src/integrations/ruvector/attention-mechanisms.ts +757 -0
  52. package/src/integrations/ruvector/attention.ts +1063 -0
  53. package/src/integrations/ruvector/gnn.ts +3050 -0
  54. package/src/integrations/ruvector/hyperbolic.ts +1948 -0
  55. package/src/integrations/ruvector/index.ts +394 -0
  56. package/src/integrations/ruvector/migrations/001_create_extension.sql +135 -0
  57. package/src/integrations/ruvector/migrations/002_create_vector_tables.sql +259 -0
  58. package/src/integrations/ruvector/migrations/003_create_indices.sql +328 -0
  59. package/src/integrations/ruvector/migrations/004_create_functions.sql +598 -0
  60. package/src/integrations/ruvector/migrations/005_create_attention_functions.sql +654 -0
  61. package/src/integrations/ruvector/migrations/006_create_gnn_functions.sql +728 -0
  62. package/src/integrations/ruvector/migrations/007_create_hyperbolic_functions.sql +762 -0
  63. package/src/integrations/ruvector/migrations/index.ts +35 -0
  64. package/src/integrations/ruvector/migrations/migrations.ts +647 -0
  65. package/src/integrations/ruvector/quantization.ts +2036 -0
  66. package/src/integrations/ruvector/ruvector-bridge.ts +2000 -0
  67. package/src/integrations/ruvector/self-learning.ts +2376 -0
  68. package/src/integrations/ruvector/streaming.ts +1737 -0
  69. package/src/integrations/ruvector/types.ts +1945 -0
  70. package/src/providers/index.ts +643 -0
  71. package/src/registry/dependency-graph.ts +568 -0
  72. package/src/registry/enhanced-plugin-registry.ts +994 -0
  73. package/src/registry/plugin-registry.ts +604 -0
  74. package/src/sdk/index.ts +563 -0
  75. package/src/security/index.ts +594 -0
  76. package/src/types/index.ts +446 -0
  77. package/src/workers/index.ts +700 -0
  78. package/tmp.json +0 -0
  79. package/tsconfig.json +25 -0
  80. package/vitest.config.ts +23 -0
@@ -0,0 +1,557 @@
1
+ /**
2
+ * RuVector PostgreSQL Bridge - Hyperbolic Embeddings Example
3
+ *
4
+ * This example demonstrates:
5
+ * - Embedding file tree structures in hyperbolic space
6
+ * - Embedding class hierarchies with Poincare ball model
7
+ * - Calculating hierarchy-aware distances
8
+ * - Comparing Euclidean vs hyperbolic representations
9
+ *
10
+ * Run with: npx ts-node examples/ruvector/hyperbolic-hierarchies.ts
11
+ *
12
+ * @module @sparkleideas/plugins/examples/ruvector/hyperbolic-hierarchies
13
+ */
14
+
15
+ import {
16
+ createRuVectorBridge,
17
+ type RuVectorBridge,
18
+ } from '../../src/integrations/ruvector/index.js';
19
+
20
+ import {
21
+ PoincareBall,
22
+ PoincareEmbedding,
23
+ type HyperbolicConfig,
24
+ type HierarchyNode,
25
+ } from '../../src/integrations/ruvector/hyperbolic.js';
26
+
27
+ // ============================================================================
28
+ // Configuration
29
+ // ============================================================================
30
+
31
+ const config = {
32
+ connection: {
33
+ host: process.env.POSTGRES_HOST || 'localhost',
34
+ port: parseInt(process.env.POSTGRES_PORT || '5432', 10),
35
+ database: process.env.POSTGRES_DB || 'vectors',
36
+ user: process.env.POSTGRES_USER || 'postgres',
37
+ password: process.env.POSTGRES_PASSWORD || 'postgres',
38
+ },
39
+ hyperbolicDim: 32,
40
+ curvature: -1.0, // Negative curvature for hyperbolic space
41
+ };
42
+
43
+ // ============================================================================
44
+ // Sample Hierarchical Data
45
+ // ============================================================================
46
+
47
+ /**
48
+ * File system tree structure.
49
+ */
50
+ const fileTree: HierarchyNode = {
51
+ id: 'root',
52
+ name: '/',
53
+ children: [
54
+ {
55
+ id: 'src',
56
+ name: 'src',
57
+ children: [
58
+ {
59
+ id: 'components',
60
+ name: 'components',
61
+ children: [
62
+ { id: 'button', name: 'Button.tsx', children: [] },
63
+ { id: 'input', name: 'Input.tsx', children: [] },
64
+ { id: 'modal', name: 'Modal.tsx', children: [] },
65
+ {
66
+ id: 'forms',
67
+ name: 'forms',
68
+ children: [
69
+ { id: 'login-form', name: 'LoginForm.tsx', children: [] },
70
+ { id: 'signup-form', name: 'SignupForm.tsx', children: [] },
71
+ ],
72
+ },
73
+ ],
74
+ },
75
+ {
76
+ id: 'services',
77
+ name: 'services',
78
+ children: [
79
+ { id: 'auth-service', name: 'auth.ts', children: [] },
80
+ { id: 'api-service', name: 'api.ts', children: [] },
81
+ { id: 'storage-service', name: 'storage.ts', children: [] },
82
+ ],
83
+ },
84
+ {
85
+ id: 'utils',
86
+ name: 'utils',
87
+ children: [
88
+ { id: 'format', name: 'format.ts', children: [] },
89
+ { id: 'validate', name: 'validate.ts', children: [] },
90
+ ],
91
+ },
92
+ ],
93
+ },
94
+ {
95
+ id: 'tests',
96
+ name: 'tests',
97
+ children: [
98
+ { id: 'unit', name: 'unit', children: [
99
+ { id: 'auth-test', name: 'auth.test.ts', children: [] },
100
+ { id: 'api-test', name: 'api.test.ts', children: [] },
101
+ ]},
102
+ { id: 'integration', name: 'integration', children: [
103
+ { id: 'e2e-test', name: 'e2e.test.ts', children: [] },
104
+ ]},
105
+ ],
106
+ },
107
+ {
108
+ id: 'config',
109
+ name: 'config',
110
+ children: [
111
+ { id: 'tsconfig', name: 'tsconfig.json', children: [] },
112
+ { id: 'eslint', name: '.eslintrc.js', children: [] },
113
+ ],
114
+ },
115
+ ],
116
+ };
117
+
118
+ /**
119
+ * Class inheritance hierarchy (TypeScript/OOP).
120
+ */
121
+ const classHierarchy: HierarchyNode = {
122
+ id: 'object',
123
+ name: 'Object',
124
+ children: [
125
+ {
126
+ id: 'error',
127
+ name: 'Error',
128
+ children: [
129
+ {
130
+ id: 'validation-error',
131
+ name: 'ValidationError',
132
+ children: [
133
+ { id: 'field-error', name: 'FieldValidationError', children: [] },
134
+ { id: 'schema-error', name: 'SchemaValidationError', children: [] },
135
+ ],
136
+ },
137
+ {
138
+ id: 'http-error',
139
+ name: 'HttpError',
140
+ children: [
141
+ { id: 'not-found', name: 'NotFoundError', children: [] },
142
+ { id: 'unauthorized', name: 'UnauthorizedError', children: [] },
143
+ { id: 'forbidden', name: 'ForbiddenError', children: [] },
144
+ ],
145
+ },
146
+ { id: 'database-error', name: 'DatabaseError', children: [] },
147
+ ],
148
+ },
149
+ {
150
+ id: 'base-service',
151
+ name: 'BaseService',
152
+ children: [
153
+ {
154
+ id: 'crud-service',
155
+ name: 'CrudService',
156
+ children: [
157
+ { id: 'user-service', name: 'UserService', children: [] },
158
+ { id: 'product-service', name: 'ProductService', children: [] },
159
+ ],
160
+ },
161
+ { id: 'auth-svc', name: 'AuthService', children: [] },
162
+ { id: 'cache-svc', name: 'CacheService', children: [] },
163
+ ],
164
+ },
165
+ {
166
+ id: 'base-controller',
167
+ name: 'BaseController',
168
+ children: [
169
+ { id: 'user-controller', name: 'UserController', children: [] },
170
+ { id: 'auth-controller', name: 'AuthController', children: [] },
171
+ ],
172
+ },
173
+ ],
174
+ };
175
+
176
+ // ============================================================================
177
+ // Helper Functions
178
+ // ============================================================================
179
+
180
+ /**
181
+ * Flatten hierarchy to list with depth information.
182
+ */
183
+ function flattenHierarchy(
184
+ node: HierarchyNode,
185
+ depth: number = 0,
186
+ parent: string | null = null
187
+ ): Array<{ node: HierarchyNode; depth: number; parent: string | null }> {
188
+ const result: Array<{ node: HierarchyNode; depth: number; parent: string | null }> = [
189
+ { node, depth, parent },
190
+ ];
191
+
192
+ for (const child of node.children) {
193
+ result.push(...flattenHierarchy(child, depth + 1, node.id));
194
+ }
195
+
196
+ return result;
197
+ }
198
+
199
+ /**
200
+ * Find path between two nodes in hierarchy.
201
+ */
202
+ function findPath(root: HierarchyNode, targetId: string): string[] | null {
203
+ if (root.id === targetId) return [root.id];
204
+
205
+ for (const child of root.children) {
206
+ const childPath = findPath(child, targetId);
207
+ if (childPath) return [root.id, ...childPath];
208
+ }
209
+
210
+ return null;
211
+ }
212
+
213
+ /**
214
+ * Calculate tree distance (number of edges in path).
215
+ */
216
+ function treeDistance(root: HierarchyNode, id1: string, id2: string): number {
217
+ const path1 = findPath(root, id1) || [];
218
+ const path2 = findPath(root, id2) || [];
219
+
220
+ // Find lowest common ancestor
221
+ let lcaDepth = 0;
222
+ while (
223
+ lcaDepth < path1.length &&
224
+ lcaDepth < path2.length &&
225
+ path1[lcaDepth] === path2[lcaDepth]
226
+ ) {
227
+ lcaDepth++;
228
+ }
229
+
230
+ // Distance = path from node1 to LCA + path from LCA to node2
231
+ return (path1.length - lcaDepth) + (path2.length - lcaDepth);
232
+ }
233
+
234
+ // ============================================================================
235
+ // Main Example
236
+ // ============================================================================
237
+
238
+ async function main(): Promise<void> {
239
+ console.log('RuVector PostgreSQL Bridge - Hyperbolic Embeddings Example');
240
+ console.log('============================================================\n');
241
+
242
+ const bridge: RuVectorBridge = createRuVectorBridge({
243
+ connectionString: `postgresql://${config.connection.user}:${config.connection.password}@${config.connection.host}:${config.connection.port}/${config.connection.database}`,
244
+ });
245
+
246
+ // Initialize Poincare ball model
247
+ const poincare = new PoincareBall({
248
+ dimension: config.hyperbolicDim,
249
+ curvature: config.curvature,
250
+ epsilon: 1e-6,
251
+ });
252
+
253
+ try {
254
+ await bridge.connect();
255
+ console.log('Connected to PostgreSQL\n');
256
+
257
+ // ========================================================================
258
+ // 1. Embed File Tree Structure
259
+ // ========================================================================
260
+ console.log('1. Embedding File Tree in Hyperbolic Space');
261
+ console.log(' ' + '-'.repeat(50));
262
+
263
+ const fileEmbedding = new PoincareEmbedding({
264
+ dimension: config.hyperbolicDim,
265
+ curvature: config.curvature,
266
+ learningRate: 0.01,
267
+ });
268
+
269
+ // Train embeddings on file hierarchy
270
+ console.log(' Training Poincare embeddings for file tree...');
271
+ const startTrain = performance.now();
272
+ await fileEmbedding.train(fileTree, { epochs: 100, batchSize: 16 });
273
+ const trainTime = performance.now() - startTrain;
274
+ console.log(` Training completed in ${trainTime.toFixed(0)}ms`);
275
+
276
+ // Get embeddings for all nodes
277
+ const flatFiles = flattenHierarchy(fileTree);
278
+ console.log(`\n Embedded ${flatFiles.length} nodes`);
279
+
280
+ // Show embedding norms (closer to 1 = deeper in hierarchy)
281
+ console.log('\n Embedding norms by depth (closer to 1 = deeper):');
282
+ const depthGroups = new Map<number, Array<{ name: string; norm: number }>>();
283
+
284
+ for (const { node, depth } of flatFiles) {
285
+ const embedding = fileEmbedding.getEmbedding(node.id);
286
+ if (embedding) {
287
+ const norm = Math.sqrt(embedding.reduce((s, v) => s + v * v, 0));
288
+ if (!depthGroups.has(depth)) depthGroups.set(depth, []);
289
+ depthGroups.get(depth)?.push({ name: node.name, norm });
290
+ }
291
+ }
292
+
293
+ depthGroups.forEach((nodes, depth) => {
294
+ const avgNorm = nodes.reduce((s, n) => s + n.norm, 0) / nodes.length;
295
+ const samples = nodes.slice(0, 3).map(n => n.name).join(', ');
296
+ console.log(` Depth ${depth}: avg norm = ${avgNorm.toFixed(4)} (${samples}${nodes.length > 3 ? '...' : ''})`);
297
+ });
298
+ console.log();
299
+
300
+ // ========================================================================
301
+ // 2. Hyperbolic Distance vs Tree Distance
302
+ // ========================================================================
303
+ console.log('2. Comparing Hyperbolic Distance to Tree Distance');
304
+ console.log(' ' + '-'.repeat(50));
305
+
306
+ const testPairs = [
307
+ ['button', 'input'], // Same directory
308
+ ['button', 'login-form'], // Nearby (components)
309
+ ['button', 'auth-service'], // Different subtrees
310
+ ['button', 'auth-test'], // Far apart (src vs tests)
311
+ ['root', 'login-form'], // Root to deep node
312
+ ];
313
+
314
+ console.log(' Node Pair | Tree Dist | Hyperbolic Dist | Correlation');
315
+ console.log(' ' + '-'.repeat(75));
316
+
317
+ for (const [id1, id2] of testPairs) {
318
+ const treeDist = treeDistance(fileTree, id1, id2);
319
+ const emb1 = fileEmbedding.getEmbedding(id1);
320
+ const emb2 = fileEmbedding.getEmbedding(id2);
321
+
322
+ if (emb1 && emb2) {
323
+ const hypDist = poincare.distance(emb1, emb2);
324
+ const node1 = flatFiles.find(f => f.node.id === id1)?.node.name || id1;
325
+ const node2 = flatFiles.find(f => f.node.id === id2)?.node.name || id2;
326
+ const pairName = `${node1} <-> ${node2}`;
327
+
328
+ console.log(
329
+ ` ${pairName.padEnd(25)} | ${treeDist.toString().padStart(9)} | ` +
330
+ `${hypDist.toFixed(4).padStart(15)} | ` +
331
+ `${treeDist > 0 ? (hypDist / treeDist).toFixed(3) : 'N/A'}`
332
+ );
333
+ }
334
+ }
335
+ console.log();
336
+
337
+ // ========================================================================
338
+ // 3. Embed Class Hierarchy
339
+ // ========================================================================
340
+ console.log('3. Embedding Class Inheritance Hierarchy');
341
+ console.log(' ' + '-'.repeat(50));
342
+
343
+ const classEmbedding = new PoincareEmbedding({
344
+ dimension: config.hyperbolicDim,
345
+ curvature: config.curvature,
346
+ learningRate: 0.01,
347
+ });
348
+
349
+ console.log(' Training Poincare embeddings for class hierarchy...');
350
+ await classEmbedding.train(classHierarchy, { epochs: 100, batchSize: 16 });
351
+
352
+ const flatClasses = flattenHierarchy(classHierarchy);
353
+ console.log(` Embedded ${flatClasses.length} classes`);
354
+
355
+ // Show class hierarchy with embeddings
356
+ console.log('\n Class hierarchy with embedding norms:');
357
+ for (const { node, depth } of flatClasses) {
358
+ const emb = classEmbedding.getEmbedding(node.id);
359
+ const norm = emb ? Math.sqrt(emb.reduce((s, v) => s + v * v, 0)) : 0;
360
+ const indent = ' '.repeat(depth);
361
+ console.log(` ${indent}${node.name} (norm: ${norm.toFixed(4)})`);
362
+ }
363
+ console.log();
364
+
365
+ // ========================================================================
366
+ // 4. Find Nearest Ancestors and Descendants
367
+ // ========================================================================
368
+ console.log('4. Finding Nearest Ancestors and Descendants');
369
+ console.log(' ' + '-'.repeat(50));
370
+
371
+ const queryClass = 'not-found'; // NotFoundError
372
+ const queryEmb = classEmbedding.getEmbedding(queryClass);
373
+
374
+ if (queryEmb) {
375
+ // Find classes by hyperbolic distance
376
+ const distances = flatClasses
377
+ .filter(c => c.node.id !== queryClass)
378
+ .map(({ node }) => {
379
+ const emb = classEmbedding.getEmbedding(node.id);
380
+ if (!emb) return null;
381
+ return {
382
+ id: node.id,
383
+ name: node.name,
384
+ distance: poincare.distance(queryEmb, emb),
385
+ isAncestor: findPath(classHierarchy, queryClass)?.includes(node.id) ?? false,
386
+ };
387
+ })
388
+ .filter((d): d is NonNullable<typeof d> => d !== null)
389
+ .sort((a, b) => a.distance - b.distance);
390
+
391
+ console.log(` Query: ${queryClass} (NotFoundError)`);
392
+ console.log('\n Nearest by hyperbolic distance:');
393
+ distances.slice(0, 5).forEach((d, i) => {
394
+ const relation = d.isAncestor ? '[ancestor]' : '';
395
+ console.log(` ${i + 1}. ${d.name} - distance: ${d.distance.toFixed(4)} ${relation}`);
396
+ });
397
+
398
+ console.log('\n Actual ancestors (by tree structure):');
399
+ const ancestors = findPath(classHierarchy, queryClass) || [];
400
+ ancestors.slice(0, -1).forEach((id, i) => {
401
+ const node = flatClasses.find(c => c.node.id === id);
402
+ if (node) {
403
+ const emb = classEmbedding.getEmbedding(id);
404
+ const dist = emb ? poincare.distance(queryEmb, emb) : 0;
405
+ console.log(` ${i + 1}. ${node.node.name} - distance: ${dist.toFixed(4)}`);
406
+ }
407
+ });
408
+ }
409
+ console.log();
410
+
411
+ // ========================================================================
412
+ // 5. Hyperbolic Operations
413
+ // ========================================================================
414
+ console.log('5. Hyperbolic Space Operations');
415
+ console.log(' ' + '-'.repeat(50));
416
+
417
+ const emb1 = classEmbedding.getEmbedding('error');
418
+ const emb2 = classEmbedding.getEmbedding('validation-error');
419
+
420
+ if (emb1 && emb2) {
421
+ // Hyperbolic midpoint (Mobius gyromidpoint)
422
+ const midpoint = poincare.mobius_add(
423
+ poincare.scalar_mult(0.5, emb1),
424
+ poincare.scalar_mult(0.5, emb2)
425
+ );
426
+ const midNorm = Math.sqrt(midpoint.reduce((s, v) => s + v * v, 0));
427
+
428
+ console.log(' Midpoint between Error and ValidationError:');
429
+ console.log(` Norm of midpoint: ${midNorm.toFixed(4)}`);
430
+ console.log(` Distance to Error: ${poincare.distance(midpoint, emb1).toFixed(4)}`);
431
+ console.log(` Distance to ValidationError: ${poincare.distance(midpoint, emb2).toFixed(4)}`);
432
+
433
+ // Exponential and logarithmic maps
434
+ console.log('\n Exponential map (tangent space -> hyperbolic):');
435
+ const tangentVector = Array.from({ length: config.hyperbolicDim }, () => Math.random() * 0.1);
436
+ const mapped = poincare.exp_map(tangentVector, emb1);
437
+ const mappedNorm = Math.sqrt(mapped.reduce((s, v) => s + v * v, 0));
438
+ console.log(` Input tangent vector norm: ${Math.sqrt(tangentVector.reduce((s, v) => s + v * v, 0)).toFixed(4)}`);
439
+ console.log(` Mapped point norm: ${mappedNorm.toFixed(4)}`);
440
+ }
441
+ console.log();
442
+
443
+ // ========================================================================
444
+ // 6. Store in PostgreSQL with Hyperbolic Distance
445
+ // ========================================================================
446
+ console.log('6. Storing Hyperbolic Embeddings in PostgreSQL');
447
+ console.log(' ' + '-'.repeat(50));
448
+
449
+ // Create collection for class embeddings
450
+ await bridge.createCollection('class_hierarchy_embeddings', {
451
+ dimensions: config.hyperbolicDim,
452
+ distanceMetric: 'euclidean', // Use Euclidean for storage, compute hyperbolic distance separately
453
+ indexType: 'hnsw',
454
+ });
455
+
456
+ // Store embeddings
457
+ for (const { node, depth, parent } of flatClasses) {
458
+ const emb = classEmbedding.getEmbedding(node.id);
459
+ if (emb) {
460
+ await bridge.insert('class_hierarchy_embeddings', {
461
+ id: node.id,
462
+ embedding: emb,
463
+ metadata: {
464
+ name: node.name,
465
+ depth,
466
+ parent,
467
+ isLeaf: node.children.length === 0,
468
+ },
469
+ });
470
+ }
471
+ }
472
+
473
+ console.log(` Stored ${flatClasses.length} hyperbolic embeddings`);
474
+
475
+ // Query and re-rank with hyperbolic distance
476
+ const queryId = 'unauthorized';
477
+ const queryHypEmb = classEmbedding.getEmbedding(queryId);
478
+
479
+ if (queryHypEmb) {
480
+ // Get candidates using Euclidean distance (fast approximation)
481
+ const candidates = await bridge.search('class_hierarchy_embeddings', queryHypEmb, {
482
+ k: 10,
483
+ includeMetadata: true,
484
+ });
485
+
486
+ // Re-rank with hyperbolic distance
487
+ const reranked = candidates
488
+ .map(c => {
489
+ const hypDist = poincare.distance(queryHypEmb, c.embedding);
490
+ return { ...c, hyperbolicDistance: hypDist };
491
+ })
492
+ .sort((a, b) => a.hyperbolicDistance - b.hyperbolicDistance);
493
+
494
+ console.log(`\n Query: ${queryId} (UnauthorizedError)`);
495
+ console.log(' Results re-ranked by hyperbolic distance:');
496
+ reranked.slice(0, 5).forEach((r, i) => {
497
+ console.log(` ${i + 1}. ${r.metadata?.name} (hyp dist: ${r.hyperbolicDistance.toFixed(4)})`);
498
+ });
499
+ }
500
+
501
+ // ========================================================================
502
+ // 7. Euclidean vs Hyperbolic Comparison
503
+ // ========================================================================
504
+ console.log('\n7. Euclidean vs Hyperbolic Distance Comparison');
505
+ console.log(' ' + '-'.repeat(50));
506
+
507
+ // Compare how well each distance metric preserves hierarchy
508
+ let euclideanCorrelation = 0;
509
+ let hyperbolicCorrelation = 0;
510
+ let comparisons = 0;
511
+
512
+ for (const { node: node1 } of flatClasses) {
513
+ for (const { node: node2 } of flatClasses) {
514
+ if (node1.id >= node2.id) continue;
515
+
516
+ const emb1 = classEmbedding.getEmbedding(node1.id);
517
+ const emb2 = classEmbedding.getEmbedding(node2.id);
518
+ if (!emb1 || !emb2) continue;
519
+
520
+ const treeDist = treeDistance(classHierarchy, node1.id, node2.id);
521
+ const eucDist = Math.sqrt(emb1.reduce((s, v, i) => s + Math.pow(v - emb2[i], 2), 0));
522
+ const hypDist = poincare.distance(emb1, emb2);
523
+
524
+ // Spearman-like correlation (rank agreement)
525
+ if (treeDist > 0) {
526
+ euclideanCorrelation += eucDist / treeDist;
527
+ hyperbolicCorrelation += hypDist / treeDist;
528
+ comparisons++;
529
+ }
530
+ }
531
+ }
532
+
533
+ euclideanCorrelation /= comparisons;
534
+ hyperbolicCorrelation /= comparisons;
535
+
536
+ console.log(' Distance metric quality (lower = better preserves tree structure):');
537
+ console.log(` Euclidean: ${euclideanCorrelation.toFixed(4)}`);
538
+ console.log(` Hyperbolic: ${hyperbolicCorrelation.toFixed(4)}`);
539
+ console.log(` Improvement: ${((euclideanCorrelation / hyperbolicCorrelation - 1) * 100).toFixed(1)}%`);
540
+
541
+ // ========================================================================
542
+ // Done
543
+ // ========================================================================
544
+ console.log('\n' + '='.repeat(60));
545
+ console.log('Hyperbolic embeddings example completed!');
546
+ console.log('='.repeat(60));
547
+
548
+ } catch (error) {
549
+ console.error('Error:', error);
550
+ throw error;
551
+ } finally {
552
+ await bridge.disconnect();
553
+ console.log('\nDisconnected from PostgreSQL.');
554
+ }
555
+ }
556
+
557
+ main().catch(console.error);
@@ -0,0 +1,119 @@
1
+ -- RuVector PostgreSQL Bridge - Database Initialization
2
+ --
3
+ -- This script is automatically executed when the PostgreSQL container starts.
4
+ -- It sets up the pgvector extension and creates necessary schemas and functions.
5
+
6
+ -- Enable pgvector extension
7
+ CREATE EXTENSION IF NOT EXISTS vector;
8
+
9
+ -- Create a schema for RuVector-specific objects
10
+ CREATE SCHEMA IF NOT EXISTS ruvector;
11
+
12
+ -- Set search path to include ruvector schema
13
+ ALTER DATABASE vectors SET search_path TO public, ruvector;
14
+
15
+ -- Create a function to normalize vectors
16
+ CREATE OR REPLACE FUNCTION ruvector.normalize_vector(v vector)
17
+ RETURNS vector AS $$
18
+ DECLARE
19
+ magnitude float;
20
+ BEGIN
21
+ SELECT sqrt(sum(x * x)) INTO magnitude
22
+ FROM unnest(v::float[]) AS x;
23
+
24
+ IF magnitude = 0 THEN
25
+ RETURN v;
26
+ END IF;
27
+
28
+ RETURN (SELECT array_agg(x / magnitude)::vector
29
+ FROM unnest(v::float[]) AS x);
30
+ END;
31
+ $$ LANGUAGE plpgsql IMMUTABLE;
32
+
33
+ -- Create a function for cosine similarity (returns similarity, not distance)
34
+ CREATE OR REPLACE FUNCTION ruvector.cosine_similarity(a vector, b vector)
35
+ RETURNS float AS $$
36
+ BEGIN
37
+ RETURN 1 - (a <=> b);
38
+ END;
39
+ $$ LANGUAGE plpgsql IMMUTABLE;
40
+
41
+ -- Create a function for batch vector insertion with normalization
42
+ CREATE OR REPLACE FUNCTION ruvector.insert_normalized(
43
+ table_name text,
44
+ id_col text,
45
+ vec_col text,
46
+ ids text[],
47
+ vectors vector[]
48
+ ) RETURNS int AS $$
49
+ DECLARE
50
+ inserted int := 0;
51
+ i int;
52
+ BEGIN
53
+ FOR i IN 1..array_length(ids, 1) LOOP
54
+ EXECUTE format(
55
+ 'INSERT INTO %I (%I, %I) VALUES ($1, ruvector.normalize_vector($2))',
56
+ table_name, id_col, vec_col
57
+ ) USING ids[i], vectors[i];
58
+ inserted := inserted + 1;
59
+ END LOOP;
60
+ RETURN inserted;
61
+ END;
62
+ $$ LANGUAGE plpgsql;
63
+
64
+ -- Create a function to analyze vector distribution
65
+ CREATE OR REPLACE FUNCTION ruvector.analyze_vectors(
66
+ table_name text,
67
+ vec_col text
68
+ ) RETURNS TABLE (
69
+ total_count bigint,
70
+ avg_magnitude float,
71
+ min_magnitude float,
72
+ max_magnitude float,
73
+ dimension int
74
+ ) AS $$
75
+ BEGIN
76
+ RETURN QUERY EXECUTE format(
77
+ 'SELECT
78
+ COUNT(*)::bigint,
79
+ AVG(sqrt((SELECT SUM(x*x) FROM unnest(%I::float[]) AS x)))::float,
80
+ MIN(sqrt((SELECT SUM(x*x) FROM unnest(%I::float[]) AS x)))::float,
81
+ MAX(sqrt((SELECT SUM(x*x) FROM unnest(%I::float[]) AS x)))::float,
82
+ vector_dims(%I)
83
+ FROM %I
84
+ LIMIT 1',
85
+ vec_col, vec_col, vec_col, vec_col, table_name
86
+ );
87
+ END;
88
+ $$ LANGUAGE plpgsql;
89
+
90
+ -- Create a sample table for testing
91
+ CREATE TABLE IF NOT EXISTS ruvector.sample_vectors (
92
+ id text PRIMARY KEY,
93
+ embedding vector(128),
94
+ metadata jsonb DEFAULT '{}'::jsonb,
95
+ created_at timestamp DEFAULT now()
96
+ );
97
+
98
+ -- Create HNSW index on sample table
99
+ CREATE INDEX IF NOT EXISTS sample_vectors_embedding_idx
100
+ ON ruvector.sample_vectors
101
+ USING hnsw (embedding vector_cosine_ops)
102
+ WITH (m = 16, ef_construction = 64);
103
+
104
+ -- Create GIN index for metadata queries
105
+ CREATE INDEX IF NOT EXISTS sample_vectors_metadata_idx
106
+ ON ruvector.sample_vectors
107
+ USING gin (metadata jsonb_path_ops);
108
+
109
+ -- Grant permissions
110
+ GRANT ALL ON SCHEMA ruvector TO postgres;
111
+ GRANT ALL ON ALL TABLES IN SCHEMA ruvector TO postgres;
112
+ GRANT ALL ON ALL FUNCTIONS IN SCHEMA ruvector TO postgres;
113
+
114
+ -- Log completion
115
+ DO $$
116
+ BEGIN
117
+ RAISE NOTICE 'RuVector database initialization complete';
118
+ RAISE NOTICE 'pgvector version: %', (SELECT extversion FROM pg_extension WHERE extname = 'vector');
119
+ END $$;