ngx-transformers 0.3.1 → 0.4.0

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/README.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # ngx-transformers
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/ngx-transformers)](https://www.npmjs.com/package/ngx-transformers)
4
+ [![npm downloads](https://img.shields.io/npm/dw/ngx-transformers)](https://www.npmjs.com/package/ngx-transformers)
4
5
  [![CI](https://github.com/qwertymuzaffar/ngx-transformers/actions/workflows/ci.yml/badge.svg)](https://github.com/qwertymuzaffar/ngx-transformers/actions/workflows/ci.yml)
5
6
  [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6
7
 
7
- Run Hugging Face [Transformers.js](https://github.com/huggingface/transformers.js) models in Angular - **on-device ML with a signals API**. Text classification, zero-shot classification, sentence embeddings, semantic search, translation, and Whisper speech-to-text that execute entirely in the browser: no server, no API key, works offline once the model is cached.
8
+ Run Hugging Face [Transformers.js](https://github.com/huggingface/transformers.js) models in Angular - **on-device ML with a signals API**. Text classification, zero-shot classification, sentence embeddings, semantic search, translation, Whisper speech-to-text and small LLMs with streaming, executed entirely in the browser (in a Web Worker if you like): no server, no API key, works offline once the model is cached.
8
9
 
9
10
  **[Documentation](https://qwertymuzaffar.github.io/ngx-transformers/)** · [Demo app](https://qwertymuzaffar.github.io/ngx-transformers/demo/) · [Storybook](https://qwertymuzaffar.github.io/ngx-transformers/storybook/) - the demos load real models in your browser.
10
11
 
@@ -133,6 +134,54 @@ readonly translator = createTranslator({ model: 'Xenova/nllb-200-distilled-600M'
133
134
  await this.translator.translate(text, { from: 'eng_Latn', to: 'tgk_Cyrl' }); // src_lang / tgt_lang forwarded
134
135
  ```
135
136
 
137
+ ## Text generation
138
+
139
+ A small language model, streamed token by token:
140
+
141
+ ```ts
142
+ import { createTextGenerator } from 'ngx-transformers';
143
+
144
+ readonly generator = createTextGenerator(); // SmolLM2-135M-Instruct, ~100 MB q4
145
+
146
+ const reply = await this.generator.generate(
147
+ [{ role: 'user', content: 'Explain signals in Angular in two sentences.' }],
148
+ { maxNewTokens: 120, onToken: (piece) => console.log(piece) },
149
+ );
150
+ // generator.output() holds the text generated so far while the model runs
151
+ ```
152
+
153
+ ## Reactive inference
154
+
155
+ `inferenceResource()` runs a handle whenever an input signal changes, as an Angular resource with debounce and latest-wins:
156
+
157
+ ```ts
158
+ readonly text = signal('');
159
+ readonly sentiment = inferenceResource({
160
+ input: () => this.text().trim() || undefined, // undefined: idle
161
+ run: (text) => this.classifier.classify(text),
162
+ debounceMs: 300,
163
+ });
164
+ // template: sentiment.value(), sentiment.isLoading(), sentiment.error()
165
+ ```
166
+
167
+ ## Web Worker
168
+
169
+ One provider moves every pipeline off the main thread; handles and signals work unchanged:
170
+
171
+ ```ts
172
+ // transformers.worker.ts
173
+ /// <reference lib="webworker" />
174
+ import { runTransformersWorker } from 'ngx-transformers/worker';
175
+ runTransformersWorker();
176
+
177
+ // app.config.ts
178
+ provideTransformersWorker(
179
+ () => new Worker(new URL('./transformers.worker', import.meta.url), { type: 'module' }),
180
+ );
181
+ ```
182
+
183
+ `ngx-transformers/worker` has no Angular dependency, so the worker chunk stays tiny; `@huggingface/transformers` loads inside the worker only.
184
+
136
185
  ## Any pipeline
137
186
 
138
187
  `createPipeline()` exposes the full Transformers.js task surface with the same signal lifecycle:
@@ -188,6 +237,9 @@ bootstrapApplication(App, {
188
237
  | `createSpeechRecognizer(options?)` | `SpeechRecognizer` - `transcribe(audio, options?)` with timestamps |
189
238
  | `createZeroShotClassifier(options?)` | `ZeroShotClassifier` - `classify(text, labels, { multiLabel?, hypothesisTemplate? })` |
190
239
  | `createTranslator(options?)` | `Translator` - `translate(text, { from?, to? })`, one model per pair, `handleFor(pair)` |
240
+ | `createTextGenerator(options?)` | `TextGenerator` - `generate(prompt, { maxNewTokens?, onToken?, ... })`, streamed into `output` |
241
+ | `inferenceResource({ input, run, debounceMs? })` | An Angular resource that re-runs a handle when an input signal changes |
242
+ | `provideTransformersWorker(createWorker)` | Run every pipeline in a Web Worker (`ngx-transformers/worker` runs the worker side) |
191
243
  | `createMicRecorder(deps?)` | `MicRecorder` - mic capture with `recording`/`seconds`/`error` signals |
192
244
  | `cosineSimilarity(a, b)` / `decodeAudio(blob)` | Standalone helpers |
193
245
  | `PIPELINE_FACTORY` / `createDefaultPipelineFactory()` | Swap or wrap how pipelines are created |
@@ -199,8 +251,9 @@ All `create*` functions must run in an injection context (field initializer, con
199
251
  | Signal | Type | Meaning |
200
252
  |---|---|---|
201
253
  | `status` | `'idle' \| 'loading' \| 'ready' \| 'busy' \| 'error'` | Lifecycle; `error` only from a failed load, retryable |
202
- | `progress` | `ModelProgress \| null` | Download progress: `file`, `progress` (0-100), bytes |
254
+ | `progress` | `ModelProgress \| null` | Download progress: the file reported last plus `overall` over every file of the model |
203
255
  | `error` | `unknown` | The load error, if any |
256
+ | `runError` | `unknown` | The error of the most recent run, if it failed |
204
257
  | `ready` / `busy` | `boolean` (computed) | Convenience for buttons and spinners |
205
258
 
206
259
  ### `<ngx-model-progress>`
@@ -216,6 +269,7 @@ Status line + download bar for any handle. Inputs: `status` (required), `progres
216
269
  | `createSpeechRecognizer` | [onnx-community/whisper-tiny.en](https://huggingface.co/onnx-community/whisper-tiny.en) | ~41 MB (q4) | Apache-2.0 |
217
270
  | `createZeroShotClassifier` | [Xenova/mobilebert-uncased-mnli](https://huggingface.co/Xenova/mobilebert-uncased-mnli) | ~26 MB | unlisted on the Hub (base MobileBERT: Apache-2.0) |
218
271
  | `createTranslator` | [Xenova/opus-mt-{from}-{to}](https://huggingface.co/models?search=Xenova/opus-mt) | ~105 MB per pair | varies per pair (Apache-2.0 or CC-BY-4.0) |
272
+ | `createTextGenerator` | [HuggingFaceTB/SmolLM2-135M-Instruct](https://huggingface.co/HuggingFaceTB/SmolLM2-135M-Instruct) | ~100 MB (q4) | Apache-2.0 |
219
273
 
220
274
  Swap any compatible checkpoint via `{ model: '...' }`. Check the license of the model you ship.
221
275
 
@@ -0,0 +1,307 @@
1
+ /**
2
+ * Messages between the main thread (client.ts) and the worker (host.ts).
3
+ * Every request carries an id the response echoes; pipelines get a pipeId.
4
+ */
5
+
6
+ /**
7
+ * Helpers shared by the in-thread and worker pipeline factories for the run
8
+ * options ngx-transformers adds on top of the Transformers.js ones.
9
+ */
10
+ const ON_TOKEN = 'onToken';
11
+ function isPlainObject(value) {
12
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
13
+ }
14
+ /**
15
+ * Pulls the `onToken` callback out of a pipeline call's trailing options
16
+ * object. Returns the arguments without it, plus the callback if present.
17
+ */
18
+ function takeOnToken(args) {
19
+ const last = args[args.length - 1];
20
+ if (!isPlainObject(last) || typeof last[ON_TOKEN] !== 'function')
21
+ return { args };
22
+ const { [ON_TOKEN]: onToken, ...rest } = last;
23
+ return { args: [...args.slice(0, -1), rest], onToken: onToken };
24
+ }
25
+ /** A shallow copy of an options object without its function-valued entries. */
26
+ function withoutFunctions(options) {
27
+ const out = {};
28
+ for (const [key, value] of Object.entries(options)) {
29
+ if (typeof value !== 'function')
30
+ out[key] = value;
31
+ }
32
+ return out;
33
+ }
34
+ /** Same as withoutFunctions() for the trailing options object of a call, if any. */
35
+ function withoutFunctionsInLast(args) {
36
+ const last = args[args.length - 1];
37
+ if (!isPlainObject(last))
38
+ return args;
39
+ return [...args.slice(0, -1), withoutFunctions(last)];
40
+ }
41
+
42
+ /** Tensor-like: the shape our TextEmbedder reads back on the main thread. */
43
+ function isTensorLike(value) {
44
+ return (typeof value === 'object' &&
45
+ value !== null &&
46
+ Array.isArray(value.dims) &&
47
+ ArrayBuffer.isView(value.data));
48
+ }
49
+ /**
50
+ * Turns a pipeline result into something structured cloning keeps intact:
51
+ * Tensors become { dims, data } with the buffer transferred, everything
52
+ * else (plain objects, arrays, strings) passes through unchanged.
53
+ */
54
+ function toCloneable(result) {
55
+ const transfer = [];
56
+ const convert = (value) => {
57
+ if (isTensorLike(value)) {
58
+ const data = value.data.slice();
59
+ transfer.push(data.buffer);
60
+ return { dims: [...value.dims], data };
61
+ }
62
+ if (Array.isArray(value))
63
+ return value.map(convert);
64
+ return value;
65
+ };
66
+ return { value: convert(result), transfer };
67
+ }
68
+ function errorResponse(id, err) {
69
+ const error = err;
70
+ return {
71
+ type: 'error',
72
+ id,
73
+ name: typeof error?.name === 'string' ? error.name : 'Error',
74
+ message: typeof error?.message === 'string' ? error.message : String(err),
75
+ };
76
+ }
77
+ /**
78
+ * The worker side of ngx-transformers/worker: creates pipelines on request,
79
+ * runs them, streams progress and generated tokens back, and disposes them.
80
+ * runTransformersWorker() wires it to the worker's message port; tests call
81
+ * handle() directly.
82
+ */
83
+ function createTransformersWorkerHost(options) {
84
+ const { post } = options;
85
+ const load = options.load ?? (() => import('@huggingface/transformers'));
86
+ const pipes = new Map();
87
+ let module = null;
88
+ let nextPipeId = 1;
89
+ // A failed import is not kept: the next create() tries again, so a load
90
+ // that failed on a network blip stays retryable, as PipelineHandle promises.
91
+ const loadModule = () => (module ??= load().catch((err) => {
92
+ module = null;
93
+ throw err;
94
+ }));
95
+ async function create(message) {
96
+ try {
97
+ const { pipeline } = await loadModule();
98
+ const pipe = (await pipeline(message.task, message.model, {
99
+ ...message.options,
100
+ progress_callback: (event) => post({ type: 'progress', id: message.id, event }),
101
+ }));
102
+ const pipeId = nextPipeId++;
103
+ pipes.set(pipeId, pipe);
104
+ post({ type: 'created', id: message.id, pipeId });
105
+ }
106
+ catch (err) {
107
+ post(errorResponse(message.id, err));
108
+ }
109
+ }
110
+ async function run(message) {
111
+ const pipe = pipes.get(message.pipeId);
112
+ if (!pipe) {
113
+ post(errorResponse(message.id, new Error(`Unknown pipeline ${message.pipeId}.`)));
114
+ return;
115
+ }
116
+ try {
117
+ let args = message.args;
118
+ if (message.stream) {
119
+ const { TextStreamer } = await loadModule();
120
+ if (TextStreamer) {
121
+ // The client already stripped onToken; the options object is last.
122
+ const options = { ...args[args.length - 1] };
123
+ options['streamer'] = new TextStreamer(pipe.tokenizer, {
124
+ skip_prompt: true,
125
+ skip_special_tokens: true,
126
+ callback_function: (text) => post({ type: 'token', id: message.id, text }),
127
+ });
128
+ args = [...args.slice(0, -1), options];
129
+ }
130
+ }
131
+ const { value, transfer } = toCloneable(await pipe(...args));
132
+ post({ type: 'result', id: message.id, result: value }, transfer);
133
+ }
134
+ catch (err) {
135
+ post(errorResponse(message.id, err));
136
+ }
137
+ }
138
+ return {
139
+ async handle(message) {
140
+ switch (message.type) {
141
+ case 'create':
142
+ return create(message);
143
+ case 'run':
144
+ return run(message);
145
+ case 'dispose': {
146
+ const pipe = pipes.get(message.pipeId);
147
+ pipes.delete(message.pipeId);
148
+ await pipe?.dispose?.();
149
+ return;
150
+ }
151
+ }
152
+ },
153
+ };
154
+ }
155
+ /**
156
+ * Entry point for the worker file of an app:
157
+ *
158
+ * ```ts
159
+ * // transformers.worker.ts
160
+ * /// <reference lib="webworker" />
161
+ * import { runTransformersWorker } from 'ngx-transformers/worker';
162
+ * runTransformersWorker();
163
+ * ```
164
+ *
165
+ * Pair it with provideTransformersWorker() on the main thread.
166
+ */
167
+ function runTransformersWorker(scope = workerGlobalScope(), load) {
168
+ const host = createTransformersWorkerHost({
169
+ post: (message, transfer) => scope.postMessage(message, transfer),
170
+ load,
171
+ });
172
+ scope.onmessage = (event) => void host.handle(event.data);
173
+ return host;
174
+ }
175
+ /**
176
+ * The current worker's global scope. On the main thread this would hook
177
+ * window.onmessage, which any origin can post to, so refuse instead.
178
+ */
179
+ function workerGlobalScope() {
180
+ const global = globalThis;
181
+ if (global.WorkerGlobalScope === undefined) {
182
+ throw new Error('runTransformersWorker() must run inside a Web Worker file; on the main thread use provideTransformersWorker().');
183
+ }
184
+ return globalThis;
185
+ }
186
+
187
+ /**
188
+ * A pipeline factory whose pipelines live in a Web Worker, so inference
189
+ * never blocks the UI thread. The worker runs runTransformersWorker() and
190
+ * is created lazily, on the first pipeline. Provide the factory through
191
+ * PIPELINE_FACTORY, or use provideTransformersWorker() from
192
+ * ngx-transformers which does exactly that.
193
+ *
194
+ * A worker that fails to start or crashes rejects every pending call and
195
+ * is dropped; the next pipeline creates a new one.
196
+ */
197
+ function createWorkerPipelineFactory(worker) {
198
+ const pending = new Map();
199
+ let instance = null;
200
+ let nextId = 1;
201
+ function dispatch(message) {
202
+ const entry = pending.get(message.id);
203
+ if (!entry)
204
+ return;
205
+ switch (message.type) {
206
+ case 'created':
207
+ pending.delete(message.id);
208
+ entry.resolve(message.pipeId);
209
+ return;
210
+ case 'result':
211
+ pending.delete(message.id);
212
+ entry.resolve(message.result);
213
+ return;
214
+ case 'error': {
215
+ pending.delete(message.id);
216
+ const error = new Error(message.message);
217
+ error.name = message.name;
218
+ entry.reject(error);
219
+ return;
220
+ }
221
+ case 'progress':
222
+ entry.progress?.(message.event);
223
+ return;
224
+ case 'token':
225
+ entry.onToken?.(message.text);
226
+ return;
227
+ }
228
+ }
229
+ /** The worker is gone: fail everything in flight and forget it. */
230
+ function fail(reason) {
231
+ const error = new Error(reason);
232
+ error.name = 'WorkerError';
233
+ const entries = [...pending.values()];
234
+ pending.clear();
235
+ instance = null;
236
+ for (const entry of entries)
237
+ entry.reject(error);
238
+ }
239
+ function getWorker() {
240
+ if (!instance) {
241
+ const created = typeof worker === 'function' ? worker() : worker;
242
+ created.addEventListener('message', (event) => dispatch(event.data));
243
+ created.addEventListener('error', (event) => fail(`Transformers worker failed: ${event.message ?? 'unknown error'}`));
244
+ created.addEventListener('messageerror', () => fail('Transformers worker sent a message that could not be deserialized.'));
245
+ instance = created;
246
+ }
247
+ return instance;
248
+ }
249
+ function request(message, extras = {}) {
250
+ return new Promise((resolve, reject) => {
251
+ pending.set(message.id, { resolve: resolve, reject, ...extras });
252
+ try {
253
+ getWorker().postMessage(message);
254
+ }
255
+ catch (err) {
256
+ // Uncloneable input, or a worker that cannot be constructed.
257
+ pending.delete(message.id);
258
+ reject(err instanceof Error ? err : new Error(String(err)));
259
+ }
260
+ });
261
+ }
262
+ const factory = async (task, model, options) => {
263
+ const { progress_callback, ...rest } = options;
264
+ const progress = typeof progress_callback === 'function'
265
+ ? progress_callback
266
+ : undefined;
267
+ const pipeId = await request({ type: 'create', id: nextId++, task, model, options: withoutFunctions(rest) }, { progress });
268
+ const pipe = (async (input, ...args) => {
269
+ const { args: cleanArgs, onToken } = takeOnToken(args);
270
+ return request({
271
+ type: 'run',
272
+ id: nextId++,
273
+ pipeId,
274
+ args: [input, ...withoutFunctionsInLast(cleanArgs)],
275
+ stream: onToken !== undefined,
276
+ }, { onToken });
277
+ });
278
+ pipe.dispose = async () => {
279
+ if (!instance)
280
+ return; // the worker is gone, and its pipelines with it
281
+ instance.postMessage({ type: 'dispose', pipeId });
282
+ };
283
+ return pipe;
284
+ };
285
+ return Object.assign(factory, {
286
+ terminate() {
287
+ const current = instance;
288
+ fail('Transformers worker terminated.');
289
+ current?.terminate?.();
290
+ },
291
+ });
292
+ }
293
+
294
+ /**
295
+ * Run Transformers.js pipelines in a Web Worker. This entry point has no
296
+ * Angular dependency, so a worker file can import it; the main thread uses
297
+ * provideTransformersWorker() from ngx-transformers.
298
+ *
299
+ * @module ngx-transformers/worker
300
+ */
301
+
302
+ /**
303
+ * Generated bundle index. Do not edit.
304
+ */
305
+
306
+ export { createTransformersWorkerHost, createWorkerPipelineFactory, runTransformersWorker, takeOnToken, withoutFunctions, withoutFunctionsInLast };
307
+ //# sourceMappingURL=ngx-transformers-worker.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngx-transformers-worker.mjs","sources":["../../../projects/ngx-transformers/worker/src/protocol.ts","../../../projects/ngx-transformers/worker/src/run-options.ts","../../../projects/ngx-transformers/worker/src/host.ts","../../../projects/ngx-transformers/worker/src/client.ts","../../../projects/ngx-transformers/worker/src/public-api.ts","../../../projects/ngx-transformers/worker/src/ngx-transformers-worker.ts"],"sourcesContent":["/**\n * Messages between the main thread (client.ts) and the worker (host.ts).\n * Every request carries an id the response echoes; pipelines get a pipeId.\n */\n\nexport interface CreateRequest {\n type: 'create';\n id: number;\n task: string;\n model?: string;\n /** Pipeline options without functions; progress is reported by message. */\n options: Record<string, unknown>;\n}\n\nexport interface RunRequest {\n type: 'run';\n id: number;\n pipeId: number;\n /** The input followed by the pipeline's positional arguments (options last). */\n args: unknown[];\n /** Stream generated text back as `token` messages (the onToken run option). */\n stream: boolean;\n}\n\nexport interface DisposeRequest {\n type: 'dispose';\n pipeId: number;\n}\n\nexport type WorkerRequest = CreateRequest | RunRequest | DisposeRequest;\n\nexport interface CreatedResponse {\n type: 'created';\n id: number;\n pipeId: number;\n}\n\nexport interface ResultResponse {\n type: 'result';\n id: number;\n result: unknown;\n}\n\nexport interface ErrorResponse {\n type: 'error';\n id: number;\n name: string;\n message: string;\n}\n\nexport interface ProgressResponse {\n type: 'progress';\n id: number;\n event: Record<string, unknown>;\n}\n\nexport interface TokenResponse {\n type: 'token';\n id: number;\n text: string;\n}\n\nexport type WorkerResponse =\n CreatedResponse | ResultResponse | ErrorResponse | ProgressResponse | TokenResponse;\n\n/**\n * A Transformers.js Tensor after crossing the worker boundary: the class is\n * lost to structured cloning, so the host sends its shape and buffer.\n */\nexport interface TensorData {\n dims: number[];\n data: ArrayLike<number>;\n}\n","/**\n * Helpers shared by the in-thread and worker pipeline factories for the run\n * options ngx-transformers adds on top of the Transformers.js ones.\n */\n\n/** Run option: called with each piece of generated text (text-generation). */\nexport type OnToken = (text: string) => void;\n\nconst ON_TOKEN = 'onToken';\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Pulls the `onToken` callback out of a pipeline call's trailing options\n * object. Returns the arguments without it, plus the callback if present.\n */\nexport function takeOnToken(args: unknown[]): { args: unknown[]; onToken?: OnToken } {\n const last = args[args.length - 1];\n if (!isPlainObject(last) || typeof last[ON_TOKEN] !== 'function') return { args };\n const { [ON_TOKEN]: onToken, ...rest } = last;\n return { args: [...args.slice(0, -1), rest], onToken: onToken as OnToken };\n}\n\n/** A shallow copy of an options object without its function-valued entries. */\nexport function withoutFunctions(options: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(options)) {\n if (typeof value !== 'function') out[key] = value;\n }\n return out;\n}\n\n/** Same as withoutFunctions() for the trailing options object of a call, if any. */\nexport function withoutFunctionsInLast(args: unknown[]): unknown[] {\n const last = args[args.length - 1];\n if (!isPlainObject(last)) return args;\n return [...args.slice(0, -1), withoutFunctions(last)];\n}\n","import type {\n CreateRequest,\n RunRequest,\n TensorData,\n WorkerRequest,\n WorkerResponse,\n} from './protocol';\n\n/** A pipeline callable inside the worker: the real Transformers.js pipeline. */\ntype Pipe = ((...args: unknown[]) => Promise<unknown>) & {\n tokenizer?: unknown;\n dispose?: () => Promise<void>;\n};\n\n/** The slice of @huggingface/transformers the host uses. */\nexport interface WorkerTransformersModule {\n pipeline(task: string, model?: string, options?: object): Promise<unknown>;\n TextStreamer?: new (\n // `never` keeps the real class (which wants a PreTrainedTokenizer) assignable.\n tokenizer: never,\n options: {\n skip_prompt?: boolean;\n skip_special_tokens?: boolean;\n callback_function?: (text: string) => void;\n },\n ) => unknown;\n}\n\nexport interface WorkerHostOptions {\n /** Sends a message to the main thread (postMessage in a worker). */\n post: (message: WorkerResponse, transfer?: Transferable[]) => void;\n /** Imports @huggingface/transformers; tests pass a stub. */\n load?: () => Promise<WorkerTransformersModule>;\n}\n\nexport interface WorkerHost {\n /** Handles one request from the main thread. Resolves once the reply is posted. */\n handle(message: WorkerRequest): Promise<void>;\n}\n\n/** Tensor-like: the shape our TextEmbedder reads back on the main thread. */\nfunction isTensorLike(value: unknown): value is TensorData & { tolist?: unknown } {\n return (\n typeof value === 'object' &&\n value !== null &&\n Array.isArray((value as TensorData).dims) &&\n ArrayBuffer.isView((value as TensorData).data)\n );\n}\n\n/**\n * Turns a pipeline result into something structured cloning keeps intact:\n * Tensors become { dims, data } with the buffer transferred, everything\n * else (plain objects, arrays, strings) passes through unchanged.\n */\nfunction toCloneable(result: unknown): { value: unknown; transfer: Transferable[] } {\n const transfer: Transferable[] = [];\n const convert = (value: unknown): unknown => {\n if (isTensorLike(value)) {\n const data = (value.data as Float32Array).slice();\n transfer.push(data.buffer);\n return { dims: [...value.dims], data } satisfies TensorData;\n }\n if (Array.isArray(value)) return value.map(convert);\n return value;\n };\n return { value: convert(result), transfer };\n}\n\nfunction errorResponse(id: number, err: unknown): WorkerResponse {\n const error = err as { name?: unknown; message?: unknown } | null;\n return {\n type: 'error',\n id,\n name: typeof error?.name === 'string' ? error.name : 'Error',\n message: typeof error?.message === 'string' ? error.message : String(err),\n };\n}\n\n/**\n * The worker side of ngx-transformers/worker: creates pipelines on request,\n * runs them, streams progress and generated tokens back, and disposes them.\n * runTransformersWorker() wires it to the worker's message port; tests call\n * handle() directly.\n */\nexport function createTransformersWorkerHost(options: WorkerHostOptions): WorkerHost {\n const { post } = options;\n const load: () => Promise<WorkerTransformersModule> =\n options.load ?? (() => import('@huggingface/transformers'));\n const pipes = new Map<number, Pipe>();\n let module: Promise<WorkerTransformersModule> | null = null;\n let nextPipeId = 1;\n\n // A failed import is not kept: the next create() tries again, so a load\n // that failed on a network blip stays retryable, as PipelineHandle promises.\n const loadModule = () =>\n (module ??= load().catch((err: unknown) => {\n module = null;\n throw err;\n }));\n\n async function create(message: CreateRequest): Promise<void> {\n try {\n const { pipeline } = await loadModule();\n const pipe = (await pipeline(message.task, message.model, {\n ...message.options,\n progress_callback: (event: Record<string, unknown>) =>\n post({ type: 'progress', id: message.id, event }),\n })) as Pipe;\n const pipeId = nextPipeId++;\n pipes.set(pipeId, pipe);\n post({ type: 'created', id: message.id, pipeId });\n } catch (err) {\n post(errorResponse(message.id, err));\n }\n }\n\n async function run(message: RunRequest): Promise<void> {\n const pipe = pipes.get(message.pipeId);\n if (!pipe) {\n post(errorResponse(message.id, new Error(`Unknown pipeline ${message.pipeId}.`)));\n return;\n }\n try {\n let args = message.args;\n if (message.stream) {\n const { TextStreamer } = await loadModule();\n if (TextStreamer) {\n // The client already stripped onToken; the options object is last.\n const options = { ...(args[args.length - 1] as Record<string, unknown>) };\n options['streamer'] = new TextStreamer(pipe.tokenizer as never, {\n skip_prompt: true,\n skip_special_tokens: true,\n callback_function: (text) => post({ type: 'token', id: message.id, text }),\n });\n args = [...args.slice(0, -1), options];\n }\n }\n const { value, transfer } = toCloneable(await pipe(...args));\n post({ type: 'result', id: message.id, result: value }, transfer);\n } catch (err) {\n post(errorResponse(message.id, err));\n }\n }\n\n return {\n async handle(message) {\n switch (message.type) {\n case 'create':\n return create(message);\n case 'run':\n return run(message);\n case 'dispose': {\n const pipe = pipes.get(message.pipeId);\n pipes.delete(message.pipeId);\n await pipe?.dispose?.();\n return;\n }\n }\n },\n };\n}\n\n/** The worker global scope surface the host needs. */\nexport interface WorkerScopeLike {\n postMessage(message: unknown, transfer?: Transferable[]): void;\n onmessage: ((event: { data: unknown }) => void) | null;\n}\n\n/**\n * Entry point for the worker file of an app:\n *\n * ```ts\n * // transformers.worker.ts\n * /// <reference lib=\"webworker\" />\n * import { runTransformersWorker } from 'ngx-transformers/worker';\n * runTransformersWorker();\n * ```\n *\n * Pair it with provideTransformersWorker() on the main thread.\n */\nexport function runTransformersWorker(\n scope: WorkerScopeLike = workerGlobalScope(),\n load?: () => Promise<WorkerTransformersModule>,\n): WorkerHost {\n const host = createTransformersWorkerHost({\n post: (message, transfer) => scope.postMessage(message, transfer),\n load,\n });\n scope.onmessage = (event) => void host.handle(event.data as WorkerRequest);\n return host;\n}\n\n/**\n * The current worker's global scope. On the main thread this would hook\n * window.onmessage, which any origin can post to, so refuse instead.\n */\nfunction workerGlobalScope(): WorkerScopeLike {\n const global = globalThis as { WorkerGlobalScope?: unknown };\n if (global.WorkerGlobalScope === undefined) {\n throw new Error(\n 'runTransformersWorker() must run inside a Web Worker file; on the main thread use provideTransformersWorker().',\n );\n }\n return globalThis as unknown as WorkerScopeLike;\n}\n","import type { WorkerRequest, WorkerResponse } from './protocol';\nimport { takeOnToken, withoutFunctions, withoutFunctionsInLast, type OnToken } from './run-options';\n\n/** What a worker's 'error' / 'messageerror' event carries, as far as the client reads it. */\nexport interface WorkerErrorEventLike {\n message?: string;\n data?: unknown;\n}\n\n/** The Worker surface the client needs; a real Worker satisfies it. */\nexport interface WorkerLike {\n postMessage(message: unknown, transfer?: Transferable[]): void;\n addEventListener(type: 'message', listener: (event: { data: unknown }) => void): void;\n addEventListener(\n type: 'error' | 'messageerror',\n listener: (event: WorkerErrorEventLike) => void,\n ): void;\n terminate?(): void;\n}\n\n/** A pipeline proxy on the main thread: calls run in the worker. */\nexport type WorkerPipeline = ((input: unknown, ...args: unknown[]) => Promise<unknown>) & {\n dispose: () => Promise<void>;\n};\n\n/** Structurally the same as ngx-transformers' PipelineFactory, plus terminate(). */\nexport type WorkerPipelineFactory = ((\n task: string,\n model: string | undefined,\n options: Record<string, unknown>,\n) => Promise<WorkerPipeline>) & {\n /** Stops the worker (if one was created); the next pipeline starts a fresh one. */\n terminate(): void;\n};\n\ninterface Pending {\n resolve: (value: unknown) => void;\n reject: (err: Error) => void;\n progress?: (event: Record<string, unknown>) => void;\n onToken?: OnToken;\n}\n\n/**\n * A pipeline factory whose pipelines live in a Web Worker, so inference\n * never blocks the UI thread. The worker runs runTransformersWorker() and\n * is created lazily, on the first pipeline. Provide the factory through\n * PIPELINE_FACTORY, or use provideTransformersWorker() from\n * ngx-transformers which does exactly that.\n *\n * A worker that fails to start or crashes rejects every pending call and\n * is dropped; the next pipeline creates a new one.\n */\nexport function createWorkerPipelineFactory(\n worker: WorkerLike | (() => WorkerLike),\n): WorkerPipelineFactory {\n const pending = new Map<number, Pending>();\n let instance: WorkerLike | null = null;\n let nextId = 1;\n\n function dispatch(message: WorkerResponse): void {\n const entry = pending.get(message.id);\n if (!entry) return;\n switch (message.type) {\n case 'created':\n pending.delete(message.id);\n entry.resolve(message.pipeId);\n return;\n case 'result':\n pending.delete(message.id);\n entry.resolve(message.result);\n return;\n case 'error': {\n pending.delete(message.id);\n const error = new Error(message.message);\n error.name = message.name;\n entry.reject(error);\n return;\n }\n case 'progress':\n entry.progress?.(message.event);\n return;\n case 'token':\n entry.onToken?.(message.text);\n return;\n }\n }\n\n /** The worker is gone: fail everything in flight and forget it. */\n function fail(reason: string): void {\n const error = new Error(reason);\n error.name = 'WorkerError';\n const entries = [...pending.values()];\n pending.clear();\n instance = null;\n for (const entry of entries) entry.reject(error);\n }\n\n function getWorker(): WorkerLike {\n if (!instance) {\n const created = typeof worker === 'function' ? worker() : worker;\n created.addEventListener('message', (event) => dispatch(event.data as WorkerResponse));\n created.addEventListener('error', (event) =>\n fail(`Transformers worker failed: ${event.message ?? 'unknown error'}`),\n );\n created.addEventListener('messageerror', () =>\n fail('Transformers worker sent a message that could not be deserialized.'),\n );\n instance = created;\n }\n return instance;\n }\n\n function request<T>(\n message: WorkerRequest & { id: number },\n extras: Omit<Pending, 'resolve' | 'reject'> = {},\n ): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n pending.set(message.id, { resolve: resolve as (value: unknown) => void, reject, ...extras });\n try {\n getWorker().postMessage(message);\n } catch (err) {\n // Uncloneable input, or a worker that cannot be constructed.\n pending.delete(message.id);\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n });\n }\n\n const factory = async (\n task: string,\n model: string | undefined,\n options: Record<string, unknown>,\n ) => {\n const { progress_callback, ...rest } = options;\n const progress =\n typeof progress_callback === 'function'\n ? (progress_callback as (event: Record<string, unknown>) => void)\n : undefined;\n const pipeId = await request<number>(\n { type: 'create', id: nextId++, task, model, options: withoutFunctions(rest) },\n { progress },\n );\n\n const pipe = (async (input: unknown, ...args: unknown[]) => {\n const { args: cleanArgs, onToken } = takeOnToken(args);\n return request(\n {\n type: 'run',\n id: nextId++,\n pipeId,\n args: [input, ...withoutFunctionsInLast(cleanArgs)],\n stream: onToken !== undefined,\n },\n { onToken },\n );\n }) as WorkerPipeline;\n pipe.dispose = async () => {\n if (!instance) return; // the worker is gone, and its pipelines with it\n instance.postMessage({ type: 'dispose', pipeId } satisfies WorkerRequest);\n };\n return pipe;\n };\n\n return Object.assign(factory, {\n terminate(): void {\n const current = instance;\n fail('Transformers worker terminated.');\n current?.terminate?.();\n },\n });\n}\n","/**\n * Run Transformers.js pipelines in a Web Worker. This entry point has no\n * Angular dependency, so a worker file can import it; the main thread uses\n * provideTransformersWorker() from ngx-transformers.\n *\n * @module ngx-transformers/worker\n */\n\nexport * from './protocol';\nexport * from './run-options';\nexport * from './host';\nexport * from './client';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}