@soulcraft/brainy 3.20.4 → 3.21.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/CHANGELOG.md +96 -1
- package/README.md +72 -2
- package/dist/brainy.js +9 -0
- package/dist/neural/entityExtractionCache.d.ts +111 -0
- package/dist/neural/entityExtractionCache.js +208 -0
- package/dist/neural/entityExtractor.d.ts +33 -1
- package/dist/neural/entityExtractor.js +66 -2
- package/dist/neural/relationshipConfidence.d.ts +79 -0
- package/dist/neural/relationshipConfidence.js +204 -0
- package/dist/storage/baseStorage.js +11 -6
- package/dist/types/brainy.types.d.ts +18 -0
- package/dist/types/progress.types.d.ts +107 -0
- package/dist/types/progress.types.js +221 -0
- package/package.json +10 -6
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standardized Progress Reporting
|
|
3
|
+
*
|
|
4
|
+
* Provides unified progress tracking across all long-running operations
|
|
5
|
+
* in Brainy (imports, clustering, large searches, etc.)
|
|
6
|
+
*
|
|
7
|
+
* PRODUCTION-READY - NO MOCKS, NO STUBS, REAL IMPLEMENTATION
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Progress tracker with automatic time estimation
|
|
11
|
+
*/
|
|
12
|
+
export class ProgressTracker {
|
|
13
|
+
constructor(total) {
|
|
14
|
+
this.status = 'pending';
|
|
15
|
+
this.processed = 0;
|
|
16
|
+
this.processingTimes = []; // Track last N processing times for estimation
|
|
17
|
+
if (total < 0) {
|
|
18
|
+
throw new Error('Total must be non-negative');
|
|
19
|
+
}
|
|
20
|
+
this.total = total;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Factory method for creating progress trackers
|
|
24
|
+
*/
|
|
25
|
+
static create(total) {
|
|
26
|
+
return new ProgressTracker(total);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Start tracking progress
|
|
30
|
+
*/
|
|
31
|
+
start() {
|
|
32
|
+
this.status = 'running';
|
|
33
|
+
this.startedAt = Date.now();
|
|
34
|
+
return this.current();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Update progress
|
|
38
|
+
*/
|
|
39
|
+
update(processed, currentItem) {
|
|
40
|
+
if (processed < 0) {
|
|
41
|
+
throw new Error('Processed count must be non-negative');
|
|
42
|
+
}
|
|
43
|
+
if (processed > this.total) {
|
|
44
|
+
throw new Error(`Processed count (${processed}) exceeds total (${this.total})`);
|
|
45
|
+
}
|
|
46
|
+
const previousProcessed = this.processed;
|
|
47
|
+
this.processed = processed;
|
|
48
|
+
this.currentItem = currentItem;
|
|
49
|
+
// Track processing time for estimation
|
|
50
|
+
if (this.startedAt && previousProcessed < processed) {
|
|
51
|
+
const itemsProcessed = processed - previousProcessed;
|
|
52
|
+
const timeTaken = Date.now() - this.startedAt;
|
|
53
|
+
const avgTimePerItem = timeTaken / processed;
|
|
54
|
+
this.processingTimes.push(avgTimePerItem);
|
|
55
|
+
// Keep only last 100 measurements for rolling average
|
|
56
|
+
if (this.processingTimes.length > 100) {
|
|
57
|
+
this.processingTimes.shift();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return this.current();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Increment progress by 1
|
|
64
|
+
*/
|
|
65
|
+
increment(currentItem) {
|
|
66
|
+
return this.update(this.processed + 1, currentItem);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Mark as completed
|
|
70
|
+
*/
|
|
71
|
+
complete(result) {
|
|
72
|
+
this.status = 'completed';
|
|
73
|
+
this.completedAt = Date.now();
|
|
74
|
+
this.processed = this.total;
|
|
75
|
+
this.result = result;
|
|
76
|
+
return this.current();
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Mark as failed
|
|
80
|
+
*/
|
|
81
|
+
fail(error) {
|
|
82
|
+
this.status = 'failed';
|
|
83
|
+
this.completedAt = Date.now();
|
|
84
|
+
this.error = error;
|
|
85
|
+
return this.current();
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Mark as cancelled
|
|
89
|
+
*/
|
|
90
|
+
cancel() {
|
|
91
|
+
this.status = 'cancelled';
|
|
92
|
+
this.completedAt = Date.now();
|
|
93
|
+
return this.current();
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Get current progress state
|
|
97
|
+
*/
|
|
98
|
+
current() {
|
|
99
|
+
const progress = this.total > 0 ? Math.round((this.processed / this.total) * 100) : 0;
|
|
100
|
+
// Generate message based on status
|
|
101
|
+
let message;
|
|
102
|
+
switch (this.status) {
|
|
103
|
+
case 'pending':
|
|
104
|
+
message = `Ready to process ${this.total} items`;
|
|
105
|
+
break;
|
|
106
|
+
case 'running':
|
|
107
|
+
message = this.currentItem
|
|
108
|
+
? `Processing: ${this.currentItem} (${this.processed}/${this.total})`
|
|
109
|
+
: `Processing ${this.processed}/${this.total} items`;
|
|
110
|
+
break;
|
|
111
|
+
case 'completed':
|
|
112
|
+
message = `Completed ${this.total} items`;
|
|
113
|
+
break;
|
|
114
|
+
case 'failed':
|
|
115
|
+
message = `Failed after ${this.processed} items: ${this.error?.message || 'Unknown error'}`;
|
|
116
|
+
break;
|
|
117
|
+
case 'cancelled':
|
|
118
|
+
message = `Cancelled after ${this.processed} items`;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
status: this.status,
|
|
123
|
+
progress,
|
|
124
|
+
message,
|
|
125
|
+
metadata: {
|
|
126
|
+
itemsProcessed: this.processed,
|
|
127
|
+
itemsTotal: this.total,
|
|
128
|
+
currentItem: this.currentItem,
|
|
129
|
+
estimatedTimeRemaining: this.estimateTimeRemaining(),
|
|
130
|
+
startedAt: this.startedAt || Date.now(),
|
|
131
|
+
completedAt: this.completedAt,
|
|
132
|
+
throughput: this.calculateThroughput()
|
|
133
|
+
},
|
|
134
|
+
result: this.result,
|
|
135
|
+
error: this.error
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Estimate time remaining based on processing history
|
|
140
|
+
*/
|
|
141
|
+
estimateTimeRemaining() {
|
|
142
|
+
if (this.status !== 'running' || !this.startedAt || this.processed === 0) {
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
const remaining = this.total - this.processed;
|
|
146
|
+
if (remaining === 0) {
|
|
147
|
+
return 0;
|
|
148
|
+
}
|
|
149
|
+
// Use rolling average if we have enough samples
|
|
150
|
+
if (this.processingTimes.length > 0) {
|
|
151
|
+
const avgTimePerItem = this.processingTimes.reduce((a, b) => a + b, 0) / this.processingTimes.length;
|
|
152
|
+
return Math.round(avgTimePerItem * remaining);
|
|
153
|
+
}
|
|
154
|
+
// Fallback to simple calculation
|
|
155
|
+
const elapsed = Date.now() - this.startedAt;
|
|
156
|
+
const avgTimePerItem = elapsed / this.processed;
|
|
157
|
+
return Math.round(avgTimePerItem * remaining);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Calculate current throughput (items/second)
|
|
161
|
+
*/
|
|
162
|
+
calculateThroughput() {
|
|
163
|
+
if (!this.startedAt || this.processed === 0) {
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
const elapsed = Date.now() - this.startedAt;
|
|
167
|
+
const seconds = elapsed / 1000;
|
|
168
|
+
return seconds > 0 ? Math.round((this.processed / seconds) * 100) / 100 : undefined;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Get progress statistics
|
|
172
|
+
*/
|
|
173
|
+
getStats() {
|
|
174
|
+
const elapsed = this.startedAt ? Date.now() - this.startedAt : 0;
|
|
175
|
+
return {
|
|
176
|
+
status: this.status,
|
|
177
|
+
processed: this.processed,
|
|
178
|
+
total: this.total,
|
|
179
|
+
remaining: this.total - this.processed,
|
|
180
|
+
progress: this.total > 0 ? this.processed / this.total : 0,
|
|
181
|
+
elapsed,
|
|
182
|
+
estimatedTotal: elapsed > 0 && this.processed > 0
|
|
183
|
+
? Math.round((elapsed / this.processed) * this.total)
|
|
184
|
+
: undefined,
|
|
185
|
+
throughput: this.calculateThroughput()
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Helper to format time duration
|
|
191
|
+
*/
|
|
192
|
+
export function formatDuration(ms) {
|
|
193
|
+
const seconds = Math.floor(ms / 1000);
|
|
194
|
+
const minutes = Math.floor(seconds / 60);
|
|
195
|
+
const hours = Math.floor(minutes / 60);
|
|
196
|
+
if (hours > 0) {
|
|
197
|
+
return `${hours}h ${minutes % 60}m`;
|
|
198
|
+
}
|
|
199
|
+
else if (minutes > 0) {
|
|
200
|
+
return `${minutes}m ${seconds % 60}s`;
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
return `${seconds}s`;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Helper to format progress percentage
|
|
208
|
+
*/
|
|
209
|
+
export function formatProgress(progress) {
|
|
210
|
+
const { status, progress: pct, metadata } = progress;
|
|
211
|
+
const remaining = metadata.estimatedTimeRemaining;
|
|
212
|
+
let str = `[${status.toUpperCase()}] ${pct}% (${metadata.itemsProcessed}/${metadata.itemsTotal})`;
|
|
213
|
+
if (metadata.throughput) {
|
|
214
|
+
str += ` - ${metadata.throughput} items/s`;
|
|
215
|
+
}
|
|
216
|
+
if (remaining && remaining > 0) {
|
|
217
|
+
str += ` - ${formatDuration(remaining)} remaining`;
|
|
218
|
+
}
|
|
219
|
+
return str;
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=progress.types.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soulcraft/brainy",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.21.0",
|
|
4
4
|
"description": "Universal Knowledge Protocol™ - World's first Triple Intelligence database unifying vector, graph, and document search in one API. 31 nouns × 40 verbs for infinite expressiveness.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -81,11 +81,15 @@
|
|
|
81
81
|
"format:check": "prettier --check \"src/**/*.{ts,js}\"",
|
|
82
82
|
"migrate:logger": "tsx scripts/migrate-to-structured-logger.ts",
|
|
83
83
|
"migrate:logger:dry": "tsx scripts/migrate-to-structured-logger.ts --dry-run",
|
|
84
|
-
"release": "
|
|
85
|
-
"release:patch": "
|
|
86
|
-
"release:minor": "
|
|
87
|
-
"release:major": "
|
|
88
|
-
"release:dry": "
|
|
84
|
+
"release": "./scripts/release.sh patch",
|
|
85
|
+
"release:patch": "./scripts/release.sh patch",
|
|
86
|
+
"release:minor": "./scripts/release.sh minor",
|
|
87
|
+
"release:major": "./scripts/release.sh major",
|
|
88
|
+
"release:dry": "./scripts/release.sh patch --dry-run",
|
|
89
|
+
"release:standard-version": "standard-version",
|
|
90
|
+
"release:standard-version:patch": "standard-version --release-as patch",
|
|
91
|
+
"release:standard-version:minor": "standard-version --release-as minor",
|
|
92
|
+
"release:standard-version:major": "standard-version --release-as major"
|
|
89
93
|
},
|
|
90
94
|
"keywords": [
|
|
91
95
|
"ai-database",
|