UNPKG

openvino-genai-node

Version:

OpenVINO™ GenAI pipelines for using from Node.js environment

165 lines 6.96 kB
// Copyright (C) 2023-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 import util from "node:util"; import { WhisperPipeline as WhisperPipelineWrapper } from "../addon.js"; import { WhisperDecodedResults, } from "../decodedResults.js"; import { StreamingStatus } from "../utils.js"; /** * Pipeline for automatic speech recognition using Whisper models. * * Expects raw audio normalized to approximately [-1, 1] at 16 kHz sample rate. * Use a WAV file or decode audio to Float32Array before calling generate(). */ export class WhisperPipeline { /** * Construct a Whisper pipeline from a folder containing model IRs and tokenizer. * @param modelPath - Path to the folder with model IRs and tokenizer (e.g. openvino_encoder_model.xml, preprocessor_config.json). * @param device - Inference device (e.g. "CPU", "GPU"). * @param properties - Device and pipeline properties (e.g. word_timestamps: true, CACHE_DIR: "cache"). */ constructor(modelPath, device, properties = {}) { this.pipeline = null; this.modelPath = modelPath; this.device = device; this.properties = properties; } /** * Load the pipeline. Must be called once before generate(). */ async init() { const pipeline = new WhisperPipelineWrapper(); const initPromise = util.promisify(pipeline.init.bind(pipeline)); await initPromise(this.modelPath, this.device, this.properties); this.pipeline = pipeline; } /** * Stream speech recognition results as an async iterator. * The iterator yields decoded text chunks during generation. * When generation finishes, the full decoded text is returned as the final * iterator value (`done: true`). This value is not available through * `for await...of`; call `next()` directly to read it. * * For custom streaming control, use {@link generate} with a streamer callback instead. * * @param rawSpeech - Audio samples as Float32Array or number[], normalized to ~[-1, 1], 16 kHz. * @param options - Optional generation config (e.g. language, task, return_timestamps). * @returns Async iterator that yields decoded text chunks as strings. */ stream(rawSpeech, options) { var _a; if (!this.pipeline) throw new Error("WhisperPipeline is not initialized"); const generationConfig = (_a = options === null || options === void 0 ? void 0 : options.generationConfig) !== null && _a !== void 0 ? _a : {}; let streamingStatus = StreamingStatus.RUNNING; let handledError; const queue = []; let resolvePromise = null; let rejectPromise = null; const callback = (error, result) => { var _a, _b; if (error) { if (rejectPromise) { rejectPromise(error); resolvePromise = null; rejectPromise = null; } else { handledError = error; } } else { const fullText = (_b = (_a = result.texts) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : ""; if (resolvePromise) { resolvePromise({ done: true, value: fullText }); resolvePromise = null; rejectPromise = null; } else { queue.push({ done: true, chunk: fullText }); } } }; const streamer = (chunk) => { if (resolvePromise) { resolvePromise({ done: false, value: chunk }); resolvePromise = null; rejectPromise = null; } else { queue.push({ done: false, chunk }); } return streamingStatus; }; this.pipeline.generate(rawSpeech, generationConfig, streamer, callback); return { async next() { if (handledError) { const error = handledError; handledError = null; return Promise.reject(error); } const data = queue.shift(); if (data) { return { value: data.chunk, done: data.done }; } return new Promise((resolve, reject) => { resolvePromise = resolve; rejectPromise = reject; }); }, async return() { streamingStatus = StreamingStatus.CANCEL; return { done: true, value: "" }; }, [Symbol.asyncIterator]() { return this; }, }; } /** * Run speech recognition with optional streaming. * * For simple streaming use cases, consider using {@link stream}, which provides * a convenient async iterator interface. * * @param rawSpeech - Audio samples as Float32Array or number[], normalized to ~[-1, 1], 16 kHz. * @param options - Optional parameters. * @param options.generationConfig - Generation config (e.g., language, task, return_timestamps). * @param options.streamer - Optional callback invoked for each decoded chunk. * - Return a `StreamingStatus` flag to indicate whether generation should be stopped or cancelled * @returns Decoded texts, scores, optional chunks with timestamps, and perf metrics. */ async generate(rawSpeech, options = {}) { if (!this.pipeline) throw new Error("WhisperPipeline is not initialized"); const { generationConfig, streamer } = options; const generatePromise = util.promisify(this.pipeline.generate.bind(this.pipeline)); const res = await generatePromise(rawSpeech, generationConfig !== null && generationConfig !== void 0 ? generationConfig : {}, streamer); return new WhisperDecodedResults(res.texts, res.scores, res.perfMetrics, res.chunks, res.words); } /** * Get the pipeline tokenizer. */ getTokenizer() { if (!this.pipeline) throw new Error("WhisperPipeline is not initialized"); return this.pipeline.getTokenizer(); } /** * Get current generation config (language, task, return_timestamps, etc.). */ getGenerationConfig() { if (!this.pipeline) throw new Error("WhisperPipeline is not initialized"); return this.pipeline.getGenerationConfig(); } /** * Update generation config (e.g. language, task, return_timestamps). */ setGenerationConfig(config) { if (!this.pipeline) throw new Error("WhisperPipeline is not initialized"); this.pipeline.setGenerationConfig(config); } } //# sourceMappingURL=whisperPipeline.js.map