@ruvector/edge-net 0.5.0 → 0.5.3

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.
package/onnx-worker.js CHANGED
@@ -22,52 +22,173 @@ let loadedGenModel = null;
22
22
 
23
23
  /**
24
24
  * Available embedding models (smallest first)
25
+ * Optimized for edge computing with size/quality tradeoffs
25
26
  */
26
27
  export const EMBEDDING_MODELS = {
28
+ // Tier 1: Ultra-fast (~20-30MB)
27
29
  'minilm-l6': {
28
30
  id: 'Xenova/all-MiniLM-L6-v2',
29
31
  dimensions: 384,
30
32
  size: '~22MB',
31
33
  description: 'Fast, good quality embeddings',
34
+ tier: 1,
32
35
  },
36
+ 'e5-small': {
37
+ id: 'Xenova/e5-small-v2',
38
+ dimensions: 384,
39
+ size: '~28MB',
40
+ description: 'Microsoft E5 - excellent retrieval',
41
+ tier: 1,
42
+ },
43
+ // Tier 2: Balanced (~30-70MB)
33
44
  'minilm-l12': {
34
45
  id: 'Xenova/all-MiniLM-L12-v2',
35
46
  dimensions: 384,
36
47
  size: '~33MB',
37
48
  description: 'Better quality, slightly slower',
49
+ tier: 2,
50
+ },
51
+ 'bge-small': {
52
+ id: 'Xenova/bge-small-en-v1.5',
53
+ dimensions: 384,
54
+ size: '~33MB',
55
+ description: 'Best for retrieval tasks',
56
+ tier: 2,
38
57
  },
39
58
  'gte-small': {
40
59
  id: 'Xenova/gte-small',
41
60
  dimensions: 384,
42
61
  size: '~67MB',
43
62
  description: 'High quality embeddings',
63
+ tier: 2,
44
64
  },
45
- 'bge-small': {
46
- id: 'Xenova/bge-small-en-v1.5',
65
+ // Tier 3: High quality (~100MB+)
66
+ 'gte-base': {
67
+ id: 'Xenova/gte-base',
68
+ dimensions: 768,
69
+ size: '~100MB',
70
+ description: 'Higher quality, 768d',
71
+ tier: 3,
72
+ },
73
+ 'bge-base': {
74
+ id: 'Xenova/bge-base-en-v1.5',
75
+ dimensions: 768,
76
+ size: '~108MB',
77
+ description: 'High quality BAAI retrieval',
78
+ tier: 3,
79
+ },
80
+ // Specialized: Multilingual
81
+ 'multilingual-e5': {
82
+ id: 'Xenova/multilingual-e5-small',
47
83
  dimensions: 384,
48
- size: '~33MB',
49
- description: 'Best for retrieval tasks',
84
+ size: '~118MB',
85
+ description: '100+ languages support',
86
+ tier: 3,
50
87
  },
51
88
  };
52
89
 
53
90
  /**
54
91
  * Available text generation models
92
+ * Organized by size and capability for edge deployment
55
93
  */
56
94
  export const GENERATION_MODELS = {
95
+ // Tier 1: Ultra-small (< 100MB) - Fast inference
96
+ 'tinystories': {
97
+ id: 'Xenova/TinyStories-33M',
98
+ size: '~65MB',
99
+ description: 'Ultra-small for stories',
100
+ tier: 1,
101
+ capabilities: ['stories', 'creative'],
102
+ },
57
103
  'distilgpt2': {
58
104
  id: 'Xenova/distilgpt2',
59
105
  size: '~82MB',
60
106
  description: 'Fast text generation',
107
+ tier: 1,
108
+ capabilities: ['general', 'completion'],
61
109
  },
110
+ // Tier 2: Small (100-300MB) - Good quality
62
111
  'gpt2': {
63
112
  id: 'Xenova/gpt2',
64
113
  size: '~250MB',
65
114
  description: 'Classic GPT-2',
115
+ tier: 2,
116
+ capabilities: ['general', 'completion', 'creative'],
66
117
  },
67
- 'tinystories': {
68
- id: 'Xenova/TinyStories-33M',
69
- size: '~65MB',
70
- description: 'Ultra-small for stories',
118
+ 'phi-1.5': {
119
+ id: 'Xenova/phi-1_5',
120
+ size: '~280MB',
121
+ description: 'Microsoft Phi-1.5 - code & reasoning',
122
+ tier: 2,
123
+ capabilities: ['code', 'reasoning', 'math'],
124
+ },
125
+ 'phi-2': {
126
+ id: 'Xenova/phi-2',
127
+ size: '~550MB',
128
+ description: 'Microsoft Phi-2 - advanced reasoning',
129
+ tier: 3,
130
+ capabilities: ['code', 'reasoning', 'math', 'general'],
131
+ },
132
+ // Tier 3: Medium (300MB-1GB) - High quality
133
+ 'qwen-0.5b': {
134
+ id: 'Xenova/Qwen1.5-0.5B',
135
+ size: '~430MB',
136
+ description: 'Qwen 0.5B - multilingual small model',
137
+ tier: 3,
138
+ capabilities: ['multilingual', 'general', 'code'],
139
+ },
140
+ 'gemma-2b': {
141
+ id: 'Xenova/gemma-2b-it',
142
+ size: '~1.1GB',
143
+ description: 'Google Gemma 2B instruction-tuned',
144
+ tier: 4,
145
+ capabilities: ['instruction', 'general', 'code', 'reasoning'],
146
+ },
147
+ // Code-specialized models
148
+ 'codegen-350m': {
149
+ id: 'Xenova/codegen-350M-mono',
150
+ size: '~320MB',
151
+ description: 'Salesforce CodeGen - Python specialist',
152
+ tier: 2,
153
+ capabilities: ['code', 'python'],
154
+ },
155
+ 'starcoder-tiny': {
156
+ id: 'Xenova/tiny_starcoder_py',
157
+ size: '~40MB',
158
+ description: 'Ultra-small Python code model',
159
+ tier: 1,
160
+ capabilities: ['code', 'python'],
161
+ },
162
+ };
163
+
164
+ /**
165
+ * Recommended models by use case
166
+ */
167
+ export const MODEL_RECOMMENDATIONS = {
168
+ 'edge-minimal': {
169
+ embedding: 'minilm-l6',
170
+ generation: 'distilgpt2',
171
+ description: 'Minimal footprint for constrained devices',
172
+ },
173
+ 'edge-balanced': {
174
+ embedding: 'e5-small',
175
+ generation: 'phi-1.5',
176
+ description: 'Best quality/size ratio for edge',
177
+ },
178
+ 'edge-code': {
179
+ embedding: 'bge-small',
180
+ generation: 'starcoder-tiny',
181
+ description: 'Optimized for code tasks',
182
+ },
183
+ 'edge-full': {
184
+ embedding: 'gte-base',
185
+ generation: 'phi-2',
186
+ description: 'Maximum quality on edge',
187
+ },
188
+ 'cloud-optimal': {
189
+ embedding: 'bge-base',
190
+ generation: 'gemma-2b',
191
+ description: 'Best quality for cloud deployment',
71
192
  },
72
193
  };
73
194
 
@@ -475,6 +596,339 @@ export class OnnxWorkerPool extends EventEmitter {
475
596
  }
476
597
  }
477
598
 
599
+ // ============================================
600
+ // ONLINE LEARNING FROM CORRECTIONS
601
+ // ============================================
602
+
603
+ /**
604
+ * OnlineLearner - Learns from user corrections in real-time
605
+ *
606
+ * Uses RAG + few-shot learning to improve model outputs
607
+ * without actual weight updates (inference-time adaptation)
608
+ */
609
+ export class OnlineLearner {
610
+ constructor(options = {}) {
611
+ this.corrections = [];
612
+ this.maxCorrections = options.maxCorrections || 100;
613
+ this.patterns = new Map(); // Pattern -> correction mapping
614
+ this.stats = {
615
+ totalCorrections: 0,
616
+ successfulApplications: 0,
617
+ avgSimilarityThreshold: 0.65,
618
+ };
619
+ }
620
+
621
+ /**
622
+ * Record a correction for learning
623
+ * @param {string} input - Original input
624
+ * @param {string} wrongOutput - Incorrect model output
625
+ * @param {string} correctOutput - User-provided correction
626
+ * @param {object} metadata - Optional metadata (task type, domain, etc.)
627
+ */
628
+ async recordCorrection(input, wrongOutput, correctOutput, metadata = {}) {
629
+ // Generate embedding for the input pattern
630
+ const result = await embed(input);
631
+ const embedding = result.embeddings?.[0]?.embedding || null;
632
+
633
+ const correction = {
634
+ input,
635
+ wrongOutput,
636
+ correctOutput,
637
+ embedding,
638
+ metadata,
639
+ timestamp: Date.now(),
640
+ useCount: 0,
641
+ };
642
+
643
+ // Store in corrections list
644
+ this.corrections.push(correction);
645
+ this.stats.totalCorrections++;
646
+
647
+ // Evict oldest if over capacity
648
+ if (this.corrections.length > this.maxCorrections) {
649
+ // Remove least used correction
650
+ this.corrections.sort((a, b) => b.useCount - a.useCount);
651
+ this.corrections = this.corrections.slice(0, this.maxCorrections);
652
+ }
653
+
654
+ // Extract and store pattern
655
+ const pattern = this.extractPattern(input, wrongOutput, correctOutput);
656
+ if (pattern) {
657
+ this.patterns.set(pattern.key, pattern);
658
+ }
659
+
660
+ return correction;
661
+ }
662
+
663
+ /**
664
+ * Extract reusable pattern from correction
665
+ */
666
+ extractPattern(input, wrongOutput, correctOutput) {
667
+ // Simple pattern extraction - can be enhanced
668
+ const inputTokens = input.toLowerCase().split(/\s+/);
669
+ const wrongTokens = wrongOutput.toLowerCase().split(/\s+/);
670
+ const correctTokens = correctOutput.toLowerCase().split(/\s+/);
671
+
672
+ // Find common elements that indicate the pattern
673
+ if (inputTokens.length > 0 && wrongTokens.length > 0) {
674
+ const key = inputTokens.slice(0, 3).join('_');
675
+ return {
676
+ key,
677
+ inputPattern: inputTokens.slice(0, 5).join(' '),
678
+ wrongPattern: wrongTokens.slice(0, 5).join(' '),
679
+ correctPattern: correctTokens.slice(0, 5).join(' '),
680
+ fullCorrection: correctOutput,
681
+ };
682
+ }
683
+ return null;
684
+ }
685
+
686
+ /**
687
+ * Find relevant corrections for a new input (RAG-style)
688
+ * @param {string} input - New input to find corrections for
689
+ * @param {number} topK - Number of corrections to return
690
+ */
691
+ async findRelevantCorrections(input, topK = 3) {
692
+ if (this.corrections.length === 0) return [];
693
+
694
+ // Embed the input
695
+ const result = await embed(input);
696
+ const queryEmb = result.embeddings?.[0]?.embedding;
697
+ if (!queryEmb) return [];
698
+
699
+ // Score all corrections by similarity
700
+ const scored = this.corrections
701
+ .filter(c => c.embedding)
702
+ .map(c => {
703
+ const sim = this.cosineSimilarity(queryEmb, c.embedding);
704
+ return { correction: c, similarity: sim };
705
+ })
706
+ .filter(s => s.similarity > this.stats.avgSimilarityThreshold)
707
+ .sort((a, b) => b.similarity - a.similarity)
708
+ .slice(0, topK);
709
+
710
+ // Update use counts
711
+ for (const s of scored) {
712
+ s.correction.useCount++;
713
+ }
714
+
715
+ return scored;
716
+ }
717
+
718
+ /**
719
+ * Generate few-shot examples from corrections
720
+ * @param {string} input - Current input
721
+ */
722
+ async generateFewShotExamples(input) {
723
+ const relevant = await this.findRelevantCorrections(input, 3);
724
+ if (relevant.length === 0) return '';
725
+
726
+ let examples = '\n\n# Previous corrections (apply similar fixes):\n';
727
+ for (const { correction, similarity } of relevant) {
728
+ examples += `\nInput: ${correction.input.slice(0, 100)}`;
729
+ examples += `\nWrong: ${correction.wrongOutput.slice(0, 100)}`;
730
+ examples += `\nCorrect: ${correction.correctOutput.slice(0, 100)}`;
731
+ examples += `\nSimilarity: ${(similarity * 100).toFixed(1)}%\n`;
732
+ }
733
+ return examples;
734
+ }
735
+
736
+ /**
737
+ * Apply learned corrections to generation
738
+ * @param {string} prompt - Original prompt
739
+ * @param {object} options - Generation options
740
+ */
741
+ async generateWithLearning(prompt, options = {}) {
742
+ // Find relevant corrections
743
+ const fewShot = await this.generateFewShotExamples(prompt);
744
+
745
+ // Inject few-shot examples into prompt
746
+ const enhancedPrompt = fewShot ? `${fewShot}\n\nNow handle this:\n${prompt}` : prompt;
747
+
748
+ // Generate with enhanced prompt
749
+ const result = await generate(enhancedPrompt, {
750
+ ...options,
751
+ maxTokens: options.maxTokens || 128,
752
+ });
753
+
754
+ if (fewShot) {
755
+ this.stats.successfulApplications++;
756
+ }
757
+
758
+ return result;
759
+ }
760
+
761
+ cosineSimilarity(a, b) {
762
+ let dot = 0, normA = 0, normB = 0;
763
+ for (let i = 0; i < a.length && i < b.length; i++) {
764
+ dot += a[i] * b[i];
765
+ normA += a[i] * a[i];
766
+ normB += b[i] * b[i];
767
+ }
768
+ return dot / (Math.sqrt(normA) * Math.sqrt(normB) + 1e-8);
769
+ }
770
+
771
+ getStats() {
772
+ return {
773
+ ...this.stats,
774
+ storedCorrections: this.corrections.length,
775
+ extractedPatterns: this.patterns.size,
776
+ };
777
+ }
778
+
779
+ export() {
780
+ return {
781
+ corrections: this.corrections,
782
+ patterns: Array.from(this.patterns.entries()),
783
+ stats: this.stats,
784
+ };
785
+ }
786
+
787
+ import(data) {
788
+ if (data.corrections) this.corrections = data.corrections;
789
+ if (data.patterns) this.patterns = new Map(data.patterns);
790
+ if (data.stats) this.stats = { ...this.stats, ...data.stats };
791
+ }
792
+ }
793
+
794
+ // ============================================
795
+ // ADAPTER INJECTION LAYER
796
+ // ============================================
797
+
798
+ /**
799
+ * AdapterInjector - Applies lightweight adapters to ONNX model outputs
800
+ *
801
+ * Since we can't modify ONNX weights at runtime, this applies post-hoc
802
+ * transformations to model outputs using learned patterns
803
+ */
804
+ export class AdapterInjector {
805
+ constructor(options = {}) {
806
+ this.rank = options.rank || 8;
807
+ this.dimension = options.dimension || 384;
808
+ this.scale = options.scale || 0.1;
809
+
810
+ // LoRA-style adapters (applied to embeddings)
811
+ this.adapterA = this.initMatrix(this.dimension, this.rank);
812
+ this.adapterB = this.initMatrix(this.rank, this.dimension, 0.01);
813
+
814
+ // Domain-specific bias terms
815
+ this.domainBiases = new Map();
816
+
817
+ this.stats = {
818
+ adaptations: 0,
819
+ domains: 0,
820
+ };
821
+ }
822
+
823
+ initMatrix(rows, cols, scale = 1) {
824
+ const matrix = [];
825
+ const std = Math.sqrt(2 / (rows + cols)) * scale;
826
+ for (let i = 0; i < rows; i++) {
827
+ const row = [];
828
+ for (let j = 0; j < cols; j++) {
829
+ row.push((Math.random() - 0.5) * 2 * std);
830
+ }
831
+ matrix.push(row);
832
+ }
833
+ return matrix;
834
+ }
835
+
836
+ /**
837
+ * Apply adapter transformation to embedding
838
+ * output = input + scale * (input @ A @ B)
839
+ */
840
+ adapt(embedding, domain = null) {
841
+ const adapted = [...embedding];
842
+
843
+ // Apply LoRA-style transformation
844
+ // Step 1: input @ A (dim -> rank)
845
+ const hidden = new Array(this.rank).fill(0);
846
+ for (let r = 0; r < this.rank; r++) {
847
+ for (let d = 0; d < Math.min(embedding.length, this.dimension); d++) {
848
+ hidden[r] += embedding[d] * this.adapterA[d][r];
849
+ }
850
+ }
851
+
852
+ // Step 2: hidden @ B (rank -> dim)
853
+ for (let d = 0; d < Math.min(adapted.length, this.dimension); d++) {
854
+ let delta = 0;
855
+ for (let r = 0; r < this.rank; r++) {
856
+ delta += hidden[r] * this.adapterB[r][d];
857
+ }
858
+ adapted[d] += this.scale * delta;
859
+ }
860
+
861
+ // Apply domain-specific bias if available
862
+ if (domain && this.domainBiases.has(domain)) {
863
+ const bias = this.domainBiases.get(domain);
864
+ for (let i = 0; i < adapted.length && i < bias.length; i++) {
865
+ adapted[i] += bias[i];
866
+ }
867
+ }
868
+
869
+ this.stats.adaptations++;
870
+ return adapted;
871
+ }
872
+
873
+ /**
874
+ * Learn from positive/negative example pairs
875
+ */
876
+ learn(anchor, positive, negatives = [], learningRate = 0.01) {
877
+ // Simple gradient descent on adapter weights
878
+ // Pull anchor closer to positive, push away from negatives
879
+
880
+ const anchorAdapted = this.adapt(anchor);
881
+
882
+ // Gradient from positive pair (pull closer)
883
+ if (positive) {
884
+ for (let d = 0; d < this.dimension && d < anchor.length; d++) {
885
+ for (let r = 0; r < this.rank; r++) {
886
+ const grad = anchor[d] * (positive[r % positive.length] - anchorAdapted[r % anchorAdapted.length]);
887
+ this.adapterA[d][r] += learningRate * grad * this.scale;
888
+ }
889
+ }
890
+ }
891
+
892
+ return this.stats.adaptations;
893
+ }
894
+
895
+ /**
896
+ * Register a domain-specific bias
897
+ */
898
+ registerDomain(domain, examples) {
899
+ if (!examples || examples.length === 0) return;
900
+
901
+ // Compute mean of examples as domain bias
902
+ const bias = new Array(this.dimension).fill(0);
903
+ for (const emb of examples) {
904
+ for (let i = 0; i < this.dimension && i < emb.length; i++) {
905
+ bias[i] += emb[i] / examples.length;
906
+ }
907
+ }
908
+
909
+ this.domainBiases.set(domain, bias);
910
+ this.stats.domains = this.domainBiases.size;
911
+ }
912
+
913
+ export() {
914
+ return {
915
+ adapterA: this.adapterA,
916
+ adapterB: this.adapterB,
917
+ domainBiases: Array.from(this.domainBiases.entries()),
918
+ stats: this.stats,
919
+ };
920
+ }
921
+
922
+ import(data) {
923
+ if (data.adapterA) this.adapterA = data.adapterA;
924
+ if (data.adapterB) this.adapterB = data.adapterB;
925
+ if (data.domainBiases) {
926
+ this.domainBiases = new Map(data.domainBiases);
927
+ }
928
+ if (data.stats) this.stats = data.stats;
929
+ }
930
+ }
931
+
478
932
  // ============================================
479
933
  // EXPORTS
480
934
  // ============================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ruvector/edge-net",
3
- "version": "0.5.0",
3
+ "version": "0.5.3",
4
4
  "type": "module",
5
5
  "description": "Distributed compute intelligence network with WASM cryptographic security - contribute browser compute, spawn distributed AI agents, earn credits. Features Ed25519 signing, PiKey identity, Time Crystal coordination, Neural DAG attention, P2P swarm intelligence, ONNX inference, WebRTC signaling, CRDT ledger, and multi-agent workflows.",
6
6
  "main": "ruvector_edge_net.js",
@@ -12,7 +12,8 @@
12
12
  "edge-net-join": "./join.js",
13
13
  "edge-net-genesis": "./genesis.js",
14
14
  "edge-net-firebase-setup": "./firebase-setup.js",
15
- "edge-net-plugins": "./plugins/cli.js"
15
+ "edge-net-plugins": "./plugins/cli.js",
16
+ "edge-net-models": "./models/models-cli.js"
16
17
  },
17
18
  "keywords": [
18
19
  "wasm",
@@ -80,6 +81,7 @@
80
81
  "node/",
81
82
  "deploy/",
82
83
  "tests/",
84
+ "models/",
83
85
  "index.js",
84
86
  "cli.js",
85
87
  "join.js",
@@ -107,6 +109,8 @@
107
109
  "credits.js",
108
110
  "task-execution-handler.js",
109
111
  "plugins/",
112
+ "network-genesis.js",
113
+ "core-invariants.js",
110
114
  "README.md",
111
115
  "LICENSE"
112
116
  ],
@@ -189,6 +193,24 @@
189
193
  },
190
194
  "./plugins/catalog": {
191
195
  "import": "./plugins/plugin-manifest.js"
196
+ },
197
+ "./genesis": {
198
+ "import": "./network-genesis.js"
199
+ },
200
+ "./models": {
201
+ "import": "./models/model-utils.js"
202
+ },
203
+ "./models/cli": {
204
+ "import": "./models/models-cli.js"
205
+ },
206
+ "./models/registry": {
207
+ "import": "./models/registry.json"
208
+ },
209
+ "./models/optimizer": {
210
+ "import": "./models/model-optimizer.js"
211
+ },
212
+ "./models/benchmark": {
213
+ "import": "./models/benchmark.js"
192
214
  }
193
215
  },
194
216
  "sideEffects": [
@@ -218,11 +240,19 @@
218
240
  "p2p": "node -e \"import('./p2p.js').then(m => m.createP2PNetwork({ nodeId: 'test' }))\"",
219
241
  "monitor": "node -e \"import('./monitor.js').then(m => { const mon = new m.Monitor(); mon.start(); setInterval(() => console.log(JSON.stringify(mon.generateReport(), null, 2)), 5000); })\"",
220
242
  "firebase:setup": "node firebase-setup.js",
221
- "firebase:check": "node firebase-setup.js --check"
243
+ "firebase:check": "node firebase-setup.js --check",
244
+ "models:list": "node models/models-cli.js list",
245
+ "models:download": "node models/models-cli.js download",
246
+ "models:optimize": "node models/models-cli.js optimize",
247
+ "models:benchmark": "node models/models-cli.js benchmark",
248
+ "models:upload": "node models/models-cli.js upload",
249
+ "models:train": "node models/models-cli.js train",
250
+ "models:cache": "node models/models-cli.js cache"
222
251
  },
223
252
  "dependencies": {
224
253
  "@ruvector/ruvllm": "^0.2.3",
225
254
  "@xenova/transformers": "^2.17.2",
255
+ "commander": "^12.1.0",
226
256
  "firebase": "^10.14.1",
227
257
  "wrtc": "^0.4.7",
228
258
  "ws": "^8.18.3"