@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,728 @@
1
+ -- ============================================================================
2
+ -- Migration 006: Create Graph Neural Network Functions
3
+ -- RuVector PostgreSQL Bridge - Claude Flow V3
4
+ --
5
+ -- Creates SQL functions for GNN operations including GCN, GAT, and GraphSAGE
6
+ -- layer implementations.
7
+ -- Compatible with PostgreSQL 14+ and pgvector 0.5+
8
+ -- ============================================================================
9
+
10
+ BEGIN;
11
+
12
+ -- ----------------------------------------------------------------------------
13
+ -- Helper: ReLU Activation
14
+ -- ----------------------------------------------------------------------------
15
+
16
+ CREATE OR REPLACE FUNCTION claude_flow.relu(
17
+ x REAL[]
18
+ ) RETURNS REAL[] AS $$
19
+ DECLARE
20
+ result REAL[];
21
+ i INTEGER;
22
+ BEGIN
23
+ result := ARRAY[]::REAL[];
24
+ FOR i IN 1..array_length(x, 1) LOOP
25
+ result := array_append(result, GREATEST(0, x[i]));
26
+ END LOOP;
27
+ RETURN result;
28
+ END;
29
+ $$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
30
+
31
+ -- ----------------------------------------------------------------------------
32
+ -- Helper: LeakyReLU Activation
33
+ -- ----------------------------------------------------------------------------
34
+
35
+ CREATE OR REPLACE FUNCTION claude_flow.leaky_relu(
36
+ x REAL[],
37
+ negative_slope REAL DEFAULT 0.2
38
+ ) RETURNS REAL[] AS $$
39
+ DECLARE
40
+ result REAL[];
41
+ i INTEGER;
42
+ BEGIN
43
+ result := ARRAY[]::REAL[];
44
+ FOR i IN 1..array_length(x, 1) LOOP
45
+ IF x[i] >= 0 THEN
46
+ result := array_append(result, x[i]);
47
+ ELSE
48
+ result := array_append(result, negative_slope * x[i]);
49
+ END IF;
50
+ END LOOP;
51
+ RETURN result;
52
+ END;
53
+ $$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
54
+
55
+ -- ----------------------------------------------------------------------------
56
+ -- Helper: Matrix-Vector Multiplication
57
+ -- ----------------------------------------------------------------------------
58
+
59
+ CREATE OR REPLACE FUNCTION claude_flow.matmul(
60
+ matrix REAL[][],
61
+ vec REAL[]
62
+ ) RETURNS REAL[] AS $$
63
+ DECLARE
64
+ result REAL[];
65
+ rows INTEGER;
66
+ cols INTEGER;
67
+ i INTEGER;
68
+ j INTEGER;
69
+ sum_val REAL;
70
+ BEGIN
71
+ rows := array_length(matrix, 1);
72
+ cols := array_length(matrix[1], 1);
73
+
74
+ IF cols != array_length(vec, 1) THEN
75
+ RAISE EXCEPTION 'Dimension mismatch: matrix cols (%) != vector length (%)', cols, array_length(vec, 1);
76
+ END IF;
77
+
78
+ result := ARRAY[]::REAL[];
79
+ FOR i IN 1..rows LOOP
80
+ sum_val := 0;
81
+ FOR j IN 1..cols LOOP
82
+ sum_val := sum_val + matrix[i][j] * vec[j];
83
+ END LOOP;
84
+ result := array_append(result, sum_val);
85
+ END LOOP;
86
+
87
+ RETURN result;
88
+ END;
89
+ $$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
90
+
91
+ -- ----------------------------------------------------------------------------
92
+ -- GCN Layer (Graph Convolutional Network)
93
+ -- H' = σ(D^(-1/2) * A * D^(-1/2) * H * W)
94
+ -- Simplified: H' = σ(Σ_j (1/sqrt(d_i * d_j)) * h_j * W)
95
+ -- ----------------------------------------------------------------------------
96
+
97
+ CREATE OR REPLACE FUNCTION claude_flow.gcn_layer(
98
+ nodes REAL[][], -- Node feature matrix [num_nodes, in_features]
99
+ edges INTEGER[][], -- Edge list [[src, dst], ...] (0-indexed)
100
+ weights REAL[][] -- Weight matrix [in_features, out_features]
101
+ ) RETURNS TABLE (
102
+ output_features REAL[][],
103
+ node_degrees INTEGER[]
104
+ ) AS $$
105
+ DECLARE
106
+ num_nodes INTEGER;
107
+ in_features INTEGER;
108
+ out_features INTEGER;
109
+ degrees INTEGER[];
110
+ aggregated REAL[][];
111
+ transformed REAL[][];
112
+ i INTEGER;
113
+ j INTEGER;
114
+ src INTEGER;
115
+ dst INTEGER;
116
+ neighbor_sum REAL[];
117
+ d_i REAL;
118
+ d_j REAL;
119
+ norm_factor REAL;
120
+ BEGIN
121
+ -- Get dimensions
122
+ num_nodes := array_length(nodes, 1);
123
+ in_features := array_length(nodes[1], 1);
124
+ out_features := array_length(weights[1], 1);
125
+
126
+ -- Calculate node degrees
127
+ degrees := ARRAY[]::INTEGER[];
128
+ FOR i IN 1..num_nodes LOOP
129
+ degrees := array_append(degrees, 1); -- Self-loop
130
+ END LOOP;
131
+
132
+ FOR i IN 1..array_length(edges, 1) LOOP
133
+ src := edges[i][1] + 1; -- Convert 0-indexed to 1-indexed
134
+ dst := edges[i][2] + 1;
135
+ IF src >= 1 AND src <= num_nodes THEN
136
+ degrees[src] := degrees[src] + 1;
137
+ END IF;
138
+ IF dst >= 1 AND dst <= num_nodes AND src != dst THEN
139
+ degrees[dst] := degrees[dst] + 1;
140
+ END IF;
141
+ END LOOP;
142
+
143
+ -- Aggregate neighbor features with symmetric normalization
144
+ aggregated := ARRAY[]::REAL[][];
145
+ FOR i IN 1..num_nodes LOOP
146
+ -- Initialize with self-feature (self-loop)
147
+ neighbor_sum := ARRAY[]::REAL[];
148
+ d_i := sqrt(degrees[i]::REAL);
149
+
150
+ FOR j IN 1..in_features LOOP
151
+ neighbor_sum := array_append(neighbor_sum, nodes[i][j] / d_i); -- Self contribution
152
+ END LOOP;
153
+
154
+ aggregated := array_cat(aggregated, ARRAY[neighbor_sum]);
155
+ END LOOP;
156
+
157
+ -- Add neighbor contributions
158
+ FOR i IN 1..array_length(edges, 1) LOOP
159
+ src := edges[i][1] + 1;
160
+ dst := edges[i][2] + 1;
161
+
162
+ IF src >= 1 AND src <= num_nodes AND dst >= 1 AND dst <= num_nodes THEN
163
+ d_i := sqrt(degrees[dst]::REAL);
164
+ d_j := sqrt(degrees[src]::REAL);
165
+ norm_factor := 1.0 / (d_i * d_j);
166
+
167
+ FOR j IN 1..in_features LOOP
168
+ aggregated[dst][j] := aggregated[dst][j] + norm_factor * nodes[src][j];
169
+ END LOOP;
170
+ END IF;
171
+ END LOOP;
172
+
173
+ -- Apply weights and activation
174
+ transformed := ARRAY[]::REAL[][];
175
+ FOR i IN 1..num_nodes LOOP
176
+ DECLARE
177
+ node_output REAL[];
178
+ BEGIN
179
+ node_output := claude_flow.matmul(weights, aggregated[i]);
180
+ node_output := claude_flow.relu(node_output);
181
+ transformed := array_cat(transformed, ARRAY[node_output]);
182
+ END;
183
+ END LOOP;
184
+
185
+ output_features := transformed;
186
+ node_degrees := degrees;
187
+ RETURN NEXT;
188
+ END;
189
+ $$ LANGUAGE plpgsql STABLE;
190
+
191
+ -- ----------------------------------------------------------------------------
192
+ -- GAT Layer (Graph Attention Network)
193
+ -- Attention coefficients: α_ij = softmax_j(LeakyReLU(a^T [W*h_i || W*h_j]))
194
+ -- H' = σ(Σ_j α_ij * W * h_j)
195
+ -- ----------------------------------------------------------------------------
196
+
197
+ CREATE OR REPLACE FUNCTION claude_flow.gat_layer(
198
+ nodes REAL[][], -- Node feature matrix [num_nodes, in_features]
199
+ edges INTEGER[][], -- Edge list [[src, dst], ...] (0-indexed)
200
+ num_heads INTEGER DEFAULT 4,
201
+ attention_dim INTEGER DEFAULT 8
202
+ ) RETURNS TABLE (
203
+ output_features REAL[][],
204
+ attention_coefficients REAL[][]
205
+ ) AS $$
206
+ DECLARE
207
+ num_nodes INTEGER;
208
+ in_features INTEGER;
209
+ out_features INTEGER;
210
+ head_outputs REAL[][][];
211
+ all_attention REAL[][];
212
+ h INTEGER;
213
+ i INTEGER;
214
+ j INTEGER;
215
+ src INTEGER;
216
+ dst INTEGER;
217
+ edge_idx INTEGER;
218
+ attention_scores REAL[];
219
+ attention_weights REAL[];
220
+ neighbor_indices INTEGER[];
221
+ weighted_sum REAL[];
222
+ final_output REAL[][];
223
+ BEGIN
224
+ -- Get dimensions
225
+ num_nodes := array_length(nodes, 1);
226
+ in_features := array_length(nodes[1], 1);
227
+ out_features := attention_dim * num_heads;
228
+
229
+ -- Initialize outputs
230
+ head_outputs := ARRAY[]::REAL[][][];
231
+ all_attention := ARRAY[]::REAL[][];
232
+
233
+ -- Build adjacency list
234
+ DECLARE
235
+ adj_list INTEGER[][];
236
+ BEGIN
237
+ adj_list := ARRAY[]::INTEGER[][];
238
+ FOR i IN 1..num_nodes LOOP
239
+ adj_list := array_cat(adj_list, ARRAY[ARRAY[i]::INTEGER[]]); -- Self-loop
240
+ END LOOP;
241
+
242
+ FOR edge_idx IN 1..array_length(edges, 1) LOOP
243
+ src := edges[edge_idx][1] + 1;
244
+ dst := edges[edge_idx][2] + 1;
245
+ IF dst >= 1 AND dst <= num_nodes AND src >= 1 AND src <= num_nodes THEN
246
+ adj_list[dst] := array_append(adj_list[dst], src);
247
+ END IF;
248
+ END LOOP;
249
+
250
+ -- Process each attention head
251
+ FOR h IN 1..num_heads LOOP
252
+ DECLARE
253
+ head_result REAL[][];
254
+ BEGIN
255
+ head_result := ARRAY[]::REAL[][];
256
+
257
+ FOR i IN 1..num_nodes LOOP
258
+ neighbor_indices := adj_list[i];
259
+
260
+ -- Compute attention scores
261
+ attention_scores := ARRAY[]::REAL[];
262
+ FOR j IN 1..array_length(neighbor_indices, 1) LOOP
263
+ DECLARE
264
+ neighbor_idx INTEGER := neighbor_indices[j];
265
+ score REAL := 0;
266
+ k INTEGER;
267
+ BEGIN
268
+ -- Simple dot-product attention
269
+ FOR k IN 1..LEAST(in_features, attention_dim) LOOP
270
+ score := score + nodes[i][k] * nodes[neighbor_idx][k];
271
+ END LOOP;
272
+ score := score / sqrt(attention_dim::REAL);
273
+ attention_scores := array_append(attention_scores, score);
274
+ END;
275
+ END LOOP;
276
+
277
+ -- Apply LeakyReLU and softmax
278
+ attention_scores := claude_flow.leaky_relu(attention_scores, 0.2);
279
+ attention_weights := claude_flow.softmax(attention_scores);
280
+
281
+ -- Store attention for debugging
282
+ all_attention := array_cat(all_attention, ARRAY[attention_weights]);
283
+
284
+ -- Compute weighted sum of neighbor features
285
+ weighted_sum := ARRAY[]::REAL[];
286
+ FOR j IN 1..LEAST(in_features, attention_dim) LOOP
287
+ DECLARE
288
+ sum_val REAL := 0;
289
+ k INTEGER;
290
+ BEGIN
291
+ FOR k IN 1..array_length(neighbor_indices, 1) LOOP
292
+ sum_val := sum_val + attention_weights[k] * nodes[neighbor_indices[k]][j];
293
+ END LOOP;
294
+ weighted_sum := array_append(weighted_sum, sum_val);
295
+ END;
296
+ END LOOP;
297
+
298
+ -- Apply activation
299
+ weighted_sum := claude_flow.relu(weighted_sum);
300
+ head_result := array_cat(head_result, ARRAY[weighted_sum]);
301
+ END LOOP;
302
+
303
+ head_outputs := array_cat(head_outputs, ARRAY[head_result]);
304
+ END;
305
+ END LOOP;
306
+
307
+ -- Concatenate head outputs
308
+ final_output := ARRAY[]::REAL[][];
309
+ FOR i IN 1..num_nodes LOOP
310
+ DECLARE
311
+ concat_features REAL[] := ARRAY[]::REAL[];
312
+ BEGIN
313
+ FOR h IN 1..num_heads LOOP
314
+ FOR j IN 1..array_length(head_outputs[h][i], 1) LOOP
315
+ concat_features := array_append(concat_features, head_outputs[h][i][j]);
316
+ END LOOP;
317
+ END LOOP;
318
+ final_output := array_cat(final_output, ARRAY[concat_features]);
319
+ END;
320
+ END LOOP;
321
+
322
+ output_features := final_output;
323
+ attention_coefficients := all_attention;
324
+ RETURN NEXT;
325
+ END;
326
+ END;
327
+ $$ LANGUAGE plpgsql STABLE;
328
+
329
+ -- ----------------------------------------------------------------------------
330
+ -- GraphSAGE Layer
331
+ -- h_v = σ(W * CONCAT(h_v, AGG({h_u : u ∈ N(v)})))
332
+ -- Aggregation: mean, max, or LSTM
333
+ -- ----------------------------------------------------------------------------
334
+
335
+ CREATE OR REPLACE FUNCTION claude_flow.graph_sage(
336
+ nodes REAL[][], -- Node feature matrix [num_nodes, in_features]
337
+ edges INTEGER[][], -- Edge list [[src, dst], ...] (0-indexed)
338
+ aggregation TEXT DEFAULT 'mean' -- Aggregation: mean, max, pool
339
+ ) RETURNS TABLE (
340
+ output_features REAL[][],
341
+ sampled_neighbors INTEGER[][]
342
+ ) AS $$
343
+ DECLARE
344
+ num_nodes INTEGER;
345
+ in_features INTEGER;
346
+ adj_list INTEGER[][];
347
+ aggregated REAL[][];
348
+ final_output REAL[][];
349
+ i INTEGER;
350
+ j INTEGER;
351
+ src INTEGER;
352
+ dst INTEGER;
353
+ edge_idx INTEGER;
354
+ neighbors INTEGER[];
355
+ neighbor_features REAL[][];
356
+ agg_result REAL[];
357
+ BEGIN
358
+ -- Get dimensions
359
+ num_nodes := array_length(nodes, 1);
360
+ in_features := array_length(nodes[1], 1);
361
+
362
+ -- Build adjacency list
363
+ adj_list := ARRAY[]::INTEGER[][];
364
+ FOR i IN 1..num_nodes LOOP
365
+ adj_list := array_cat(adj_list, ARRAY[ARRAY[]::INTEGER[]]);
366
+ END LOOP;
367
+
368
+ FOR edge_idx IN 1..array_length(edges, 1) LOOP
369
+ src := edges[edge_idx][1] + 1;
370
+ dst := edges[edge_idx][2] + 1;
371
+ IF dst >= 1 AND dst <= num_nodes AND src >= 1 AND src <= num_nodes THEN
372
+ adj_list[dst] := array_append(adj_list[dst], src);
373
+ END IF;
374
+ END LOOP;
375
+
376
+ -- Aggregate neighbor features
377
+ aggregated := ARRAY[]::REAL[][];
378
+
379
+ FOR i IN 1..num_nodes LOOP
380
+ neighbors := adj_list[i];
381
+
382
+ IF array_length(neighbors, 1) IS NULL OR array_length(neighbors, 1) = 0 THEN
383
+ -- No neighbors, use self
384
+ aggregated := array_cat(aggregated, ARRAY[nodes[i]]);
385
+ ELSE
386
+ -- Collect neighbor features
387
+ neighbor_features := ARRAY[]::REAL[][];
388
+ FOR j IN 1..array_length(neighbors, 1) LOOP
389
+ neighbor_features := array_cat(neighbor_features, ARRAY[nodes[neighbors[j]]]);
390
+ END LOOP;
391
+
392
+ -- Aggregate based on method
393
+ agg_result := ARRAY[]::REAL[];
394
+
395
+ CASE aggregation
396
+ WHEN 'mean' THEN
397
+ FOR j IN 1..in_features LOOP
398
+ DECLARE
399
+ mean_val REAL := 0;
400
+ k INTEGER;
401
+ BEGIN
402
+ FOR k IN 1..array_length(neighbor_features, 1) LOOP
403
+ mean_val := mean_val + neighbor_features[k][j];
404
+ END LOOP;
405
+ agg_result := array_append(agg_result, mean_val / array_length(neighbor_features, 1));
406
+ END;
407
+ END LOOP;
408
+
409
+ WHEN 'max' THEN
410
+ FOR j IN 1..in_features LOOP
411
+ DECLARE
412
+ max_val REAL := neighbor_features[1][j];
413
+ k INTEGER;
414
+ BEGIN
415
+ FOR k IN 2..array_length(neighbor_features, 1) LOOP
416
+ IF neighbor_features[k][j] > max_val THEN
417
+ max_val := neighbor_features[k][j];
418
+ END IF;
419
+ END LOOP;
420
+ agg_result := array_append(agg_result, max_val);
421
+ END;
422
+ END LOOP;
423
+
424
+ WHEN 'pool' THEN
425
+ -- Max-pooling after element-wise transformation
426
+ FOR j IN 1..in_features LOOP
427
+ DECLARE
428
+ max_val REAL := GREATEST(0, neighbor_features[1][j]); -- ReLU
429
+ k INTEGER;
430
+ BEGIN
431
+ FOR k IN 2..array_length(neighbor_features, 1) LOOP
432
+ IF GREATEST(0, neighbor_features[k][j]) > max_val THEN
433
+ max_val := GREATEST(0, neighbor_features[k][j]);
434
+ END IF;
435
+ END LOOP;
436
+ agg_result := array_append(agg_result, max_val);
437
+ END;
438
+ END LOOP;
439
+
440
+ ELSE
441
+ -- Default to mean
442
+ FOR j IN 1..in_features LOOP
443
+ DECLARE
444
+ mean_val REAL := 0;
445
+ k INTEGER;
446
+ BEGIN
447
+ FOR k IN 1..array_length(neighbor_features, 1) LOOP
448
+ mean_val := mean_val + neighbor_features[k][j];
449
+ END LOOP;
450
+ agg_result := array_append(agg_result, mean_val / array_length(neighbor_features, 1));
451
+ END;
452
+ END LOOP;
453
+ END CASE;
454
+
455
+ aggregated := array_cat(aggregated, ARRAY[agg_result]);
456
+ END IF;
457
+ END LOOP;
458
+
459
+ -- Concatenate self features with aggregated neighbor features and apply activation
460
+ final_output := ARRAY[]::REAL[][];
461
+ FOR i IN 1..num_nodes LOOP
462
+ DECLARE
463
+ concat_features REAL[];
464
+ BEGIN
465
+ -- Concatenate: [self || aggregated]
466
+ concat_features := nodes[i] || aggregated[i];
467
+ -- Apply ReLU activation
468
+ concat_features := claude_flow.relu(concat_features);
469
+ -- Normalize
470
+ concat_features := claude_flow.l2_normalize(concat_features);
471
+ final_output := array_cat(final_output, ARRAY[concat_features]);
472
+ END;
473
+ END LOOP;
474
+
475
+ output_features := final_output;
476
+ sampled_neighbors := adj_list;
477
+ RETURN NEXT;
478
+ END;
479
+ $$ LANGUAGE plpgsql STABLE;
480
+
481
+ -- ----------------------------------------------------------------------------
482
+ -- Message Passing Neural Network (MPNN) - Generic Framework
483
+ -- ----------------------------------------------------------------------------
484
+
485
+ CREATE OR REPLACE FUNCTION claude_flow.mpnn_layer(
486
+ nodes REAL[][], -- Node features
487
+ edges INTEGER[][], -- Edge list
488
+ edge_features REAL[][] DEFAULT NULL, -- Optional edge features
489
+ message_fn TEXT DEFAULT 'concat', -- concat, add, mul
490
+ aggregate_fn TEXT DEFAULT 'sum' -- sum, mean, max
491
+ ) RETURNS REAL[][] AS $$
492
+ DECLARE
493
+ num_nodes INTEGER;
494
+ in_features INTEGER;
495
+ edge_dim INTEGER;
496
+ messages REAL[][];
497
+ aggregated REAL[][];
498
+ i INTEGER;
499
+ j INTEGER;
500
+ src INTEGER;
501
+ dst INTEGER;
502
+ edge_idx INTEGER;
503
+ BEGIN
504
+ num_nodes := array_length(nodes, 1);
505
+ in_features := array_length(nodes[1], 1);
506
+
507
+ IF edge_features IS NOT NULL THEN
508
+ edge_dim := array_length(edge_features[1], 1);
509
+ ELSE
510
+ edge_dim := 0;
511
+ END IF;
512
+
513
+ -- Initialize aggregated features
514
+ aggregated := ARRAY[]::REAL[][];
515
+ FOR i IN 1..num_nodes LOOP
516
+ DECLARE
517
+ zero_features REAL[] := ARRAY[]::REAL[];
518
+ BEGIN
519
+ FOR j IN 1..in_features LOOP
520
+ zero_features := array_append(zero_features, 0.0);
521
+ END LOOP;
522
+ aggregated := array_cat(aggregated, ARRAY[zero_features]);
523
+ END;
524
+ END LOOP;
525
+
526
+ -- Compute and aggregate messages
527
+ DECLARE
528
+ neighbor_counts INTEGER[] := ARRAY[]::INTEGER[];
529
+ BEGIN
530
+ FOR i IN 1..num_nodes LOOP
531
+ neighbor_counts := array_append(neighbor_counts, 0);
532
+ END LOOP;
533
+
534
+ FOR edge_idx IN 1..array_length(edges, 1) LOOP
535
+ src := edges[edge_idx][1] + 1;
536
+ dst := edges[edge_idx][2] + 1;
537
+
538
+ IF src >= 1 AND src <= num_nodes AND dst >= 1 AND dst <= num_nodes THEN
539
+ neighbor_counts[dst] := neighbor_counts[dst] + 1;
540
+
541
+ -- Compute message
542
+ DECLARE
543
+ message REAL[] := nodes[src];
544
+ BEGIN
545
+ -- Apply message function
546
+ CASE message_fn
547
+ WHEN 'concat' THEN
548
+ IF edge_features IS NOT NULL THEN
549
+ message := message || edge_features[edge_idx];
550
+ END IF;
551
+ WHEN 'mul' THEN
552
+ IF edge_features IS NOT NULL THEN
553
+ FOR j IN 1..LEAST(in_features, edge_dim) LOOP
554
+ message[j] := message[j] * edge_features[edge_idx][j];
555
+ END LOOP;
556
+ END IF;
557
+ ELSE
558
+ NULL; -- Default: just use source features
559
+ END CASE;
560
+
561
+ -- Aggregate
562
+ FOR j IN 1..LEAST(array_length(message, 1), in_features) LOOP
563
+ CASE aggregate_fn
564
+ WHEN 'sum' THEN
565
+ aggregated[dst][j] := aggregated[dst][j] + message[j];
566
+ WHEN 'max' THEN
567
+ IF message[j] > aggregated[dst][j] THEN
568
+ aggregated[dst][j] := message[j];
569
+ END IF;
570
+ ELSE -- mean (accumulated, divide later)
571
+ aggregated[dst][j] := aggregated[dst][j] + message[j];
572
+ END CASE;
573
+ END LOOP;
574
+ END;
575
+ END IF;
576
+ END LOOP;
577
+
578
+ -- Finalize mean aggregation
579
+ IF aggregate_fn = 'mean' THEN
580
+ FOR i IN 1..num_nodes LOOP
581
+ IF neighbor_counts[i] > 0 THEN
582
+ FOR j IN 1..in_features LOOP
583
+ aggregated[i][j] := aggregated[i][j] / neighbor_counts[i];
584
+ END LOOP;
585
+ END IF;
586
+ END LOOP;
587
+ END IF;
588
+ END;
589
+
590
+ -- Update: combine self with aggregated
591
+ FOR i IN 1..num_nodes LOOP
592
+ FOR j IN 1..in_features LOOP
593
+ aggregated[i][j] := aggregated[i][j] + nodes[i][j];
594
+ END LOOP;
595
+ aggregated[i] := claude_flow.relu(aggregated[i]);
596
+ END LOOP;
597
+
598
+ RETURN aggregated;
599
+ END;
600
+ $$ LANGUAGE plpgsql STABLE;
601
+
602
+ -- ----------------------------------------------------------------------------
603
+ -- GNN Cache Management
604
+ -- ----------------------------------------------------------------------------
605
+
606
+ -- Cache GNN layer output
607
+ CREATE OR REPLACE FUNCTION claude_flow.cache_gnn_result(
608
+ p_graph_hash TEXT,
609
+ p_layer_type TEXT,
610
+ p_layer_index INTEGER,
611
+ p_num_nodes INTEGER,
612
+ p_num_edges INTEGER,
613
+ p_node_features_dim INTEGER,
614
+ p_node_embeddings REAL[],
615
+ p_output_dim INTEGER,
616
+ p_aggregation TEXT DEFAULT 'mean',
617
+ p_num_heads INTEGER DEFAULT 1,
618
+ p_ttl_hours INTEGER DEFAULT 24
619
+ ) RETURNS UUID AS $$
620
+ DECLARE
621
+ v_cache_key TEXT;
622
+ v_id UUID;
623
+ BEGIN
624
+ v_cache_key := md5(p_graph_hash || p_layer_type || p_layer_index::TEXT);
625
+
626
+ INSERT INTO claude_flow.gnn_cache (
627
+ cache_key,
628
+ graph_hash,
629
+ layer_type,
630
+ layer_index,
631
+ num_nodes,
632
+ num_edges,
633
+ node_features_dim,
634
+ node_embeddings,
635
+ output_dim,
636
+ aggregation,
637
+ num_heads,
638
+ expires_at
639
+ ) VALUES (
640
+ v_cache_key,
641
+ p_graph_hash,
642
+ p_layer_type,
643
+ p_layer_index,
644
+ p_num_nodes,
645
+ p_num_edges,
646
+ p_node_features_dim,
647
+ p_node_embeddings,
648
+ p_output_dim,
649
+ p_aggregation,
650
+ p_num_heads,
651
+ NOW() + (p_ttl_hours || ' hours')::INTERVAL
652
+ )
653
+ ON CONFLICT (cache_key) DO UPDATE
654
+ SET node_embeddings = EXCLUDED.node_embeddings,
655
+ hit_count = claude_flow.gnn_cache.hit_count + 1,
656
+ last_accessed_at = NOW(),
657
+ expires_at = EXCLUDED.expires_at
658
+ RETURNING id INTO v_id;
659
+
660
+ RETURN v_id;
661
+ END;
662
+ $$ LANGUAGE plpgsql;
663
+
664
+ -- Retrieve cached GNN result
665
+ CREATE OR REPLACE FUNCTION claude_flow.get_cached_gnn(
666
+ p_graph_hash TEXT,
667
+ p_layer_type TEXT,
668
+ p_layer_index INTEGER
669
+ ) RETURNS TABLE (
670
+ node_embeddings REAL[],
671
+ output_dim INTEGER,
672
+ cache_hit BOOLEAN
673
+ ) AS $$
674
+ DECLARE
675
+ v_cache_key TEXT;
676
+ v_record RECORD;
677
+ BEGIN
678
+ v_cache_key := md5(p_graph_hash || p_layer_type || p_layer_index::TEXT);
679
+
680
+ SELECT gc.node_embeddings, gc.output_dim
681
+ INTO v_record
682
+ FROM claude_flow.gnn_cache gc
683
+ WHERE gc.cache_key = v_cache_key
684
+ AND (gc.expires_at IS NULL OR gc.expires_at > NOW());
685
+
686
+ IF FOUND THEN
687
+ UPDATE claude_flow.gnn_cache
688
+ SET hit_count = hit_count + 1,
689
+ last_accessed_at = NOW()
690
+ WHERE cache_key = v_cache_key;
691
+
692
+ node_embeddings := v_record.node_embeddings;
693
+ output_dim := v_record.output_dim;
694
+ cache_hit := TRUE;
695
+ ELSE
696
+ node_embeddings := NULL;
697
+ output_dim := NULL;
698
+ cache_hit := FALSE;
699
+ END IF;
700
+
701
+ RETURN NEXT;
702
+ END;
703
+ $$ LANGUAGE plpgsql;
704
+
705
+ -- ----------------------------------------------------------------------------
706
+ -- Record migration
707
+ -- ----------------------------------------------------------------------------
708
+ INSERT INTO claude_flow.migrations (name, checksum)
709
+ VALUES ('006_create_gnn_functions', md5('006_create_gnn_functions'))
710
+ ON CONFLICT (name) DO NOTHING;
711
+
712
+ COMMIT;
713
+
714
+ -- ============================================================================
715
+ -- Rollback Script
716
+ -- ============================================================================
717
+ -- BEGIN;
718
+ -- DROP FUNCTION IF EXISTS claude_flow.get_cached_gnn(TEXT, TEXT, INTEGER);
719
+ -- DROP FUNCTION IF EXISTS claude_flow.cache_gnn_result(TEXT, TEXT, INTEGER, INTEGER, INTEGER, INTEGER, REAL[], INTEGER, TEXT, INTEGER, INTEGER);
720
+ -- DROP FUNCTION IF EXISTS claude_flow.mpnn_layer(REAL[][], INTEGER[][], REAL[][], TEXT, TEXT);
721
+ -- DROP FUNCTION IF EXISTS claude_flow.graph_sage(REAL[][], INTEGER[][], TEXT);
722
+ -- DROP FUNCTION IF EXISTS claude_flow.gat_layer(REAL[][], INTEGER[][], INTEGER, INTEGER);
723
+ -- DROP FUNCTION IF EXISTS claude_flow.gcn_layer(REAL[][], INTEGER[][], REAL[][]);
724
+ -- DROP FUNCTION IF EXISTS claude_flow.matmul(REAL[][], REAL[]);
725
+ -- DROP FUNCTION IF EXISTS claude_flow.leaky_relu(REAL[], REAL);
726
+ -- DROP FUNCTION IF EXISTS claude_flow.relu(REAL[]);
727
+ -- DELETE FROM claude_flow.migrations WHERE name = '006_create_gnn_functions';
728
+ -- COMMIT;