ngx-transformers 0.2.1 → 0.3.1
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/LICENSE +21 -0
- package/README.md +87 -7
- package/fesm2022/ngx-transformers.mjs +332 -33
- package/fesm2022/ngx-transformers.mjs.map +1 -1
- package/package.json +5 -3
- package/types/ngx-transformers.d.ts +180 -6
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Muzaffar Qosimov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
[](https://github.com/qwertymuzaffar/ngx-transformers/actions/workflows/ci.yml)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
|
|
7
|
-
Run Hugging Face [Transformers.js](https://github.com/huggingface/transformers.js) models in Angular - **on-device ML with a signals API**. Text classification, sentence embeddings, semantic search, and Whisper speech-to-text that execute entirely in the browser: no server, no API key, works offline once the model is cached.
|
|
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
8
|
|
|
9
|
-
**[
|
|
9
|
+
**[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
10
|
|
|
11
11
|
## Why
|
|
12
12
|
|
|
@@ -90,6 +90,49 @@ async toggle() {
|
|
|
90
90
|
|
|
91
91
|
> Note: the recognizer defaults to `dtype: 'q4'` - q8 Whisper decoders currently fail on the v4 WASM runtime ([transformers.js#1707](https://github.com/huggingface/transformers.js/issues/1707)). Multilingual checkpoints (e.g. `onnx-community/whisper-tiny`) accept `language` and `task: 'translate'` options.
|
|
92
92
|
|
|
93
|
+
## Zero-shot classification
|
|
94
|
+
|
|
95
|
+
Score labels you name against a text, no fine-tuning - an NLI model judges whether "This example is {label}." follows from the text:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { createZeroShotClassifier } from 'ngx-transformers';
|
|
99
|
+
|
|
100
|
+
readonly classifier = createZeroShotClassifier(); // mobilebert-uncased-mnli, ~26 MB q8
|
|
101
|
+
|
|
102
|
+
const scored = await this.classifier.classify(ticketText, ['billing', 'bug report', 'feature request']);
|
|
103
|
+
// [{ label: 'bug report', score: 0.91 }, { label: 'billing', score: 0.06 }, ...] - scores sum to 1
|
|
104
|
+
|
|
105
|
+
// several labels can apply at once - score each on its own:
|
|
106
|
+
const tags = await this.classifier.classify(text, ['food', 'repair', 'politics'], {
|
|
107
|
+
multiLabel: true,
|
|
108
|
+
hypothesisTemplate: 'This text is about {}.',
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`Xenova/distilbert-base-uncased-mnli` (~80 MB) is the more accurate drop-in: `createZeroShotClassifier({ model: '...' })`.
|
|
113
|
+
|
|
114
|
+
## Translation
|
|
115
|
+
|
|
116
|
+
Marian opus-mt checkpoints, one per language pair (~105 MB q8 each), resolved from the pair and loaded on demand:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { createTranslator } from 'ngx-transformers';
|
|
120
|
+
|
|
121
|
+
readonly translator = createTranslator({ from: 'en', to: 'ru' }); // Xenova/opus-mt-en-ru
|
|
122
|
+
|
|
123
|
+
const russian = await this.translator.translate('The model runs entirely in the browser.');
|
|
124
|
+
const german = await this.translator.translate('Good morning.', { to: 'de' }); // opus-mt-en-de, its own download
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The translator's `status` / `progress` signals follow the model used by the latest call, so one `<ngx-model-progress>` covers every pair; `handleFor({ to: 'de' })` returns the underlying `PipelineHandle` when you want one per pair. Override the checkpoint for a pair globally with `provideTransformers({ translationModels: { 'en-ru': 'my-org/en-ru-tiny' } })`.
|
|
128
|
+
|
|
129
|
+
Multilingual checkpoints (NLLB, M2M100) serve every pair from one model and take the codes per call:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
readonly translator = createTranslator({ model: 'Xenova/nllb-200-distilled-600M' });
|
|
133
|
+
await this.translator.translate(text, { from: 'eng_Latn', to: 'tgk_Cyrl' }); // src_lang / tgt_lang forwarded
|
|
134
|
+
```
|
|
135
|
+
|
|
93
136
|
## Any pipeline
|
|
94
137
|
|
|
95
138
|
`createPipeline()` exposes the full Transformers.js task surface with the same signal lifecycle:
|
|
@@ -115,7 +158,23 @@ bootstrapApplication(App, {
|
|
|
115
158
|
});
|
|
116
159
|
```
|
|
117
160
|
|
|
118
|
-
Per-pipeline `device`/`dtype`/`options` win over the global config.
|
|
161
|
+
Per-pipeline `device`/`dtype`/`options` win over the global config. `translationModels` maps a `"from-to"` pair to a checkpoint for `createTranslator()`.
|
|
162
|
+
|
|
163
|
+
Every handle gets its pipeline from the `PIPELINE_FACTORY` injection token. Provide your own to add options or logging, route work to a Web Worker, or stub models in tests. `createDefaultPipelineFactory()` is the default; it lazy-imports `@huggingface/transformers` on the first pipeline, so wrapping it keeps the initial bundle small:
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
import { PIPELINE_FACTORY, createDefaultPipelineFactory, type PipelineFactory } from 'ngx-transformers';
|
|
167
|
+
|
|
168
|
+
const base = createDefaultPipelineFactory();
|
|
169
|
+
const logging: PipelineFactory = (task, model, options) => {
|
|
170
|
+
console.log('loading', task, model);
|
|
171
|
+
return base(task, model, options);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
bootstrapApplication(App, {
|
|
175
|
+
providers: [{ provide: PIPELINE_FACTORY, useValue: logging }],
|
|
176
|
+
});
|
|
177
|
+
```
|
|
119
178
|
|
|
120
179
|
## API
|
|
121
180
|
|
|
@@ -127,10 +186,13 @@ Per-pipeline `device`/`dtype`/`options` win over the global config.
|
|
|
127
186
|
| `createTextClassifier(options?)` | `TextClassifier` - sentiment/classification, `classify(text, topK?)` |
|
|
128
187
|
| `createTextEmbedder(options?)` | `TextEmbedder` - `embed()`, `similarity()`, `rank()` |
|
|
129
188
|
| `createSpeechRecognizer(options?)` | `SpeechRecognizer` - `transcribe(audio, options?)` with timestamps |
|
|
189
|
+
| `createZeroShotClassifier(options?)` | `ZeroShotClassifier` - `classify(text, labels, { multiLabel?, hypothesisTemplate? })` |
|
|
190
|
+
| `createTranslator(options?)` | `Translator` - `translate(text, { from?, to? })`, one model per pair, `handleFor(pair)` |
|
|
130
191
|
| `createMicRecorder(deps?)` | `MicRecorder` - mic capture with `recording`/`seconds`/`error` signals |
|
|
131
192
|
| `cosineSimilarity(a, b)` / `decodeAudio(blob)` | Standalone helpers |
|
|
193
|
+
| `PIPELINE_FACTORY` / `createDefaultPipelineFactory()` | Swap or wrap how pipelines are created |
|
|
132
194
|
|
|
133
|
-
All `create*` functions must run in an injection context (field initializer, constructor, or `runInInjectionContext`); handles are
|
|
195
|
+
All `create*` functions must run in an injection context (field initializer, constructor, or `runInInjectionContext`); handles are destroyed with the surrounding component (the model is released and later calls reject). Call `dispose()` yourself to free a model early and load it again later.
|
|
134
196
|
|
|
135
197
|
### PipelineHandle signals
|
|
136
198
|
|
|
@@ -152,6 +214,8 @@ Status line + download bar for any handle. Inputs: `status` (required), `progres
|
|
|
152
214
|
| `createTextClassifier` | [Xenova/distilbert-base-uncased-finetuned-sst-2-english](https://huggingface.co/Xenova/distilbert-base-uncased-finetuned-sst-2-english) | ~65 MB | Apache-2.0 |
|
|
153
215
|
| `createTextEmbedder` | [Xenova/all-MiniLM-L6-v2](https://huggingface.co/Xenova/all-MiniLM-L6-v2) | ~23 MB | Apache-2.0 |
|
|
154
216
|
| `createSpeechRecognizer` | [onnx-community/whisper-tiny.en](https://huggingface.co/onnx-community/whisper-tiny.en) | ~41 MB (q4) | Apache-2.0 |
|
|
217
|
+
| `createZeroShotClassifier` | [Xenova/mobilebert-uncased-mnli](https://huggingface.co/Xenova/mobilebert-uncased-mnli) | ~26 MB | unlisted on the Hub (base MobileBERT: Apache-2.0) |
|
|
218
|
+
| `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) |
|
|
155
219
|
|
|
156
220
|
Swap any compatible checkpoint via `{ model: '...' }`. Check the license of the model you ship.
|
|
157
221
|
|
|
@@ -159,10 +223,26 @@ Swap any compatible checkpoint via `{ model: '...' }`. Check the license of the
|
|
|
159
223
|
|
|
160
224
|
Model loading is browser-only (WASM/WebGPU). Creating handles is safe on the server - nothing downloads until `load()`/`run()` - but call those only in browser code paths.
|
|
161
225
|
|
|
162
|
-
##
|
|
226
|
+
## Device selection
|
|
227
|
+
|
|
228
|
+
WebGPU is several times faster than WASM where it works, but `navigator.gpu` existing is not enough: `requestAdapter()` can still hand back nothing on a machine without a usable GPU. `hasWebGpu()` runs that probe once (cached) and `detectDevice()` turns the answer into a device name; both resolve to the WASM answer on the server.
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
import { createTextClassifier, detectDevice } from 'ngx-transformers';
|
|
232
|
+
|
|
233
|
+
const device = await detectDevice(); // 'webgpu' | 'wasm'
|
|
234
|
+
const classifier = createTextClassifier({ device });
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Or let every handle decide for itself:
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
bootstrapApplication(App, {
|
|
241
|
+
providers: [provideTransformers({ autoDevice: true, dtype: 'q8' })],
|
|
242
|
+
});
|
|
243
|
+
```
|
|
163
244
|
|
|
164
|
-
|
|
165
|
-
- WebGPU feature-detection helper
|
|
245
|
+
With `autoDevice`, a handle that sets no `device` (or `'auto'`) probes WebGPU on its first load and uses it when available, else WASM. An explicit `device` on the handle or in the global config still wins. `resetDeviceDetection()` clears the cached answer.
|
|
166
246
|
|
|
167
247
|
## License
|
|
168
248
|
|
|
@@ -1,14 +1,32 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { InjectionToken, makeEnvironmentProviders, signal, computed, inject, DestroyRef, input, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
3
|
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
const importTransformers = () => import('@huggingface/transformers');
|
|
5
|
+
/**
|
|
6
|
+
* The factory behind PIPELINE_FACTORY. It imports @huggingface/transformers
|
|
7
|
+
* lazily, on the first pipeline, so the library adds nothing to the initial
|
|
8
|
+
* bundle. Wrap it to add options or logging while keeping the lazy import:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* const base = createDefaultPipelineFactory();
|
|
12
|
+
* const factory: PipelineFactory = (task, model, options) =>
|
|
13
|
+
* base(task, model, { ...options, revision: 'v2' });
|
|
14
|
+
* providers: [{ provide: PIPELINE_FACTORY, useValue: factory }]
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* `load` is the importer. Tests pass a stub module; apps that need to
|
|
18
|
+
* configure Transformers.js (`env.allowRemoteModels`, `env.localModelPath`)
|
|
19
|
+
* do it there, before returning the module.
|
|
20
|
+
*/
|
|
21
|
+
function createDefaultPipelineFactory(load = importTransformers) {
|
|
22
|
+
return async (task, model, options) => {
|
|
23
|
+
const { pipeline } = await load();
|
|
24
|
+
return (await pipeline(task, model, options));
|
|
25
|
+
};
|
|
26
|
+
}
|
|
9
27
|
const PIPELINE_FACTORY = new InjectionToken('ngx-transformers.pipeline-factory', {
|
|
10
28
|
providedIn: 'root',
|
|
11
|
-
factory: () =>
|
|
29
|
+
factory: () => createDefaultPipelineFactory(),
|
|
12
30
|
});
|
|
13
31
|
const NGX_TRANSFORMERS_CONFIG = new InjectionToken('ngx-transformers.config', {
|
|
14
32
|
providedIn: 'root',
|
|
@@ -27,6 +45,38 @@ function provideTransformers(config) {
|
|
|
27
45
|
return makeEnvironmentProviders([{ provide: NGX_TRANSFORMERS_CONFIG, useValue: config }]);
|
|
28
46
|
}
|
|
29
47
|
|
|
48
|
+
let webGpuProbe = null;
|
|
49
|
+
/**
|
|
50
|
+
* Whether models can run on WebGPU here: `navigator.gpu` exists AND
|
|
51
|
+
* `requestAdapter()` hands back an adapter - it returns null on machines
|
|
52
|
+
* without a usable GPU or with WebGPU disabled, even when the API exists.
|
|
53
|
+
* Resolves false on the server. The probe runs once and is cached.
|
|
54
|
+
*/
|
|
55
|
+
function hasWebGpu() {
|
|
56
|
+
webGpuProbe ??= probeWebGpu();
|
|
57
|
+
return webGpuProbe;
|
|
58
|
+
}
|
|
59
|
+
/** `'webgpu'` when hasWebGpu() resolves true, else `'wasm'`. */
|
|
60
|
+
async function detectDevice() {
|
|
61
|
+
return (await hasWebGpu()) ? 'webgpu' : 'wasm';
|
|
62
|
+
}
|
|
63
|
+
/** Forgets the cached probe so the next hasWebGpu() asks the browser again. */
|
|
64
|
+
function resetDeviceDetection() {
|
|
65
|
+
webGpuProbe = null;
|
|
66
|
+
}
|
|
67
|
+
async function probeWebGpu() {
|
|
68
|
+
const gpu = globalThis.navigator?.gpu;
|
|
69
|
+
if (!gpu)
|
|
70
|
+
return false;
|
|
71
|
+
try {
|
|
72
|
+
return (await gpu.requestAdapter()) !== null;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const newSession = () => ({ pipe: null, loading: null, inFlight: 0 });
|
|
30
80
|
/**
|
|
31
81
|
* A lazily-loaded Transformers.js pipeline wrapped in signals.
|
|
32
82
|
*
|
|
@@ -51,8 +101,8 @@ class PipelineHandle {
|
|
|
51
101
|
...(ngDevMode ? [{ debugName: "ready" }] : /* istanbul ignore next */ []));
|
|
52
102
|
busy = computed(() => this.status() === 'busy' || this.status() === 'loading', /* @ts-ignore */
|
|
53
103
|
...(ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []));
|
|
54
|
-
|
|
55
|
-
|
|
104
|
+
session = newSession();
|
|
105
|
+
destroyed = false;
|
|
56
106
|
constructor(request, factory, config) {
|
|
57
107
|
this.request = request;
|
|
58
108
|
this.factory = factory;
|
|
@@ -60,57 +110,119 @@ class PipelineHandle {
|
|
|
60
110
|
}
|
|
61
111
|
/** Downloads and initializes the model. Idempotent; retries after error. */
|
|
62
112
|
load() {
|
|
63
|
-
if (this.
|
|
113
|
+
if (this.destroyed)
|
|
114
|
+
return Promise.reject(destroyedError());
|
|
115
|
+
const session = this.session;
|
|
116
|
+
if (session.pipe)
|
|
64
117
|
return Promise.resolve();
|
|
65
|
-
|
|
66
|
-
|
|
118
|
+
session.loading ??= this.doLoad(session).finally(() => {
|
|
119
|
+
session.loading = null;
|
|
120
|
+
});
|
|
121
|
+
return session.loading;
|
|
67
122
|
}
|
|
68
123
|
/** Runs the pipeline, loading the model first if needed. */
|
|
69
|
-
|
|
124
|
+
run(input, runOptions) {
|
|
125
|
+
return this.runWith(input, runOptions);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Runs the pipeline with extra positional arguments after the input, for
|
|
129
|
+
* tasks whose pipeline signature is not (input, options): zero-shot
|
|
130
|
+
* classification takes (text, candidateLabels, options).
|
|
131
|
+
*/
|
|
132
|
+
async runWith(input, ...extraArgs) {
|
|
70
133
|
await this.load();
|
|
134
|
+
const session = this.session;
|
|
135
|
+
const pipe = session.pipe;
|
|
136
|
+
if (!pipe) {
|
|
137
|
+
throw new Error('PipelineHandle: disposed before the model finished loading.');
|
|
138
|
+
}
|
|
139
|
+
session.inFlight++;
|
|
71
140
|
this.status.set('busy');
|
|
72
141
|
try {
|
|
73
|
-
return (await
|
|
142
|
+
return (await pipe(input, ...extraArgs));
|
|
74
143
|
}
|
|
75
144
|
finally {
|
|
76
|
-
//
|
|
77
|
-
|
|
145
|
+
// Back to ready once the last overlapping run finishes - a failed run
|
|
146
|
+
// leaves the model intact. A run that outlived dispose() settles its
|
|
147
|
+
// own, retired session and leaves the status alone.
|
|
148
|
+
session.inFlight--;
|
|
149
|
+
if (session.inFlight === 0 && this.session === session)
|
|
150
|
+
this.status.set('ready');
|
|
78
151
|
}
|
|
79
152
|
}
|
|
80
|
-
/**
|
|
153
|
+
/**
|
|
154
|
+
* Frees the model. A download still in flight is cancelled: its model is
|
|
155
|
+
* released on arrival and the handle stays idle. The handle can be loaded
|
|
156
|
+
* again afterwards; destroy() is the terminal variant.
|
|
157
|
+
*/
|
|
81
158
|
async dispose() {
|
|
82
|
-
const
|
|
83
|
-
this.
|
|
159
|
+
const session = this.session;
|
|
160
|
+
this.session = newSession();
|
|
84
161
|
this.status.set('idle');
|
|
85
162
|
this.progress.set(null);
|
|
86
|
-
await pipe?.dispose?.();
|
|
163
|
+
await session.pipe?.dispose?.();
|
|
87
164
|
}
|
|
88
|
-
|
|
165
|
+
/**
|
|
166
|
+
* dispose() for good: later load() and run() calls reject instead of
|
|
167
|
+
* downloading a model nobody would release. create*() registers this with
|
|
168
|
+
* the DestroyRef of the injection context, so a handle declared in a
|
|
169
|
+
* component ends with the component.
|
|
170
|
+
*/
|
|
171
|
+
destroy() {
|
|
172
|
+
this.destroyed = true;
|
|
173
|
+
return this.dispose();
|
|
174
|
+
}
|
|
175
|
+
async doLoad(session) {
|
|
89
176
|
this.status.set('loading');
|
|
90
177
|
this.error.set(null);
|
|
91
178
|
const options = {
|
|
92
179
|
...this.config.pipelineOptions,
|
|
93
180
|
...this.request.options,
|
|
94
|
-
progress_callback: (event) => this.onProgress(event),
|
|
181
|
+
progress_callback: (event) => this.onProgress(event, session),
|
|
95
182
|
};
|
|
96
|
-
const device =
|
|
183
|
+
const device = await this.resolveDevice();
|
|
184
|
+
// Disposed while probing the device: do not start the download at all.
|
|
185
|
+
if (this.session !== session)
|
|
186
|
+
return;
|
|
97
187
|
const dtype = this.request.dtype ?? this.config.dtype;
|
|
98
188
|
if (device && device !== 'auto')
|
|
99
189
|
options['device'] = device;
|
|
100
190
|
if (dtype)
|
|
101
191
|
options['dtype'] = dtype;
|
|
102
192
|
try {
|
|
103
|
-
|
|
193
|
+
const pipe = await this.factory(this.request.task, this.request.model, options);
|
|
194
|
+
if (this.session !== session) {
|
|
195
|
+
// Disposed while downloading: nobody wants this model any more.
|
|
196
|
+
await pipe.dispose?.();
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
session.pipe = pipe;
|
|
104
200
|
this.progress.set(null);
|
|
105
201
|
this.status.set('ready');
|
|
106
202
|
}
|
|
107
203
|
catch (err) {
|
|
204
|
+
// A cancelled load is not an error - dispose() already reset the handle.
|
|
205
|
+
if (this.session !== session)
|
|
206
|
+
return;
|
|
108
207
|
this.error.set(err);
|
|
109
208
|
this.status.set('error');
|
|
110
209
|
throw err;
|
|
111
210
|
}
|
|
112
211
|
}
|
|
113
|
-
|
|
212
|
+
/**
|
|
213
|
+
* An explicit request or global device wins; with `autoDevice` on, an
|
|
214
|
+
* unset or 'auto' device is probed for WebGPU (once, cached) at load time.
|
|
215
|
+
*/
|
|
216
|
+
async resolveDevice() {
|
|
217
|
+
const configured = this.request.device ?? this.config.device;
|
|
218
|
+
if (configured && configured !== 'auto')
|
|
219
|
+
return configured;
|
|
220
|
+
return this.config.autoDevice ? detectDevice() : configured;
|
|
221
|
+
}
|
|
222
|
+
onProgress(event, session) {
|
|
223
|
+
// Events from a load that dispose() cancelled must not resurrect the bar.
|
|
224
|
+
if (this.session !== session)
|
|
225
|
+
return;
|
|
114
226
|
if (event.status !== 'progress' || !event.file)
|
|
115
227
|
return;
|
|
116
228
|
this.progress.set({
|
|
@@ -121,14 +233,17 @@ class PipelineHandle {
|
|
|
121
233
|
});
|
|
122
234
|
}
|
|
123
235
|
}
|
|
236
|
+
function destroyedError() {
|
|
237
|
+
return new Error('PipelineHandle: destroyed with its component; create a new handle.');
|
|
238
|
+
}
|
|
124
239
|
/**
|
|
125
240
|
* Creates a PipelineHandle in an injection context (constructor, field
|
|
126
|
-
* initializer, or runInInjectionContext). The handle is
|
|
241
|
+
* initializer, or runInInjectionContext). The handle is destroyed with the
|
|
127
242
|
* surrounding component/injector.
|
|
128
243
|
*/
|
|
129
244
|
function createPipeline(request) {
|
|
130
245
|
const handle = new PipelineHandle(request, inject(PIPELINE_FACTORY), inject(NGX_TRANSFORMERS_CONFIG));
|
|
131
|
-
inject(DestroyRef, { optional: true })?.onDestroy(() => void handle.
|
|
246
|
+
inject(DestroyRef, { optional: true })?.onDestroy(() => void handle.destroy());
|
|
132
247
|
return handle;
|
|
133
248
|
}
|
|
134
249
|
|
|
@@ -150,7 +265,7 @@ class TextClassifier extends PipelineHandle {
|
|
|
150
265
|
/** Creates a TextClassifier in an injection context. */
|
|
151
266
|
function createTextClassifier(options = {}) {
|
|
152
267
|
const classifier = new TextClassifier({ task: 'text-classification', model: DEFAULT_TEXT_CLASSIFICATION_MODEL, ...options }, inject(PIPELINE_FACTORY), inject(NGX_TRANSFORMERS_CONFIG));
|
|
153
|
-
inject(DestroyRef, { optional: true })?.onDestroy(() => void classifier.
|
|
268
|
+
inject(DestroyRef, { optional: true })?.onDestroy(() => void classifier.destroy());
|
|
154
269
|
return classifier;
|
|
155
270
|
}
|
|
156
271
|
|
|
@@ -196,7 +311,11 @@ class TextEmbedder extends PipelineHandle {
|
|
|
196
311
|
return [];
|
|
197
312
|
const [queryVec, ...docVecs] = await this.embed([query, ...documents]);
|
|
198
313
|
return docVecs
|
|
199
|
-
.map((vec, index) => ({
|
|
314
|
+
.map((vec, index) => ({
|
|
315
|
+
text: documents[index],
|
|
316
|
+
score: cosineSimilarity(queryVec, vec),
|
|
317
|
+
index,
|
|
318
|
+
}))
|
|
200
319
|
.sort((a, b) => b.score - a.score);
|
|
201
320
|
}
|
|
202
321
|
}
|
|
@@ -214,7 +333,7 @@ function toRows(out) {
|
|
|
214
333
|
/** Creates a TextEmbedder in an injection context. */
|
|
215
334
|
function createTextEmbedder(options = {}) {
|
|
216
335
|
const embedder = new TextEmbedder({ task: 'feature-extraction', model: DEFAULT_EMBEDDING_MODEL, ...options }, inject(PIPELINE_FACTORY), inject(NGX_TRANSFORMERS_CONFIG));
|
|
217
|
-
inject(DestroyRef, { optional: true })?.onDestroy(() => void embedder.
|
|
336
|
+
inject(DestroyRef, { optional: true })?.onDestroy(() => void embedder.destroy());
|
|
218
337
|
return embedder;
|
|
219
338
|
}
|
|
220
339
|
|
|
@@ -293,10 +412,178 @@ class SpeechRecognizer extends PipelineHandle {
|
|
|
293
412
|
*/
|
|
294
413
|
function createSpeechRecognizer(options = {}) {
|
|
295
414
|
const recognizer = new SpeechRecognizer({ task: 'automatic-speech-recognition', model: DEFAULT_ASR_MODEL, dtype: 'q4', ...options }, inject(PIPELINE_FACTORY), inject(NGX_TRANSFORMERS_CONFIG));
|
|
296
|
-
inject(DestroyRef, { optional: true })?.onDestroy(() => void recognizer.
|
|
415
|
+
inject(DestroyRef, { optional: true })?.onDestroy(() => void recognizer.destroy());
|
|
297
416
|
return recognizer;
|
|
298
417
|
}
|
|
299
418
|
|
|
419
|
+
// The Transformers.js docs example for this task and the smallest MNLI
|
|
420
|
+
// checkpoint that behaves well on the WASM runtime (~26 MB q8).
|
|
421
|
+
const DEFAULT_ZERO_SHOT_MODEL = 'Xenova/mobilebert-uncased-mnli';
|
|
422
|
+
/**
|
|
423
|
+
* Zero-shot classification: score any labels you name against a text with
|
|
424
|
+
* an NLI model, no fine-tuning. The default checkpoint is MobileBERT MNLI
|
|
425
|
+
* (~26 MB q8); Xenova/distilbert-base-uncased-mnli (~80 MB) is the
|
|
426
|
+
* higher-accuracy alternative.
|
|
427
|
+
*/
|
|
428
|
+
class ZeroShotClassifier extends PipelineHandle {
|
|
429
|
+
/**
|
|
430
|
+
* Scores each candidate label for one text; resolves sorted by score (top
|
|
431
|
+
* first). Single-label mode (default) normalizes across labels so scores
|
|
432
|
+
* sum to 1; `multiLabel` scores each label on its own.
|
|
433
|
+
*/
|
|
434
|
+
async classify(text, labels, options = {}) {
|
|
435
|
+
if (labels.length === 0)
|
|
436
|
+
return [];
|
|
437
|
+
const runOptions = {};
|
|
438
|
+
if (options.multiLabel !== undefined)
|
|
439
|
+
runOptions['multi_label'] = options.multiLabel;
|
|
440
|
+
if (options.hypothesisTemplate !== undefined)
|
|
441
|
+
runOptions['hypothesis_template'] = options.hypothesisTemplate;
|
|
442
|
+
const raw = await this.runWith(text, [...labels], runOptions);
|
|
443
|
+
const first = Array.isArray(raw) ? raw[0] : raw;
|
|
444
|
+
const scoredLabels = first?.labels ?? [];
|
|
445
|
+
const scores = first?.scores ?? [];
|
|
446
|
+
return scoredLabels
|
|
447
|
+
.map((label, index) => ({ label, score: scores[index] ?? 0 }))
|
|
448
|
+
.sort((left, right) => right.score - left.score);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
/** Creates a ZeroShotClassifier in an injection context. */
|
|
452
|
+
function createZeroShotClassifier(options = {}) {
|
|
453
|
+
const classifier = new ZeroShotClassifier({ task: 'zero-shot-classification', model: DEFAULT_ZERO_SHOT_MODEL, ...options }, inject(PIPELINE_FACTORY), inject(NGX_TRANSFORMERS_CONFIG));
|
|
454
|
+
inject(DestroyRef, { optional: true })?.onDestroy(() => void classifier.destroy());
|
|
455
|
+
return classifier;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Default checkpoint for a language pair: Helsinki-NLP's Marian opus-mt
|
|
460
|
+
* models, one per direction, ~105 MB q8 each (e.g. Xenova/opus-mt-en-ru).
|
|
461
|
+
*/
|
|
462
|
+
function defaultTranslationModel(from, to) {
|
|
463
|
+
return `Xenova/opus-mt-${from}-${to}`;
|
|
464
|
+
}
|
|
465
|
+
/** Checkpoint for a pair: provideTransformers({ translationModels }) first, then opus-mt. */
|
|
466
|
+
function resolveTranslationModel(from, to, config) {
|
|
467
|
+
return config.translationModels?.[`${from}-${to}`] ?? defaultTranslationModel(from, to);
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Multilingual families take the pair per call as src_lang / tgt_lang;
|
|
471
|
+
* Marian opus-mt checkpoints translate one fixed direction and ignore them.
|
|
472
|
+
*/
|
|
473
|
+
const MULTILINGUAL_MODEL_PATTERN = /nllb|m2m[_-]?100|mbart|madlad/i;
|
|
474
|
+
/**
|
|
475
|
+
* Text translation. Pipelines are created lazily, one per checkpoint: with
|
|
476
|
+
* the default opus-mt family every language pair is its own model, so a
|
|
477
|
+
* Translator holds one handle per pair it has been asked for, while a
|
|
478
|
+
* pinned multilingual `model` (NLLB, M2M100) serves every pair from one.
|
|
479
|
+
*
|
|
480
|
+
* The signals mirror the handle used by the most recent call, so a single
|
|
481
|
+
* `<ngx-model-progress>` covers the translator.
|
|
482
|
+
*/
|
|
483
|
+
class Translator {
|
|
484
|
+
options;
|
|
485
|
+
factory;
|
|
486
|
+
config;
|
|
487
|
+
active = signal(null, /* @ts-ignore */
|
|
488
|
+
...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
|
|
489
|
+
handles = new Map();
|
|
490
|
+
destroyed = false;
|
|
491
|
+
status = computed(() => this.active()?.status() ?? 'idle', /* @ts-ignore */
|
|
492
|
+
...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
|
|
493
|
+
progress = computed(() => this.active()?.progress() ?? null, /* @ts-ignore */
|
|
494
|
+
...(ngDevMode ? [{ debugName: "progress" }] : /* istanbul ignore next */ []));
|
|
495
|
+
error = computed(() => this.active()?.error() ?? null, /* @ts-ignore */
|
|
496
|
+
...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
|
|
497
|
+
ready = computed(() => this.active()?.ready() ?? false, /* @ts-ignore */
|
|
498
|
+
...(ngDevMode ? [{ debugName: "ready" }] : /* istanbul ignore next */ []));
|
|
499
|
+
busy = computed(() => this.active()?.busy() ?? false, /* @ts-ignore */
|
|
500
|
+
...(ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []));
|
|
501
|
+
constructor(options, factory, config) {
|
|
502
|
+
this.options = options;
|
|
503
|
+
this.factory = factory;
|
|
504
|
+
this.config = config;
|
|
505
|
+
}
|
|
506
|
+
/** Downloads the model for a pair ahead of the first translate() call. */
|
|
507
|
+
async load(pair = {}) {
|
|
508
|
+
return this.handleFor(pair).load();
|
|
509
|
+
}
|
|
510
|
+
/** Translates one text; the pair defaults to the one given at creation. */
|
|
511
|
+
async translate(text, pair = {}) {
|
|
512
|
+
const handle = this.handleFor(pair);
|
|
513
|
+
const raw = await handle.run(text, this.languageOptions(pair));
|
|
514
|
+
const first = Array.isArray(raw) ? raw[0] : raw;
|
|
515
|
+
return (first?.translation_text ?? '').trim();
|
|
516
|
+
}
|
|
517
|
+
/** Checkpoint for a pair: the pinned model, else the configured or default one. */
|
|
518
|
+
modelFor(pair = {}) {
|
|
519
|
+
if (this.options.model)
|
|
520
|
+
return this.options.model;
|
|
521
|
+
const from = pair.from ?? this.options.from;
|
|
522
|
+
const to = pair.to ?? this.options.to;
|
|
523
|
+
if (!from || !to) {
|
|
524
|
+
throw new Error('Translator: no language pair - pass { from, to } to createTranslator() or translate(), or pin a multilingual model.');
|
|
525
|
+
}
|
|
526
|
+
return resolveTranslationModel(from, to, this.config);
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* The handle serving a pair, created on first use. Exposed for apps that
|
|
530
|
+
* keep several pairs warm and want a progress line per model.
|
|
531
|
+
*/
|
|
532
|
+
handleFor(pair = {}) {
|
|
533
|
+
if (this.destroyed) {
|
|
534
|
+
throw new Error('Translator: destroyed with its component; create a new translator.');
|
|
535
|
+
}
|
|
536
|
+
const model = this.modelFor(pair);
|
|
537
|
+
let handle = this.handles.get(model);
|
|
538
|
+
if (!handle) {
|
|
539
|
+
handle = new PipelineHandle({
|
|
540
|
+
task: 'translation',
|
|
541
|
+
model,
|
|
542
|
+
device: this.options.device,
|
|
543
|
+
dtype: this.options.dtype,
|
|
544
|
+
options: this.options.options,
|
|
545
|
+
}, this.factory, this.config);
|
|
546
|
+
this.handles.set(model, handle);
|
|
547
|
+
}
|
|
548
|
+
this.active.set(handle);
|
|
549
|
+
return handle;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* dispose() for good: later calls reject instead of loading a model nobody
|
|
553
|
+
* would release. createTranslator() registers this with the DestroyRef.
|
|
554
|
+
*/
|
|
555
|
+
destroy() {
|
|
556
|
+
this.destroyed = true;
|
|
557
|
+
return this.dispose();
|
|
558
|
+
}
|
|
559
|
+
/** Frees every model. The translator can be used again afterwards. */
|
|
560
|
+
async dispose() {
|
|
561
|
+
const handles = [...this.handles.values()];
|
|
562
|
+
this.handles.clear();
|
|
563
|
+
this.active.set(null);
|
|
564
|
+
await Promise.all(handles.map((handle) => handle.dispose()));
|
|
565
|
+
}
|
|
566
|
+
/** src_lang / tgt_lang for multilingual checkpoints; opus-mt models get nothing. */
|
|
567
|
+
languageOptions(pair) {
|
|
568
|
+
const runOptions = {};
|
|
569
|
+
if (!MULTILINGUAL_MODEL_PATTERN.test(this.modelFor(pair)))
|
|
570
|
+
return runOptions;
|
|
571
|
+
const from = pair.from ?? this.options.from;
|
|
572
|
+
const to = pair.to ?? this.options.to;
|
|
573
|
+
if (from !== undefined)
|
|
574
|
+
runOptions['src_lang'] = from;
|
|
575
|
+
if (to !== undefined)
|
|
576
|
+
runOptions['tgt_lang'] = to;
|
|
577
|
+
return runOptions;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
/** Creates a Translator in an injection context. */
|
|
581
|
+
function createTranslator(options = {}) {
|
|
582
|
+
const translator = new Translator(options, inject(PIPELINE_FACTORY), inject(NGX_TRANSFORMERS_CONFIG));
|
|
583
|
+
inject(DestroyRef, { optional: true })?.onDestroy(() => void translator.destroy());
|
|
584
|
+
return translator;
|
|
585
|
+
}
|
|
586
|
+
|
|
300
587
|
/**
|
|
301
588
|
* Microphone capture with a signals API. start() asks for mic permission
|
|
302
589
|
* and records; stop() resolves with the encoded audio Blob - pass it to
|
|
@@ -431,7 +718,13 @@ class ModelProgressComponent {
|
|
|
431
718
|
<span class="nt-label">{{ label() }}</span>
|
|
432
719
|
@if (status() === 'loading' && progress(); as p) {
|
|
433
720
|
<span class="nt-file">{{ p.file }}</span>
|
|
434
|
-
<div
|
|
721
|
+
<div
|
|
722
|
+
class="nt-track"
|
|
723
|
+
role="progressbar"
|
|
724
|
+
[attr.aria-valuenow]="p.progress"
|
|
725
|
+
aria-valuemin="0"
|
|
726
|
+
aria-valuemax="100"
|
|
727
|
+
>
|
|
435
728
|
<div class="nt-fill" [style.width.%]="p.progress"></div>
|
|
436
729
|
</div>
|
|
437
730
|
<span class="nt-pct">{{ p.progress }}%</span>
|
|
@@ -447,7 +740,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
447
740
|
<span class="nt-label">{{ label() }}</span>
|
|
448
741
|
@if (status() === 'loading' && progress(); as p) {
|
|
449
742
|
<span class="nt-file">{{ p.file }}</span>
|
|
450
|
-
<div
|
|
743
|
+
<div
|
|
744
|
+
class="nt-track"
|
|
745
|
+
role="progressbar"
|
|
746
|
+
[attr.aria-valuenow]="p.progress"
|
|
747
|
+
aria-valuemin="0"
|
|
748
|
+
aria-valuemax="100"
|
|
749
|
+
>
|
|
451
750
|
<div class="nt-fill" [style.width.%]="p.progress"></div>
|
|
452
751
|
</div>
|
|
453
752
|
<span class="nt-pct">{{ p.progress }}%</span>
|
|
@@ -464,5 +763,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
464
763
|
* Generated bundle index. Do not edit.
|
|
465
764
|
*/
|
|
466
765
|
|
|
467
|
-
export { DEFAULT_ASR_MODEL, DEFAULT_EMBEDDING_MODEL, DEFAULT_TEXT_CLASSIFICATION_MODEL, MicRecorder, ModelProgressComponent, NGX_TRANSFORMERS_CONFIG, PIPELINE_FACTORY, PipelineHandle, SpeechRecognizer, TextClassifier, TextEmbedder, WHISPER_SAMPLE_RATE, cosineSimilarity, createMicRecorder, createPipeline, createSpeechRecognizer, createTextClassifier, createTextEmbedder, decodeAudio, provideTransformers };
|
|
766
|
+
export { DEFAULT_ASR_MODEL, DEFAULT_EMBEDDING_MODEL, DEFAULT_TEXT_CLASSIFICATION_MODEL, DEFAULT_ZERO_SHOT_MODEL, MicRecorder, ModelProgressComponent, NGX_TRANSFORMERS_CONFIG, PIPELINE_FACTORY, PipelineHandle, SpeechRecognizer, TextClassifier, TextEmbedder, Translator, WHISPER_SAMPLE_RATE, ZeroShotClassifier, cosineSimilarity, createDefaultPipelineFactory, createMicRecorder, createPipeline, createSpeechRecognizer, createTextClassifier, createTextEmbedder, createTranslator, createZeroShotClassifier, decodeAudio, defaultTranslationModel, detectDevice, hasWebGpu, provideTransformers, resetDeviceDetection, resolveTranslationModel };
|
|
468
767
|
//# sourceMappingURL=ngx-transformers.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngx-transformers.mjs","sources":["../../../projects/ngx-transformers/src/lib/transformers.providers.ts","../../../projects/ngx-transformers/src/lib/pipeline.ts","../../../projects/ngx-transformers/src/lib/text-classifier.ts","../../../projects/ngx-transformers/src/lib/text-embedder.ts","../../../projects/ngx-transformers/src/lib/speech-recognizer.ts","../../../projects/ngx-transformers/src/lib/mic-recorder.ts","../../../projects/ngx-transformers/src/lib/model-progress.component.ts","../../../projects/ngx-transformers/src/public-api.ts","../../../projects/ngx-transformers/src/ngx-transformers.ts"],"sourcesContent":["import { EnvironmentProviders, InjectionToken, makeEnvironmentProviders } from '@angular/core';\nimport type { NgxTransformersConfig } from './transformers.models';\n\n/**\n * The callable returned by Transformers.js pipeline(), reduced to the\n * surface this library relies on.\n */\nexport type PipelineLike = ((input: unknown, options?: Record<string, unknown>) => Promise<unknown>) & {\n dispose?: () => Promise<void>;\n};\n\n/**\n * Creates a pipeline for a task. The default implementation lazy-imports\n * @huggingface/transformers; tests and SSR shims can provide their own.\n */\nexport type PipelineFactory = (\n task: string,\n model: string | undefined,\n options: Record<string, unknown>,\n) => Promise<PipelineLike>;\n\nconst defaultPipelineFactory: PipelineFactory = async (task, model, options) => {\n const { pipeline } = await import('@huggingface/transformers');\n const pipe = await (pipeline as (t: string, m?: string, o?: object) => Promise<unknown>)(\n task,\n model,\n options,\n );\n return pipe as PipelineLike;\n};\n\nexport const PIPELINE_FACTORY = new InjectionToken<PipelineFactory>('ngx-transformers.pipeline-factory', {\n providedIn: 'root',\n factory: () => defaultPipelineFactory,\n});\n\nexport const NGX_TRANSFORMERS_CONFIG = new InjectionToken<NgxTransformersConfig>('ngx-transformers.config', {\n providedIn: 'root',\n factory: () => ({}),\n});\n\n/**\n * Sets workspace-wide defaults (device, dtype, extra pipeline options):\n *\n * ```ts\n * bootstrapApplication(App, {\n * providers: [provideTransformers({ device: 'webgpu', dtype: 'q8' })],\n * });\n * ```\n */\nexport function provideTransformers(config: NgxTransformersConfig): EnvironmentProviders {\n return makeEnvironmentProviders([{ provide: NGX_TRANSFORMERS_CONFIG, useValue: config }]);\n}\n","import { DestroyRef, computed, inject, signal } from '@angular/core';\nimport type { ModelProgress, NgxTransformersConfig, PipelineRequest, PipelineStatus } from './transformers.models';\nimport { NGX_TRANSFORMERS_CONFIG, PIPELINE_FACTORY, type PipelineFactory, type PipelineLike } from './transformers.providers';\n\n/** Shape of Transformers.js progress_callback events (subset we consume). */\ninterface RawProgressEvent {\n status?: string;\n file?: string;\n progress?: number;\n loaded?: number;\n total?: number;\n}\n\n/**\n * A lazily-loaded Transformers.js pipeline wrapped in signals.\n *\n * The model is not downloaded until load() or the first run() call, so\n * creating handles is free. All state is exposed as signals and safe to\n * bind in zoneless apps.\n */\nexport class PipelineHandle<TIn = unknown, TOut = unknown> {\n /** idle -> loading -> ready <-> busy; error only on load failure. */\n readonly status = signal<PipelineStatus>('idle');\n /** Download progress for the file currently transferring, else null. */\n readonly progress = signal<ModelProgress | null>(null);\n readonly error = signal<unknown>(null);\n /** True once the model is usable (including while a run is in flight). */\n readonly ready = computed(() => this.status() === 'ready' || this.status() === 'busy');\n readonly busy = computed(() => this.status() === 'busy' || this.status() === 'loading');\n\n private pipe: PipelineLike | null = null;\n private loading: Promise<void> | null = null;\n\n constructor(\n private readonly request: PipelineRequest,\n private readonly factory: PipelineFactory,\n private readonly config: NgxTransformersConfig,\n ) {}\n\n /** Downloads and initializes the model. Idempotent; retries after error. */\n load(): Promise<void> {\n if (this.pipe) return Promise.resolve();\n this.loading ??= this.doLoad().finally(() => (this.loading = null));\n return this.loading;\n }\n\n /** Runs the pipeline, loading the model first if needed. */\n async run(input: TIn, runOptions?: Record<string, unknown>): Promise<TOut> {\n await this.load();\n this.status.set('busy');\n try {\n return (await this.pipe!(input, runOptions)) as TOut;\n } finally {\n // A failed run leaves the model intact - back to ready either way.\n this.status.set('ready');\n }\n }\n\n /** Frees the model. The handle can be loaded again afterwards. */\n async dispose(): Promise<void> {\n const pipe = this.pipe;\n this.pipe = null;\n this.status.set('idle');\n this.progress.set(null);\n await pipe?.dispose?.();\n }\n\n private async doLoad(): Promise<void> {\n this.status.set('loading');\n this.error.set(null);\n const options: Record<string, unknown> = {\n ...this.config.pipelineOptions,\n ...this.request.options,\n progress_callback: (event: RawProgressEvent) => this.onProgress(event),\n };\n const device = this.request.device ?? this.config.device;\n const dtype = this.request.dtype ?? this.config.dtype;\n if (device && device !== 'auto') options['device'] = device;\n if (dtype) options['dtype'] = dtype;\n\n try {\n this.pipe = await this.factory(this.request.task, this.request.model, options);\n this.progress.set(null);\n this.status.set('ready');\n } catch (err) {\n this.error.set(err);\n this.status.set('error');\n throw err;\n }\n }\n\n private onProgress(event: RawProgressEvent): void {\n if (event.status !== 'progress' || !event.file) return;\n this.progress.set({\n file: event.file,\n progress: Math.round(event.progress ?? 0),\n loadedBytes: event.loaded ?? 0,\n totalBytes: event.total ?? 0,\n });\n }\n}\n\n/**\n * Creates a PipelineHandle in an injection context (constructor, field\n * initializer, or runInInjectionContext). The handle is disposed with the\n * surrounding component/injector.\n */\nexport function createPipeline<TIn = unknown, TOut = unknown>(request: PipelineRequest): PipelineHandle<TIn, TOut> {\n const handle = new PipelineHandle<TIn, TOut>(\n request,\n inject(PIPELINE_FACTORY),\n inject(NGX_TRANSFORMERS_CONFIG),\n );\n inject(DestroyRef, { optional: true })?.onDestroy(() => void handle.dispose());\n return handle;\n}\n","import { DestroyRef, inject } from '@angular/core';\nimport { PipelineHandle } from './pipeline';\nimport type { ClassificationResult, PipelineRequest } from './transformers.models';\nimport { NGX_TRANSFORMERS_CONFIG, PIPELINE_FACTORY } from './transformers.providers';\n\nexport const DEFAULT_TEXT_CLASSIFICATION_MODEL = 'Xenova/distilbert-base-uncased-finetuned-sst-2-english';\n\n/**\n * Text classification (sentiment by default). The default model is\n * DistilBERT SST-2 (~65 MB, q8) - swap `model` for any Transformers.js\n * compatible text-classification checkpoint.\n */\nexport class TextClassifier extends PipelineHandle<string, ClassificationResult[] | ClassificationResult[][]> {\n /** Classifies one text; resolves to labels sorted by score (top first). */\n async classify(text: string, topK = 1): Promise<ClassificationResult[]> {\n const out = await this.run(text, { top_k: topK });\n // Single input: transformers.js returns a flat array of {label, score}.\n const flat = (Array.isArray(out[0]) ? out[0] : out) as ClassificationResult[];\n return [...flat].sort((a, b) => b.score - a.score);\n }\n}\n\n/** Creates a TextClassifier in an injection context. */\nexport function createTextClassifier(options: Partial<Omit<PipelineRequest, 'task'>> = {}): TextClassifier {\n const classifier = new TextClassifier(\n { task: 'text-classification', model: DEFAULT_TEXT_CLASSIFICATION_MODEL, ...options },\n inject(PIPELINE_FACTORY),\n inject(NGX_TRANSFORMERS_CONFIG),\n );\n inject(DestroyRef, { optional: true })?.onDestroy(() => void classifier.dispose());\n return classifier;\n}\n","import { DestroyRef, inject } from '@angular/core';\nimport { PipelineHandle } from './pipeline';\nimport type { PipelineRequest, RankedResult } from './transformers.models';\nimport { NGX_TRANSFORMERS_CONFIG, PIPELINE_FACTORY } from './transformers.providers';\n\nexport const DEFAULT_EMBEDDING_MODEL = 'Xenova/all-MiniLM-L6-v2';\n\n/** Tensor-ish output of feature-extraction: enough shape to read rows. */\ninterface TensorLike {\n dims: number[];\n data: ArrayLike<number>;\n tolist?: () => number[][];\n}\n\n/** Cosine similarity of two vectors. For normalized vectors this is the dot product. */\nexport function cosineSimilarity(a: readonly number[], b: readonly number[]): number {\n if (a.length !== b.length || a.length === 0) {\n throw new Error(`cosineSimilarity: incompatible lengths ${a.length} and ${b.length}`);\n }\n let dot = 0;\n let normA = 0;\n let normB = 0;\n for (let i = 0; i < a.length; i++) {\n dot += a[i] * b[i];\n normA += a[i] * a[i];\n normB += b[i] * b[i];\n }\n const denom = Math.sqrt(normA) * Math.sqrt(normB);\n return denom === 0 ? 0 : dot / denom;\n}\n\n/**\n * Sentence embeddings via feature-extraction (all-MiniLM-L6-v2 by default,\n * ~23 MB q8). Outputs mean-pooled, L2-normalized vectors ready for cosine\n * similarity / semantic search.\n */\nexport class TextEmbedder extends PipelineHandle<string | string[], TensorLike> {\n /** Embeds one or many texts; always resolves to one vector per text. */\n async embed(texts: string | string[]): Promise<number[][]> {\n const input = Array.isArray(texts) ? texts : [texts];\n if (input.length === 0) return [];\n const out = await this.run(input, { pooling: 'mean', normalize: true });\n return toRows(out);\n }\n\n /** Cosine similarity of two texts in [-1, 1]. */\n async similarity(a: string, b: string): Promise<number> {\n const [va, vb] = await this.embed([a, b]);\n return cosineSimilarity(va, vb);\n }\n\n /** Ranks documents against a query, most similar first. */\n async rank(query: string, documents: string[]): Promise<RankedResult[]> {\n if (documents.length === 0) return [];\n const [queryVec, ...docVecs] = await this.embed([query, ...documents]);\n return docVecs\n .map((vec, index) => ({ text: documents[index], score: cosineSimilarity(queryVec, vec), index }))\n .sort((a, b) => b.score - a.score);\n }\n}\n\nfunction toRows(out: TensorLike): number[][] {\n if (typeof out.tolist === 'function') return out.tolist();\n const [rows, cols] = out.dims.length === 2 ? out.dims : [1, out.dims[out.dims.length - 1]];\n const data = out.data;\n const result: number[][] = [];\n for (let r = 0; r < rows; r++) {\n result.push(Array.from({ length: cols }, (_, c) => data[r * cols + c]));\n }\n return result;\n}\n\n/** Creates a TextEmbedder in an injection context. */\nexport function createTextEmbedder(options: Partial<Omit<PipelineRequest, 'task'>> = {}): TextEmbedder {\n const embedder = new TextEmbedder(\n { task: 'feature-extraction', model: DEFAULT_EMBEDDING_MODEL, ...options },\n inject(PIPELINE_FACTORY),\n inject(NGX_TRANSFORMERS_CONFIG),\n );\n inject(DestroyRef, { optional: true })?.onDestroy(() => void embedder.dispose());\n return embedder;\n}\n","import { DestroyRef, inject } from '@angular/core';\nimport { PipelineHandle } from './pipeline';\nimport type { PipelineRequest, TranscribeOptions, Transcription, TranscriptionChunk } from './transformers.models';\nimport { NGX_TRANSFORMERS_CONFIG, PIPELINE_FACTORY } from './transformers.providers';\n\n// onnx-community, not the legacy Xenova export: the old ONNX decoder lacks\n// the quantization scales transformers.js v4's runtime requires.\nexport const DEFAULT_ASR_MODEL = 'onnx-community/whisper-tiny.en';\n\n/** Whisper expects 16 kHz mono float PCM. */\nexport const WHISPER_SAMPLE_RATE = 16000;\n\n/** Raw ASR pipeline output (subset we consume). */\ninterface RawTranscription {\n text?: string;\n chunks?: { text: string; timestamp?: [number | null, number | null] }[];\n}\n\n/**\n * Decodes an encoded audio file (wav/mp3/webm/ogg - anything the browser\n * can decode) to 16 kHz mono Float32Array, ready for Whisper. Browser-only:\n * uses AudioContext with a target sample rate, which resamples on decode.\n */\nexport async function decodeAudio(\n source: Blob | ArrayBuffer,\n targetSampleRate = WHISPER_SAMPLE_RATE,\n): Promise<Float32Array> {\n const AudioContextCtor = (globalThis as { AudioContext?: typeof AudioContext }).AudioContext;\n if (!AudioContextCtor) {\n throw new Error('decodeAudio requires a browser AudioContext (not available in this environment).');\n }\n const buffer = source instanceof Blob ? await source.arrayBuffer() : source;\n const ctx = new AudioContextCtor({ sampleRate: targetSampleRate });\n try {\n const decoded = await ctx.decodeAudioData(buffer);\n if (decoded.numberOfChannels === 1) return decoded.getChannelData(0);\n // Mix down to mono by averaging channels.\n const left = decoded.getChannelData(0);\n const right = decoded.getChannelData(1);\n const mono = new Float32Array(left.length);\n for (let i = 0; i < left.length; i++) mono[i] = (left[i] + right[i]) / 2;\n return mono;\n } finally {\n await ctx.close();\n }\n}\n\n/**\n * Speech-to-text via Whisper. The default checkpoint is whisper-tiny.en\n * (~41 MB q8, English-only); swap `model` for a multilingual or larger one\n * (e.g. Xenova/whisper-small).\n */\nexport class SpeechRecognizer extends PipelineHandle<string | Float32Array, RawTranscription | RawTranscription[]> {\n /**\n * Transcribes audio: a URL, a decoded 16 kHz Float32Array, or an encoded\n * Blob/ArrayBuffer (decoded via decodeAudio first - browser-only).\n */\n async transcribe(\n audio: string | Float32Array | Blob | ArrayBuffer,\n options: TranscribeOptions = {},\n ): Promise<Transcription> {\n const input =\n audio instanceof Blob || audio instanceof ArrayBuffer ? await decodeAudio(audio) : audio;\n\n const runOptions: Record<string, unknown> = {};\n if (options.returnTimestamps !== undefined) runOptions['return_timestamps'] = options.returnTimestamps;\n if (options.chunkLengthS !== undefined) runOptions['chunk_length_s'] = options.chunkLengthS;\n if (options.strideLengthS !== undefined) runOptions['stride_length_s'] = options.strideLengthS;\n if (options.language !== undefined) runOptions['language'] = options.language;\n if (options.task !== undefined) runOptions['task'] = options.task;\n\n const raw = await this.run(input, runOptions);\n const first = (Array.isArray(raw) ? raw[0] : raw) ?? {};\n const chunks: TranscriptionChunk[] | undefined = first.chunks?.map((c) => ({\n text: c.text,\n start: c.timestamp?.[0] ?? null,\n end: c.timestamp?.[1] ?? null,\n }));\n return { text: (first.text ?? '').trim(), ...(chunks ? { chunks } : {}) };\n }\n}\n\n/**\n * Creates a SpeechRecognizer in an injection context.\n *\n * dtype defaults to 'q4' (not the library-wide default): q8 Whisper\n * decoders fail session creation on the v4 WASM runtime\n * (huggingface/transformers.js#1707), while q4 loads and runs everywhere.\n */\nexport function createSpeechRecognizer(options: Partial<Omit<PipelineRequest, 'task'>> = {}): SpeechRecognizer {\n const recognizer = new SpeechRecognizer(\n { task: 'automatic-speech-recognition', model: DEFAULT_ASR_MODEL, dtype: 'q4', ...options },\n inject(PIPELINE_FACTORY),\n inject(NGX_TRANSFORMERS_CONFIG),\n );\n inject(DestroyRef, { optional: true })?.onDestroy(() => void recognizer.dispose());\n return recognizer;\n}\n","import { DestroyRef, inject, signal } from '@angular/core';\n\n/** Minimal recorder surface - lets tests (and exotic runtimes) inject fakes. */\nexport interface RecorderLike {\n start(): void;\n stop(): void;\n ondataavailable: ((event: { data: Blob }) => void) | null;\n onstop: (() => void) | null;\n mimeType?: string;\n}\n\nexport interface MicRecorderDeps {\n /** Defaults to navigator.mediaDevices.getUserMedia({ audio: true }). */\n getUserMedia?: () => Promise<MediaStream>;\n /** Defaults to new MediaRecorder(stream). */\n createRecorder?: (stream: MediaStream) => RecorderLike;\n}\n\n/**\n * Microphone capture with a signals API. start() asks for mic permission\n * and records; stop() resolves with the encoded audio Blob - pass it to\n * SpeechRecognizer.transcribe(). Browser-only.\n *\n * ```ts\n * readonly mic = createMicRecorder();\n * readonly whisper = createSpeechRecognizer();\n *\n * async toggle() {\n * if (this.mic.recording()) {\n * const audio = await this.mic.stop();\n * const { text } = await this.whisper.transcribe(audio);\n * } else {\n * await this.mic.start();\n * }\n * }\n * ```\n */\nexport class MicRecorder {\n readonly recording = signal(false);\n /** Elapsed recording time in whole seconds. */\n readonly seconds = signal(0);\n readonly error = signal<unknown>(null);\n\n private stream: MediaStream | null = null;\n private recorder: RecorderLike | null = null;\n private chunks: Blob[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n\n constructor(private readonly deps: MicRecorderDeps = {}) {}\n\n /** Requests the microphone and starts recording. No-op while recording. */\n async start(): Promise<void> {\n if (this.recording()) return;\n this.error.set(null);\n try {\n this.stream = await (this.deps.getUserMedia\n ? this.deps.getUserMedia()\n : navigator.mediaDevices.getUserMedia({ audio: true }));\n this.recorder = this.deps.createRecorder\n ? this.deps.createRecorder(this.stream)\n : (new MediaRecorder(this.stream) as unknown as RecorderLike);\n this.chunks = [];\n this.recorder.ondataavailable = (event) => {\n if (event.data.size > 0) this.chunks.push(event.data);\n };\n this.recorder.start();\n this.recording.set(true);\n this.seconds.set(0);\n this.timer = setInterval(() => this.seconds.set(this.seconds() + 1), 1000);\n } catch (err) {\n this.error.set(err);\n this.cleanup();\n throw err;\n }\n }\n\n /** Stops recording and resolves with the captured audio. */\n stop(): Promise<Blob> {\n const recorder = this.recorder;\n if (!recorder || !this.recording()) {\n return Promise.reject(new Error('MicRecorder.stop() called while not recording.'));\n }\n return new Promise<Blob>((resolve) => {\n recorder.onstop = () => {\n const blob = new Blob(this.chunks, { type: recorder.mimeType || 'audio/webm' });\n this.cleanup();\n resolve(blob);\n };\n recorder.stop();\n });\n }\n\n /** Stops tracks and timers without resolving audio (e.g. on destroy). */\n dispose(): void {\n this.cleanup();\n }\n\n private cleanup(): void {\n if (this.timer !== null) clearInterval(this.timer);\n this.timer = null;\n this.stream?.getTracks().forEach((track) => track.stop());\n this.stream = null;\n this.recorder = null;\n this.recording.set(false);\n }\n}\n\n/** Creates a MicRecorder in an injection context; disposed with the component. */\nexport function createMicRecorder(deps: MicRecorderDeps = {}): MicRecorder {\n const recorder = new MicRecorder(deps);\n inject(DestroyRef, { optional: true })?.onDestroy(() => recorder.dispose());\n return recorder;\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport type { ModelProgress, PipelineStatus } from './transformers.models';\n\n/**\n * Drop-in status line for a PipelineHandle: shows model download progress\n * while loading, then the ready/busy/error state. Themeable via CSS custom\n * properties (--nt-accent, --nt-ink, --nt-muted, --nt-track).\n *\n * ```html\n * <ngx-model-progress [status]=\"classifier.status()\" [progress]=\"classifier.progress()\" />\n * ```\n */\n@Component({\n selector: 'ngx-model-progress',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"nt-row\" [class.nt-error]=\"status() === 'error'\" role=\"status\">\n <span class=\"nt-dot\" [attr.data-status]=\"status()\"></span>\n <span class=\"nt-label\">{{ label() }}</span>\n @if (status() === 'loading' && progress(); as p) {\n <span class=\"nt-file\">{{ p.file }}</span>\n <div class=\"nt-track\" role=\"progressbar\" [attr.aria-valuenow]=\"p.progress\" aria-valuemin=\"0\" aria-valuemax=\"100\">\n <div class=\"nt-fill\" [style.width.%]=\"p.progress\"></div>\n </div>\n <span class=\"nt-pct\">{{ p.progress }}%</span>\n }\n </div>\n `,\n styles: `\n :host {\n --nt-accent: #f59e0b;\n --nt-ink: #1e293b;\n --nt-muted: #64748b;\n --nt-track: #e2e8f0;\n display: block;\n font-size: 12.5px;\n color: var(--nt-ink);\n }\n .nt-row { display: flex; align-items: center; gap: 8px; }\n .nt-dot {\n width: 8px; height: 8px; border-radius: 50%; flex: none;\n background: var(--nt-muted);\n }\n .nt-dot[data-status='loading'] { background: var(--nt-accent); animation: nt-blink 1s ease-in-out infinite; }\n .nt-dot[data-status='ready'] { background: #22c55e; }\n .nt-dot[data-status='busy'] { background: #3b82f6; animation: nt-blink 0.7s ease-in-out infinite; }\n .nt-dot[data-status='error'] { background: #dc2626; }\n .nt-label { font-weight: 600; }\n .nt-file { color: var(--nt-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 180px; }\n .nt-track {\n flex: 1; min-width: 60px; height: 6px; border-radius: 3px;\n background: var(--nt-track); overflow: hidden;\n }\n .nt-fill { height: 100%; background: var(--nt-accent); border-radius: 3px; transition: width 120ms linear; }\n .nt-pct { font-variant-numeric: tabular-nums; color: var(--nt-muted); }\n .nt-error .nt-label { color: #dc2626; }\n @keyframes nt-blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }\n @media (prefers-reduced-motion: reduce) {\n .nt-dot { animation: none; }\n .nt-fill { transition: none; }\n }\n `,\n})\nexport class ModelProgressComponent {\n status = input.required<PipelineStatus>();\n progress = input<ModelProgress | null>(null);\n /** Labels per status; override to localize. */\n labels = input<Partial<Record<PipelineStatus, string>>>({});\n\n readonly label = computed(() => {\n const defaults: Record<PipelineStatus, string> = {\n idle: 'Model not loaded',\n loading: 'Downloading model',\n ready: 'Model ready',\n busy: 'Running',\n error: 'Failed to load model',\n };\n return this.labels()[this.status()] ?? defaults[this.status()];\n });\n}\n","/*\n * Public API Surface of ngx-transformers\n */\n\nexport * from './lib/transformers.models';\nexport * from './lib/transformers.providers';\nexport * from './lib/pipeline';\nexport * from './lib/text-classifier';\nexport * from './lib/text-embedder';\nexport * from './lib/speech-recognizer';\nexport * from './lib/mic-recorder';\nexport * from './lib/model-progress.component';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngx-transformers.mjs","sources":["../../../projects/ngx-transformers/src/lib/transformers.providers.ts","../../../projects/ngx-transformers/src/lib/device-detection.ts","../../../projects/ngx-transformers/src/lib/pipeline.ts","../../../projects/ngx-transformers/src/lib/text-classifier.ts","../../../projects/ngx-transformers/src/lib/text-embedder.ts","../../../projects/ngx-transformers/src/lib/speech-recognizer.ts","../../../projects/ngx-transformers/src/lib/zero-shot-classifier.ts","../../../projects/ngx-transformers/src/lib/translator.ts","../../../projects/ngx-transformers/src/lib/mic-recorder.ts","../../../projects/ngx-transformers/src/lib/model-progress.component.ts","../../../projects/ngx-transformers/src/public-api.ts","../../../projects/ngx-transformers/src/ngx-transformers.ts"],"sourcesContent":["import { EnvironmentProviders, InjectionToken, makeEnvironmentProviders } from '@angular/core';\nimport type { NgxTransformersConfig } from './transformers.models';\n\n/**\n * The callable returned by Transformers.js pipeline(), reduced to the\n * surface this library relies on.\n */\nexport type PipelineLike = ((\n input: unknown,\n options?: Record<string, unknown>,\n) => Promise<unknown>) & {\n dispose?: () => Promise<void>;\n};\n\n/**\n * Creates a pipeline for a task. The default implementation lazy-imports\n * @huggingface/transformers; tests and SSR shims can provide their own.\n */\nexport type PipelineFactory = (\n task: string,\n model: string | undefined,\n options: Record<string, unknown>,\n) => Promise<PipelineLike>;\n\n/**\n * The slice of the @huggingface/transformers module the default factory\n * uses. The real module satisfies it, so an importer can configure\n * `env` and return the module as is.\n */\nexport interface TransformersModuleLike {\n // A method signature on purpose: it keeps the overloaded, generic\n // pipeline() of the real module assignable.\n pipeline(task: string, model?: string, options?: object): Promise<unknown>;\n}\n\nconst importTransformers = (): Promise<TransformersModuleLike> =>\n import('@huggingface/transformers');\n\n/**\n * The factory behind PIPELINE_FACTORY. It imports @huggingface/transformers\n * lazily, on the first pipeline, so the library adds nothing to the initial\n * bundle. Wrap it to add options or logging while keeping the lazy import:\n *\n * ```ts\n * const base = createDefaultPipelineFactory();\n * const factory: PipelineFactory = (task, model, options) =>\n * base(task, model, { ...options, revision: 'v2' });\n * providers: [{ provide: PIPELINE_FACTORY, useValue: factory }]\n * ```\n *\n * `load` is the importer. Tests pass a stub module; apps that need to\n * configure Transformers.js (`env.allowRemoteModels`, `env.localModelPath`)\n * do it there, before returning the module.\n */\nexport function createDefaultPipelineFactory(\n load: () => Promise<TransformersModuleLike> = importTransformers,\n): PipelineFactory {\n return async (task, model, options) => {\n const { pipeline } = await load();\n return (await pipeline(task, model, options)) as PipelineLike;\n };\n}\n\nexport const PIPELINE_FACTORY = new InjectionToken<PipelineFactory>(\n 'ngx-transformers.pipeline-factory',\n {\n providedIn: 'root',\n factory: () => createDefaultPipelineFactory(),\n },\n);\n\nexport const NGX_TRANSFORMERS_CONFIG = new InjectionToken<NgxTransformersConfig>(\n 'ngx-transformers.config',\n {\n providedIn: 'root',\n factory: () => ({}),\n },\n);\n\n/**\n * Sets workspace-wide defaults (device, dtype, extra pipeline options):\n *\n * ```ts\n * bootstrapApplication(App, {\n * providers: [provideTransformers({ device: 'webgpu', dtype: 'q8' })],\n * });\n * ```\n */\nexport function provideTransformers(config: NgxTransformersConfig): EnvironmentProviders {\n return makeEnvironmentProviders([{ provide: NGX_TRANSFORMERS_CONFIG, useValue: config }]);\n}\n","/** The slice of navigator.gpu we probe; the WebGPU lib types are not in every project's tsconfig. */\ninterface GpuLike {\n requestAdapter(): Promise<unknown | null>;\n}\n\nlet webGpuProbe: Promise<boolean> | null = null;\n\n/**\n * Whether models can run on WebGPU here: `navigator.gpu` exists AND\n * `requestAdapter()` hands back an adapter - it returns null on machines\n * without a usable GPU or with WebGPU disabled, even when the API exists.\n * Resolves false on the server. The probe runs once and is cached.\n */\nexport function hasWebGpu(): Promise<boolean> {\n webGpuProbe ??= probeWebGpu();\n return webGpuProbe;\n}\n\n/** `'webgpu'` when hasWebGpu() resolves true, else `'wasm'`. */\nexport async function detectDevice(): Promise<'webgpu' | 'wasm'> {\n return (await hasWebGpu()) ? 'webgpu' : 'wasm';\n}\n\n/** Forgets the cached probe so the next hasWebGpu() asks the browser again. */\nexport function resetDeviceDetection(): void {\n webGpuProbe = null;\n}\n\nasync function probeWebGpu(): Promise<boolean> {\n const gpu = (globalThis as { navigator?: { gpu?: GpuLike } }).navigator?.gpu;\n if (!gpu) return false;\n try {\n return (await gpu.requestAdapter()) !== null;\n } catch {\n return false;\n }\n}\n","import { DestroyRef, computed, inject, signal } from '@angular/core';\nimport { detectDevice } from './device-detection';\nimport type {\n ModelProgress,\n NgxTransformersConfig,\n PipelineRequest,\n PipelineStatus,\n TransformersDevice,\n} from './transformers.models';\nimport {\n NGX_TRANSFORMERS_CONFIG,\n PIPELINE_FACTORY,\n type PipelineFactory,\n type PipelineLike,\n} from './transformers.providers';\n\n/** Shape of Transformers.js progress_callback events (subset we consume). */\ninterface RawProgressEvent {\n status?: string;\n file?: string;\n progress?: number;\n loaded?: number;\n total?: number;\n}\n\n/** A pipeline callable with the positional extras some tasks take after the input. */\ntype Runnable = (...args: unknown[]) => Promise<unknown>;\n\n/**\n * One load-to-dispose span of a handle. dispose() swaps in a fresh session,\n * so async work still holding the old one knows it was cancelled and keeps\n * its bookkeeping (in-flight runs, the loading promise) to itself.\n */\ninterface Session {\n pipe: PipelineLike | null;\n loading: Promise<void> | null;\n /** Runs executing against this session's pipe. */\n inFlight: number;\n}\n\nconst newSession = (): Session => ({ pipe: null, loading: null, inFlight: 0 });\n\n/**\n * A lazily-loaded Transformers.js pipeline wrapped in signals.\n *\n * The model is not downloaded until load() or the first run() call, so\n * creating handles is free. All state is exposed as signals and safe to\n * bind in zoneless apps.\n */\nexport class PipelineHandle<TIn = unknown, TOut = unknown> {\n /** idle -> loading -> ready <-> busy; error only on load failure. */\n readonly status = signal<PipelineStatus>('idle');\n /** Download progress for the file currently transferring, else null. */\n readonly progress = signal<ModelProgress | null>(null);\n readonly error = signal<unknown>(null);\n /** True once the model is usable (including while a run is in flight). */\n readonly ready = computed(() => this.status() === 'ready' || this.status() === 'busy');\n readonly busy = computed(() => this.status() === 'busy' || this.status() === 'loading');\n\n private session = newSession();\n private destroyed = false;\n\n constructor(\n private readonly request: PipelineRequest,\n private readonly factory: PipelineFactory,\n private readonly config: NgxTransformersConfig,\n ) {}\n\n /** Downloads and initializes the model. Idempotent; retries after error. */\n load(): Promise<void> {\n if (this.destroyed) return Promise.reject(destroyedError());\n const session = this.session;\n if (session.pipe) return Promise.resolve();\n session.loading ??= this.doLoad(session).finally(() => {\n session.loading = null;\n });\n return session.loading;\n }\n\n /** Runs the pipeline, loading the model first if needed. */\n run(input: TIn, runOptions?: Record<string, unknown>): Promise<TOut> {\n return this.runWith(input, runOptions);\n }\n\n /**\n * Runs the pipeline with extra positional arguments after the input, for\n * tasks whose pipeline signature is not (input, options): zero-shot\n * classification takes (text, candidateLabels, options).\n */\n protected async runWith(input: TIn, ...extraArgs: unknown[]): Promise<TOut> {\n await this.load();\n const session = this.session;\n const pipe = session.pipe;\n if (!pipe) {\n throw new Error('PipelineHandle: disposed before the model finished loading.');\n }\n session.inFlight++;\n this.status.set('busy');\n try {\n return (await (pipe as Runnable)(input, ...extraArgs)) as TOut;\n } finally {\n // Back to ready once the last overlapping run finishes - a failed run\n // leaves the model intact. A run that outlived dispose() settles its\n // own, retired session and leaves the status alone.\n session.inFlight--;\n if (session.inFlight === 0 && this.session === session) this.status.set('ready');\n }\n }\n\n /**\n * Frees the model. A download still in flight is cancelled: its model is\n * released on arrival and the handle stays idle. The handle can be loaded\n * again afterwards; destroy() is the terminal variant.\n */\n async dispose(): Promise<void> {\n const session = this.session;\n this.session = newSession();\n this.status.set('idle');\n this.progress.set(null);\n await session.pipe?.dispose?.();\n }\n\n /**\n * dispose() for good: later load() and run() calls reject instead of\n * downloading a model nobody would release. create*() registers this with\n * the DestroyRef of the injection context, so a handle declared in a\n * component ends with the component.\n */\n destroy(): Promise<void> {\n this.destroyed = true;\n return this.dispose();\n }\n\n private async doLoad(session: Session): Promise<void> {\n this.status.set('loading');\n this.error.set(null);\n const options: Record<string, unknown> = {\n ...this.config.pipelineOptions,\n ...this.request.options,\n progress_callback: (event: RawProgressEvent) => this.onProgress(event, session),\n };\n const device = await this.resolveDevice();\n // Disposed while probing the device: do not start the download at all.\n if (this.session !== session) return;\n const dtype = this.request.dtype ?? this.config.dtype;\n if (device && device !== 'auto') options['device'] = device;\n if (dtype) options['dtype'] = dtype;\n\n try {\n const pipe = await this.factory(this.request.task, this.request.model, options);\n if (this.session !== session) {\n // Disposed while downloading: nobody wants this model any more.\n await pipe.dispose?.();\n return;\n }\n session.pipe = pipe;\n this.progress.set(null);\n this.status.set('ready');\n } catch (err) {\n // A cancelled load is not an error - dispose() already reset the handle.\n if (this.session !== session) return;\n this.error.set(err);\n this.status.set('error');\n throw err;\n }\n }\n\n /**\n * An explicit request or global device wins; with `autoDevice` on, an\n * unset or 'auto' device is probed for WebGPU (once, cached) at load time.\n */\n private async resolveDevice(): Promise<TransformersDevice | undefined> {\n const configured = this.request.device ?? this.config.device;\n if (configured && configured !== 'auto') return configured;\n return this.config.autoDevice ? detectDevice() : configured;\n }\n\n private onProgress(event: RawProgressEvent, session: Session): void {\n // Events from a load that dispose() cancelled must not resurrect the bar.\n if (this.session !== session) return;\n if (event.status !== 'progress' || !event.file) return;\n this.progress.set({\n file: event.file,\n progress: Math.round(event.progress ?? 0),\n loadedBytes: event.loaded ?? 0,\n totalBytes: event.total ?? 0,\n });\n }\n}\n\nfunction destroyedError(): Error {\n return new Error('PipelineHandle: destroyed with its component; create a new handle.');\n}\n\n/**\n * Creates a PipelineHandle in an injection context (constructor, field\n * initializer, or runInInjectionContext). The handle is destroyed with the\n * surrounding component/injector.\n */\nexport function createPipeline<TIn = unknown, TOut = unknown>(\n request: PipelineRequest,\n): PipelineHandle<TIn, TOut> {\n const handle = new PipelineHandle<TIn, TOut>(\n request,\n inject(PIPELINE_FACTORY),\n inject(NGX_TRANSFORMERS_CONFIG),\n );\n inject(DestroyRef, { optional: true })?.onDestroy(() => void handle.destroy());\n return handle;\n}\n","import { DestroyRef, inject } from '@angular/core';\nimport { PipelineHandle } from './pipeline';\nimport type { ClassificationResult, PipelineRequest } from './transformers.models';\nimport { NGX_TRANSFORMERS_CONFIG, PIPELINE_FACTORY } from './transformers.providers';\n\nexport const DEFAULT_TEXT_CLASSIFICATION_MODEL =\n 'Xenova/distilbert-base-uncased-finetuned-sst-2-english';\n\n/**\n * Text classification (sentiment by default). The default model is\n * DistilBERT SST-2 (~65 MB, q8) - swap `model` for any Transformers.js\n * compatible text-classification checkpoint.\n */\nexport class TextClassifier extends PipelineHandle<\n string,\n ClassificationResult[] | ClassificationResult[][]\n> {\n /** Classifies one text; resolves to labels sorted by score (top first). */\n async classify(text: string, topK = 1): Promise<ClassificationResult[]> {\n const out = await this.run(text, { top_k: topK });\n // Single input: transformers.js returns a flat array of {label, score}.\n const flat = (Array.isArray(out[0]) ? out[0] : out) as ClassificationResult[];\n return [...flat].sort((a, b) => b.score - a.score);\n }\n}\n\n/** Creates a TextClassifier in an injection context. */\nexport function createTextClassifier(\n options: Partial<Omit<PipelineRequest, 'task'>> = {},\n): TextClassifier {\n const classifier = new TextClassifier(\n { task: 'text-classification', model: DEFAULT_TEXT_CLASSIFICATION_MODEL, ...options },\n inject(PIPELINE_FACTORY),\n inject(NGX_TRANSFORMERS_CONFIG),\n );\n inject(DestroyRef, { optional: true })?.onDestroy(() => void classifier.destroy());\n return classifier;\n}\n","import { DestroyRef, inject } from '@angular/core';\nimport { PipelineHandle } from './pipeline';\nimport type { PipelineRequest, RankedResult } from './transformers.models';\nimport { NGX_TRANSFORMERS_CONFIG, PIPELINE_FACTORY } from './transformers.providers';\n\nexport const DEFAULT_EMBEDDING_MODEL = 'Xenova/all-MiniLM-L6-v2';\n\n/** Tensor-ish output of feature-extraction: enough shape to read rows. */\ninterface TensorLike {\n dims: number[];\n data: ArrayLike<number>;\n tolist?: () => number[][];\n}\n\n/** Cosine similarity of two vectors. For normalized vectors this is the dot product. */\nexport function cosineSimilarity(a: readonly number[], b: readonly number[]): number {\n if (a.length !== b.length || a.length === 0) {\n throw new Error(`cosineSimilarity: incompatible lengths ${a.length} and ${b.length}`);\n }\n let dot = 0;\n let normA = 0;\n let normB = 0;\n for (let i = 0; i < a.length; i++) {\n dot += a[i] * b[i];\n normA += a[i] * a[i];\n normB += b[i] * b[i];\n }\n const denom = Math.sqrt(normA) * Math.sqrt(normB);\n return denom === 0 ? 0 : dot / denom;\n}\n\n/**\n * Sentence embeddings via feature-extraction (all-MiniLM-L6-v2 by default,\n * ~23 MB q8). Outputs mean-pooled, L2-normalized vectors ready for cosine\n * similarity / semantic search.\n */\nexport class TextEmbedder extends PipelineHandle<string | string[], TensorLike> {\n /** Embeds one or many texts; always resolves to one vector per text. */\n async embed(texts: string | string[]): Promise<number[][]> {\n const input = Array.isArray(texts) ? texts : [texts];\n if (input.length === 0) return [];\n const out = await this.run(input, { pooling: 'mean', normalize: true });\n return toRows(out);\n }\n\n /** Cosine similarity of two texts in [-1, 1]. */\n async similarity(a: string, b: string): Promise<number> {\n const [va, vb] = await this.embed([a, b]);\n return cosineSimilarity(va, vb);\n }\n\n /** Ranks documents against a query, most similar first. */\n async rank(query: string, documents: string[]): Promise<RankedResult[]> {\n if (documents.length === 0) return [];\n const [queryVec, ...docVecs] = await this.embed([query, ...documents]);\n return docVecs\n .map((vec, index) => ({\n text: documents[index],\n score: cosineSimilarity(queryVec, vec),\n index,\n }))\n .sort((a, b) => b.score - a.score);\n }\n}\n\nfunction toRows(out: TensorLike): number[][] {\n if (typeof out.tolist === 'function') return out.tolist();\n const [rows, cols] = out.dims.length === 2 ? out.dims : [1, out.dims[out.dims.length - 1]];\n const data = out.data;\n const result: number[][] = [];\n for (let r = 0; r < rows; r++) {\n result.push(Array.from({ length: cols }, (_, c) => data[r * cols + c]));\n }\n return result;\n}\n\n/** Creates a TextEmbedder in an injection context. */\nexport function createTextEmbedder(\n options: Partial<Omit<PipelineRequest, 'task'>> = {},\n): TextEmbedder {\n const embedder = new TextEmbedder(\n { task: 'feature-extraction', model: DEFAULT_EMBEDDING_MODEL, ...options },\n inject(PIPELINE_FACTORY),\n inject(NGX_TRANSFORMERS_CONFIG),\n );\n inject(DestroyRef, { optional: true })?.onDestroy(() => void embedder.destroy());\n return embedder;\n}\n","import { DestroyRef, inject } from '@angular/core';\nimport { PipelineHandle } from './pipeline';\nimport type {\n PipelineRequest,\n TranscribeOptions,\n Transcription,\n TranscriptionChunk,\n} from './transformers.models';\nimport { NGX_TRANSFORMERS_CONFIG, PIPELINE_FACTORY } from './transformers.providers';\n\n// onnx-community, not the legacy Xenova export: the old ONNX decoder lacks\n// the quantization scales transformers.js v4's runtime requires.\nexport const DEFAULT_ASR_MODEL = 'onnx-community/whisper-tiny.en';\n\n/** Whisper expects 16 kHz mono float PCM. */\nexport const WHISPER_SAMPLE_RATE = 16000;\n\n/** Raw ASR pipeline output (subset we consume). */\ninterface RawTranscription {\n text?: string;\n chunks?: { text: string; timestamp?: [number | null, number | null] }[];\n}\n\n/**\n * Decodes an encoded audio file (wav/mp3/webm/ogg - anything the browser\n * can decode) to 16 kHz mono Float32Array, ready for Whisper. Browser-only:\n * uses AudioContext with a target sample rate, which resamples on decode.\n */\nexport async function decodeAudio(\n source: Blob | ArrayBuffer,\n targetSampleRate = WHISPER_SAMPLE_RATE,\n): Promise<Float32Array> {\n const AudioContextCtor = (globalThis as { AudioContext?: typeof AudioContext }).AudioContext;\n if (!AudioContextCtor) {\n throw new Error(\n 'decodeAudio requires a browser AudioContext (not available in this environment).',\n );\n }\n const buffer = source instanceof Blob ? await source.arrayBuffer() : source;\n const ctx = new AudioContextCtor({ sampleRate: targetSampleRate });\n try {\n const decoded = await ctx.decodeAudioData(buffer);\n if (decoded.numberOfChannels === 1) return decoded.getChannelData(0);\n // Mix down to mono by averaging channels.\n const left = decoded.getChannelData(0);\n const right = decoded.getChannelData(1);\n const mono = new Float32Array(left.length);\n for (let i = 0; i < left.length; i++) mono[i] = (left[i] + right[i]) / 2;\n return mono;\n } finally {\n await ctx.close();\n }\n}\n\n/**\n * Speech-to-text via Whisper. The default checkpoint is whisper-tiny.en\n * (~41 MB q8, English-only); swap `model` for a multilingual or larger one\n * (e.g. Xenova/whisper-small).\n */\nexport class SpeechRecognizer extends PipelineHandle<\n string | Float32Array,\n RawTranscription | RawTranscription[]\n> {\n /**\n * Transcribes audio: a URL, a decoded 16 kHz Float32Array, or an encoded\n * Blob/ArrayBuffer (decoded via decodeAudio first - browser-only).\n */\n async transcribe(\n audio: string | Float32Array | Blob | ArrayBuffer,\n options: TranscribeOptions = {},\n ): Promise<Transcription> {\n const input =\n audio instanceof Blob || audio instanceof ArrayBuffer ? await decodeAudio(audio) : audio;\n\n const runOptions: Record<string, unknown> = {};\n if (options.returnTimestamps !== undefined)\n runOptions['return_timestamps'] = options.returnTimestamps;\n if (options.chunkLengthS !== undefined) runOptions['chunk_length_s'] = options.chunkLengthS;\n if (options.strideLengthS !== undefined) runOptions['stride_length_s'] = options.strideLengthS;\n if (options.language !== undefined) runOptions['language'] = options.language;\n if (options.task !== undefined) runOptions['task'] = options.task;\n\n const raw = await this.run(input, runOptions);\n const first = (Array.isArray(raw) ? raw[0] : raw) ?? {};\n const chunks: TranscriptionChunk[] | undefined = first.chunks?.map((c) => ({\n text: c.text,\n start: c.timestamp?.[0] ?? null,\n end: c.timestamp?.[1] ?? null,\n }));\n return { text: (first.text ?? '').trim(), ...(chunks ? { chunks } : {}) };\n }\n}\n\n/**\n * Creates a SpeechRecognizer in an injection context.\n *\n * dtype defaults to 'q4' (not the library-wide default): q8 Whisper\n * decoders fail session creation on the v4 WASM runtime\n * (huggingface/transformers.js#1707), while q4 loads and runs everywhere.\n */\nexport function createSpeechRecognizer(\n options: Partial<Omit<PipelineRequest, 'task'>> = {},\n): SpeechRecognizer {\n const recognizer = new SpeechRecognizer(\n { task: 'automatic-speech-recognition', model: DEFAULT_ASR_MODEL, dtype: 'q4', ...options },\n inject(PIPELINE_FACTORY),\n inject(NGX_TRANSFORMERS_CONFIG),\n );\n inject(DestroyRef, { optional: true })?.onDestroy(() => void recognizer.destroy());\n return recognizer;\n}\n","import { DestroyRef, inject } from '@angular/core';\nimport { PipelineHandle } from './pipeline';\nimport type { ClassificationResult, PipelineRequest, ZeroShotOptions } from './transformers.models';\nimport { NGX_TRANSFORMERS_CONFIG, PIPELINE_FACTORY } from './transformers.providers';\n\n// The Transformers.js docs example for this task and the smallest MNLI\n// checkpoint that behaves well on the WASM runtime (~26 MB q8).\nexport const DEFAULT_ZERO_SHOT_MODEL = 'Xenova/mobilebert-uncased-mnli';\n\n/** Raw zero-shot output: labels ordered by score, scores aligned by index. */\ninterface RawZeroShotResult {\n sequence: string;\n labels: string[];\n scores: number[];\n}\n\n/**\n * Zero-shot classification: score any labels you name against a text with\n * an NLI model, no fine-tuning. The default checkpoint is MobileBERT MNLI\n * (~26 MB q8); Xenova/distilbert-base-uncased-mnli (~80 MB) is the\n * higher-accuracy alternative.\n */\nexport class ZeroShotClassifier extends PipelineHandle<\n string,\n RawZeroShotResult | RawZeroShotResult[]\n> {\n /**\n * Scores each candidate label for one text; resolves sorted by score (top\n * first). Single-label mode (default) normalizes across labels so scores\n * sum to 1; `multiLabel` scores each label on its own.\n */\n async classify(\n text: string,\n labels: readonly string[],\n options: ZeroShotOptions = {},\n ): Promise<ClassificationResult[]> {\n if (labels.length === 0) return [];\n const runOptions: Record<string, unknown> = {};\n if (options.multiLabel !== undefined) runOptions['multi_label'] = options.multiLabel;\n if (options.hypothesisTemplate !== undefined)\n runOptions['hypothesis_template'] = options.hypothesisTemplate;\n\n const raw = await this.runWith(text, [...labels], runOptions);\n const first = Array.isArray(raw) ? raw[0] : raw;\n const scoredLabels = first?.labels ?? [];\n const scores = first?.scores ?? [];\n return scoredLabels\n .map((label, index) => ({ label, score: scores[index] ?? 0 }))\n .sort((left, right) => right.score - left.score);\n }\n}\n\n/** Creates a ZeroShotClassifier in an injection context. */\nexport function createZeroShotClassifier(\n options: Partial<Omit<PipelineRequest, 'task'>> = {},\n): ZeroShotClassifier {\n const classifier = new ZeroShotClassifier(\n { task: 'zero-shot-classification', model: DEFAULT_ZERO_SHOT_MODEL, ...options },\n inject(PIPELINE_FACTORY),\n inject(NGX_TRANSFORMERS_CONFIG),\n );\n inject(DestroyRef, { optional: true })?.onDestroy(() => void classifier.destroy());\n return classifier;\n}\n","import { DestroyRef, computed, inject, signal } from '@angular/core';\nimport { PipelineHandle } from './pipeline';\nimport type {\n NgxTransformersConfig,\n TranslateOptions,\n TranslatorOptions,\n} from './transformers.models';\nimport {\n NGX_TRANSFORMERS_CONFIG,\n PIPELINE_FACTORY,\n type PipelineFactory,\n} from './transformers.providers';\n\n/**\n * Default checkpoint for a language pair: Helsinki-NLP's Marian opus-mt\n * models, one per direction, ~105 MB q8 each (e.g. Xenova/opus-mt-en-ru).\n */\nexport function defaultTranslationModel(from: string, to: string): string {\n return `Xenova/opus-mt-${from}-${to}`;\n}\n\n/** Checkpoint for a pair: provideTransformers({ translationModels }) first, then opus-mt. */\nexport function resolveTranslationModel(\n from: string,\n to: string,\n config: NgxTransformersConfig,\n): string {\n return config.translationModels?.[`${from}-${to}`] ?? defaultTranslationModel(from, to);\n}\n\n/**\n * Multilingual families take the pair per call as src_lang / tgt_lang;\n * Marian opus-mt checkpoints translate one fixed direction and ignore them.\n */\nconst MULTILINGUAL_MODEL_PATTERN = /nllb|m2m[_-]?100|mbart|madlad/i;\n\n/** Raw translation pipeline output, one entry per input text. */\ninterface RawTranslation {\n translation_text?: string;\n}\n\nexport type TranslationHandle = PipelineHandle<string, RawTranslation | RawTranslation[]>;\n\n/**\n * Text translation. Pipelines are created lazily, one per checkpoint: with\n * the default opus-mt family every language pair is its own model, so a\n * Translator holds one handle per pair it has been asked for, while a\n * pinned multilingual `model` (NLLB, M2M100) serves every pair from one.\n *\n * The signals mirror the handle used by the most recent call, so a single\n * `<ngx-model-progress>` covers the translator.\n */\nexport class Translator {\n private readonly active = signal<TranslationHandle | null>(null);\n private readonly handles = new Map<string, TranslationHandle>();\n private destroyed = false;\n\n readonly status = computed(() => this.active()?.status() ?? 'idle');\n readonly progress = computed(() => this.active()?.progress() ?? null);\n readonly error = computed(() => this.active()?.error() ?? null);\n readonly ready = computed(() => this.active()?.ready() ?? false);\n readonly busy = computed(() => this.active()?.busy() ?? false);\n\n constructor(\n private readonly options: TranslatorOptions,\n private readonly factory: PipelineFactory,\n private readonly config: NgxTransformersConfig,\n ) {}\n\n /** Downloads the model for a pair ahead of the first translate() call. */\n async load(pair: TranslateOptions = {}): Promise<void> {\n return this.handleFor(pair).load();\n }\n\n /** Translates one text; the pair defaults to the one given at creation. */\n async translate(text: string, pair: TranslateOptions = {}): Promise<string> {\n const handle = this.handleFor(pair);\n const raw = await handle.run(text, this.languageOptions(pair));\n const first = Array.isArray(raw) ? raw[0] : raw;\n return (first?.translation_text ?? '').trim();\n }\n\n /** Checkpoint for a pair: the pinned model, else the configured or default one. */\n modelFor(pair: TranslateOptions = {}): string {\n if (this.options.model) return this.options.model;\n const from = pair.from ?? this.options.from;\n const to = pair.to ?? this.options.to;\n if (!from || !to) {\n throw new Error(\n 'Translator: no language pair - pass { from, to } to createTranslator() or translate(), or pin a multilingual model.',\n );\n }\n return resolveTranslationModel(from, to, this.config);\n }\n\n /**\n * The handle serving a pair, created on first use. Exposed for apps that\n * keep several pairs warm and want a progress line per model.\n */\n handleFor(pair: TranslateOptions = {}): TranslationHandle {\n if (this.destroyed) {\n throw new Error('Translator: destroyed with its component; create a new translator.');\n }\n const model = this.modelFor(pair);\n let handle = this.handles.get(model);\n if (!handle) {\n handle = new PipelineHandle(\n {\n task: 'translation',\n model,\n device: this.options.device,\n dtype: this.options.dtype,\n options: this.options.options,\n },\n this.factory,\n this.config,\n );\n this.handles.set(model, handle);\n }\n this.active.set(handle);\n return handle;\n }\n\n /**\n * dispose() for good: later calls reject instead of loading a model nobody\n * would release. createTranslator() registers this with the DestroyRef.\n */\n destroy(): Promise<void> {\n this.destroyed = true;\n return this.dispose();\n }\n\n /** Frees every model. The translator can be used again afterwards. */\n async dispose(): Promise<void> {\n const handles = [...this.handles.values()];\n this.handles.clear();\n this.active.set(null);\n await Promise.all(handles.map((handle) => handle.dispose()));\n }\n\n /** src_lang / tgt_lang for multilingual checkpoints; opus-mt models get nothing. */\n private languageOptions(pair: TranslateOptions): Record<string, unknown> {\n const runOptions: Record<string, unknown> = {};\n if (!MULTILINGUAL_MODEL_PATTERN.test(this.modelFor(pair))) return runOptions;\n const from = pair.from ?? this.options.from;\n const to = pair.to ?? this.options.to;\n if (from !== undefined) runOptions['src_lang'] = from;\n if (to !== undefined) runOptions['tgt_lang'] = to;\n return runOptions;\n }\n}\n\n/** Creates a Translator in an injection context. */\nexport function createTranslator(options: TranslatorOptions = {}): Translator {\n const translator = new Translator(\n options,\n inject(PIPELINE_FACTORY),\n inject(NGX_TRANSFORMERS_CONFIG),\n );\n inject(DestroyRef, { optional: true })?.onDestroy(() => void translator.destroy());\n return translator;\n}\n","import { DestroyRef, inject, signal } from '@angular/core';\n\n/** Minimal recorder surface - lets tests (and exotic runtimes) inject fakes. */\nexport interface RecorderLike {\n start(): void;\n stop(): void;\n ondataavailable: ((event: { data: Blob }) => void) | null;\n onstop: (() => void) | null;\n mimeType?: string;\n}\n\nexport interface MicRecorderDeps {\n /** Defaults to navigator.mediaDevices.getUserMedia({ audio: true }). */\n getUserMedia?: () => Promise<MediaStream>;\n /** Defaults to new MediaRecorder(stream). */\n createRecorder?: (stream: MediaStream) => RecorderLike;\n}\n\n/**\n * Microphone capture with a signals API. start() asks for mic permission\n * and records; stop() resolves with the encoded audio Blob - pass it to\n * SpeechRecognizer.transcribe(). Browser-only.\n *\n * ```ts\n * readonly mic = createMicRecorder();\n * readonly whisper = createSpeechRecognizer();\n *\n * async toggle() {\n * if (this.mic.recording()) {\n * const audio = await this.mic.stop();\n * const { text } = await this.whisper.transcribe(audio);\n * } else {\n * await this.mic.start();\n * }\n * }\n * ```\n */\nexport class MicRecorder {\n readonly recording = signal(false);\n /** Elapsed recording time in whole seconds. */\n readonly seconds = signal(0);\n readonly error = signal<unknown>(null);\n\n private stream: MediaStream | null = null;\n private recorder: RecorderLike | null = null;\n private chunks: Blob[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n\n constructor(private readonly deps: MicRecorderDeps = {}) {}\n\n /** Requests the microphone and starts recording. No-op while recording. */\n async start(): Promise<void> {\n if (this.recording()) return;\n this.error.set(null);\n try {\n this.stream = await (this.deps.getUserMedia\n ? this.deps.getUserMedia()\n : navigator.mediaDevices.getUserMedia({ audio: true }));\n this.recorder = this.deps.createRecorder\n ? this.deps.createRecorder(this.stream)\n : (new MediaRecorder(this.stream) as unknown as RecorderLike);\n this.chunks = [];\n this.recorder.ondataavailable = (event) => {\n if (event.data.size > 0) this.chunks.push(event.data);\n };\n this.recorder.start();\n this.recording.set(true);\n this.seconds.set(0);\n this.timer = setInterval(() => this.seconds.set(this.seconds() + 1), 1000);\n } catch (err) {\n this.error.set(err);\n this.cleanup();\n throw err;\n }\n }\n\n /** Stops recording and resolves with the captured audio. */\n stop(): Promise<Blob> {\n const recorder = this.recorder;\n if (!recorder || !this.recording()) {\n return Promise.reject(new Error('MicRecorder.stop() called while not recording.'));\n }\n return new Promise<Blob>((resolve) => {\n recorder.onstop = () => {\n const blob = new Blob(this.chunks, { type: recorder.mimeType || 'audio/webm' });\n this.cleanup();\n resolve(blob);\n };\n recorder.stop();\n });\n }\n\n /** Stops tracks and timers without resolving audio (e.g. on destroy). */\n dispose(): void {\n this.cleanup();\n }\n\n private cleanup(): void {\n if (this.timer !== null) clearInterval(this.timer);\n this.timer = null;\n this.stream?.getTracks().forEach((track) => track.stop());\n this.stream = null;\n this.recorder = null;\n this.recording.set(false);\n }\n}\n\n/** Creates a MicRecorder in an injection context; disposed with the component. */\nexport function createMicRecorder(deps: MicRecorderDeps = {}): MicRecorder {\n const recorder = new MicRecorder(deps);\n inject(DestroyRef, { optional: true })?.onDestroy(() => recorder.dispose());\n return recorder;\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport type { ModelProgress, PipelineStatus } from './transformers.models';\n\n/**\n * Drop-in status line for a PipelineHandle: shows model download progress\n * while loading, then the ready/busy/error state. Themeable via CSS custom\n * properties (--nt-accent, --nt-ink, --nt-muted, --nt-track).\n *\n * ```html\n * <ngx-model-progress [status]=\"classifier.status()\" [progress]=\"classifier.progress()\" />\n * ```\n */\n@Component({\n selector: 'ngx-model-progress',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"nt-row\" [class.nt-error]=\"status() === 'error'\" role=\"status\">\n <span class=\"nt-dot\" [attr.data-status]=\"status()\"></span>\n <span class=\"nt-label\">{{ label() }}</span>\n @if (status() === 'loading' && progress(); as p) {\n <span class=\"nt-file\">{{ p.file }}</span>\n <div\n class=\"nt-track\"\n role=\"progressbar\"\n [attr.aria-valuenow]=\"p.progress\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n >\n <div class=\"nt-fill\" [style.width.%]=\"p.progress\"></div>\n </div>\n <span class=\"nt-pct\">{{ p.progress }}%</span>\n }\n </div>\n `,\n styles: `\n :host {\n --nt-accent: #f59e0b;\n --nt-ink: #1e293b;\n --nt-muted: #64748b;\n --nt-track: #e2e8f0;\n display: block;\n font-size: 12.5px;\n color: var(--nt-ink);\n }\n .nt-row {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n .nt-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex: none;\n background: var(--nt-muted);\n }\n .nt-dot[data-status='loading'] {\n background: var(--nt-accent);\n animation: nt-blink 1s ease-in-out infinite;\n }\n .nt-dot[data-status='ready'] {\n background: #22c55e;\n }\n .nt-dot[data-status='busy'] {\n background: #3b82f6;\n animation: nt-blink 0.7s ease-in-out infinite;\n }\n .nt-dot[data-status='error'] {\n background: #dc2626;\n }\n .nt-label {\n font-weight: 600;\n }\n .nt-file {\n color: var(--nt-muted);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: 180px;\n }\n .nt-track {\n flex: 1;\n min-width: 60px;\n height: 6px;\n border-radius: 3px;\n background: var(--nt-track);\n overflow: hidden;\n }\n .nt-fill {\n height: 100%;\n background: var(--nt-accent);\n border-radius: 3px;\n transition: width 120ms linear;\n }\n .nt-pct {\n font-variant-numeric: tabular-nums;\n color: var(--nt-muted);\n }\n .nt-error .nt-label {\n color: #dc2626;\n }\n @keyframes nt-blink {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.35;\n }\n }\n @media (prefers-reduced-motion: reduce) {\n .nt-dot {\n animation: none;\n }\n .nt-fill {\n transition: none;\n }\n }\n `,\n})\nexport class ModelProgressComponent {\n status = input.required<PipelineStatus>();\n progress = input<ModelProgress | null>(null);\n /** Labels per status; override to localize. */\n labels = input<Partial<Record<PipelineStatus, string>>>({});\n\n readonly label = computed(() => {\n const defaults: Record<PipelineStatus, string> = {\n idle: 'Model not loaded',\n loading: 'Downloading model',\n ready: 'Model ready',\n busy: 'Running',\n error: 'Failed to load model',\n };\n return this.labels()[this.status()] ?? defaults[this.status()];\n });\n}\n","/*\n * Public API Surface of ngx-transformers\n */\n\nexport * from './lib/transformers.models';\nexport * from './lib/transformers.providers';\nexport * from './lib/pipeline';\nexport * from './lib/device-detection';\nexport * from './lib/text-classifier';\nexport * from './lib/text-embedder';\nexport * from './lib/speech-recognizer';\nexport * from './lib/zero-shot-classifier';\nexport * from './lib/translator';\nexport * from './lib/mic-recorder';\nexport * from './lib/model-progress.component';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ngx-transformers",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Run Hugging Face Transformers.js models in Angular - on-device ML with a signals API: text classification, embeddings, semantic search, Whisper speech-to-text. No server, no API key.",
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "Run Hugging Face Transformers.js models in Angular - on-device ML with a signals API: text classification, zero-shot classification, embeddings, semantic search, translation, Whisper speech-to-text. No server, no API key.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
7
7
|
"transformers",
|
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
"signals",
|
|
18
18
|
"whisper",
|
|
19
19
|
"speech-to-text",
|
|
20
|
-
"speech-recognition"
|
|
20
|
+
"speech-recognition",
|
|
21
|
+
"zero-shot-classification",
|
|
22
|
+
"translation"
|
|
21
23
|
],
|
|
22
24
|
"license": "MIT",
|
|
23
25
|
"author": "Muzaffar Qosimov",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as _angular_core from '@angular/core';
|
|
2
2
|
import { InjectionToken, EnvironmentProviders } from '@angular/core';
|
|
3
|
+
import * as ngx_transformers from 'ngx-transformers';
|
|
3
4
|
|
|
4
5
|
/** Lifecycle of a model pipeline. `error` is terminal until load() is retried. */
|
|
5
6
|
type PipelineStatus = 'idle' | 'loading' | 'ready' | 'busy' | 'error';
|
|
@@ -17,9 +18,19 @@ type TransformersDtype = 'fp32' | 'fp16' | 'q8' | 'q4';
|
|
|
17
18
|
/** Global defaults applied to every pipeline; see provideTransformers(). */
|
|
18
19
|
interface NgxTransformersConfig {
|
|
19
20
|
device?: TransformersDevice;
|
|
21
|
+
/**
|
|
22
|
+
* Probe WebGPU once and use it when available, else WASM, for every
|
|
23
|
+
* handle that sets no device (or 'auto'). An explicit device still wins.
|
|
24
|
+
*/
|
|
25
|
+
autoDevice?: boolean;
|
|
20
26
|
dtype?: TransformersDtype;
|
|
21
27
|
/** Extra options forwarded verbatim to every pipeline() call. */
|
|
22
28
|
pipelineOptions?: Record<string, unknown>;
|
|
29
|
+
/**
|
|
30
|
+
* Translation checkpoints per language pair, keyed "from-to" (e.g.
|
|
31
|
+
* "en-ru"). Pairs not listed fall back to Xenova/opus-mt-{from}-{to}.
|
|
32
|
+
*/
|
|
33
|
+
translationModels?: Record<string, string>;
|
|
23
34
|
}
|
|
24
35
|
/** Per-pipeline request; wins over the global config where both are set. */
|
|
25
36
|
interface PipelineRequest {
|
|
@@ -66,6 +77,28 @@ interface TranscribeOptions {
|
|
|
66
77
|
/** 'transcribe' (default) or 'translate' (multilingual checkpoints only). */
|
|
67
78
|
task?: 'transcribe' | 'translate';
|
|
68
79
|
}
|
|
80
|
+
/** Options for ZeroShotClassifier.classify(). */
|
|
81
|
+
interface ZeroShotOptions {
|
|
82
|
+
/** Score every label on its own (several can be high) instead of picking one. */
|
|
83
|
+
multiLabel?: boolean;
|
|
84
|
+
/** NLI hypothesis with a {} placeholder for the label; default "This example is {}.". */
|
|
85
|
+
hypothesisTemplate?: string;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Language pair for one translate() call. Codes follow the checkpoint:
|
|
89
|
+
* ISO 639-1 for opus-mt ("en", "ru"), FLORES-200 for NLLB ("eng_Latn").
|
|
90
|
+
*/
|
|
91
|
+
interface TranslateOptions {
|
|
92
|
+
from?: string;
|
|
93
|
+
to?: string;
|
|
94
|
+
}
|
|
95
|
+
/** createTranslator() options: a default pair and/or a pinned checkpoint. */
|
|
96
|
+
interface TranslatorOptions extends Partial<Omit<PipelineRequest, 'task'>> {
|
|
97
|
+
/** Default source language for translate(); required unless every call passes one or `model` is pinned. */
|
|
98
|
+
from?: string;
|
|
99
|
+
/** Default target language for translate(). */
|
|
100
|
+
to?: string;
|
|
101
|
+
}
|
|
69
102
|
|
|
70
103
|
/**
|
|
71
104
|
* The callable returned by Transformers.js pipeline(), reduced to the
|
|
@@ -79,6 +112,31 @@ type PipelineLike = ((input: unknown, options?: Record<string, unknown>) => Prom
|
|
|
79
112
|
* @huggingface/transformers; tests and SSR shims can provide their own.
|
|
80
113
|
*/
|
|
81
114
|
type PipelineFactory = (task: string, model: string | undefined, options: Record<string, unknown>) => Promise<PipelineLike>;
|
|
115
|
+
/**
|
|
116
|
+
* The slice of the @huggingface/transformers module the default factory
|
|
117
|
+
* uses. The real module satisfies it, so an importer can configure
|
|
118
|
+
* `env` and return the module as is.
|
|
119
|
+
*/
|
|
120
|
+
interface TransformersModuleLike {
|
|
121
|
+
pipeline(task: string, model?: string, options?: object): Promise<unknown>;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* The factory behind PIPELINE_FACTORY. It imports @huggingface/transformers
|
|
125
|
+
* lazily, on the first pipeline, so the library adds nothing to the initial
|
|
126
|
+
* bundle. Wrap it to add options or logging while keeping the lazy import:
|
|
127
|
+
*
|
|
128
|
+
* ```ts
|
|
129
|
+
* const base = createDefaultPipelineFactory();
|
|
130
|
+
* const factory: PipelineFactory = (task, model, options) =>
|
|
131
|
+
* base(task, model, { ...options, revision: 'v2' });
|
|
132
|
+
* providers: [{ provide: PIPELINE_FACTORY, useValue: factory }]
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
* `load` is the importer. Tests pass a stub module; apps that need to
|
|
136
|
+
* configure Transformers.js (`env.allowRemoteModels`, `env.localModelPath`)
|
|
137
|
+
* do it there, before returning the module.
|
|
138
|
+
*/
|
|
139
|
+
declare function createDefaultPipelineFactory(load?: () => Promise<TransformersModuleLike>): PipelineFactory;
|
|
82
140
|
declare const PIPELINE_FACTORY: InjectionToken<PipelineFactory>;
|
|
83
141
|
declare const NGX_TRANSFORMERS_CONFIG: InjectionToken<NgxTransformersConfig>;
|
|
84
142
|
/**
|
|
@@ -111,25 +169,59 @@ declare class PipelineHandle<TIn = unknown, TOut = unknown> {
|
|
|
111
169
|
/** True once the model is usable (including while a run is in flight). */
|
|
112
170
|
readonly ready: _angular_core.Signal<boolean>;
|
|
113
171
|
readonly busy: _angular_core.Signal<boolean>;
|
|
114
|
-
private
|
|
115
|
-
private
|
|
172
|
+
private session;
|
|
173
|
+
private destroyed;
|
|
116
174
|
constructor(request: PipelineRequest, factory: PipelineFactory, config: NgxTransformersConfig);
|
|
117
175
|
/** Downloads and initializes the model. Idempotent; retries after error. */
|
|
118
176
|
load(): Promise<void>;
|
|
119
177
|
/** Runs the pipeline, loading the model first if needed. */
|
|
120
178
|
run(input: TIn, runOptions?: Record<string, unknown>): Promise<TOut>;
|
|
121
|
-
/**
|
|
179
|
+
/**
|
|
180
|
+
* Runs the pipeline with extra positional arguments after the input, for
|
|
181
|
+
* tasks whose pipeline signature is not (input, options): zero-shot
|
|
182
|
+
* classification takes (text, candidateLabels, options).
|
|
183
|
+
*/
|
|
184
|
+
protected runWith(input: TIn, ...extraArgs: unknown[]): Promise<TOut>;
|
|
185
|
+
/**
|
|
186
|
+
* Frees the model. A download still in flight is cancelled: its model is
|
|
187
|
+
* released on arrival and the handle stays idle. The handle can be loaded
|
|
188
|
+
* again afterwards; destroy() is the terminal variant.
|
|
189
|
+
*/
|
|
122
190
|
dispose(): Promise<void>;
|
|
191
|
+
/**
|
|
192
|
+
* dispose() for good: later load() and run() calls reject instead of
|
|
193
|
+
* downloading a model nobody would release. create*() registers this with
|
|
194
|
+
* the DestroyRef of the injection context, so a handle declared in a
|
|
195
|
+
* component ends with the component.
|
|
196
|
+
*/
|
|
197
|
+
destroy(): Promise<void>;
|
|
123
198
|
private doLoad;
|
|
199
|
+
/**
|
|
200
|
+
* An explicit request or global device wins; with `autoDevice` on, an
|
|
201
|
+
* unset or 'auto' device is probed for WebGPU (once, cached) at load time.
|
|
202
|
+
*/
|
|
203
|
+
private resolveDevice;
|
|
124
204
|
private onProgress;
|
|
125
205
|
}
|
|
126
206
|
/**
|
|
127
207
|
* Creates a PipelineHandle in an injection context (constructor, field
|
|
128
|
-
* initializer, or runInInjectionContext). The handle is
|
|
208
|
+
* initializer, or runInInjectionContext). The handle is destroyed with the
|
|
129
209
|
* surrounding component/injector.
|
|
130
210
|
*/
|
|
131
211
|
declare function createPipeline<TIn = unknown, TOut = unknown>(request: PipelineRequest): PipelineHandle<TIn, TOut>;
|
|
132
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Whether models can run on WebGPU here: `navigator.gpu` exists AND
|
|
215
|
+
* `requestAdapter()` hands back an adapter - it returns null on machines
|
|
216
|
+
* without a usable GPU or with WebGPU disabled, even when the API exists.
|
|
217
|
+
* Resolves false on the server. The probe runs once and is cached.
|
|
218
|
+
*/
|
|
219
|
+
declare function hasWebGpu(): Promise<boolean>;
|
|
220
|
+
/** `'webgpu'` when hasWebGpu() resolves true, else `'wasm'`. */
|
|
221
|
+
declare function detectDevice(): Promise<'webgpu' | 'wasm'>;
|
|
222
|
+
/** Forgets the cached probe so the next hasWebGpu() asks the browser again. */
|
|
223
|
+
declare function resetDeviceDetection(): void;
|
|
224
|
+
|
|
133
225
|
declare const DEFAULT_TEXT_CLASSIFICATION_MODEL = "Xenova/distilbert-base-uncased-finetuned-sst-2-english";
|
|
134
226
|
/**
|
|
135
227
|
* Text classification (sentiment by default). The default model is
|
|
@@ -206,6 +298,88 @@ declare class SpeechRecognizer extends PipelineHandle<string | Float32Array, Raw
|
|
|
206
298
|
*/
|
|
207
299
|
declare function createSpeechRecognizer(options?: Partial<Omit<PipelineRequest, 'task'>>): SpeechRecognizer;
|
|
208
300
|
|
|
301
|
+
declare const DEFAULT_ZERO_SHOT_MODEL = "Xenova/mobilebert-uncased-mnli";
|
|
302
|
+
/** Raw zero-shot output: labels ordered by score, scores aligned by index. */
|
|
303
|
+
interface RawZeroShotResult {
|
|
304
|
+
sequence: string;
|
|
305
|
+
labels: string[];
|
|
306
|
+
scores: number[];
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Zero-shot classification: score any labels you name against a text with
|
|
310
|
+
* an NLI model, no fine-tuning. The default checkpoint is MobileBERT MNLI
|
|
311
|
+
* (~26 MB q8); Xenova/distilbert-base-uncased-mnli (~80 MB) is the
|
|
312
|
+
* higher-accuracy alternative.
|
|
313
|
+
*/
|
|
314
|
+
declare class ZeroShotClassifier extends PipelineHandle<string, RawZeroShotResult | RawZeroShotResult[]> {
|
|
315
|
+
/**
|
|
316
|
+
* Scores each candidate label for one text; resolves sorted by score (top
|
|
317
|
+
* first). Single-label mode (default) normalizes across labels so scores
|
|
318
|
+
* sum to 1; `multiLabel` scores each label on its own.
|
|
319
|
+
*/
|
|
320
|
+
classify(text: string, labels: readonly string[], options?: ZeroShotOptions): Promise<ClassificationResult[]>;
|
|
321
|
+
}
|
|
322
|
+
/** Creates a ZeroShotClassifier in an injection context. */
|
|
323
|
+
declare function createZeroShotClassifier(options?: Partial<Omit<PipelineRequest, 'task'>>): ZeroShotClassifier;
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Default checkpoint for a language pair: Helsinki-NLP's Marian opus-mt
|
|
327
|
+
* models, one per direction, ~105 MB q8 each (e.g. Xenova/opus-mt-en-ru).
|
|
328
|
+
*/
|
|
329
|
+
declare function defaultTranslationModel(from: string, to: string): string;
|
|
330
|
+
/** Checkpoint for a pair: provideTransformers({ translationModels }) first, then opus-mt. */
|
|
331
|
+
declare function resolveTranslationModel(from: string, to: string, config: NgxTransformersConfig): string;
|
|
332
|
+
/** Raw translation pipeline output, one entry per input text. */
|
|
333
|
+
interface RawTranslation {
|
|
334
|
+
translation_text?: string;
|
|
335
|
+
}
|
|
336
|
+
type TranslationHandle = PipelineHandle<string, RawTranslation | RawTranslation[]>;
|
|
337
|
+
/**
|
|
338
|
+
* Text translation. Pipelines are created lazily, one per checkpoint: with
|
|
339
|
+
* the default opus-mt family every language pair is its own model, so a
|
|
340
|
+
* Translator holds one handle per pair it has been asked for, while a
|
|
341
|
+
* pinned multilingual `model` (NLLB, M2M100) serves every pair from one.
|
|
342
|
+
*
|
|
343
|
+
* The signals mirror the handle used by the most recent call, so a single
|
|
344
|
+
* `<ngx-model-progress>` covers the translator.
|
|
345
|
+
*/
|
|
346
|
+
declare class Translator {
|
|
347
|
+
private readonly options;
|
|
348
|
+
private readonly factory;
|
|
349
|
+
private readonly config;
|
|
350
|
+
private readonly active;
|
|
351
|
+
private readonly handles;
|
|
352
|
+
private destroyed;
|
|
353
|
+
readonly status: _angular_core.Signal<ngx_transformers.PipelineStatus>;
|
|
354
|
+
readonly progress: _angular_core.Signal<ngx_transformers.ModelProgress | null>;
|
|
355
|
+
readonly error: _angular_core.Signal<{} | null>;
|
|
356
|
+
readonly ready: _angular_core.Signal<boolean>;
|
|
357
|
+
readonly busy: _angular_core.Signal<boolean>;
|
|
358
|
+
constructor(options: TranslatorOptions, factory: PipelineFactory, config: NgxTransformersConfig);
|
|
359
|
+
/** Downloads the model for a pair ahead of the first translate() call. */
|
|
360
|
+
load(pair?: TranslateOptions): Promise<void>;
|
|
361
|
+
/** Translates one text; the pair defaults to the one given at creation. */
|
|
362
|
+
translate(text: string, pair?: TranslateOptions): Promise<string>;
|
|
363
|
+
/** Checkpoint for a pair: the pinned model, else the configured or default one. */
|
|
364
|
+
modelFor(pair?: TranslateOptions): string;
|
|
365
|
+
/**
|
|
366
|
+
* The handle serving a pair, created on first use. Exposed for apps that
|
|
367
|
+
* keep several pairs warm and want a progress line per model.
|
|
368
|
+
*/
|
|
369
|
+
handleFor(pair?: TranslateOptions): TranslationHandle;
|
|
370
|
+
/**
|
|
371
|
+
* dispose() for good: later calls reject instead of loading a model nobody
|
|
372
|
+
* would release. createTranslator() registers this with the DestroyRef.
|
|
373
|
+
*/
|
|
374
|
+
destroy(): Promise<void>;
|
|
375
|
+
/** Frees every model. The translator can be used again afterwards. */
|
|
376
|
+
dispose(): Promise<void>;
|
|
377
|
+
/** src_lang / tgt_lang for multilingual checkpoints; opus-mt models get nothing. */
|
|
378
|
+
private languageOptions;
|
|
379
|
+
}
|
|
380
|
+
/** Creates a Translator in an injection context. */
|
|
381
|
+
declare function createTranslator(options?: TranslatorOptions): Translator;
|
|
382
|
+
|
|
209
383
|
/** Minimal recorder surface - lets tests (and exotic runtimes) inject fakes. */
|
|
210
384
|
interface RecorderLike {
|
|
211
385
|
start(): void;
|
|
@@ -282,5 +456,5 @@ declare class ModelProgressComponent {
|
|
|
282
456
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ModelProgressComponent, "ngx-model-progress", never, { "status": { "alias": "status"; "required": true; "isSignal": true; }; "progress": { "alias": "progress"; "required": false; "isSignal": true; }; "labels": { "alias": "labels"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
283
457
|
}
|
|
284
458
|
|
|
285
|
-
export { DEFAULT_ASR_MODEL, DEFAULT_EMBEDDING_MODEL, DEFAULT_TEXT_CLASSIFICATION_MODEL, MicRecorder, ModelProgressComponent, NGX_TRANSFORMERS_CONFIG, PIPELINE_FACTORY, PipelineHandle, SpeechRecognizer, TextClassifier, TextEmbedder, WHISPER_SAMPLE_RATE, cosineSimilarity, createMicRecorder, createPipeline, createSpeechRecognizer, createTextClassifier, createTextEmbedder, decodeAudio, provideTransformers };
|
|
286
|
-
export type { ClassificationResult, MicRecorderDeps, ModelProgress, NgxTransformersConfig, PipelineFactory, PipelineLike, PipelineRequest, PipelineStatus, RankedResult, RecorderLike, TranscribeOptions, Transcription, TranscriptionChunk, TransformersDevice, TransformersDtype };
|
|
459
|
+
export { DEFAULT_ASR_MODEL, DEFAULT_EMBEDDING_MODEL, DEFAULT_TEXT_CLASSIFICATION_MODEL, DEFAULT_ZERO_SHOT_MODEL, MicRecorder, ModelProgressComponent, NGX_TRANSFORMERS_CONFIG, PIPELINE_FACTORY, PipelineHandle, SpeechRecognizer, TextClassifier, TextEmbedder, Translator, WHISPER_SAMPLE_RATE, ZeroShotClassifier, cosineSimilarity, createDefaultPipelineFactory, createMicRecorder, createPipeline, createSpeechRecognizer, createTextClassifier, createTextEmbedder, createTranslator, createZeroShotClassifier, decodeAudio, defaultTranslationModel, detectDevice, hasWebGpu, provideTransformers, resetDeviceDetection, resolveTranslationModel };
|
|
460
|
+
export type { ClassificationResult, MicRecorderDeps, ModelProgress, NgxTransformersConfig, PipelineFactory, PipelineLike, PipelineRequest, PipelineStatus, RankedResult, RecorderLike, TranscribeOptions, Transcription, TranscriptionChunk, TransformersDevice, TransformersDtype, TransformersModuleLike, TranslateOptions, TranslationHandle, TranslatorOptions, ZeroShotOptions };
|