prompt-api-polyfill 1.17.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,6 +8,7 @@ supporting dynamic backends:
8
8
  - **Google Gemini API** (cloud)
9
9
  - **OpenAI API** (cloud)
10
10
  - **Transformers.js** (local after initial model download, **default backend**)
11
+ - **WebLLM** (local after initial model download)
11
12
 
12
13
  When loaded in the browser, it defines a global:
13
14
 
@@ -57,6 +58,13 @@ configured, the polyfill will use Transformers.js with the default model.
57
58
  - **Model**: Uses default if not specified (see
58
59
  [`backends/defaults.js`](backends/defaults.js)).
59
60
 
61
+ ### WebLLM (local after initial model download)
62
+
63
+ - **Uses**: `@mlc-ai/web-llm` SDK (MLC engine, runs via WebGPU).
64
+ - **Select by setting**: `window.WEBLLM_CONFIG`.
65
+ - **Model**: Uses default if not specified (see
66
+ [`backends/defaults.js`](backends/defaults.js)).
67
+
60
68
  ---
61
69
 
62
70
  ## Installation
@@ -166,6 +174,28 @@ npm install prompt-api-polyfill
166
174
  </script>
167
175
  ```
168
176
 
177
+ ### Backed by WebLLM (local after initial model download)
178
+
179
+ 1. **Only a dummy API Key required** (runs locally in the browser via WebGPU).
180
+ 2. **Provide configuration** on `window.WEBLLM_CONFIG`.
181
+ 3. **Import the polyfill**.
182
+
183
+ ```html
184
+ <script type="module">
185
+ // Set WEBLLM_CONFIG to select the WebLLM backend
186
+ window.WEBLLM_CONFIG = {
187
+ apiKey: 'dummy', // Required for now by the loader
188
+ modelName: 'Llama-3.2-3B-Instruct-q4f32_1-MLC', // Optional: override default
189
+ };
190
+
191
+ if (!('LanguageModel' in window)) {
192
+ await import('prompt-api-polyfill');
193
+ }
194
+
195
+ const session = await LanguageModel.create();
196
+ </script>
197
+ ```
198
+
169
199
  ---
170
200
 
171
201
  ## Configuration
@@ -322,13 +352,22 @@ Then open `.env.json` and fill in the values.
322
352
  }
323
353
  ```
324
354
 
355
+ **For WebLLM:**
356
+
357
+ ```json
358
+ {
359
+ "apiKey": "dummy",
360
+ "modelName": "Llama-3.2-3B-Instruct-q4f32_1-MLC"
361
+ }
362
+ ```
363
+
325
364
  ### Field-by-field explanation
326
365
 
327
366
  - `apiKey`:
328
367
  - **Firebase AI Logic**: Your Firebase Web API key.
329
368
  - **Gemini**: Your Gemini API Key.
330
369
  - **OpenAI**: Your OpenAI API Key.
331
- - **Transformers.js**: Use `"dummy"`.
370
+ - **Transformers.js** / **WebLLM**: Use `"dummy"`.
332
371
  - `projectId` / `appId`: **Firebase AI Logic only**.
333
372
  - `geminiApiProvider`: **Firebase AI Logic only**. Either `"developer"` (Gemini
334
373
  Developer API, default) or `"vertex"` (Vertex AI).
@@ -344,6 +383,10 @@ Then open `.env.json` and fill in the values.
344
383
  - `env` (optional): **Transformers.js only**. A flexible object to override
345
384
  [Transformers.js environment variables](https://huggingface.co/docs/transformers.js/api/env).
346
385
  This is useful for specifying local `wasmPaths` or proxy settings.
386
+ - **WebLLM** only requires `apiKey: "dummy"` and an optional `modelName`. Model
387
+ IDs must be valid
388
+ [MLC model identifiers](https://github.com/mlc-ai/web-llm?tab=readme-ov-file#list-available-models)
389
+ (e.g., `"Llama-3.2-3B-Instruct-q4f32_1-MLC"`).
347
390
 
348
391
  - `modelName` (optional): The model ID to use. If not provided, the polyfill
349
392
  uses the defaults defined in [`backends/defaults.js`](backends/defaults.js).
@@ -1,4 +1,4 @@
1
- import { n as e, t } from "../chunks/defaults-B5W7MP9T.js";
1
+ import { n as e, t } from "../chunks/defaults-DMD-8IKq.js";
2
2
  import { initializeApp as n } from "firebase/app";
3
3
  import { ReCaptchaEnterpriseProvider as r, initializeAppCheck as i } from "firebase/app-check";
4
4
  import { GoogleAIBackend as a, InferenceMode as o, Schema as s, VertexAIBackend as c, getAI as l, getGenerativeModel as u } from "firebase/ai";
@@ -1,4 +1,4 @@
1
- import { n as e, t } from "../chunks/defaults-B5W7MP9T.js";
1
+ import { n as e, t } from "../chunks/defaults-DMD-8IKq.js";
2
2
  import { GoogleGenAI as n } from "@google/genai";
3
3
  //#region backends/gemini.js
4
4
  var r = class extends e {
@@ -1,4 +1,4 @@
1
- import { n as e, t } from "../chunks/defaults-B5W7MP9T.js";
1
+ import { n as e, t } from "../chunks/defaults-DMD-8IKq.js";
2
2
  import n from "openai";
3
3
  //#region backends/openai.js
4
4
  var r = class extends e {
@@ -1,4 +1,4 @@
1
- import { n as e, t } from "../chunks/defaults-B5W7MP9T.js";
1
+ import { n as e, t } from "../chunks/defaults-DMD-8IKq.js";
2
2
  import { TextStreamer as n, env as r, pipeline as i } from "@huggingface/transformers";
3
3
  //#region backends/transformers.js
4
4
  var a = class extends e {
@@ -0,0 +1,107 @@
1
+ import { n as e, t } from "../chunks/defaults-DMD-8IKq.js";
2
+ import { CreateMLCEngine as n, prebuiltAppConfig as r } from "@mlc-ai/web-llm";
3
+ //#region backends/webllm.js
4
+ var i = class extends e {
5
+ #e;
6
+ #t;
7
+ #n = 0;
8
+ constructor(e = {}) {
9
+ super(e.modelName || t.webllm.modelName);
10
+ }
11
+ async #r(e) {
12
+ if (!this.#e) {
13
+ let t = (t) => {
14
+ if (!e) return;
15
+ let n = 1 / 65536, r = Math.floor(t / n) * n;
16
+ r <= e.__lastProgressLoaded || (e.dispatchEvent(new ProgressEvent("downloadprogress", {
17
+ loaded: r,
18
+ total: 1,
19
+ lengthComputable: !0
20
+ })), e.__lastProgressLoaded = r);
21
+ };
22
+ t(0);
23
+ let i = {
24
+ ...r,
25
+ cacheBackend: "cross-origin"
26
+ };
27
+ this.#e = await n(this.modelName, {
28
+ appConfig: i,
29
+ initProgressCallback: (e) => {
30
+ t(e.progress);
31
+ }
32
+ }), t(1);
33
+ }
34
+ return this.#e;
35
+ }
36
+ static availability(e) {
37
+ if (e?.expectedInputs && Array.isArray(e.expectedInputs)) {
38
+ for (let t of e.expectedInputs) if (t.type === "audio" || t.type === "image") return "unavailable";
39
+ }
40
+ return "available";
41
+ }
42
+ async createSession(e, t, n) {
43
+ return await this.#r(n), this.generationConfig = { max_tokens: 512 }, this.#t = t.systemInstruction, this.responseSchema = t.generationConfig?.responseSchema, this.#e;
44
+ }
45
+ async generateContent(e) {
46
+ let t = await this.#r(), n = {
47
+ messages: this.#i(e),
48
+ ...this.generationConfig
49
+ };
50
+ this.responseSchema && (n.response_format = {
51
+ type: "json_object",
52
+ schema: JSON.stringify(this.responseSchema)
53
+ });
54
+ let r = await t.chat.completions.create(n), i = r.choices[0].message.content;
55
+ return this.#n += (r.usage?.prompt_tokens ?? 0) + (r.usage?.completion_tokens ?? 0), {
56
+ text: i,
57
+ usage: this.#n
58
+ };
59
+ }
60
+ async generateContentStream(e) {
61
+ let t = await this.#r(), n = {
62
+ messages: this.#i(e),
63
+ ...this.generationConfig,
64
+ stream: !0,
65
+ stream_options: { include_usage: !0 }
66
+ };
67
+ this.responseSchema && (n.response_format = {
68
+ type: "json_object",
69
+ schema: JSON.stringify(this.responseSchema)
70
+ });
71
+ let r = await t.chat.completions.create(n), i = this;
72
+ return (async function* () {
73
+ let e = null;
74
+ for await (let t of r) {
75
+ let n = t.choices[0]?.delta?.content ?? "";
76
+ n && (yield {
77
+ text: () => n,
78
+ usageMetadata: { totalTokenCount: 0 }
79
+ }), t.usage && (e = t.usage);
80
+ }
81
+ i.#n += (e?.prompt_tokens ?? 0) + (e?.completion_tokens ?? 0), yield {
82
+ text: () => "",
83
+ usageMetadata: { totalTokenCount: i.#n }
84
+ };
85
+ })();
86
+ }
87
+ async countTokens(e) {
88
+ let t = "";
89
+ for (let n of e ?? []) for (let e of n?.parts ?? []) t += e.text ?? "";
90
+ return Math.ceil(t.length / 4);
91
+ }
92
+ #i(e) {
93
+ let t = e.map((e) => ({
94
+ role: e.role === "model" ? "assistant" : e.role === "system" ? "system" : "user",
95
+ content: e.parts.map((e) => e.text).join("")
96
+ }));
97
+ return this.#t && !t.some((e) => e.role === "system") && t.unshift({
98
+ role: "system",
99
+ content: this.#t
100
+ }), this.responseSchema && (t.length > 0 && t[0].role === "system" ? t[0].content += "\n\nRespond with valid JSON." : t.unshift({
101
+ role: "system",
102
+ content: "Respond with valid JSON."
103
+ })), t;
104
+ }
105
+ };
106
+ //#endregion
107
+ export { i as default };
@@ -29,7 +29,8 @@ var e = class {
29
29
  modelName: "onnx-community/gemma-3-1b-it-ONNX-GQA",
30
30
  device: "webgpu",
31
31
  dtype: "q4f16"
32
- }
32
+ },
33
+ webllm: { modelName: "Llama-3.2-3B-Instruct-q4f32_1-MLC" }
33
34
  };
34
35
  //#endregion
35
36
  export { e as n, t };
@@ -158,6 +158,10 @@ var e = class {
158
158
  {
159
159
  config: "TRANSFORMERS_CONFIG",
160
160
  path: "./backends/transformers.js"
161
+ },
162
+ {
163
+ config: "WEBLLM_CONFIG",
164
+ path: "./backends/webllm.js"
161
165
  }
162
166
  ];
163
167
  async function n(e) {
@@ -165,6 +169,7 @@ async function n(e) {
165
169
  if (e === "./backends/gemini.js") return (await import("./backends/gemini.js")).default;
166
170
  if (e === "./backends/openai.js") return (await import("./backends/openai.js")).default;
167
171
  if (e === "./backends/transformers.js") return (await import("./backends/transformers.js")).default;
172
+ if (e === "./backends/webllm.js") return (await import("./backends/webllm.js")).default;
168
173
  throw Error(`Unknown backend path "${e}"`);
169
174
  }
170
175
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prompt-api-polyfill",
3
- "version": "1.17.0",
3
+ "version": "1.18.0",
4
4
  "description": "Polyfill for the Prompt API (`LanguageModel`) backed by Firebase AI Logic, Gemini API, OpenAI API, or Transformers.js.",
5
5
  "type": "module",
6
6
  "main": "./dist/prompt-api-polyfill.js",
@@ -47,19 +47,20 @@
47
47
  },
48
48
  "devDependencies": {
49
49
  "@eslint/js": "^10.0.1",
50
- "eslint": "^10.3.0",
50
+ "eslint": "^10.4.0",
51
51
  "eslint-config-prettier": "^10.1.8",
52
52
  "globals": "^17.6.0",
53
53
  "node-addon-api": "^8.7.0",
54
54
  "node-gyp": "^12.3.0",
55
55
  "prettier": "^3.8.3",
56
56
  "prettier-plugin-curly": "^0.4.1",
57
- "vite": "^8.0.11"
57
+ "vite": "^8.0.13"
58
58
  },
59
59
  "dependencies": {
60
- "@google/genai": "^1.52.0",
60
+ "@google/genai": "^2.3.0",
61
61
  "@huggingface/transformers": "^4.2.0",
62
- "firebase": "^12.12.1",
63
- "openai": "^6.36.0"
62
+ "@mlc-ai/web-llm": "^0.2.83",
63
+ "firebase": "^12.13.0",
64
+ "openai": "^6.37.0"
64
65
  }
65
66
  }