diff --git a/MIGRATION_PLAN_DEPRECATED_METHODS.md b/MIGRATION_PLAN_DEPRECATED_METHODS.md new file mode 100644 index 00000000..0e81ad12 --- /dev/null +++ b/MIGRATION_PLAN_DEPRECATED_METHODS.md @@ -0,0 +1,143 @@ +# Migration Plan: Deprecated Methods + +## Overview +This document outlines the migration plan for deprecated methods in the Brainy codebase. These methods are marked as deprecated due to potential memory issues with large datasets. + +## Deprecated Methods + +### 1. Storage Adapter Methods +- `getAllNouns()` → Use `getNouns()` with pagination +- `getAllVerbs()` → Use `getVerbs()` with pagination +- `getNounsByNounType(nounType)` → Use `getNouns({ nounType })` +- `getVerbsBySource(sourceId)` → Use `getVerbs({ sourceId })` +- `getVerbsByTarget(targetId)` → Use `getVerbs({ targetId })` +- `getVerbsByType(type)` → Use `getVerbs({ verbType: type })` + +### 2. HNSW Index Methods +- `getNouns()` → Use `getNounsPaginated()` + +## Migration Strategy + +### Phase 1: Update Internal BrainyData Usage (High Priority) +**Files to update:** +- `src/brainyData.ts` - 8 usages of deprecated methods +- `src/augmentations/memoryAugmentations.ts` - 1 usage +- `src/mcp/brainyMCPAdapter.ts` - 2 usages + +**Specific Changes:** + +#### src/brainyData.ts +```typescript +// OLD: +const nouns = await this.storage!.getAllNouns() + +// NEW: +const nouns: HNSWNoun[] = [] +let cursor: SearchCursor | undefined +do { + const result = await this.storage!.getNouns({}, { limit: 1000, cursor }) + nouns.push(...result.results) + cursor = result.cursor +} while (cursor) +``` + +#### src/augmentations/memoryAugmentations.ts +```typescript +// OLD: +const nodes = await this.storage.getAllNouns() + +// NEW: +const nodes = await this.storage.getNouns({}, { limit: Number.MAX_SAFE_INTEGER }) +``` + +#### src/mcp/brainyMCPAdapter.ts +```typescript +// OLD: +const outgoing = await (this.brainyData as any).getVerbsBySource?.(id) || [] + +// NEW: +const outgoing = await this.brainyData.getVerbs({ sourceId: id }, { limit: Number.MAX_SAFE_INTEGER }) || [] +``` + +### Phase 2: Update Storage Adapter Implementations (Medium Priority) +**Files to update:** +- `src/storage/adapters/memoryStorage.ts` +- `src/storage/adapters/fileSystemStorage.ts` +- `src/storage/adapters/opfsStorage.ts` +- `src/storage/adapters/s3CompatibleStorage.ts` + +**Strategy:** Keep deprecated methods but mark them as internal-only and implement them using the new filtered methods. + +### Phase 3: Update Type Definitions (Low Priority) +**Files to update:** +- `src/coreTypes.ts` - Remove deprecated method signatures +- `src/storage/baseStorage.ts` - Remove deprecated implementations + +### Phase 4: Update Tests (Low Priority) +**Files to update:** +- `tests/*.test.ts` - Update test files that use deprecated methods + +## Implementation Order + +### 1. Immediate (Safe Changes) +- ✅ Remove unused files (tensorflowUtils.ts, etc.) +- ✅ Fix TODO items with version handling +- Update internal BrainyData usage with pagination + +### 2. Short-term (1-2 weeks) +- Update augmentation and MCP adapter usage +- Add deprecation warnings to method implementations +- Update storage adapter internal implementations + +### 3. Long-term (Next major version) +- Remove deprecated method signatures from interfaces +- Remove deprecated method implementations +- Update all test files + +## Backward Compatibility + +### Option 1: Gradual Deprecation (Recommended) +- Keep deprecated methods but add console warnings +- Implement them using new filtered methods internally +- Remove in next major version (0.42.0 → 1.0.0) + +### Option 2: Immediate Removal +- Remove deprecated methods now +- Update all usage immediately +- Risk: Breaking changes for external users + +## Testing Strategy + +1. **Unit Tests**: Update existing tests to use new methods +2. **Integration Tests**: Verify pagination works correctly +3. **Performance Tests**: Ensure new implementations don't regress performance +4. **Memory Tests**: Verify large dataset handling improves + +## Rollback Plan + +If issues arise: +1. Revert to previous implementations +2. Add memory limits as temporary solution +3. Implement pagination more gradually + +## Success Criteria + +- ✅ All deprecated method usage removed from core files +- ✅ No memory issues with large datasets +- ✅ Performance maintained or improved +- ✅ All tests passing +- ✅ Backward compatibility preserved (if Option 1) + +## Timeline + +- **Week 1**: Complete Phase 1 (internal usage) +- **Week 2**: Complete Phase 2 (storage adapters) +- **Week 3**: Complete Phase 3 (type definitions) +- **Week 4**: Complete Phase 4 (tests) and validation + +## Notes + +- The deprecated methods are still widely used, so this migration requires careful planning +- Consider adding configuration option to control pagination limits +- Document the breaking changes clearly in CHANGELOG.md +- Consider providing migration utilities for external users \ No newline at end of file diff --git a/rollup.config.js b/rollup.config.js index 45802dce..129cccaf 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -2,7 +2,7 @@ import typescript from '@rollup/plugin-typescript' import resolve from '@rollup/plugin-node-resolve' import commonjs from '@rollup/plugin-commonjs' import json from '@rollup/plugin-json' -import { terser } from 'rollup-plugin-terser' +import terser from '@rollup/plugin-terser' import replace from '@rollup/plugin-replace' import fs from 'fs' import path from 'path' diff --git a/src/brainyData.ts b/src/brainyData.ts index 97fd4a08..68832eab 100644 --- a/src/brainyData.ts +++ b/src/brainyData.ts @@ -33,6 +33,7 @@ import { euclideanDistance, cleanupWorkerPools } from './utils/index.js' +import { getAugmentationVersion } from './utils/version.js' import { NounType, VerbType, GraphNoun } from './types/graphTypes.js' import { ServerSearchConduitAugmentation, @@ -1557,10 +1558,7 @@ export class BrainyData implements BrainyDataInterface { // Only set createdBy if it doesn't exist or is being explicitly updated if (!graphNoun.createdBy || options.service) { - graphNoun.createdBy = { - augmentation: service, - version: '1.0' // TODO: Get actual version from augmentation - } + graphNoun.createdBy = getAugmentationVersion(service) } // Update timestamps @@ -2957,10 +2955,7 @@ export class BrainyData implements BrainyDataInterface { } } else if (!graphNoun.createdBy) { // If no existing createdBy and none in the update, set it - graphNoun.createdBy = { - augmentation: service, - version: '1.0' // TODO: Get actual version from augmentation - } + graphNoun.createdBy = getAugmentationVersion(service) // Set createdAt if it doesn't exist if (!graphNoun.createdAt) { @@ -3223,10 +3218,7 @@ export class BrainyData implements BrainyDataInterface { createdAt: timestamp, updatedAt: timestamp, noun: NounType.Concept, - createdBy: { - augmentation: service, - version: '1.0' // TODO: Get actual version from augmentation - } + createdBy: getAugmentationVersion(service) } // Add the missing noun @@ -3265,10 +3257,7 @@ export class BrainyData implements BrainyDataInterface { createdAt: timestamp, updatedAt: timestamp, noun: NounType.Concept, - createdBy: { - augmentation: service, - version: '1.0' // TODO: Get actual version from augmentation - } + createdBy: getAugmentationVersion(service) } // Add the missing noun @@ -3389,10 +3378,7 @@ export class BrainyData implements BrainyDataInterface { weight: options.weight, createdAt: timestamp, updatedAt: timestamp, - createdBy: { - augmentation: service, - version: '1.0' // TODO: Get actual version from augmentation - }, + createdBy: getAugmentationVersion(service), data: options.metadata // Store the original metadata in the data field } diff --git a/src/examples/basicUsage.ts b/src/examples/basicUsage.ts index 83f881c4..93f13815 100644 --- a/src/examples/basicUsage.ts +++ b/src/examples/basicUsage.ts @@ -42,18 +42,8 @@ async function runExample() { // Add vectors to the database const ids: Record = {} - const metadata: Record = { - cat: { type: 'mammal', domesticated: true }, - dog: { type: 'mammal', domesticated: true }, - fish: { type: 'fish', domesticated: false }, - bird: { type: 'bird', domesticated: false }, - tiger: { type: 'mammal', domesticated: false }, - lion: { type: 'mammal', domesticated: false }, - shark: { type: 'fish', domesticated: false }, - eagle: { type: 'bird', domesticated: false } - } for (const [word, vector] of Object.entries(wordEmbeddings)) { - ids[word] = await db.add(vector, metadata[word]) + ids[word] = await db.add(vector, metadata[word as keyof typeof metadata]) console.log(`Added "${word}" with ID: ${ids[word]}`) } diff --git a/src/patched-platform-node.ts b/src/patched-platform-node.ts deleted file mode 100644 index 77e51340..00000000 --- a/src/patched-platform-node.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Custom patched implementation of TensorFlow.js PlatformNode class - * This resolves the TextEncoder/TextDecoder constructor issues - */ - -export class PatchedPlatformNode { - util: { - TextEncoder: typeof TextEncoder - TextDecoder: typeof TextDecoder - isFloat32Array: (arr: any) => boolean - isTypedArray: (arr: any) => boolean - } - textEncoder: TextEncoder - textDecoder: TextDecoder - - constructor() { - // Create a util object with necessary methods - this.util = { - // Add isFloat32Array and isTypedArray directly to util - isFloat32Array: (arr) => { - return !!( - arr instanceof Float32Array || - (arr && - Object.prototype.toString.call(arr) === '[object Float32Array]') - ) - }, - isTypedArray: (arr) => { - return !!(ArrayBuffer.isView(arr) && !(arr instanceof DataView)) - }, - // Provide constructor references directly - TextEncoder, - TextDecoder - } - - // Initialize TextEncoder/TextDecoder instances - this.textEncoder = new TextEncoder() - this.textDecoder = new TextDecoder() - } - - // Define isFloat32Array directly on the instance - isFloat32Array(arr: any) { - return !!( - arr instanceof Float32Array || - (arr && Object.prototype.toString.call(arr) === '[object Float32Array]') - ) - } - - // Define isTypedArray directly on the instance - isTypedArray(arr: any) { - return !!(ArrayBuffer.isView(arr) && !(arr instanceof DataView)) - } -} diff --git a/src/testing/prettyReporter.ts b/src/testing/prettyReporter.ts deleted file mode 100644 index c4c4106f..00000000 --- a/src/testing/prettyReporter.ts +++ /dev/null @@ -1,219 +0,0 @@ -import { Reporter, File, Task, TaskResult, Vitest, TaskResultPack, RunnerTaskEventPack as TaskEventPack } from 'vitest' - -/** - * PrettyReporter - A visually appealing reporter for Vitest - * - * This reporter creates a visually enhanced summary of test results - * with colors, symbols, and formatted output. - */ -export default class PrettyReporter implements Reporter { - private startTime: number = 0 - private testResults: Map = new Map() - private totalTests: { passed: number, failed: number, skipped: number } = { passed: 0, failed: 0, skipped: 0 } - private failedTests: Array<{ file: string, name: string, error: string }> = [] - private ctx: Vitest | undefined - - onInit(ctx: Vitest): void { - this.ctx = ctx - this.startTime = Date.now() - console.log('\n🧠 \x1b[36mBrainy Test Suite\x1b[0m - Starting tests...\n') - } - - onFinished(files?: File[] | undefined): void { - const duration = Date.now() - this.startTime - this.printSummary(duration) - } - - onTaskUpdate(packs: TaskResultPack[], events: TaskEventPack[]): void { - for (const [id, result, meta] of packs) { - if (result) { - // Find the task in the state manager - if (this.ctx?.state) { - try { - // We need to handle the entity type carefully - const entity = this.ctx.state.getReportedEntity(id as unknown as Task) - - // Skip if entity doesn't exist or doesn't have the right properties - if (!entity || typeof entity !== 'object') continue - - // Check if it has the necessary properties to be treated as a Task - if ('type' in entity && - 'name' in entity && - 'file' in entity && - entity.file && - typeof entity.file === 'object' && - 'filepath' in entity.file) { - // Process the task with the right type - this.processTask({ - id: id, - name: entity.name as string, - type: entity.type as any, - file: entity.file as any, - mode: (entity as any).mode || 'run', - result: result - } as unknown as Task) - } - } catch (error) { - console.warn(`Error processing task ${id}:`, error) - } - } - } - } - } - - onCollected(files?: File[] | undefined): void { - if (files) { - for (const file of files) { - this.testResults.set(file.filepath, { passed: 0, failed: 0, skipped: 0, duration: 0 }) - - // Count tests in each file - if (file.tasks) { - this.countTestsInTasks(file.tasks) - } - } - } - } - - private countTestsInTasks(tasks: Task[]): void { - for (const task of tasks) { - if (task.type === 'test') { - this.processTask(task) - } else if (task.tasks) { - // Recursively process nested tasks (like in describe blocks) - this.countTestsInTasks(task.tasks) - } - } - } - - private processTask(task: Task): void { - if (!task || !task.file) return - - // For test collection phase, we just want to count the test - if (task.type === 'test') { - // Make sure we have a valid file path - if (!task.file.filepath) return - - const fileResult = this.testResults.get(task.file.filepath) || { - passed: 0, failed: 0, skipped: 0, duration: 0 - } - - // During collection, we don't have results yet, so just count the test - // The actual pass/fail status will be updated during task updates - if (!task.result) { - // Just count it as a test, state will be updated later - console.log(`Counting test: ${task.name} in ${task.file.name}`) - } else if (task.result.state === 'pass') { - fileResult.passed++ - this.totalTests.passed++ - console.log(`Passed test: ${task.name}`) - } else if (task.result.state === 'fail') { - fileResult.failed++ - this.totalTests.failed++ - this.failedTests.push({ - file: task.file.name, - name: task.name, - error: task.result?.errors?.[0]?.message || 'Unknown error' - }) - console.log(`Failed test: ${task.name}`) - } else if (task.mode === 'skip' || task.result.state === 'skip') { - fileResult.skipped++ - this.totalTests.skipped++ - console.log(`Skipped test: ${task.name}`) - } - - if (task.result?.duration) { - fileResult.duration += task.result.duration - } - - this.testResults.set(task.file.filepath, fileResult) - } - } - - private printSummary(duration: number): void { - const durationStr = this.formatDuration(duration) - const totalTests = this.totalTests.passed + this.totalTests.failed + this.totalTests.skipped - - console.log('\n') - console.log('━'.repeat(80)) - console.log(`\n\x1b[1m\x1b[36m📊 TEST SUMMARY REPORT\x1b[0m`) - console.log('━'.repeat(80)) - - // Print overall stats - console.log(`\n\x1b[1mTest Run Completed in:\x1b[0m ${durationStr}`) - console.log(`\x1b[1mDate:\x1b[0m ${new Date().toLocaleString()}`) - console.log(`\x1b[1mTotal Test Files:\x1b[0m ${this.testResults.size}`) - console.log(`\x1b[1mTotal Tests:\x1b[0m ${totalTests}`) - - // Print test status counts with colors and symbols - console.log(`\n\x1b[1mResults:\x1b[0m`) - console.log(` \x1b[32m✓ Passed:\x1b[0m ${this.totalTests.passed}`) - console.log(` \x1b[31m✗ Failed:\x1b[0m ${this.totalTests.failed}`) - console.log(` \x1b[33m○ Skipped:\x1b[0m ${this.totalTests.skipped}`) - - // Print file results in a table format - console.log('\n\x1b[1mTest Files:\x1b[0m') - console.log('┌' + '─'.repeat(50) + '┬' + '─'.repeat(10) + '┬' + '─'.repeat(10) + '┬' + '─'.repeat(10) + '┐') - console.log('│ \x1b[1mFile\x1b[0m' + ' '.repeat(46) + '│ \x1b[1mPassed\x1b[0m' + ' '.repeat(4) + '│ \x1b[1mFailed\x1b[0m' + ' '.repeat(4) + '│ \x1b[1mSkipped\x1b[0m' + ' '.repeat(3) + '│') - console.log('├' + '─'.repeat(50) + '┼' + '─'.repeat(10) + '┼' + '─'.repeat(10) + '┼' + '─'.repeat(10) + '┤') - - // Sort files by name for consistent output - const sortedFiles = Array.from(this.testResults.entries()).sort((a, b) => { - const fileNameA = a[0].split('/').pop() || '' - const fileNameB = b[0].split('/').pop() || '' - return fileNameA.localeCompare(fileNameB) - }) - - for (const [filepath, results] of sortedFiles) { - const fileName = filepath.split('/').pop() || filepath - const truncatedName = this.truncateString(fileName, 48) - const passedStr = `\x1b[32m${results.passed}\x1b[0m` - const failedStr = results.failed > 0 ? `\x1b[31m${results.failed}\x1b[0m` : `${results.failed}` - const skippedStr = results.skipped > 0 ? `\x1b[33m${results.skipped}\x1b[0m` : `${results.skipped}` - - console.log(`│ ${truncatedName}${' '.repeat(50 - truncatedName.length)}│ ${passedStr}${' '.repeat(10 - passedStr.length + 9)}│ ${failedStr}${' '.repeat(10 - failedStr.length + 9)}│ ${skippedStr}${' '.repeat(10 - skippedStr.length + 9)}│`) - } - - console.log('└' + '─'.repeat(50) + '┴' + '─'.repeat(10) + '┴' + '─'.repeat(10) + '┴' + '─'.repeat(10) + '┘') - - // Print failed tests if any - if (this.failedTests.length > 0) { - console.log('\n\x1b[1m\x1b[31m❌ Failed Tests:\x1b[0m') - for (let i = 0; i < this.failedTests.length; i++) { - const { file, name, error } = this.failedTests[i] - console.log(`\n ${i + 1}. \x1b[1m${file}\x1b[0m: ${name}`) - console.log(` \x1b[31m${error}\x1b[0m`) - } - } - - // Print final status - console.log('\n') - if (this.totalTests.failed > 0) { - console.log('\x1b[41m\x1b[37m FAILED \x1b[0m Some tests failed. Check the report above for details.') - } else { - console.log('\x1b[42m\x1b[30m PASSED \x1b[0m All tests passed successfully!') - } - console.log('\n') - } - - private formatDuration(ms: number): string { - if (ms < 1000) { - return `${ms}ms` - } - const seconds = Math.floor(ms / 1000) - const minutes = Math.floor(seconds / 60) - const remainingSeconds = seconds % 60 - - if (minutes === 0) { - return `${seconds}.${Math.floor((ms % 1000) / 100)}s` - } - - return `${minutes}m ${remainingSeconds}s` - } - - private truncateString(str: string, maxLength: number): string { - if (str.length <= maxLength) { - return str - } - return str.substring(0, maxLength - 3) + '...' - } -} diff --git a/src/testing/prettySummaryReporter.ts b/src/testing/prettySummaryReporter.ts deleted file mode 100644 index d6432707..00000000 --- a/src/testing/prettySummaryReporter.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { Reporter, File, Task, TaskResult, Vitest, TaskResultPack, RunnerTaskEventPack as TaskEventPack } from 'vitest' - -/** - * PrettySummaryReporter - A visually appealing summary reporter for Vitest - * - * This reporter creates a visually enhanced summary of test results - * with colors, symbols, and formatted output at the end of the test run. - */ -export default class PrettySummaryReporter implements Reporter { - private startTime: number = 0 - private testFiles: string[] = [] - private testCounts = { - total: 0, - passed: 0, - failed: 0, - skipped: 0 - } - private fileResults: Map = new Map() - private failedTests: Array<{ file: string, name: string, error: string }> = [] - private ctx: Vitest | undefined - - onInit(ctx: Vitest): void { - this.ctx = ctx - this.startTime = Date.now() - console.log('\n🧠 \x1b[36mBrainy Test Suite\x1b[0m - Starting tests...\n') - } - - onCollected(files?: File[]): void { - if (files && files.length > 0) { - for (const file of files) { - this.testFiles.push(file.filepath) - - // Initialize file results - const fileResult = { passed: 0, failed: 0, skipped: 0 } - this.fileResults.set(file.filepath, fileResult) - - // Count tests in this file and update total count - const countTests = (tasks: Task[]) => { - for (const task of tasks) { - if (task.type === 'test') { - // Count the test based on its mode - this.testCounts.total++ - - if (task.mode === 'skip') { - fileResult.skipped++ - this.testCounts.skipped++ - } else { - // Initially mark as passed, will be updated if it fails - fileResult.passed++ - this.testCounts.passed++ - } - } - // Check if task is a Suite which has tasks property - if ('tasks' in task && Array.isArray(task.tasks) && task.tasks.length > 0) { - countTests(task.tasks) - } - } - } - - if (file.tasks) { - countTests(file.tasks) - } - } - } - } - - onFinished(files?: File[]): void { - const duration = Date.now() - this.startTime - this.printSummary(duration) - } - - onTaskUpdate(packs: TaskResultPack[], events: TaskEventPack[]): void { - for (const [id, result, meta] of packs) { - if (!result) continue - - // Find the task in the state manager - if (!this.ctx?.state) continue - - try { - const entity = this.ctx.state.getReportedEntity(id as unknown as Task) - if (!entity || !('type' in entity) || entity.type !== 'test') continue - - // Safely access file properties - if (!('file' in entity) || !entity.file) continue - - const file = entity.file as { filepath: string; name: string } - const filepath = file.filepath - const fileName = file.name - - // Get file results (should already be initialized in onCollected) - if (!this.fileResults.has(filepath)) { - // If for some reason the file wasn't processed in onCollected, initialize it now - this.fileResults.set(filepath, { passed: 0, failed: 0, skipped: 0 }) - if (!this.testFiles.includes(filepath)) { - this.testFiles.push(filepath) - } - } - - const fileResult = this.fileResults.get(filepath)! - - // Only handle failures - we already counted passes during collection - if (result.state === 'fail') { - // Update counts: decrement passed, increment failed - fileResult.passed-- - fileResult.failed++ - this.testCounts.passed-- - this.testCounts.failed++ - - // Track the failed test - this.failedTests.push({ - file: fileName, - name: entity.name, - error: result.errors?.[0]?.message || 'Unknown error' - }) - } - } catch (error) { - console.warn(`Error processing task ${id}:`, error) - } - } - } - - private printSummary(duration: number): void { - const durationStr = this.formatDuration(duration) - - console.log('\n') - console.log('━'.repeat(80)) - console.log(`\n\x1b[1m\x1b[36m📊 TEST SUMMARY REPORT\x1b[0m`) - console.log('━'.repeat(80)) - - // Print overall stats - console.log(`\n\x1b[1mTest Run Completed in:\x1b[0m ${durationStr}`) - console.log(`\x1b[1mDate:\x1b[0m ${new Date().toLocaleString()}`) - console.log(`\x1b[1mTotal Test Files:\x1b[0m ${this.testFiles.length}`) - console.log(`\x1b[1mTotal Tests:\x1b[0m ${this.testCounts.total}`) - - // Print test status counts with colors and symbols - console.log(`\n\x1b[1mResults:\x1b[0m`) - console.log(` \x1b[32m✓ Passed:\x1b[0m ${this.testCounts.passed}`) - console.log(` \x1b[31m✗ Failed:\x1b[0m ${this.testCounts.failed}`) - console.log(` \x1b[33m○ Skipped:\x1b[0m ${this.testCounts.skipped}`) - - // Print file results in a table format - console.log('\n\x1b[1mTest Files:\x1b[0m') - console.log('┌' + '─'.repeat(50) + '┬' + '─'.repeat(10) + '┬' + '─'.repeat(10) + '┬' + '─'.repeat(10) + '┐') - console.log('│ \x1b[1mFile\x1b[0m' + ' '.repeat(46) + '│ \x1b[1mPassed\x1b[0m' + ' '.repeat(4) + '│ \x1b[1mFailed\x1b[0m' + ' '.repeat(4) + '│ \x1b[1mSkipped\x1b[0m' + ' '.repeat(3) + '│') - console.log('├' + '─'.repeat(50) + '┼' + '─'.repeat(10) + '┼' + '─'.repeat(10) + '┼' + '─'.repeat(10) + '┤') - - // Sort files by name for consistent output - const sortedFiles = Array.from(this.fileResults.entries()).sort((a, b) => { - const fileNameA = a[0].split('/').pop() || '' - const fileNameB = b[0].split('/').pop() || '' - return fileNameA.localeCompare(fileNameB) - }) - - for (const [filepath, results] of sortedFiles) { - const fileName = filepath.split('/').pop() || filepath - const truncatedName = this.truncateString(fileName, 48) - const passedStr = `\x1b[32m${results.passed}\x1b[0m` - const failedStr = results.failed > 0 ? `\x1b[31m${results.failed}\x1b[0m` : `${results.failed}` - const skippedStr = results.skipped > 0 ? `\x1b[33m${results.skipped}\x1b[0m` : `${results.skipped}` - - console.log(`│ ${truncatedName}${' '.repeat(50 - truncatedName.length)}│ ${passedStr}${' '.repeat(10 - passedStr.length + 9)}│ ${failedStr}${' '.repeat(10 - failedStr.length + 9)}│ ${skippedStr}${' '.repeat(10 - skippedStr.length + 9)}│`) - } - - console.log('└' + '─'.repeat(50) + '┴' + '─'.repeat(10) + '┴' + '─'.repeat(10) + '┴' + '─'.repeat(10) + '┘') - - // Print failed tests if any - if (this.failedTests.length > 0) { - console.log('\n\x1b[1m\x1b[31m❌ Failed Tests:\x1b[0m') - for (let i = 0; i < this.failedTests.length; i++) { - const { file, name, error } = this.failedTests[i] - console.log(`\n ${i + 1}. \x1b[1m${file}\x1b[0m: ${name}`) - console.log(` \x1b[31m${error}\x1b[0m`) - } - } - - // Print final status - console.log('\n') - if (this.testCounts.failed > 0) { - console.log('\x1b[41m\x1b[37m FAILED \x1b[0m Some tests failed. Check the report above for details.') - } else if (this.testCounts.total === 0) { - console.log('\x1b[43m\x1b[30m WARNING \x1b[0m No tests were run!') - } else { - console.log('\x1b[42m\x1b[30m PASSED \x1b[0m All tests passed successfully!') - } - console.log('\n') - } - - private formatDuration(ms: number): string { - if (ms < 1000) { - return `${ms}ms` - } - const seconds = Math.floor(ms / 1000) - const minutes = Math.floor(seconds / 60) - const remainingSeconds = seconds % 60 - - if (minutes === 0) { - return `${seconds}.${Math.floor((ms % 1000) / 100)}s` - } - - return `${minutes}m ${remainingSeconds}s` - } - - private truncateString(str: string, maxLength: number): string { - if (str.length <= maxLength) { - return str - } - return str.substring(0, maxLength - 3) + '...' - } -} diff --git a/src/utils/index.ts b/src/utils/index.ts index 09fa9c5f..3308f9f3 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -4,3 +4,4 @@ export * from './workerUtils.js' export * from './statistics.js' export * from './jsonProcessing.js' export * from './fieldNameTracking.js' +export * from './version.js' diff --git a/src/utils/tensorflowUtils.ts b/src/utils/tensorflowUtils.ts deleted file mode 100644 index d0139476..00000000 --- a/src/utils/tensorflowUtils.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Utility functions for TensorFlow.js compatibility - * This module provides utility functions that TensorFlow.js might need - * and avoids the need to use globalThis.util - */ - -// Import the TensorFlowUtilObject interface -import type { - TensorFlowUtilObject, - PlatformNodeObject -} from '../types/tensorflowTypes.js' - -// Note: TensorFlow.js platform patch is applied in setup.ts -// This ensures the global PlatformNode class uses our text encoding utilities - -/** - * Check if an array is a Float32Array - * @param arr - The array to check - * @returns True if the array is a Float32Array - */ -export function isFloat32Array(arr: unknown): boolean { - return !!( - arr instanceof Float32Array || - (arr && Object.prototype.toString.call(arr) === '[object Float32Array]') - ) -} - -/** - * Check if an array is a TypedArray - * @param arr - The array to check - * @returns True if the array is a TypedArray - */ -export function isTypedArray(arr: unknown): boolean { - return !!(ArrayBuffer.isView(arr) && !(arr instanceof DataView)) -} diff --git a/src/utils/version.ts b/src/utils/version.ts new file mode 100644 index 00000000..03ede225 --- /dev/null +++ b/src/utils/version.ts @@ -0,0 +1,26 @@ +/** + * Version utilities for Brainy + */ + +// Package version - this should be updated during the build process +const BRAINY_VERSION = '0.41.0' + +/** + * Get the current Brainy package version + * @returns The current version string + */ +export function getBrainyVersion(): string { + return BRAINY_VERSION +} + +/** + * Get version information for augmentation metadata + * @param service The service/augmentation name + * @returns Version metadata object + */ +export function getAugmentationVersion(service: string): { augmentation: string; version: string } { + return { + augmentation: service, + version: getBrainyVersion() + } +} \ No newline at end of file