@nodify_at/hailo.js
Version:
High-performance Node.js bindings for Hailo AI acceleration processors (NPU). Run neural network inference with hardware acceleration on Hailo-8 devices.
186 lines • 5.88 kB
JavaScript
import { createRequire } from 'node:module';
/**
* HailoDevice - High-level wrapper for Hailo AI inference
* Provides a clean, type-safe interface with performance monitoring
*/
import { EventEmitter } from 'events';
import { ModelType, } from './types.js';
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { join } from 'path';
// Create require for CommonJS modules
const require = createRequire(import.meta.url);
const __dirname = dirname(fileURLToPath(import.meta.url));
// Load node-gyp-build and cast it
const nodeGypBuild = require('node-gyp-build');
const addon = nodeGypBuild(join(__dirname, '..'));
/**
* High-level Hailo device interface with enhanced functionality
*
* @example
* ```typescript
* const device = new HailoDevice();
* await device.loadModel({
* type: ModelType.YOLOV8,
* path: './models/yolov8n.hef',
* numClasses: 80
* });
*
* const output = await device.infer(inputTensor);
* ```
*/
export class HailoDevice extends EventEmitter {
device;
modelConfig;
performanceBuffer = [];
options;
constructor(options = {}) {
super();
this.options = { debug: false, deviceIndex: 0, ...options };
// Create native device instance
this.device = new addon.HailoDevice();
if (this.options.debug) {
this.on('debug', msg => console.log(`[HailoDevice] ${msg}`));
}
}
/**
* Load a model with configuration
* Supports automatic model type detection from filename
*/
async loadModel(config) {
const startTime = performance.now();
// Handle string path with auto-detection
if (typeof config === 'string') {
config = this.detectModelConfig(config);
}
this.modelConfig = config;
this.emit('debug', `Loading model: ${config.path} (type: ${config.type})`);
try {
const success = await this.device.loadModel(config.path);
if (!success) {
throw new Error('Failed to load model');
}
const loadTime = performance.now() - startTime;
this.emit('modelLoaded', { config, loadTime, info: this.device.getModelInfo() });
this.emit('debug', `Model loaded in ${loadTime.toFixed(2)}ms`);
}
catch (error) {
this.emit('error', error);
throw error;
}
}
/**
* Run inference with performance tracking
*/
async infer(inputs) {
if (!this.device.isActive()) {
throw new Error('No model loaded');
}
const metrics = { inferenceTime: 0, totalTime: 0, fps: 0 };
const startTime = performance.now();
try {
// Run inference
const inferStart = performance.now();
const outputs = await this.device.infer(inputs);
metrics.inferenceTime = performance.now() - inferStart;
metrics.totalTime = performance.now() - startTime;
metrics.fps = 1000 / metrics.totalTime;
// Track performance
this.trackPerformance(metrics);
this.emit('inference', { outputs, metrics });
return outputs;
}
catch (error) {
this.emit('error', error);
throw error;
}
}
/**
* Get model information
*/
getModelInfo() {
return this.device.getModelInfo();
}
/**
* Check if device is ready for inference
*/
isReady() {
return this.device.isActive();
}
/**
* Get average performance metrics
*/
getPerformanceStats() {
if (this.performanceBuffer.length === 0) {
return { avgInferenceTime: 0, avgFps: 0, minInferenceTime: 0, maxInferenceTime: 0 };
}
const times = this.performanceBuffer.map(m => m.inferenceTime);
const sum = times.reduce((a, b) => a + b, 0);
return {
avgInferenceTime: sum / times.length,
avgFps: this.performanceBuffer.reduce((a, b) => a + b.fps, 0) / this.performanceBuffer.length,
minInferenceTime: Math.min(...times),
maxInferenceTime: Math.max(...times),
};
}
/**
* Reset performance tracking
*/
resetPerformanceStats() {
this.performanceBuffer = [];
}
/**
* Get current model configuration
*/
getModelConfig() {
return this.modelConfig;
}
/**
* Detect model configuration from filename
*/
detectModelConfig(path) {
const filename = path.toLowerCase();
let type = ModelType.CUSTOM;
let numClasses = 80; // Default COCO classes
if (filename.includes('yolov8') || filename.includes('yolo8')) {
type = ModelType.YOLOV8;
}
else if (filename.includes('yolox')) {
type = ModelType.YOLOX;
}
else if (filename.includes('yolo')) {
type = ModelType.YOLO;
}
// Try to detect number of classes from filename
const classMatch = filename.match(/(\d+)classes?/);
if (classMatch) {
numClasses = parseInt(classMatch[1], 10);
}
return { type, path, numClasses };
}
/**
* Track performance metrics with sliding window
*/
trackPerformance(metrics) {
this.performanceBuffer.push(metrics);
// Keep last 100 measurements
if (this.performanceBuffer.length > 100) {
this.performanceBuffer.shift();
}
}
/**
* Static method to scan for available devices
*/
static async scanDevices() {
return addon.scanDevices();
}
/**
* Get addon version
*/
static get version() {
return addon.version;
}
}
// Re-export types
export * from './types.js';
//# sourceMappingURL=hailo-device.js.map