**feat(patch): enhance TextEncoder/TextDecoder compatibility for TensorFlow.js**
- Refactored `applyTensorFlowPatch` in `textEncoding.ts` to introduce a unified `Platform` class supporting both Node.js and browser environments with native `TextEncoder`/`TextDecoder`. - Modified global object handling to target `PlatformNode` in Node.js and `PlatformBrowser` in browser environments. - Added `getTextEncoder` and `getTextDecoder` utility functions to simplify text encoding/decoding across platforms. - Introduced `setup.ts` to apply environment patches before other modules load. - Updated CLI entry points (`cli.ts`, `rollup.config.js`) to ensure patching precedes TensorFlow.js usage. - Enhanced test coverage for TextEncoder compatibility with `test-fix.js`. - Adjusted imports in `test-tensorflow-textencoder.ts` to align with the updated `./utils/textEncoding.js`. - Streamlined constructor definitions in `Platform` for improved maintainability. This update ensures robust compatibility for TensorFlow.js by patching and standardizing text encoding/decoding functionality across environments.
This commit is contained in:
parent
5f267b14ed
commit
e3d7398022
7 changed files with 142 additions and 84 deletions
|
|
@ -7,6 +7,7 @@ import { terser } from 'rollup-plugin-terser'
|
||||||
// CLI configuration
|
// CLI configuration
|
||||||
export default {
|
export default {
|
||||||
input: 'src/cli.ts',
|
input: 'src/cli.ts',
|
||||||
|
context: 'this', // Preserve 'this' context to fix TensorFlow.js issue
|
||||||
output: {
|
output: {
|
||||||
dir: 'dist',
|
dir: 'dist',
|
||||||
entryFileNames: 'cli.js',
|
entryFileNames: 'cli.js',
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,11 @@
|
||||||
* A command-line interface for interacting with the Brainy vector database
|
* A command-line interface for interacting with the Brainy vector database
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Import the setup file for its side-effects.
|
||||||
|
// This MUST be the very first import to ensure patches are applied
|
||||||
|
// before any other module (like TensorFlow.js) is loaded.
|
||||||
|
import './setup.js'
|
||||||
|
|
||||||
// Log environment information
|
// Log environment information
|
||||||
console.log('Brainy running in Node.js environment')
|
console.log('Brainy running in Node.js environment')
|
||||||
|
|
||||||
|
|
|
||||||
12
cli-package/src/setup.ts
Normal file
12
cli-package/src/setup.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
/**
|
||||||
|
* This file is imported for its side effects to patch the environment
|
||||||
|
* for TensorFlow.js before any other library code runs.
|
||||||
|
*
|
||||||
|
* It ensures that by the time TensorFlow.js is imported by any other
|
||||||
|
* module, the necessary compatibility fixes for the current Node.js
|
||||||
|
* environment are already in place.
|
||||||
|
*/
|
||||||
|
import { applyTensorFlowPatch } from './utils/textEncoding.js'
|
||||||
|
|
||||||
|
// Apply the TensorFlow.js platform patch if needed
|
||||||
|
applyTensorFlowPatch()
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
import {
|
import {
|
||||||
getTextEncoder,
|
getTextEncoder,
|
||||||
getTextDecoder
|
getTextDecoder
|
||||||
} from '@soulcraft/brainy/dist/utils/textEncoding.js'
|
} from './utils/textEncoding.js'
|
||||||
import * as tf from '@tensorflow/tfjs'
|
import * as tf from '@tensorflow/tfjs'
|
||||||
import '@tensorflow/tfjs-backend-cpu'
|
import '@tensorflow/tfjs-backend-cpu'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,101 +1,94 @@
|
||||||
/**
|
/**
|
||||||
* Unified Text Encoding Utilities for CLI
|
* Unified Text Encoding Utilities
|
||||||
*
|
*
|
||||||
* This module provides a consistent way to handle text encoding/decoding across all environments
|
* This module provides a consistent way to handle text encoding/decoding across all environments
|
||||||
* using the native TextEncoder/TextDecoder APIs.
|
* using the native TextEncoder/TextDecoder APIs.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a text encoder that works in the current environment
|
||||||
|
* @returns A TextEncoder instance
|
||||||
|
*/
|
||||||
|
export function getTextEncoder(): TextEncoder {
|
||||||
|
return new TextEncoder()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a text decoder that works in the current environment
|
||||||
|
* @returns A TextDecoder instance
|
||||||
|
*/
|
||||||
|
export function getTextDecoder(): TextDecoder {
|
||||||
|
return new TextDecoder()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply the TensorFlow.js platform patch if needed
|
* Apply the TensorFlow.js platform patch if needed
|
||||||
* This function patches the global object to provide a PlatformNode class
|
* This function patches the global object to provide a PlatformNode class
|
||||||
* that uses native TextEncoder/TextDecoder
|
* that uses native TextEncoder/TextDecoder
|
||||||
*/
|
*/
|
||||||
export function applyTensorFlowPatch(): void {
|
export function applyTensorFlowPatch(): void {
|
||||||
// Only apply in Node.js environment
|
try {
|
||||||
if (
|
// Define a custom Platform class that works in both Node.js and browser environments
|
||||||
typeof global !== 'undefined' &&
|
class Platform {
|
||||||
typeof process !== 'undefined' &&
|
util: any
|
||||||
process.versions &&
|
textEncoder: TextEncoder
|
||||||
process.versions.node
|
textDecoder: TextDecoder
|
||||||
) {
|
|
||||||
try {
|
|
||||||
// Define a custom PlatformNode class
|
|
||||||
class PlatformNode {
|
|
||||||
util: any
|
|
||||||
textEncoder: TextEncoder
|
|
||||||
textDecoder: TextDecoder
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// Create a util object with necessary methods and constructors
|
// Create a util object with necessary methods and constructors
|
||||||
this.util = {
|
this.util = {
|
||||||
// Add isFloat32Array and isTypedArray directly to util
|
// Use native TextEncoder and TextDecoder
|
||||||
isFloat32Array: (arr: any) => {
|
TextEncoder: globalThis.TextEncoder || TextEncoder,
|
||||||
return !!(
|
TextDecoder: globalThis.TextDecoder || TextDecoder
|
||||||
arr instanceof Float32Array ||
|
|
||||||
(arr &&
|
|
||||||
Object.prototype.toString.call(arr) ===
|
|
||||||
'[object Float32Array]')
|
|
||||||
)
|
|
||||||
},
|
|
||||||
isTypedArray: (arr: any) => {
|
|
||||||
return !!(ArrayBuffer.isView(arr) && !(arr instanceof DataView))
|
|
||||||
},
|
|
||||||
// Use native TextEncoder and TextDecoder
|
|
||||||
TextEncoder: TextEncoder,
|
|
||||||
TextDecoder: TextDecoder
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize using native constructors
|
|
||||||
this.textEncoder = new TextEncoder()
|
|
||||||
this.textDecoder = new TextDecoder()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define isFloat32Array directly on the instance
|
// Initialize using native constructors directly
|
||||||
isFloat32Array(arr: any) {
|
this.textEncoder = new (globalThis.TextEncoder || TextEncoder)()
|
||||||
return !!(
|
this.textDecoder = new (globalThis.TextDecoder || TextDecoder)()
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign the PlatformNode class to the global object
|
// Define isFloat32Array directly on the instance
|
||||||
;(global as any).PlatformNode = PlatformNode
|
isFloat32Array(arr: any) {
|
||||||
|
return !!(
|
||||||
// Also create an instance and assign it to global.platformNode (lowercase p)
|
arr instanceof Float32Array ||
|
||||||
;(global as any).platformNode = new PlatformNode()
|
(arr &&
|
||||||
|
Object.prototype.toString.call(arr) === '[object Float32Array]')
|
||||||
// Ensure global.util exists and has the necessary methods
|
)
|
||||||
// This is needed because TensorFlow.js might look for these methods in global.util
|
|
||||||
if (!(global as any).util) {
|
|
||||||
;(global as any).util = {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add isFloat32Array method if it doesn't exist
|
// Define isTypedArray directly on the instance
|
||||||
if (!(global as any).util.isFloat32Array) {
|
isTypedArray(arr: any) {
|
||||||
;(global as any).util.isFloat32Array = (arr: any) => {
|
return !!(ArrayBuffer.isView(arr) && !(arr instanceof DataView))
|
||||||
return !!(
|
|
||||||
arr instanceof Float32Array ||
|
|
||||||
(arr &&
|
|
||||||
Object.prototype.toString.call(arr) === '[object Float32Array]')
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add isTypedArray method if it doesn't exist
|
|
||||||
if (!(global as any).util.isTypedArray) {
|
|
||||||
;(global as any).util.isTypedArray = (arr: any) => {
|
|
||||||
return !!(ArrayBuffer.isView(arr) && !(arr instanceof DataView))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('Failed to apply TensorFlow.js platform patch:', error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the global object in a way that works in both Node.js and browser
|
||||||
|
const globalObj =
|
||||||
|
typeof global !== 'undefined'
|
||||||
|
? global
|
||||||
|
: typeof window !== 'undefined'
|
||||||
|
? window
|
||||||
|
: typeof self !== 'undefined'
|
||||||
|
? self
|
||||||
|
: {}
|
||||||
|
|
||||||
|
// Only apply in Node.js environment
|
||||||
|
if (
|
||||||
|
typeof process !== 'undefined' &&
|
||||||
|
process.versions &&
|
||||||
|
process.versions.node
|
||||||
|
) {
|
||||||
|
// Assign the Platform class to the global object as PlatformNode for Node.js
|
||||||
|
;(globalObj as any).PlatformNode = Platform
|
||||||
|
// Also create an instance and assign it to global.platformNode (lowercase p)
|
||||||
|
;(globalObj as any).platformNode = new Platform()
|
||||||
|
} else if (typeof window !== 'undefined' || typeof self !== 'undefined') {
|
||||||
|
// In browser environments, we might need to provide similar functionality
|
||||||
|
// but we'll use a different name to avoid conflicts
|
||||||
|
;(globalObj as any).PlatformBrowser = Platform
|
||||||
|
;(globalObj as any).platformBrowser = new Platform()
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to apply TensorFlow.js platform patch:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,13 +38,13 @@ export function applyTensorFlowPatch(): void {
|
||||||
// Create a util object with necessary methods and constructors
|
// Create a util object with necessary methods and constructors
|
||||||
this.util = {
|
this.util = {
|
||||||
// Use native TextEncoder and TextDecoder
|
// Use native TextEncoder and TextDecoder
|
||||||
TextEncoder: TextEncoder,
|
TextEncoder: globalThis.TextEncoder || TextEncoder,
|
||||||
TextDecoder: TextDecoder
|
TextDecoder: globalThis.TextDecoder || TextDecoder
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize using native constructors
|
// Initialize using native constructors directly
|
||||||
this.textEncoder = new TextEncoder()
|
this.textEncoder = new (globalThis.TextEncoder || TextEncoder)()
|
||||||
this.textDecoder = new TextDecoder()
|
this.textDecoder = new (globalThis.TextDecoder || TextDecoder)()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define isFloat32Array directly on the instance
|
// Define isFloat32Array directly on the instance
|
||||||
|
|
|
||||||
47
test-fix.js
Normal file
47
test-fix.js
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Test script to verify the TextEncoder fix
|
||||||
|
import { applyTensorFlowPatch } from './dist/utils/textEncoding.js'
|
||||||
|
|
||||||
|
console.log('Testing TextEncoder fix...')
|
||||||
|
|
||||||
|
// Apply the TensorFlow.js platform patch
|
||||||
|
applyTensorFlowPatch()
|
||||||
|
|
||||||
|
// Check if PlatformNode is defined in the global object
|
||||||
|
if (typeof global.PlatformNode === 'function') {
|
||||||
|
console.log('PlatformNode is defined in the global object')
|
||||||
|
|
||||||
|
// Create an instance of PlatformNode
|
||||||
|
try {
|
||||||
|
const platform = new global.PlatformNode()
|
||||||
|
console.log('Successfully created PlatformNode instance')
|
||||||
|
|
||||||
|
// Check if textEncoder is defined
|
||||||
|
if (platform.textEncoder) {
|
||||||
|
console.log('textEncoder is defined')
|
||||||
|
|
||||||
|
// Test encoding a string
|
||||||
|
const testString = 'Hello, world! 👋'
|
||||||
|
const encoded = platform.textEncoder.encode(testString)
|
||||||
|
console.log(`Successfully encoded string: ${testString}`)
|
||||||
|
console.log(`Encoded: [${encoded}]`)
|
||||||
|
|
||||||
|
// Test decoding
|
||||||
|
const decoded = platform.textDecoder.decode(encoded)
|
||||||
|
console.log(`Successfully decoded back to: ${decoded}`)
|
||||||
|
|
||||||
|
if (testString === decoded) {
|
||||||
|
console.log('✅ TextEncoder/TextDecoder test passed!')
|
||||||
|
} else {
|
||||||
|
console.error('❌ TextEncoder/TextDecoder test failed!')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('textEncoder is not defined in the platform instance')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating PlatformNode instance:', error)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('PlatformNode is not defined in the global object')
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Test completed')
|
||||||
Loading…
Add table
Add a link
Reference in a new issue