@soulcraft/brainy 4.1.2 → 4.1.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/CHANGELOG.md +26 -0
- package/dist/brainy.d.ts +31 -2
- package/dist/brainy.js +60 -18
- package/dist/neural/improvedNeuralAPI.js +5 -5
- package/dist/storage/baseStorage.js +5 -3
- package/dist/types/brainy.types.d.ts +69 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [4.1.3](https://github.com/soulcraftlabs/brainy/compare/v4.1.2...v4.1.3) (2025-10-21)
|
|
6
|
+
|
|
7
|
+
- perf: make getRelations() pagination consistent and efficient (54d819c)
|
|
8
|
+
- fix: resolve getRelations() empty array bug and add string ID shorthand (8d217f3)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### [4.1.3](https://github.com/soulcraftlabs/brainy/compare/v4.1.2...v4.1.3) (2025-10-21)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### 🐛 Bug Fixes
|
|
15
|
+
|
|
16
|
+
* **api**: fix getRelations() returning empty array when called without parameters
|
|
17
|
+
- Fixed critical bug where `brain.getRelations()` returned `[]` instead of all relationships
|
|
18
|
+
- Added support for retrieving all relationships with pagination (default limit: 100)
|
|
19
|
+
- Added string ID shorthand syntax: `brain.getRelations(entityId)` as alias for `brain.getRelations({ from: entityId })`
|
|
20
|
+
- **Performance**: Made pagination consistent - now ALL query patterns paginate at storage layer
|
|
21
|
+
- **Efficiency**: `getRelations({ from: id, limit: 10 })` now fetches only 10 instead of fetching ALL then slicing
|
|
22
|
+
- Fixed storage.getVerbs() offset handling - now properly converts offset to cursor for adapters
|
|
23
|
+
- Production safety: Warns when fetching >10k relationships without filters
|
|
24
|
+
- Fixed broken method calls in improvedNeuralAPI.ts (replaced non-existent `getVerbsForNoun` with `getRelations`)
|
|
25
|
+
- Fixed property access bugs: `verb.target` → `verb.to`, `verb.verb` → `verb.type`
|
|
26
|
+
- Added comprehensive integration tests (14 tests covering all query patterns)
|
|
27
|
+
- Updated JSDoc documentation with usage examples
|
|
28
|
+
- **Impact**: Resolves Workshop team bug where 524 imported relationships were inaccessible
|
|
29
|
+
- **Breaking**: None - fully backward compatible
|
|
30
|
+
|
|
5
31
|
### [4.1.2](https://github.com/soulcraftlabs/brainy/compare/v4.1.1...v4.1.2) (2025-10-21)
|
|
6
32
|
|
|
7
33
|
|
package/dist/brainy.d.ts
CHANGED
|
@@ -314,9 +314,38 @@ export declare class Brainy<T = any> implements BrainyInterface<T> {
|
|
|
314
314
|
*/
|
|
315
315
|
unrelate(id: string): Promise<void>;
|
|
316
316
|
/**
|
|
317
|
-
* Get relationships
|
|
317
|
+
* Get relationships between entities
|
|
318
|
+
*
|
|
319
|
+
* Supports multiple query patterns:
|
|
320
|
+
* - No parameters: Returns all relationships (paginated, default limit: 100)
|
|
321
|
+
* - String ID: Returns relationships from that entity (shorthand for { from: id })
|
|
322
|
+
* - Parameters object: Fine-grained filtering and pagination
|
|
323
|
+
*
|
|
324
|
+
* @param paramsOrId - Optional string ID or parameters object
|
|
325
|
+
* @returns Promise resolving to array of relationships
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* ```typescript
|
|
329
|
+
* // Get all relationships (first 100)
|
|
330
|
+
* const all = await brain.getRelations()
|
|
331
|
+
*
|
|
332
|
+
* // Get relationships from specific entity (shorthand syntax)
|
|
333
|
+
* const fromEntity = await brain.getRelations(entityId)
|
|
334
|
+
*
|
|
335
|
+
* // Get relationships with filters
|
|
336
|
+
* const filtered = await brain.getRelations({
|
|
337
|
+
* type: VerbType.FriendOf,
|
|
338
|
+
* limit: 50
|
|
339
|
+
* })
|
|
340
|
+
*
|
|
341
|
+
* // Pagination
|
|
342
|
+
* const page2 = await brain.getRelations({ offset: 100, limit: 100 })
|
|
343
|
+
* ```
|
|
344
|
+
*
|
|
345
|
+
* @since v4.1.3 - Fixed bug where calling without parameters returned empty array
|
|
346
|
+
* @since v4.1.3 - Added string ID shorthand syntax: getRelations(id)
|
|
318
347
|
*/
|
|
319
|
-
getRelations(
|
|
348
|
+
getRelations(paramsOrId?: string | GetRelationsParams): Promise<Relation<T>[]>;
|
|
320
349
|
/**
|
|
321
350
|
* Unified find method - supports natural language and structured queries
|
|
322
351
|
* Implements Triple Intelligence with parallel search optimization
|
package/dist/brainy.js
CHANGED
|
@@ -719,33 +719,75 @@ export class Brainy {
|
|
|
719
719
|
});
|
|
720
720
|
}
|
|
721
721
|
/**
|
|
722
|
-
* Get relationships
|
|
722
|
+
* Get relationships between entities
|
|
723
|
+
*
|
|
724
|
+
* Supports multiple query patterns:
|
|
725
|
+
* - No parameters: Returns all relationships (paginated, default limit: 100)
|
|
726
|
+
* - String ID: Returns relationships from that entity (shorthand for { from: id })
|
|
727
|
+
* - Parameters object: Fine-grained filtering and pagination
|
|
728
|
+
*
|
|
729
|
+
* @param paramsOrId - Optional string ID or parameters object
|
|
730
|
+
* @returns Promise resolving to array of relationships
|
|
731
|
+
*
|
|
732
|
+
* @example
|
|
733
|
+
* ```typescript
|
|
734
|
+
* // Get all relationships (first 100)
|
|
735
|
+
* const all = await brain.getRelations()
|
|
736
|
+
*
|
|
737
|
+
* // Get relationships from specific entity (shorthand syntax)
|
|
738
|
+
* const fromEntity = await brain.getRelations(entityId)
|
|
739
|
+
*
|
|
740
|
+
* // Get relationships with filters
|
|
741
|
+
* const filtered = await brain.getRelations({
|
|
742
|
+
* type: VerbType.FriendOf,
|
|
743
|
+
* limit: 50
|
|
744
|
+
* })
|
|
745
|
+
*
|
|
746
|
+
* // Pagination
|
|
747
|
+
* const page2 = await brain.getRelations({ offset: 100, limit: 100 })
|
|
748
|
+
* ```
|
|
749
|
+
*
|
|
750
|
+
* @since v4.1.3 - Fixed bug where calling without parameters returned empty array
|
|
751
|
+
* @since v4.1.3 - Added string ID shorthand syntax: getRelations(id)
|
|
723
752
|
*/
|
|
724
|
-
async getRelations(
|
|
753
|
+
async getRelations(paramsOrId) {
|
|
725
754
|
await this.ensureInitialized();
|
|
726
|
-
|
|
755
|
+
// Handle string ID shorthand: getRelations(id) -> getRelations({ from: id })
|
|
756
|
+
const params = typeof paramsOrId === 'string'
|
|
757
|
+
? { from: paramsOrId }
|
|
758
|
+
: (paramsOrId || {});
|
|
759
|
+
const limit = params.limit || 100;
|
|
760
|
+
const offset = params.offset || 0;
|
|
761
|
+
// Production safety: warn for large unfiltered queries
|
|
762
|
+
if (!params.from && !params.to && !params.type && limit > 10000) {
|
|
763
|
+
console.warn(`[Brainy] getRelations(): Fetching ${limit} relationships without filters. ` +
|
|
764
|
+
`Consider adding 'from', 'to', or 'type' filter for better performance.`);
|
|
765
|
+
}
|
|
766
|
+
// Build filter for storage query
|
|
767
|
+
const filter = {};
|
|
727
768
|
if (params.from) {
|
|
728
|
-
|
|
729
|
-
relations.push(...this.verbsToRelations(verbs));
|
|
769
|
+
filter.sourceId = params.from;
|
|
730
770
|
}
|
|
731
771
|
if (params.to) {
|
|
732
|
-
|
|
733
|
-
relations.push(...this.verbsToRelations(verbs));
|
|
772
|
+
filter.targetId = params.to;
|
|
734
773
|
}
|
|
735
|
-
// Filter by type
|
|
736
|
-
let filtered = relations;
|
|
737
774
|
if (params.type) {
|
|
738
|
-
|
|
739
|
-
filtered = relations.filter((r) => types.includes(r.type));
|
|
775
|
+
filter.verbType = Array.isArray(params.type) ? params.type : [params.type];
|
|
740
776
|
}
|
|
741
|
-
// Filter by service
|
|
742
777
|
if (params.service) {
|
|
743
|
-
|
|
744
|
-
}
|
|
745
|
-
//
|
|
746
|
-
const
|
|
747
|
-
|
|
748
|
-
|
|
778
|
+
filter.service = params.service;
|
|
779
|
+
}
|
|
780
|
+
// Fetch from storage with pagination at storage layer (efficient!)
|
|
781
|
+
const result = await this.storage.getVerbs({
|
|
782
|
+
pagination: {
|
|
783
|
+
limit,
|
|
784
|
+
offset,
|
|
785
|
+
cursor: params.cursor
|
|
786
|
+
},
|
|
787
|
+
filter: Object.keys(filter).length > 0 ? filter : undefined
|
|
788
|
+
});
|
|
789
|
+
// Convert to Relation format
|
|
790
|
+
return this.verbsToRelations(result.items);
|
|
749
791
|
}
|
|
750
792
|
// ============= SEARCH & DISCOVERY =============
|
|
751
793
|
/**
|
|
@@ -1310,14 +1310,14 @@ export class ImprovedNeuralAPI {
|
|
|
1310
1310
|
for (const sourceId of itemIds) {
|
|
1311
1311
|
const sourceVerbs = await this.brain.getRelations(sourceId);
|
|
1312
1312
|
for (const verb of sourceVerbs) {
|
|
1313
|
-
const targetId = verb.
|
|
1313
|
+
const targetId = verb.to;
|
|
1314
1314
|
if (nodes.has(targetId) && sourceId !== targetId) {
|
|
1315
1315
|
// Initialize edge map if needed
|
|
1316
1316
|
if (!edges.has(sourceId)) {
|
|
1317
1317
|
edges.set(sourceId, new Map());
|
|
1318
1318
|
}
|
|
1319
1319
|
// Calculate edge weight from verb type and metadata
|
|
1320
|
-
const verbType = verb.
|
|
1320
|
+
const verbType = verb.type;
|
|
1321
1321
|
const baseWeight = relationshipWeights[verbType] || 0.5;
|
|
1322
1322
|
const confidenceWeight = verb.confidence || 1.0;
|
|
1323
1323
|
const weight = baseWeight * confidenceWeight;
|
|
@@ -2743,7 +2743,7 @@ export class ImprovedNeuralAPI {
|
|
|
2743
2743
|
const sampleSize = Math.min(50, itemIds.length);
|
|
2744
2744
|
for (let i = 0; i < sampleSize; i++) {
|
|
2745
2745
|
try {
|
|
2746
|
-
const verbs = await this.brain.
|
|
2746
|
+
const verbs = await this.brain.getRelations({ from: itemIds[i] });
|
|
2747
2747
|
connectionCount += verbs.length;
|
|
2748
2748
|
}
|
|
2749
2749
|
catch (error) {
|
|
@@ -2797,9 +2797,9 @@ export class ImprovedNeuralAPI {
|
|
|
2797
2797
|
if (fromType !== toType) {
|
|
2798
2798
|
for (const fromItem of fromItems.slice(0, 10)) { // Sample to avoid N^2
|
|
2799
2799
|
try {
|
|
2800
|
-
const verbs = await this.brain.
|
|
2800
|
+
const verbs = await this.brain.getRelations({ from: fromItem.id });
|
|
2801
2801
|
for (const verb of verbs) {
|
|
2802
|
-
const toItem = toItems.find(item => item.id === verb.
|
|
2802
|
+
const toItem = toItems.find(item => item.id === verb.to);
|
|
2803
2803
|
if (toItem) {
|
|
2804
2804
|
connections.push({
|
|
2805
2805
|
from: fromItem.id,
|
|
@@ -600,13 +600,15 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
600
600
|
// Check if the adapter has a paginated method for getting verbs
|
|
601
601
|
if (typeof this.getVerbsWithPagination === 'function') {
|
|
602
602
|
// Use the adapter's paginated method
|
|
603
|
+
// Convert offset to cursor if no cursor provided (adapters use cursor for offset)
|
|
604
|
+
const effectiveCursor = cursor || (offset > 0 ? offset.toString() : undefined);
|
|
603
605
|
const result = await this.getVerbsWithPagination({
|
|
604
606
|
limit,
|
|
605
|
-
cursor,
|
|
607
|
+
cursor: effectiveCursor,
|
|
606
608
|
filter: options?.filter
|
|
607
609
|
});
|
|
608
|
-
//
|
|
609
|
-
const items = result.items
|
|
610
|
+
// Items are already offset by the adapter via cursor, no need to slice
|
|
611
|
+
const items = result.items;
|
|
610
612
|
// CRITICAL SAFETY CHECK: Prevent infinite loops
|
|
611
613
|
// If we have no items but hasMore is true, force hasMore to false
|
|
612
614
|
// This prevents pagination bugs from causing infinite loops
|
|
@@ -172,14 +172,83 @@ export interface SimilarParams<T = any> {
|
|
|
172
172
|
}
|
|
173
173
|
/**
|
|
174
174
|
* Parameters for getting relationships
|
|
175
|
+
*
|
|
176
|
+
* All parameters are optional. When called without parameters, returns all relationships
|
|
177
|
+
* with pagination (default limit: 100).
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```typescript
|
|
181
|
+
* // Get all relationships (default limit: 100)
|
|
182
|
+
* const all = await brain.getRelations()
|
|
183
|
+
*
|
|
184
|
+
* // Get relationships from a specific entity (string shorthand)
|
|
185
|
+
* const fromEntity = await brain.getRelations(entityId)
|
|
186
|
+
*
|
|
187
|
+
* // Equivalent to:
|
|
188
|
+
* const fromEntity2 = await brain.getRelations({ from: entityId })
|
|
189
|
+
*
|
|
190
|
+
* // Get relationships to a specific entity
|
|
191
|
+
* const toEntity = await brain.getRelations({ to: entityId })
|
|
192
|
+
*
|
|
193
|
+
* // Filter by relationship type
|
|
194
|
+
* const friends = await brain.getRelations({ type: VerbType.FriendOf })
|
|
195
|
+
*
|
|
196
|
+
* // Pagination
|
|
197
|
+
* const page2 = await brain.getRelations({ offset: 100, limit: 50 })
|
|
198
|
+
*
|
|
199
|
+
* // Combined filters
|
|
200
|
+
* const filtered = await brain.getRelations({
|
|
201
|
+
* from: entityId,
|
|
202
|
+
* type: VerbType.WorksWith,
|
|
203
|
+
* limit: 20
|
|
204
|
+
* })
|
|
205
|
+
* ```
|
|
206
|
+
*
|
|
207
|
+
* @since v4.1.3 - Fixed bug where calling without parameters returned empty array
|
|
208
|
+
* @since v4.1.3 - Added string ID shorthand syntax
|
|
175
209
|
*/
|
|
176
210
|
export interface GetRelationsParams {
|
|
211
|
+
/**
|
|
212
|
+
* Filter by source entity ID
|
|
213
|
+
*
|
|
214
|
+
* Returns all relationships originating from this entity.
|
|
215
|
+
*/
|
|
177
216
|
from?: string;
|
|
217
|
+
/**
|
|
218
|
+
* Filter by target entity ID
|
|
219
|
+
*
|
|
220
|
+
* Returns all relationships pointing to this entity.
|
|
221
|
+
*/
|
|
178
222
|
to?: string;
|
|
223
|
+
/**
|
|
224
|
+
* Filter by relationship type(s)
|
|
225
|
+
*
|
|
226
|
+
* Can be a single VerbType or array of VerbTypes.
|
|
227
|
+
*/
|
|
179
228
|
type?: VerbType | VerbType[];
|
|
229
|
+
/**
|
|
230
|
+
* Maximum number of results to return
|
|
231
|
+
*
|
|
232
|
+
* @default 100
|
|
233
|
+
*/
|
|
180
234
|
limit?: number;
|
|
235
|
+
/**
|
|
236
|
+
* Number of results to skip (offset-based pagination)
|
|
237
|
+
*
|
|
238
|
+
* @default 0
|
|
239
|
+
*/
|
|
181
240
|
offset?: number;
|
|
241
|
+
/**
|
|
242
|
+
* Cursor for cursor-based pagination
|
|
243
|
+
*
|
|
244
|
+
* More efficient than offset for large result sets.
|
|
245
|
+
*/
|
|
182
246
|
cursor?: string;
|
|
247
|
+
/**
|
|
248
|
+
* Filter by service (multi-tenancy)
|
|
249
|
+
*
|
|
250
|
+
* Only return relationships belonging to this service.
|
|
251
|
+
*/
|
|
183
252
|
service?: string;
|
|
184
253
|
}
|
|
185
254
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soulcraft/brainy",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.3",
|
|
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",
|