@soulcraft/brainy 5.10.4 → 5.11.0
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/dist/brainy.d.ts +89 -0
- package/dist/brainy.js +154 -1
- package/dist/storage/adapters/azureBlobStorage.d.ts +2 -5
- package/dist/storage/adapters/azureBlobStorage.js +4 -40
- package/dist/storage/adapters/fileSystemStorage.d.ts +2 -5
- package/dist/storage/adapters/fileSystemStorage.js +4 -42
- package/dist/storage/adapters/gcsStorage.d.ts +2 -5
- package/dist/storage/adapters/gcsStorage.js +10 -47
- package/dist/storage/adapters/historicalStorageAdapter.d.ts +2 -5
- package/dist/storage/adapters/historicalStorageAdapter.js +2 -9
- package/dist/storage/adapters/memoryStorage.d.ts +2 -5
- package/dist/storage/adapters/memoryStorage.js +2 -10
- package/dist/storage/adapters/opfsStorage.d.ts +2 -5
- package/dist/storage/adapters/opfsStorage.js +12 -54
- package/dist/storage/adapters/r2Storage.d.ts +0 -13
- package/dist/storage/adapters/r2Storage.js +18 -53
- package/dist/storage/adapters/s3CompatibleStorage.d.ts +2 -5
- package/dist/storage/adapters/s3CompatibleStorage.js +18 -70
- package/dist/storage/baseStorage.d.ts +8 -14
- package/dist/storage/baseStorage.js +16 -40
- package/dist/storage/cow/CommitLog.d.ts +24 -0
- package/dist/storage/cow/CommitLog.js +37 -0
- package/dist/types/brainy.types.d.ts +2 -0
- package/dist/utils/paramValidation.d.ts +43 -0
- package/dist/utils/paramValidation.js +135 -22
- package/package.json +1 -1
|
@@ -5,14 +5,16 @@
|
|
|
5
5
|
* Only enforces universal truths, learns everything else
|
|
6
6
|
*/
|
|
7
7
|
import { NounType, VerbType } from '../types/graphTypes.js';
|
|
8
|
-
// Dynamic import for Node.js os
|
|
8
|
+
// Dynamic import for Node.js os and fs modules
|
|
9
9
|
let os = null;
|
|
10
|
+
let fs = null;
|
|
10
11
|
if (typeof window === 'undefined') {
|
|
11
12
|
try {
|
|
12
13
|
os = await import('node:os');
|
|
14
|
+
fs = await import('node:fs');
|
|
13
15
|
}
|
|
14
16
|
catch (e) {
|
|
15
|
-
// OS
|
|
17
|
+
// OS/FS modules not available
|
|
16
18
|
}
|
|
17
19
|
}
|
|
18
20
|
// Browser-safe memory detection
|
|
@@ -30,46 +32,157 @@ const getAvailableMemory = () => {
|
|
|
30
32
|
// Browser fallback: assume 2GB available
|
|
31
33
|
return 2 * 1024 * 1024 * 1024;
|
|
32
34
|
};
|
|
35
|
+
/**
|
|
36
|
+
* Detect container memory limit (Docker/Kubernetes/Cloud Run)
|
|
37
|
+
*
|
|
38
|
+
* Production-grade detection for containerized environments.
|
|
39
|
+
* Supports:
|
|
40
|
+
* - cgroup v1 (legacy Docker/K8s)
|
|
41
|
+
* - cgroup v2 (modern systems)
|
|
42
|
+
* - Environment variables (Cloud Run, GCP, AWS, Azure)
|
|
43
|
+
*
|
|
44
|
+
* @returns Container memory limit in bytes, or null if not containerized
|
|
45
|
+
*/
|
|
46
|
+
const getContainerMemoryLimit = () => {
|
|
47
|
+
// Not in Node.js environment
|
|
48
|
+
if (!fs) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
// 1. Check environment variables first (fastest, most reliable for Cloud Run)
|
|
53
|
+
// Google Cloud Run
|
|
54
|
+
if (process.env.CLOUD_RUN_MEMORY) {
|
|
55
|
+
// Format: "512Mi", "1Gi", "2Gi", "4Gi"
|
|
56
|
+
const match = process.env.CLOUD_RUN_MEMORY.match(/^(\d+)(Mi|Gi)$/);
|
|
57
|
+
if (match) {
|
|
58
|
+
const value = parseInt(match[1]);
|
|
59
|
+
const unit = match[2];
|
|
60
|
+
return unit === 'Gi' ? value * 1024 * 1024 * 1024 : value * 1024 * 1024;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Generic MEMORY_LIMIT env var (bytes)
|
|
64
|
+
if (process.env.MEMORY_LIMIT) {
|
|
65
|
+
const limit = parseInt(process.env.MEMORY_LIMIT);
|
|
66
|
+
if (!isNaN(limit) && limit > 0) {
|
|
67
|
+
return limit;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// 2. Check cgroup v2 (modern Docker/K8s)
|
|
71
|
+
try {
|
|
72
|
+
const cgroupV2Path = '/sys/fs/cgroup/memory.max';
|
|
73
|
+
const cgroupV2Content = fs.readFileSync(cgroupV2Path, 'utf8').trim();
|
|
74
|
+
// "max" means no limit, otherwise it's bytes
|
|
75
|
+
if (cgroupV2Content !== 'max') {
|
|
76
|
+
const limit = parseInt(cgroupV2Content);
|
|
77
|
+
if (!isNaN(limit) && limit > 0) {
|
|
78
|
+
return limit;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
// cgroup v2 not available, try v1
|
|
84
|
+
}
|
|
85
|
+
// 3. Check cgroup v1 (legacy Docker/K8s)
|
|
86
|
+
try {
|
|
87
|
+
const cgroupV1Path = '/sys/fs/cgroup/memory/memory.limit_in_bytes';
|
|
88
|
+
const cgroupV1Content = fs.readFileSync(cgroupV1Path, 'utf8').trim();
|
|
89
|
+
const limit = parseInt(cgroupV1Content);
|
|
90
|
+
// Very large values (> 1 PB) indicate no limit
|
|
91
|
+
const ONE_PETABYTE = 1024 * 1024 * 1024 * 1024 * 1024;
|
|
92
|
+
if (!isNaN(limit) && limit > 0 && limit < ONE_PETABYTE) {
|
|
93
|
+
return limit;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
// cgroup v1 not available
|
|
98
|
+
}
|
|
99
|
+
// Not containerized or no limit set
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
// Error reading cgroup files
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
33
107
|
/**
|
|
34
108
|
* Auto-configured limits based on system resources
|
|
35
109
|
* These adapt to available memory and observed performance
|
|
36
110
|
*/
|
|
37
|
-
class ValidationConfig {
|
|
38
|
-
constructor() {
|
|
111
|
+
export class ValidationConfig {
|
|
112
|
+
constructor(options) {
|
|
39
113
|
// Performance observations
|
|
40
114
|
this.avgQueryTime = 0;
|
|
41
115
|
this.queryCount = 0;
|
|
42
|
-
// Auto-configure based on system resources
|
|
43
|
-
const totalMemory = getSystemMemory();
|
|
44
|
-
const availableMemory = getAvailableMemory();
|
|
45
|
-
// Scale limits based on available memory
|
|
46
|
-
// 1GB = 10K limit, 8GB = 80K limit, etc.
|
|
47
|
-
this.maxLimit = Math.min(100000, // Absolute max for safety
|
|
48
|
-
Math.floor(availableMemory / (1024 * 1024 * 100)) * 1000);
|
|
49
|
-
// Query length scales with memory too
|
|
50
|
-
this.maxQueryLength = Math.min(50000, Math.floor(availableMemory / (1024 * 1024 * 10)) * 1000);
|
|
51
116
|
// Vector dimensions (standard for all-MiniLM-L6-v2)
|
|
52
117
|
this.maxVectorDimensions = 384;
|
|
118
|
+
// Detect container memory limit
|
|
119
|
+
this.detectedContainerLimit = getContainerMemoryLimit();
|
|
120
|
+
// Priority 1: Explicit override (highest priority)
|
|
121
|
+
if (options?.maxQueryLimit !== undefined) {
|
|
122
|
+
this.maxLimit = Math.min(options.maxQueryLimit, 100000); // Still cap at 100k for safety
|
|
123
|
+
this.limitBasis = 'override';
|
|
124
|
+
// Scale query length with limit
|
|
125
|
+
this.maxQueryLength = Math.min(50000, this.maxLimit * 5);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
// Priority 2: Reserved memory specified
|
|
129
|
+
if (options?.reservedQueryMemory !== undefined) {
|
|
130
|
+
this.maxLimit = Math.min(100000, Math.floor(options.reservedQueryMemory / (1024 * 1024 * 100)) * 1000);
|
|
131
|
+
this.limitBasis = 'reservedMemory';
|
|
132
|
+
this.maxQueryLength = Math.min(50000, Math.floor(options.reservedQueryMemory / (1024 * 1024 * 10)) * 1000);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
// Priority 3: Container detected (smart containerized behavior)
|
|
136
|
+
if (this.detectedContainerLimit) {
|
|
137
|
+
// In containers, assume 75% used by graph data (EXPECTED)
|
|
138
|
+
// Reserve 25% for query operations
|
|
139
|
+
const queryMemory = this.detectedContainerLimit * 0.25;
|
|
140
|
+
this.maxLimit = Math.min(100000, Math.floor(queryMemory / (1024 * 1024 * 100)) * 1000);
|
|
141
|
+
this.limitBasis = 'containerMemory';
|
|
142
|
+
this.maxQueryLength = Math.min(50000, Math.floor(queryMemory / (1024 * 1024 * 10)) * 1000);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// Priority 4: Free memory (fallback, current behavior)
|
|
146
|
+
const availableMemory = getAvailableMemory();
|
|
147
|
+
this.maxLimit = Math.min(100000, Math.floor(availableMemory / (1024 * 1024 * 100)) * 1000);
|
|
148
|
+
this.limitBasis = 'freeMemory';
|
|
149
|
+
this.maxQueryLength = Math.min(50000, Math.floor(availableMemory / (1024 * 1024 * 10)) * 1000);
|
|
53
150
|
}
|
|
54
|
-
static getInstance() {
|
|
151
|
+
static getInstance(options) {
|
|
55
152
|
if (!ValidationConfig.instance) {
|
|
56
|
-
ValidationConfig.instance = new ValidationConfig();
|
|
153
|
+
ValidationConfig.instance = new ValidationConfig(options);
|
|
57
154
|
}
|
|
58
155
|
return ValidationConfig.instance;
|
|
59
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* Reset singleton (for testing or reconfiguration)
|
|
159
|
+
*/
|
|
160
|
+
static reset() {
|
|
161
|
+
ValidationConfig.instance = null;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Reconfigure with new options
|
|
165
|
+
*/
|
|
166
|
+
static reconfigure(options) {
|
|
167
|
+
ValidationConfig.instance = new ValidationConfig(options);
|
|
168
|
+
return ValidationConfig.instance;
|
|
169
|
+
}
|
|
60
170
|
/**
|
|
61
171
|
* Learn from actual usage to adjust limits
|
|
62
172
|
*/
|
|
63
173
|
recordQuery(duration, resultCount) {
|
|
64
174
|
this.queryCount++;
|
|
65
175
|
this.avgQueryTime = (this.avgQueryTime * (this.queryCount - 1) + duration) / this.queryCount;
|
|
66
|
-
//
|
|
67
|
-
if (this.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
176
|
+
// Only auto-adjust if not using explicit overrides
|
|
177
|
+
if (this.limitBasis !== 'override') {
|
|
178
|
+
// If queries are consistently fast with large results, increase limits
|
|
179
|
+
if (this.avgQueryTime < 100 && resultCount > this.maxLimit * 0.8) {
|
|
180
|
+
this.maxLimit = Math.min(this.maxLimit * 1.5, 100000);
|
|
181
|
+
}
|
|
182
|
+
// If queries are slow, reduce limits
|
|
183
|
+
if (this.avgQueryTime > 1000) {
|
|
184
|
+
this.maxLimit = Math.max(this.maxLimit * 0.8, 1000);
|
|
185
|
+
}
|
|
73
186
|
}
|
|
74
187
|
}
|
|
75
188
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soulcraft/brainy",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.11.0",
|
|
4
4
|
"description": "Universal Knowledge Protocol™ - World's first Triple Intelligence database unifying vector, graph, and document search in one API. Stage 3 CANONICAL: 42 nouns × 127 verbs covering 96-97% of all human knowledge.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|