@aparte/provider-transformers 0.16.3 → 0.16.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -5
- package/dist/assets/image-text-to-text-BR39-mrM.js +65 -0
- package/dist/assets/image-text-to-text-BR39-mrM.js.map +1 -0
- package/dist/assets/shared-ClkqaoeM.js +49 -0
- package/dist/assets/shared-ClkqaoeM.js.map +1 -0
- package/dist/assets/text-generation-BTp593TI.js +50 -0
- package/dist/assets/text-generation-BTp593TI.js.map +1 -0
- package/dist/assets/worker-Bm0eBd4l.js +153 -0
- package/dist/assets/worker-Bm0eBd4l.js.map +1 -0
- package/dist/index.d.ts +27 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +95 -46
- package/dist/index.js.map +1 -1
- package/dist/runners/image-text-to-text.d.ts +36 -0
- package/dist/runners/image-text-to-text.d.ts.map +1 -0
- package/dist/runners/shared.d.ts +30 -0
- package/dist/runners/shared.d.ts.map +1 -0
- package/dist/runners/text-generation.d.ts +22 -0
- package/dist/runners/text-generation.d.ts.map +1 -0
- package/dist/runners/types.d.ts +69 -0
- package/dist/runners/types.d.ts.map +1 -0
- package/dist/worker-host.d.ts +102 -0
- package/dist/worker-host.d.ts.map +1 -0
- package/dist/worker.d.ts +5 -16
- package/dist/worker.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/assets/worker-Dz5bU2S7.js +0 -93
- package/dist/assets/worker-Dz5bU2S7.js.map +0 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The runner contract — where the provider's plumbing stops and the model begins.
|
|
3
|
+
*
|
|
4
|
+
* A runner owns loading and calling a model; the provider owns everything around it
|
|
5
|
+
* (the worker, the queue, download progress, cancel, the stream into the transcript, the
|
|
6
|
+
* catalogue). Two runners ship with the package (`text-generation`, `image-text-to-text`),
|
|
7
|
+
* and an app can point `TransformersModelConfig.runner` at a module of its own that
|
|
8
|
+
* exports `createRunner` — the worker imports it.
|
|
9
|
+
*
|
|
10
|
+
* Sized for the hardest known consumer, not the easiest: a runner EMITS the full stream
|
|
11
|
+
* vocabulary (text, thinking, tool_use, done, error — not tokens), has a command lane for
|
|
12
|
+
* what is not a generation (swapping an adapter, warming something up), reports progress
|
|
13
|
+
* with a free-form `detail`, and can dispose. A research-grade provider that today
|
|
14
|
+
* carries its own worker fits here without giving up anything of its own.
|
|
15
|
+
*/
|
|
16
|
+
import type { AparteChatMessage, AparteStreamEvent } from '@aparte/core';
|
|
17
|
+
/** The Transformers.js module, as the worker resolved it — a runner never imports its own copy. */
|
|
18
|
+
export type TransformersModule = typeof import('@huggingface/transformers');
|
|
19
|
+
/** ONNX dtype, or a per-part map (`{ decoder_model_merged: 'q4' }`). */
|
|
20
|
+
export type Dtype = string | Record<string, string>;
|
|
21
|
+
export type Device = 'webgpu' | 'wasm' | 'auto';
|
|
22
|
+
/** The runners this package ships. Selected with `TransformersModelConfig.task`. */
|
|
23
|
+
export type BuiltInRunner = 'text-generation' | 'image-text-to-text';
|
|
24
|
+
export interface RunnerProgress {
|
|
25
|
+
status: 'downloading' | 'loading' | 'cached' | 'ready';
|
|
26
|
+
file?: string;
|
|
27
|
+
/** 0–100. */
|
|
28
|
+
progress?: number;
|
|
29
|
+
/** Anything a runner wants to say that the four statuses do not — an adapter name, a stage. */
|
|
30
|
+
detail?: unknown;
|
|
31
|
+
}
|
|
32
|
+
export interface GenerationOptions {
|
|
33
|
+
maxTokens?: number;
|
|
34
|
+
temperature?: number;
|
|
35
|
+
seed?: number;
|
|
36
|
+
}
|
|
37
|
+
/** What a runner is created with. */
|
|
38
|
+
export interface RunnerContext {
|
|
39
|
+
transformers: TransformersModule;
|
|
40
|
+
modelId: string;
|
|
41
|
+
dtype?: Dtype;
|
|
42
|
+
device?: Device;
|
|
43
|
+
/** Download / load progress, forwarded to the page. */
|
|
44
|
+
progress(p: RunnerProgress): void;
|
|
45
|
+
/** A developer-facing warning (dropped content, an unsupported option). Said once per message. */
|
|
46
|
+
warn(message: string): void;
|
|
47
|
+
}
|
|
48
|
+
export interface RunnerGenerateInput {
|
|
49
|
+
/** The conversation, WITH its content parts — the runner decides what to keep. */
|
|
50
|
+
messages: AparteChatMessage[];
|
|
51
|
+
options: GenerationOptions;
|
|
52
|
+
/** The stream, in aparté's vocabulary. `done` and `error` are the host's to close on. */
|
|
53
|
+
emit(event: AparteStreamEvent): void;
|
|
54
|
+
/** Fired when the page cancels; wire it to the model's stop. */
|
|
55
|
+
signal: AbortSignal;
|
|
56
|
+
}
|
|
57
|
+
export interface TransformersRunner {
|
|
58
|
+
/** Resolve after the last emit. Emitting `done` is optional — the host closes the stream either way. */
|
|
59
|
+
generate(input: RunnerGenerateInput): Promise<void>;
|
|
60
|
+
/** The extended surface: anything that is not a generation, opaque to the provider. */
|
|
61
|
+
command?(name: string, payload: unknown): Promise<unknown>;
|
|
62
|
+
dispose?(): void | Promise<void>;
|
|
63
|
+
}
|
|
64
|
+
export type CreateRunner = (ctx: RunnerContext) => Promise<TransformersRunner>;
|
|
65
|
+
/** What a runner module exports. */
|
|
66
|
+
export interface RunnerModule {
|
|
67
|
+
createRunner: CreateRunner;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/runners/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEzE,mGAAmG;AACnG,MAAM,MAAM,kBAAkB,GAAG,cAAc,2BAA2B,CAAC,CAAC;AAE5E,wEAAwE;AACxE,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACpD,MAAM,MAAM,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAEhD,oFAAoF;AACpF,MAAM,MAAM,aAAa,GAAG,iBAAiB,GAAG,oBAAoB,CAAC;AAErE,MAAM,WAAW,cAAc;IAC3B,MAAM,EAAE,aAAa,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+FAA+F;IAC/F,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qCAAqC;AACrC,MAAM,WAAW,aAAa;IAC1B,YAAY,EAAE,kBAAkB,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,QAAQ,CAAC,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAClC,kGAAkG;IAClG,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB;IAChC,kFAAkF;IAClF,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,yFAAyF;IACzF,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACrC,gEAAgE;IAChE,MAAM,EAAE,WAAW,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IAC/B,wGAAwG;IACxG,QAAQ,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,uFAAuF;IACvF,OAAO,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D,OAAO,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAE/E,oCAAoC;AACpC,MAAM,WAAW,YAAY;IACzB,YAAY,EAAE,YAAY,CAAC;CAC9B"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The worker's logic, as a function of its three seams.
|
|
3
|
+
*
|
|
4
|
+
* `worker.ts` is the two-line shell that binds this to `self`; everything it decides is
|
|
5
|
+
* here, with `post`, `loadTransformers` and `importRunner` injected — so the protocol is
|
|
6
|
+
* tested with fakes where no Worker and no model can load (`worker-host.test.ts`).
|
|
7
|
+
*
|
|
8
|
+
* The protocol (main ⇄ worker):
|
|
9
|
+
*
|
|
10
|
+
* → init { transformersUrl? } once, first
|
|
11
|
+
* → prepare { id, modelId, task?, runner?, dtype?, device? }
|
|
12
|
+
* → generate { id, modelId, messages, options, task?, runner?, dtype?, device? }
|
|
13
|
+
* → cancel { id }
|
|
14
|
+
* → command { id, modelId, name, payload, task?, runner?, dtype?, device? }
|
|
15
|
+
* ← progress { id, status, file?, progress?, detail? }
|
|
16
|
+
* ← pipeline-ready { modelId } a runner is up
|
|
17
|
+
* ← prepare-error { id, message }
|
|
18
|
+
* ← gen-event { id, event } text | thinking | tool_use
|
|
19
|
+
* ← gen-done { id, usage? } the host closes; `done` is never forwarded raw
|
|
20
|
+
* ← gen-error { id, message } emitted or thrown
|
|
21
|
+
* ← warning { message } once per distinct text
|
|
22
|
+
* ← command-result { id, result } | { id, error }
|
|
23
|
+
*
|
|
24
|
+
* One runner is resident at a time, keyed by model AND runner: a switch disposes the
|
|
25
|
+
* previous one first, which is the "one pipeline per tab" rule this package has always
|
|
26
|
+
* kept — a local model is gigabytes, and two of them resident is the failure to avoid.
|
|
27
|
+
*/
|
|
28
|
+
import type { AparteChatMessage, AparteStreamEvent, AparteUsage } from '@aparte/core';
|
|
29
|
+
import type { BuiltInRunner, Device, Dtype, GenerationOptions, RunnerModule, RunnerProgress, TransformersModule } from './runners/types.js';
|
|
30
|
+
interface RunnerSelection {
|
|
31
|
+
modelId: string;
|
|
32
|
+
task?: BuiltInRunner;
|
|
33
|
+
runner?: string;
|
|
34
|
+
dtype?: Dtype;
|
|
35
|
+
device?: Device;
|
|
36
|
+
}
|
|
37
|
+
export type InMessage = {
|
|
38
|
+
type: 'init';
|
|
39
|
+
transformersUrl?: string;
|
|
40
|
+
} | ({
|
|
41
|
+
type: 'prepare';
|
|
42
|
+
id: string;
|
|
43
|
+
} & RunnerSelection) | ({
|
|
44
|
+
type: 'generate';
|
|
45
|
+
id: string;
|
|
46
|
+
messages: AparteChatMessage[];
|
|
47
|
+
options: GenerationOptions;
|
|
48
|
+
} & RunnerSelection) | {
|
|
49
|
+
type: 'cancel';
|
|
50
|
+
id: string;
|
|
51
|
+
} | ({
|
|
52
|
+
type: 'command';
|
|
53
|
+
id: string;
|
|
54
|
+
name: string;
|
|
55
|
+
payload: unknown;
|
|
56
|
+
} & RunnerSelection);
|
|
57
|
+
export type OutMessage = ({
|
|
58
|
+
type: 'progress';
|
|
59
|
+
id: string;
|
|
60
|
+
} & RunnerProgress) | {
|
|
61
|
+
type: 'pipeline-ready';
|
|
62
|
+
modelId: string;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'prepare-error';
|
|
65
|
+
id: string;
|
|
66
|
+
message: string;
|
|
67
|
+
} | {
|
|
68
|
+
type: 'gen-event';
|
|
69
|
+
id: string;
|
|
70
|
+
event: AparteStreamEvent;
|
|
71
|
+
} | {
|
|
72
|
+
type: 'gen-done';
|
|
73
|
+
id: string;
|
|
74
|
+
usage?: AparteUsage;
|
|
75
|
+
} | {
|
|
76
|
+
type: 'gen-error';
|
|
77
|
+
id: string;
|
|
78
|
+
message: string;
|
|
79
|
+
} | {
|
|
80
|
+
type: 'warning';
|
|
81
|
+
message: string;
|
|
82
|
+
} | {
|
|
83
|
+
type: 'command-result';
|
|
84
|
+
id: string;
|
|
85
|
+
result?: unknown;
|
|
86
|
+
error?: string;
|
|
87
|
+
};
|
|
88
|
+
export interface WorkerHostDeps {
|
|
89
|
+
post(message: OutMessage): void;
|
|
90
|
+
/** Resolve Transformers.js — the bundled specifier first, else the URL `init` carried. */
|
|
91
|
+
loadTransformers(moduleUrl?: string): Promise<TransformersModule>;
|
|
92
|
+
/** Import the runner module a selection names: a built-in by `task`, a custom one by `runner` URL. */
|
|
93
|
+
importRunner(spec: {
|
|
94
|
+
task: BuiltInRunner;
|
|
95
|
+
runner?: string;
|
|
96
|
+
}): Promise<RunnerModule>;
|
|
97
|
+
}
|
|
98
|
+
export declare function createWorkerHost(deps: WorkerHostDeps): {
|
|
99
|
+
onMessage(msg: InMessage): void;
|
|
100
|
+
};
|
|
101
|
+
export {};
|
|
102
|
+
//# sourceMappingURL=worker-host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-host.d.ts","sourceRoot":"","sources":["../src/worker-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACtF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAsB,MAAM,oBAAoB,CAAC;AAEhK,UAAU,eAAe;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,SAAS,GACf;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1C,CAAC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,eAAe,CAAC,GACnD,CAAC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,GAAG,eAAe,CAAC,GAC/G;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC9B,CAAC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG,eAAe,CAAC,CAAC;AAE1F,MAAM,MAAM,UAAU,GAChB,CAAC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,cAAc,CAAC,GACnD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,WAAW,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/E,MAAM,WAAW,cAAc;IAC3B,IAAI,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI,CAAC;IAChC,0FAA0F;IAC1F,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAClE,sGAAsG;IACtG,YAAY,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CACvF;AAID,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG;IAAE,SAAS,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAAA;CAAE,CAkG1F"}
|
package/dist/worker.d.ts
CHANGED
|
@@ -1,21 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The Transformers.js inference worker — the shell.
|
|
3
3
|
*
|
|
4
|
-
* Runs entirely off the main thread.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* main → worker : { type: 'prepare', id, modelId, dtype?, device? }
|
|
9
|
-
* { type: 'generate', id, modelId, messages, options, dtype?, device? }
|
|
10
|
-
* worker → main : { type: 'progress', id, status, file?, progress? }
|
|
11
|
-
* { type: 'prepare-error', id, message }
|
|
12
|
-
* { type: 'pipeline-ready', modelId }
|
|
13
|
-
* { type: 'gen-chunk', id, chunkType: 'text', delta }
|
|
14
|
-
* { type: 'gen-done', id }
|
|
15
|
-
* { type: 'gen-error', id, message }
|
|
16
|
-
*
|
|
17
|
-
* Deliberately generic: no vision, no low-level ORT session management, no
|
|
18
|
-
* model-family specifics — just the high-level `pipeline()` + `TextStreamer`.
|
|
4
|
+
* Runs entirely off the main thread. What it decides lives in `worker-host.ts` (the
|
|
5
|
+
* protocol, the one-runner-at-a-time rule, cancel, warnings); what a model IS lives in a
|
|
6
|
+
* runner (`runners/`). This file binds the two to `self` and owns the two things only a
|
|
7
|
+
* real worker can do: resolve Transformers.js, and import a runner module.
|
|
19
8
|
*/
|
|
20
9
|
export {};
|
|
21
10
|
//# sourceMappingURL=worker.d.ts.map
|
package/dist/worker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aparte/provider-transformers",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.5",
|
|
4
4
|
"description": "Run LLMs 100% in the browser via Transformers.js (WebGPU/WASM) — a local, keyless AI provider for aparté. Streams tokens off the main thread in a Web Worker.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"node": ">=18"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
|
-
"@aparte/core": ">=0.16.
|
|
27
|
+
"@aparte/core": ">=0.16.5 <1.0.0",
|
|
28
28
|
"@huggingface/transformers": "^4.2.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@types/node": "^22.0.0",
|
|
33
33
|
"typescript": "^5.4.0",
|
|
34
34
|
"vite": "^6.0.0",
|
|
35
|
-
"@aparte/core": "0.16.
|
|
35
|
+
"@aparte/core": "0.16.5"
|
|
36
36
|
},
|
|
37
37
|
"keywords": [
|
|
38
38
|
"ai",
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
let _moduleUrl;
|
|
2
|
-
let _tf = null;
|
|
3
|
-
function transformers() {
|
|
4
|
-
_tf ??= (async () => {
|
|
5
|
-
let mod;
|
|
6
|
-
try {
|
|
7
|
-
mod = await import("@huggingface/transformers");
|
|
8
|
-
} catch (bundlerPathFailed) {
|
|
9
|
-
if (!_moduleUrl) throw bundlerPathFailed;
|
|
10
|
-
mod = await import(
|
|
11
|
-
/* @vite-ignore */
|
|
12
|
-
_moduleUrl
|
|
13
|
-
);
|
|
14
|
-
}
|
|
15
|
-
mod.env.allowLocalModels = false;
|
|
16
|
-
mod.env.useBrowserCache = true;
|
|
17
|
-
return mod;
|
|
18
|
-
})();
|
|
19
|
-
return _tf;
|
|
20
|
-
}
|
|
21
|
-
const ctx = self;
|
|
22
|
-
function post(message) {
|
|
23
|
-
ctx.postMessage(message);
|
|
24
|
-
}
|
|
25
|
-
let _current = null;
|
|
26
|
-
const _activeStops = /* @__PURE__ */ new Map();
|
|
27
|
-
async function ensurePipeline(modelId, dtype, device, id) {
|
|
28
|
-
if (_current?.modelId === modelId) return _current.pipe;
|
|
29
|
-
const opts = {
|
|
30
|
-
progress_callback: (p) => {
|
|
31
|
-
if (!id) return;
|
|
32
|
-
if (p.status === "progress") {
|
|
33
|
-
post({ type: "progress", id, status: "downloading", file: p.file, progress: Math.round(p.progress ?? 0) });
|
|
34
|
-
} else if (p.status === "done") {
|
|
35
|
-
post({ type: "progress", id, status: "loading", file: p.file });
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
if (dtype) opts["dtype"] = dtype;
|
|
40
|
-
if (device && device !== "auto") opts["device"] = device;
|
|
41
|
-
const { pipeline } = await transformers();
|
|
42
|
-
const pipe = await pipeline("text-generation", modelId, opts);
|
|
43
|
-
_current = { modelId, pipe };
|
|
44
|
-
post({ type: "pipeline-ready", modelId });
|
|
45
|
-
return pipe;
|
|
46
|
-
}
|
|
47
|
-
async function handlePrepare(msg) {
|
|
48
|
-
try {
|
|
49
|
-
await ensurePipeline(msg.modelId, msg.dtype, msg.device, msg.id);
|
|
50
|
-
post({ type: "progress", id: msg.id, status: "ready" });
|
|
51
|
-
} catch (err) {
|
|
52
|
-
post({ type: "prepare-error", id: msg.id, message: err?.message ?? "Failed to load model" });
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
async function handleGenerate(msg) {
|
|
56
|
-
const { TextStreamer, InterruptableStoppingCriteria } = await transformers();
|
|
57
|
-
const stoppingCriteria = new InterruptableStoppingCriteria();
|
|
58
|
-
_activeStops.set(msg.id, stoppingCriteria);
|
|
59
|
-
try {
|
|
60
|
-
const pipe = await ensurePipeline(msg.modelId, msg.dtype, msg.device, msg.id);
|
|
61
|
-
const streamer = new TextStreamer(pipe.tokenizer, {
|
|
62
|
-
skip_prompt: true,
|
|
63
|
-
skip_special_tokens: true,
|
|
64
|
-
callback_function: (text) => {
|
|
65
|
-
if (text) post({ type: "gen-chunk", id: msg.id, chunkType: "text", delta: text });
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
const temperature = msg.options.temperature ?? 0;
|
|
69
|
-
await pipe(msg.messages, {
|
|
70
|
-
max_new_tokens: msg.options.maxTokens ?? 512,
|
|
71
|
-
do_sample: temperature > 0,
|
|
72
|
-
temperature: temperature > 0 ? temperature : void 0,
|
|
73
|
-
streamer,
|
|
74
|
-
stopping_criteria: stoppingCriteria
|
|
75
|
-
});
|
|
76
|
-
post({ type: "gen-done", id: msg.id });
|
|
77
|
-
} catch (err) {
|
|
78
|
-
post({ type: "gen-error", id: msg.id, message: err?.message ?? "Generation failed" });
|
|
79
|
-
} finally {
|
|
80
|
-
_activeStops.delete(msg.id);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
ctx.addEventListener("message", (event) => {
|
|
84
|
-
const msg = event.data;
|
|
85
|
-
if (msg.type === "init") {
|
|
86
|
-
_moduleUrl = msg.transformersUrl;
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
if (msg.type === "prepare") void handlePrepare(msg);
|
|
90
|
-
else if (msg.type === "generate") void handleGenerate(msg);
|
|
91
|
-
else if (msg.type === "cancel") _activeStops.get(msg.id)?.interrupt();
|
|
92
|
-
});
|
|
93
|
-
//# sourceMappingURL=worker-Dz5bU2S7.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"worker-Dz5bU2S7.js","sources":["../src/worker.ts"],"sourcesContent":["/**\n * Generic Transformers.js inference worker.\n *\n * Runs entirely off the main thread. It holds ONE text-generation pipeline at a\n * time and speaks a tiny postMessage protocol with the provider on the main\n * thread (see `index.ts`):\n *\n * main → worker : { type: 'prepare', id, modelId, dtype?, device? }\n * { type: 'generate', id, modelId, messages, options, dtype?, device? }\n * worker → main : { type: 'progress', id, status, file?, progress? }\n * { type: 'prepare-error', id, message }\n * { type: 'pipeline-ready', modelId }\n * { type: 'gen-chunk', id, chunkType: 'text', delta }\n * { type: 'gen-done', id }\n * { type: 'gen-error', id, message }\n *\n * Deliberately generic: no vision, no low-level ORT session management, no\n * model-family specifics — just the high-level `pipeline()` + `TextStreamer`.\n */\n\nimport type { pipeline as Pipeline, TextStreamer as TextStreamerClass, InterruptableStoppingCriteria as StoppingCriteriaClass, TextGenerationPipeline } from '@huggingface/transformers';\n\n/**\n * Where Transformers.js comes from, resolved once, from whichever path has it.\n *\n * A static `import … from '@huggingface/transformers'` is unresolvable in a worker\n * served without a bundler: an import map lives on the DOCUMENT and, by spec, does not\n * reach a worker — so the page can map the specifier for itself and the worker still\n * cannot. The two paths, in order:\n *\n * 1. `import('@huggingface/transformers')` — a bare specifier, statically visible, so a\n * consumer's bundler resolves and bundles the peer exactly as it did before.\n * 2. the absolute URL the main thread read from the page's own import map and sent in\n * the first message — the CDN path, where that map is the consumer's manifest.\n *\n * The order matters: a bundled app must never reach for the network copy.\n */\ntype TransformersModule = {\n pipeline: typeof Pipeline;\n TextStreamer: typeof TextStreamerClass;\n InterruptableStoppingCriteria: typeof StoppingCriteriaClass;\n env: { allowLocalModels: boolean; useBrowserCache: boolean };\n};\n\nlet _moduleUrl: string | undefined;\nlet _tf: Promise<TransformersModule> | null = null;\n\nfunction transformers(): Promise<TransformersModule> {\n _tf ??= (async () => {\n let mod: TransformersModule;\n try {\n mod = await import('@huggingface/transformers') as unknown as TransformersModule;\n } catch (bundlerPathFailed) {\n if (!_moduleUrl) throw bundlerPathFailed;\n mod = await import(/* @vite-ignore */ _moduleUrl) as unknown as TransformersModule;\n }\n // Fetch weights from the Hugging Face hub (not local paths) and cache them in the\n // browser Cache API — this is what `listCachedModels()` scans on the main thread.\n mod.env.allowLocalModels = false;\n mod.env.useBrowserCache = true;\n return mod;\n })();\n return _tf;\n}\n\n// DOM's `Worker` interface types `postMessage` + typed `addEventListener('message')`,\n// which is enough for the worker scope — avoids pulling the WebWorker lib (it clashes\n// with DOM's global `postMessage`).\nconst ctx = self as unknown as Worker;\n\ntype Dtype = string | Record<string, string>;\ntype Device = 'webgpu' | 'wasm' | 'auto';\ninterface GenOptions { maxTokens?: number; temperature?: number; seed?: number }\ntype SimpleMessage = { role: 'user' | 'assistant' | 'system'; content: string };\n\ntype InMessage =\n | { type: 'prepare'; id: string; modelId: string; dtype?: Dtype; device?: Device }\n | { type: 'generate'; id: string; modelId: string; messages: SimpleMessage[]; options: GenOptions; dtype?: Dtype; device?: Device }\n | { type: 'cancel'; id: string }\n // Sent once, before anything else, when the main thread could read the peer's URL out\n // of the page's import map. Absent under a bundler, which has already resolved it.\n | { type: 'init'; transformersUrl?: string };\n\nfunction post(message: unknown): void {\n ctx.postMessage(message);\n}\n\nlet _current: { modelId: string; pipe: TextGenerationPipeline } | null = null;\n// Per-generate interrupts, so a consumer's stream-cancel actually STOPS the model\n// (not just detaches the reader) — otherwise generation runs to max_new_tokens\n// off-thread, wasting exactly the CPU/GPU/battery this provider exists to save.\nconst _activeStops = new Map<string, InstanceType<typeof StoppingCriteriaClass>>();\n\n/**\n * Ensure the pipeline for `modelId` is loaded, reusing the current one when it\n * matches. On a fresh load it forwards download progress (when `id` is given) and\n * announces `pipeline-ready`.\n */\nasync function ensurePipeline(modelId: string, dtype: Dtype | undefined, device: Device | undefined, id?: string): Promise<TextGenerationPipeline> {\n if (_current?.modelId === modelId) return _current.pipe;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const opts: Record<string, any> = {\n progress_callback: (p: { status?: string; file?: string; progress?: number }) => {\n if (!id) return;\n if (p.status === 'progress') {\n post({ type: 'progress', id, status: 'downloading', file: p.file, progress: Math.round(p.progress ?? 0) });\n } else if (p.status === 'done') {\n post({ type: 'progress', id, status: 'loading', file: p.file });\n }\n },\n };\n if (dtype) opts['dtype'] = dtype;\n if (device && device !== 'auto') opts['device'] = device;\n\n const { pipeline } = await transformers();\n const pipe = await pipeline('text-generation', modelId, opts) as TextGenerationPipeline;\n _current = { modelId, pipe };\n post({ type: 'pipeline-ready', modelId });\n return pipe;\n}\n\nasync function handlePrepare(msg: Extract<InMessage, { type: 'prepare' }>): Promise<void> {\n try {\n await ensurePipeline(msg.modelId, msg.dtype, msg.device, msg.id);\n post({ type: 'progress', id: msg.id, status: 'ready' });\n } catch (err) {\n post({ type: 'prepare-error', id: msg.id, message: (err as Error)?.message ?? 'Failed to load model' });\n }\n}\n\nasync function handleGenerate(msg: Extract<InMessage, { type: 'generate' }>): Promise<void> {\n const { TextStreamer, InterruptableStoppingCriteria } = await transformers();\n const stoppingCriteria = new InterruptableStoppingCriteria();\n _activeStops.set(msg.id, stoppingCriteria);\n try {\n const pipe = await ensurePipeline(msg.modelId, msg.dtype, msg.device, msg.id);\n\n const streamer = new TextStreamer(pipe.tokenizer, {\n skip_prompt: true,\n skip_special_tokens: true,\n callback_function: (text: string) => {\n if (text) post({ type: 'gen-chunk', id: msg.id, chunkType: 'text', delta: text });\n },\n });\n\n const temperature = msg.options.temperature ?? 0;\n await pipe(msg.messages, {\n max_new_tokens: msg.options.maxTokens ?? 512,\n do_sample: temperature > 0,\n temperature: temperature > 0 ? temperature : undefined,\n streamer,\n stopping_criteria: stoppingCriteria,\n });\n\n post({ type: 'gen-done', id: msg.id });\n } catch (err) {\n post({ type: 'gen-error', id: msg.id, message: (err as Error)?.message ?? 'Generation failed' });\n } finally {\n _activeStops.delete(msg.id);\n }\n}\n\nctx.addEventListener('message', (event: MessageEvent<InMessage>) => {\n const msg = event.data;\n if (msg.type === 'init') { _moduleUrl = msg.transformersUrl; return; }\n if (msg.type === 'prepare') void handlePrepare(msg);\n else if (msg.type === 'generate') void handleGenerate(msg);\n else if (msg.type === 'cancel') _activeStops.get(msg.id)?.interrupt();\n});\n"],"names":[],"mappings":"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"}
|