@browser-ai/web-llm 1.0.0 → 2.0.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/dist/index.mjs CHANGED
@@ -2,7 +2,8 @@
2
2
  var marker = "vercel.ai.error";
3
3
  var symbol = Symbol.for(marker);
4
4
  var _a;
5
- var _AISDKError = class _AISDKError2 extends Error {
5
+ var _b;
6
+ var AISDKError = class _AISDKError extends (_b = Error, _a = symbol, _b) {
6
7
  /**
7
8
  * Creates an AI SDK Error.
8
9
  *
@@ -27,55 +28,172 @@ var _AISDKError = class _AISDKError2 extends Error {
27
28
  * @returns {boolean} True if the error is an AI SDK Error, false otherwise.
28
29
  */
29
30
  static isInstance(error) {
30
- return _AISDKError2.hasMarker(error, marker);
31
+ return _AISDKError.hasMarker(error, marker);
31
32
  }
32
33
  static hasMarker(error, marker15) {
33
34
  const markerSymbol = Symbol.for(marker15);
34
35
  return error != null && typeof error === "object" && markerSymbol in error && typeof error[markerSymbol] === "boolean" && error[markerSymbol] === true;
35
36
  }
36
37
  };
37
- _a = symbol;
38
- var AISDKError = _AISDKError;
39
38
  var name = "AI_APICallError";
40
39
  var marker2 = `vercel.ai.error.${name}`;
41
40
  var symbol2 = Symbol.for(marker2);
42
41
  var _a2;
43
- _a2 = symbol2;
42
+ var _b2;
43
+ var APICallError = class extends (_b2 = AISDKError, _a2 = symbol2, _b2) {
44
+ constructor({
45
+ message,
46
+ url,
47
+ requestBodyValues,
48
+ statusCode,
49
+ responseHeaders,
50
+ responseBody,
51
+ cause,
52
+ isRetryable = statusCode != null && (statusCode === 408 || // request timeout
53
+ statusCode === 409 || // conflict
54
+ statusCode === 429 || // too many requests
55
+ statusCode >= 500),
56
+ // server error
57
+ data
58
+ }) {
59
+ super({ name, message, cause });
60
+ this[_a2] = true;
61
+ this.url = url;
62
+ this.requestBodyValues = requestBodyValues;
63
+ this.statusCode = statusCode;
64
+ this.responseHeaders = responseHeaders;
65
+ this.responseBody = responseBody;
66
+ this.isRetryable = isRetryable;
67
+ this.data = data;
68
+ }
69
+ static isInstance(error) {
70
+ return AISDKError.hasMarker(error, marker2);
71
+ }
72
+ };
44
73
  var name2 = "AI_EmptyResponseBodyError";
45
74
  var marker3 = `vercel.ai.error.${name2}`;
46
75
  var symbol3 = Symbol.for(marker3);
47
76
  var _a3;
48
- _a3 = symbol3;
77
+ var _b3;
78
+ var EmptyResponseBodyError = class extends (_b3 = AISDKError, _a3 = symbol3, _b3) {
79
+ // used in isInstance
80
+ constructor({ message = "Empty response body" } = {}) {
81
+ super({ name: name2, message });
82
+ this[_a3] = true;
83
+ }
84
+ static isInstance(error) {
85
+ return AISDKError.hasMarker(error, marker3);
86
+ }
87
+ };
88
+ function getErrorMessage(error) {
89
+ if (error == null) {
90
+ return "unknown error";
91
+ }
92
+ if (typeof error === "string") {
93
+ return error;
94
+ }
95
+ if (error instanceof Error) {
96
+ return error.message;
97
+ }
98
+ return JSON.stringify(error);
99
+ }
49
100
  var name3 = "AI_InvalidArgumentError";
50
101
  var marker4 = `vercel.ai.error.${name3}`;
51
102
  var symbol4 = Symbol.for(marker4);
52
103
  var _a4;
53
- _a4 = symbol4;
104
+ var _b4;
105
+ var InvalidArgumentError = class extends (_b4 = AISDKError, _a4 = symbol4, _b4) {
106
+ constructor({
107
+ message,
108
+ cause,
109
+ argument
110
+ }) {
111
+ super({ name: name3, message, cause });
112
+ this[_a4] = true;
113
+ this.argument = argument;
114
+ }
115
+ static isInstance(error) {
116
+ return AISDKError.hasMarker(error, marker4);
117
+ }
118
+ };
54
119
  var name4 = "AI_InvalidPromptError";
55
120
  var marker5 = `vercel.ai.error.${name4}`;
56
121
  var symbol5 = Symbol.for(marker5);
57
122
  var _a5;
58
- _a5 = symbol5;
123
+ var _b5;
124
+ var InvalidPromptError = class extends (_b5 = AISDKError, _a5 = symbol5, _b5) {
125
+ constructor({
126
+ prompt,
127
+ message,
128
+ cause
129
+ }) {
130
+ super({ name: name4, message: `Invalid prompt: ${message}`, cause });
131
+ this[_a5] = true;
132
+ this.prompt = prompt;
133
+ }
134
+ static isInstance(error) {
135
+ return AISDKError.hasMarker(error, marker5);
136
+ }
137
+ };
59
138
  var name5 = "AI_InvalidResponseDataError";
60
139
  var marker6 = `vercel.ai.error.${name5}`;
61
140
  var symbol6 = Symbol.for(marker6);
62
141
  var _a6;
63
- _a6 = symbol6;
142
+ var _b6;
143
+ var InvalidResponseDataError = class extends (_b6 = AISDKError, _a6 = symbol6, _b6) {
144
+ constructor({
145
+ data,
146
+ message = `Invalid response data: ${JSON.stringify(data)}.`
147
+ }) {
148
+ super({ name: name5, message });
149
+ this[_a6] = true;
150
+ this.data = data;
151
+ }
152
+ static isInstance(error) {
153
+ return AISDKError.hasMarker(error, marker6);
154
+ }
155
+ };
64
156
  var name6 = "AI_JSONParseError";
65
157
  var marker7 = `vercel.ai.error.${name6}`;
66
158
  var symbol7 = Symbol.for(marker7);
67
159
  var _a7;
68
- _a7 = symbol7;
160
+ var _b7;
161
+ var JSONParseError = class extends (_b7 = AISDKError, _a7 = symbol7, _b7) {
162
+ constructor({ text, cause }) {
163
+ super({
164
+ name: name6,
165
+ message: `JSON parsing failed: Text: ${text}.
166
+ Error message: ${getErrorMessage(cause)}`,
167
+ cause
168
+ });
169
+ this[_a7] = true;
170
+ this.text = text;
171
+ }
172
+ static isInstance(error) {
173
+ return AISDKError.hasMarker(error, marker7);
174
+ }
175
+ };
69
176
  var name7 = "AI_LoadAPIKeyError";
70
177
  var marker8 = `vercel.ai.error.${name7}`;
71
178
  var symbol8 = Symbol.for(marker8);
72
179
  var _a8;
73
- _a8 = symbol8;
180
+ var _b8;
181
+ var LoadAPIKeyError = class extends (_b8 = AISDKError, _a8 = symbol8, _b8) {
182
+ // used in isInstance
183
+ constructor({ message }) {
184
+ super({ name: name7, message });
185
+ this[_a8] = true;
186
+ }
187
+ static isInstance(error) {
188
+ return AISDKError.hasMarker(error, marker8);
189
+ }
190
+ };
74
191
  var name8 = "AI_LoadSettingError";
75
192
  var marker9 = `vercel.ai.error.${name8}`;
76
193
  var symbol9 = Symbol.for(marker9);
77
194
  var _a9;
78
- var LoadSettingError = class extends AISDKError {
195
+ var _b9;
196
+ var LoadSettingError = class extends (_b9 = AISDKError, _a9 = symbol9, _b9) {
79
197
  // used in isInstance
80
198
  constructor({ message }) {
81
199
  super({ name: name8, message });
@@ -85,32 +203,107 @@ var LoadSettingError = class extends AISDKError {
85
203
  return AISDKError.hasMarker(error, marker9);
86
204
  }
87
205
  };
88
- _a9 = symbol9;
89
206
  var name9 = "AI_NoContentGeneratedError";
90
207
  var marker10 = `vercel.ai.error.${name9}`;
91
208
  var symbol10 = Symbol.for(marker10);
92
209
  var _a10;
93
- _a10 = symbol10;
210
+ var _b10;
211
+ var NoContentGeneratedError = class extends (_b10 = AISDKError, _a10 = symbol10, _b10) {
212
+ // used in isInstance
213
+ constructor({
214
+ message = "No content generated."
215
+ } = {}) {
216
+ super({ name: name9, message });
217
+ this[_a10] = true;
218
+ }
219
+ static isInstance(error) {
220
+ return AISDKError.hasMarker(error, marker10);
221
+ }
222
+ };
94
223
  var name10 = "AI_NoSuchModelError";
95
224
  var marker11 = `vercel.ai.error.${name10}`;
96
225
  var symbol11 = Symbol.for(marker11);
97
226
  var _a11;
98
- _a11 = symbol11;
227
+ var _b11;
228
+ var NoSuchModelError = class extends (_b11 = AISDKError, _a11 = symbol11, _b11) {
229
+ constructor({
230
+ errorName = name10,
231
+ modelId,
232
+ modelType,
233
+ message = `No such ${modelType}: ${modelId}`
234
+ }) {
235
+ super({ name: errorName, message });
236
+ this[_a11] = true;
237
+ this.modelId = modelId;
238
+ this.modelType = modelType;
239
+ }
240
+ static isInstance(error) {
241
+ return AISDKError.hasMarker(error, marker11);
242
+ }
243
+ };
99
244
  var name11 = "AI_TooManyEmbeddingValuesForCallError";
100
245
  var marker12 = `vercel.ai.error.${name11}`;
101
246
  var symbol12 = Symbol.for(marker12);
102
247
  var _a12;
103
- _a12 = symbol12;
248
+ var _b12;
249
+ var TooManyEmbeddingValuesForCallError = class extends (_b12 = AISDKError, _a12 = symbol12, _b12) {
250
+ constructor(options) {
251
+ super({
252
+ name: name11,
253
+ message: `Too many values for a single embedding call. The ${options.provider} model "${options.modelId}" can only embed up to ${options.maxEmbeddingsPerCall} values per call, but ${options.values.length} values were provided.`
254
+ });
255
+ this[_a12] = true;
256
+ this.provider = options.provider;
257
+ this.modelId = options.modelId;
258
+ this.maxEmbeddingsPerCall = options.maxEmbeddingsPerCall;
259
+ this.values = options.values;
260
+ }
261
+ static isInstance(error) {
262
+ return AISDKError.hasMarker(error, marker12);
263
+ }
264
+ };
104
265
  var name12 = "AI_TypeValidationError";
105
266
  var marker13 = `vercel.ai.error.${name12}`;
106
267
  var symbol13 = Symbol.for(marker13);
107
268
  var _a13;
108
- _a13 = symbol13;
269
+ var _b13;
270
+ var TypeValidationError = class _TypeValidationError extends (_b13 = AISDKError, _a13 = symbol13, _b13) {
271
+ constructor({ value, cause }) {
272
+ super({
273
+ name: name12,
274
+ message: `Type validation failed: Value: ${JSON.stringify(value)}.
275
+ Error message: ${getErrorMessage(cause)}`,
276
+ cause
277
+ });
278
+ this[_a13] = true;
279
+ this.value = value;
280
+ }
281
+ static isInstance(error) {
282
+ return AISDKError.hasMarker(error, marker13);
283
+ }
284
+ /**
285
+ * Wraps an error into a TypeValidationError.
286
+ * If the cause is already a TypeValidationError with the same value, it returns the cause.
287
+ * Otherwise, it creates a new TypeValidationError.
288
+ *
289
+ * @param {Object} params - The parameters for wrapping the error.
290
+ * @param {unknown} params.value - The value that failed validation.
291
+ * @param {unknown} params.cause - The original error or cause of the validation failure.
292
+ * @returns {TypeValidationError} A TypeValidationError instance.
293
+ */
294
+ static wrap({
295
+ value,
296
+ cause
297
+ }) {
298
+ return _TypeValidationError.isInstance(cause) && cause.value === value ? cause : new _TypeValidationError({ value, cause });
299
+ }
300
+ };
109
301
  var name13 = "AI_UnsupportedFunctionalityError";
110
302
  var marker14 = `vercel.ai.error.${name13}`;
111
303
  var symbol14 = Symbol.for(marker14);
112
304
  var _a14;
113
- var UnsupportedFunctionalityError = class extends AISDKError {
305
+ var _b14;
306
+ var UnsupportedFunctionalityError = class extends (_b14 = AISDKError, _a14 = symbol14, _b14) {
114
307
  constructor({
115
308
  functionality,
116
309
  message = `'${functionality}' functionality not supported.`
@@ -123,7 +316,6 @@ var UnsupportedFunctionalityError = class extends AISDKError {
123
316
  return AISDKError.hasMarker(error, marker14);
124
317
  }
125
318
  };
126
- _a14 = symbol14;
127
319
 
128
320
  // src/tool-calling/build-json-system-prompt.ts
129
321
  function buildJsonToolSystemPrompt(originalSystemPrompt, tools, options) {
@@ -267,6 +459,8 @@ function convertToolResultOutput(output) {
267
459
  return { value: output.value, isError: true };
268
460
  case "content":
269
461
  return { value: output.value, isError: false };
462
+ case "execution-denied":
463
+ return { value: output.reason, isError: true };
270
464
  default: {
271
465
  const exhaustiveCheck = output;
272
466
  return { value: exhaustiveCheck, isError: false };
@@ -379,7 +573,7 @@ function convertToWebLLMMessages(prompt) {
379
573
  }
380
574
  break;
381
575
  case "tool":
382
- const toolResults = message.content.map(toToolResult);
576
+ const toolResults = message.content.filter((part) => part.type === "tool-result").map(toToolResult);
383
577
  const formattedResults = formatToolResults(toolResults);
384
578
  messages.push({
385
579
  role: "user",
@@ -398,17 +592,17 @@ import {
398
592
  } from "@mlc-ai/web-llm";
399
593
 
400
594
  // src/utils/warnings.ts
401
- function createUnsupportedSettingWarning(setting, details) {
595
+ function createUnsupportedSettingWarning(feature, details) {
402
596
  return {
403
- type: "unsupported-setting",
404
- setting,
597
+ type: "unsupported",
598
+ feature,
405
599
  details
406
600
  };
407
601
  }
408
602
  function createUnsupportedToolWarning(tool, details) {
409
603
  return {
410
- type: "unsupported-tool",
411
- tool,
604
+ type: "unsupported",
605
+ feature: `tool:${tool.name}`,
412
606
  details
413
607
  };
414
608
  }
@@ -658,8 +852,21 @@ ${this.FENCE_END}`;
658
852
  };
659
853
 
660
854
  // src/web-llm-language-model.ts
855
+ function isMobile() {
856
+ if (typeof navigator === "undefined") return false;
857
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
858
+ navigator.userAgent
859
+ );
860
+ }
861
+ function checkWebGPU() {
862
+ try {
863
+ return !!globalThis?.navigator?.gpu;
864
+ } catch {
865
+ return false;
866
+ }
867
+ }
661
868
  function doesBrowserSupportWebLLM() {
662
- return globalThis?.navigator?.gpu !== void 0;
869
+ return checkWebGPU();
663
870
  }
664
871
  function extractToolName(content) {
665
872
  const jsonMatch = content.match(/\{\s*"name"\s*:\s*"([^"]+)"/);
@@ -720,7 +927,7 @@ function extractArgumentsContent(content) {
720
927
  }
721
928
  var WebLLMLanguageModel = class {
722
929
  constructor(modelId, options = {}) {
723
- this.specificationVersion = "v2";
930
+ this.specificationVersion = "v3";
724
931
  this.provider = "web-llm";
725
932
  this.isInitialized = false;
726
933
  this.supportedUrls = {
@@ -944,11 +1151,19 @@ var WebLLMLanguageModel = class {
944
1151
  }
945
1152
  return {
946
1153
  content: parts,
947
- finishReason: "tool-calls",
1154
+ finishReason: { unified: "tool-calls", raw: "tool-calls" },
948
1155
  usage: {
949
- inputTokens: response.usage?.prompt_tokens,
950
- outputTokens: response.usage?.completion_tokens,
951
- totalTokens: response.usage?.total_tokens
1156
+ inputTokens: {
1157
+ total: response.usage?.prompt_tokens,
1158
+ noCache: void 0,
1159
+ cacheRead: void 0,
1160
+ cacheWrite: void 0
1161
+ },
1162
+ outputTokens: {
1163
+ total: response.usage?.completion_tokens,
1164
+ text: void 0,
1165
+ reasoning: void 0
1166
+ }
952
1167
  },
953
1168
  request: { body: { messages: promptMessages, ...requestOptions } },
954
1169
  warnings
@@ -960,17 +1175,31 @@ var WebLLMLanguageModel = class {
960
1175
  text: textContent || rawResponse
961
1176
  }
962
1177
  ];
963
- let finishReason = "stop";
1178
+ let finishReason = {
1179
+ unified: "stop",
1180
+ raw: choice.finish_reason
1181
+ };
964
1182
  if (choice.finish_reason === "abort") {
965
- finishReason = "other";
1183
+ finishReason = { unified: "other", raw: choice.finish_reason };
966
1184
  }
967
1185
  return {
968
1186
  content,
969
1187
  finishReason,
970
1188
  usage: {
971
- inputTokens: response.usage?.prompt_tokens,
972
- outputTokens: response.usage?.completion_tokens,
973
- totalTokens: response.usage?.total_tokens
1189
+ inputTokens: {
1190
+ total: response.usage?.prompt_tokens,
1191
+ noCache: void 0,
1192
+ cacheRead: void 0,
1193
+ cacheWrite: void 0
1194
+ },
1195
+ outputTokens: {
1196
+ total: response.usage?.completion_tokens,
1197
+ text: void 0,
1198
+ reasoning: void 0
1199
+ },
1200
+ raw: {
1201
+ total: response.usage?.total_tokens
1202
+ }
974
1203
  },
975
1204
  request: { body: { messages: promptMessages, ...requestOptions } },
976
1205
  warnings
@@ -986,17 +1215,20 @@ var WebLLMLanguageModel = class {
986
1215
  }
987
1216
  }
988
1217
  /**
989
- * Check the availability of the WebLLM model
990
- * @returns Promise resolving to "unavailable", "available", or "available-after-download"
1218
+ * Check the availability of the WebLLM model.
1219
+ * Note: On mobile devices with a worker, WebGPU detection is skipped since it
1220
+ * can't be done reliably. The actual availability will be determined at init.
1221
+ * @returns Promise resolving to "unavailable", "available", or "downloadable"
991
1222
  */
992
1223
  async availability() {
993
- if (!doesBrowserSupportWebLLM()) {
994
- return "unavailable";
995
- }
996
1224
  if (this.isInitialized) {
997
1225
  return "available";
998
1226
  }
999
- return "downloadable";
1227
+ if (this.config.options.worker && isMobile()) {
1228
+ return "downloadable";
1229
+ }
1230
+ const supported = checkWebGPU();
1231
+ return supported ? "downloadable" : "unavailable";
1000
1232
  }
1001
1233
  /**
1002
1234
  * Creates an engine session with download progress monitoring.
@@ -1093,9 +1325,20 @@ var WebLLMLanguageModel = class {
1093
1325
  type: "finish",
1094
1326
  finishReason,
1095
1327
  usage: {
1096
- inputTokens: usage?.prompt_tokens,
1097
- outputTokens: usage?.completion_tokens,
1098
- totalTokens: usage?.total_tokens
1328
+ inputTokens: {
1329
+ total: usage?.prompt_tokens,
1330
+ noCache: void 0,
1331
+ cacheRead: void 0,
1332
+ cacheWrite: void 0
1333
+ },
1334
+ outputTokens: {
1335
+ total: usage?.completion_tokens,
1336
+ text: void 0,
1337
+ reasoning: void 0
1338
+ },
1339
+ raw: {
1340
+ total: usage?.total_tokens
1341
+ }
1099
1342
  }
1100
1343
  });
1101
1344
  controller.close();
@@ -1293,20 +1536,23 @@ var WebLLMLanguageModel = class {
1293
1536
  emitTextDelta(fenceDetector.getBuffer());
1294
1537
  fenceDetector.clearBuffer();
1295
1538
  }
1296
- let finishReason = "stop";
1539
+ let finishReason = {
1540
+ unified: "stop",
1541
+ raw: "stop"
1542
+ };
1297
1543
  if (choice.finish_reason === "abort") {
1298
- finishReason = "other";
1544
+ finishReason = { unified: "other", raw: "abort" };
1299
1545
  } else {
1300
1546
  const { toolCalls } = parseJsonFunctionCalls(accumulatedText);
1301
1547
  if (toolCalls.length > 0) {
1302
- finishReason = "tool-calls";
1548
+ finishReason = { unified: "tool-calls", raw: "tool-calls" };
1303
1549
  }
1304
1550
  }
1305
1551
  finishStream(finishReason, chunk.usage);
1306
1552
  }
1307
1553
  }
1308
1554
  if (!finished) {
1309
- finishStream("stop");
1555
+ finishStream({ unified: "stop", raw: "stop" });
1310
1556
  }
1311
1557
  } catch (error) {
1312
1558
  controller.error(error);