@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.js CHANGED
@@ -31,7 +31,8 @@ module.exports = __toCommonJS(index_exports);
31
31
  var marker = "vercel.ai.error";
32
32
  var symbol = Symbol.for(marker);
33
33
  var _a;
34
- var _AISDKError = class _AISDKError2 extends Error {
34
+ var _b;
35
+ var AISDKError = class _AISDKError extends (_b = Error, _a = symbol, _b) {
35
36
  /**
36
37
  * Creates an AI SDK Error.
37
38
  *
@@ -56,55 +57,172 @@ var _AISDKError = class _AISDKError2 extends Error {
56
57
  * @returns {boolean} True if the error is an AI SDK Error, false otherwise.
57
58
  */
58
59
  static isInstance(error) {
59
- return _AISDKError2.hasMarker(error, marker);
60
+ return _AISDKError.hasMarker(error, marker);
60
61
  }
61
62
  static hasMarker(error, marker15) {
62
63
  const markerSymbol = Symbol.for(marker15);
63
64
  return error != null && typeof error === "object" && markerSymbol in error && typeof error[markerSymbol] === "boolean" && error[markerSymbol] === true;
64
65
  }
65
66
  };
66
- _a = symbol;
67
- var AISDKError = _AISDKError;
68
67
  var name = "AI_APICallError";
69
68
  var marker2 = `vercel.ai.error.${name}`;
70
69
  var symbol2 = Symbol.for(marker2);
71
70
  var _a2;
72
- _a2 = symbol2;
71
+ var _b2;
72
+ var APICallError = class extends (_b2 = AISDKError, _a2 = symbol2, _b2) {
73
+ constructor({
74
+ message,
75
+ url,
76
+ requestBodyValues,
77
+ statusCode,
78
+ responseHeaders,
79
+ responseBody,
80
+ cause,
81
+ isRetryable = statusCode != null && (statusCode === 408 || // request timeout
82
+ statusCode === 409 || // conflict
83
+ statusCode === 429 || // too many requests
84
+ statusCode >= 500),
85
+ // server error
86
+ data
87
+ }) {
88
+ super({ name, message, cause });
89
+ this[_a2] = true;
90
+ this.url = url;
91
+ this.requestBodyValues = requestBodyValues;
92
+ this.statusCode = statusCode;
93
+ this.responseHeaders = responseHeaders;
94
+ this.responseBody = responseBody;
95
+ this.isRetryable = isRetryable;
96
+ this.data = data;
97
+ }
98
+ static isInstance(error) {
99
+ return AISDKError.hasMarker(error, marker2);
100
+ }
101
+ };
73
102
  var name2 = "AI_EmptyResponseBodyError";
74
103
  var marker3 = `vercel.ai.error.${name2}`;
75
104
  var symbol3 = Symbol.for(marker3);
76
105
  var _a3;
77
- _a3 = symbol3;
106
+ var _b3;
107
+ var EmptyResponseBodyError = class extends (_b3 = AISDKError, _a3 = symbol3, _b3) {
108
+ // used in isInstance
109
+ constructor({ message = "Empty response body" } = {}) {
110
+ super({ name: name2, message });
111
+ this[_a3] = true;
112
+ }
113
+ static isInstance(error) {
114
+ return AISDKError.hasMarker(error, marker3);
115
+ }
116
+ };
117
+ function getErrorMessage(error) {
118
+ if (error == null) {
119
+ return "unknown error";
120
+ }
121
+ if (typeof error === "string") {
122
+ return error;
123
+ }
124
+ if (error instanceof Error) {
125
+ return error.message;
126
+ }
127
+ return JSON.stringify(error);
128
+ }
78
129
  var name3 = "AI_InvalidArgumentError";
79
130
  var marker4 = `vercel.ai.error.${name3}`;
80
131
  var symbol4 = Symbol.for(marker4);
81
132
  var _a4;
82
- _a4 = symbol4;
133
+ var _b4;
134
+ var InvalidArgumentError = class extends (_b4 = AISDKError, _a4 = symbol4, _b4) {
135
+ constructor({
136
+ message,
137
+ cause,
138
+ argument
139
+ }) {
140
+ super({ name: name3, message, cause });
141
+ this[_a4] = true;
142
+ this.argument = argument;
143
+ }
144
+ static isInstance(error) {
145
+ return AISDKError.hasMarker(error, marker4);
146
+ }
147
+ };
83
148
  var name4 = "AI_InvalidPromptError";
84
149
  var marker5 = `vercel.ai.error.${name4}`;
85
150
  var symbol5 = Symbol.for(marker5);
86
151
  var _a5;
87
- _a5 = symbol5;
152
+ var _b5;
153
+ var InvalidPromptError = class extends (_b5 = AISDKError, _a5 = symbol5, _b5) {
154
+ constructor({
155
+ prompt,
156
+ message,
157
+ cause
158
+ }) {
159
+ super({ name: name4, message: `Invalid prompt: ${message}`, cause });
160
+ this[_a5] = true;
161
+ this.prompt = prompt;
162
+ }
163
+ static isInstance(error) {
164
+ return AISDKError.hasMarker(error, marker5);
165
+ }
166
+ };
88
167
  var name5 = "AI_InvalidResponseDataError";
89
168
  var marker6 = `vercel.ai.error.${name5}`;
90
169
  var symbol6 = Symbol.for(marker6);
91
170
  var _a6;
92
- _a6 = symbol6;
171
+ var _b6;
172
+ var InvalidResponseDataError = class extends (_b6 = AISDKError, _a6 = symbol6, _b6) {
173
+ constructor({
174
+ data,
175
+ message = `Invalid response data: ${JSON.stringify(data)}.`
176
+ }) {
177
+ super({ name: name5, message });
178
+ this[_a6] = true;
179
+ this.data = data;
180
+ }
181
+ static isInstance(error) {
182
+ return AISDKError.hasMarker(error, marker6);
183
+ }
184
+ };
93
185
  var name6 = "AI_JSONParseError";
94
186
  var marker7 = `vercel.ai.error.${name6}`;
95
187
  var symbol7 = Symbol.for(marker7);
96
188
  var _a7;
97
- _a7 = symbol7;
189
+ var _b7;
190
+ var JSONParseError = class extends (_b7 = AISDKError, _a7 = symbol7, _b7) {
191
+ constructor({ text, cause }) {
192
+ super({
193
+ name: name6,
194
+ message: `JSON parsing failed: Text: ${text}.
195
+ Error message: ${getErrorMessage(cause)}`,
196
+ cause
197
+ });
198
+ this[_a7] = true;
199
+ this.text = text;
200
+ }
201
+ static isInstance(error) {
202
+ return AISDKError.hasMarker(error, marker7);
203
+ }
204
+ };
98
205
  var name7 = "AI_LoadAPIKeyError";
99
206
  var marker8 = `vercel.ai.error.${name7}`;
100
207
  var symbol8 = Symbol.for(marker8);
101
208
  var _a8;
102
- _a8 = symbol8;
209
+ var _b8;
210
+ var LoadAPIKeyError = class extends (_b8 = AISDKError, _a8 = symbol8, _b8) {
211
+ // used in isInstance
212
+ constructor({ message }) {
213
+ super({ name: name7, message });
214
+ this[_a8] = true;
215
+ }
216
+ static isInstance(error) {
217
+ return AISDKError.hasMarker(error, marker8);
218
+ }
219
+ };
103
220
  var name8 = "AI_LoadSettingError";
104
221
  var marker9 = `vercel.ai.error.${name8}`;
105
222
  var symbol9 = Symbol.for(marker9);
106
223
  var _a9;
107
- var LoadSettingError = class extends AISDKError {
224
+ var _b9;
225
+ var LoadSettingError = class extends (_b9 = AISDKError, _a9 = symbol9, _b9) {
108
226
  // used in isInstance
109
227
  constructor({ message }) {
110
228
  super({ name: name8, message });
@@ -114,32 +232,107 @@ var LoadSettingError = class extends AISDKError {
114
232
  return AISDKError.hasMarker(error, marker9);
115
233
  }
116
234
  };
117
- _a9 = symbol9;
118
235
  var name9 = "AI_NoContentGeneratedError";
119
236
  var marker10 = `vercel.ai.error.${name9}`;
120
237
  var symbol10 = Symbol.for(marker10);
121
238
  var _a10;
122
- _a10 = symbol10;
239
+ var _b10;
240
+ var NoContentGeneratedError = class extends (_b10 = AISDKError, _a10 = symbol10, _b10) {
241
+ // used in isInstance
242
+ constructor({
243
+ message = "No content generated."
244
+ } = {}) {
245
+ super({ name: name9, message });
246
+ this[_a10] = true;
247
+ }
248
+ static isInstance(error) {
249
+ return AISDKError.hasMarker(error, marker10);
250
+ }
251
+ };
123
252
  var name10 = "AI_NoSuchModelError";
124
253
  var marker11 = `vercel.ai.error.${name10}`;
125
254
  var symbol11 = Symbol.for(marker11);
126
255
  var _a11;
127
- _a11 = symbol11;
256
+ var _b11;
257
+ var NoSuchModelError = class extends (_b11 = AISDKError, _a11 = symbol11, _b11) {
258
+ constructor({
259
+ errorName = name10,
260
+ modelId,
261
+ modelType,
262
+ message = `No such ${modelType}: ${modelId}`
263
+ }) {
264
+ super({ name: errorName, message });
265
+ this[_a11] = true;
266
+ this.modelId = modelId;
267
+ this.modelType = modelType;
268
+ }
269
+ static isInstance(error) {
270
+ return AISDKError.hasMarker(error, marker11);
271
+ }
272
+ };
128
273
  var name11 = "AI_TooManyEmbeddingValuesForCallError";
129
274
  var marker12 = `vercel.ai.error.${name11}`;
130
275
  var symbol12 = Symbol.for(marker12);
131
276
  var _a12;
132
- _a12 = symbol12;
277
+ var _b12;
278
+ var TooManyEmbeddingValuesForCallError = class extends (_b12 = AISDKError, _a12 = symbol12, _b12) {
279
+ constructor(options) {
280
+ super({
281
+ name: name11,
282
+ 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.`
283
+ });
284
+ this[_a12] = true;
285
+ this.provider = options.provider;
286
+ this.modelId = options.modelId;
287
+ this.maxEmbeddingsPerCall = options.maxEmbeddingsPerCall;
288
+ this.values = options.values;
289
+ }
290
+ static isInstance(error) {
291
+ return AISDKError.hasMarker(error, marker12);
292
+ }
293
+ };
133
294
  var name12 = "AI_TypeValidationError";
134
295
  var marker13 = `vercel.ai.error.${name12}`;
135
296
  var symbol13 = Symbol.for(marker13);
136
297
  var _a13;
137
- _a13 = symbol13;
298
+ var _b13;
299
+ var TypeValidationError = class _TypeValidationError extends (_b13 = AISDKError, _a13 = symbol13, _b13) {
300
+ constructor({ value, cause }) {
301
+ super({
302
+ name: name12,
303
+ message: `Type validation failed: Value: ${JSON.stringify(value)}.
304
+ Error message: ${getErrorMessage(cause)}`,
305
+ cause
306
+ });
307
+ this[_a13] = true;
308
+ this.value = value;
309
+ }
310
+ static isInstance(error) {
311
+ return AISDKError.hasMarker(error, marker13);
312
+ }
313
+ /**
314
+ * Wraps an error into a TypeValidationError.
315
+ * If the cause is already a TypeValidationError with the same value, it returns the cause.
316
+ * Otherwise, it creates a new TypeValidationError.
317
+ *
318
+ * @param {Object} params - The parameters for wrapping the error.
319
+ * @param {unknown} params.value - The value that failed validation.
320
+ * @param {unknown} params.cause - The original error or cause of the validation failure.
321
+ * @returns {TypeValidationError} A TypeValidationError instance.
322
+ */
323
+ static wrap({
324
+ value,
325
+ cause
326
+ }) {
327
+ return _TypeValidationError.isInstance(cause) && cause.value === value ? cause : new _TypeValidationError({ value, cause });
328
+ }
329
+ };
138
330
  var name13 = "AI_UnsupportedFunctionalityError";
139
331
  var marker14 = `vercel.ai.error.${name13}`;
140
332
  var symbol14 = Symbol.for(marker14);
141
333
  var _a14;
142
- var UnsupportedFunctionalityError = class extends AISDKError {
334
+ var _b14;
335
+ var UnsupportedFunctionalityError = class extends (_b14 = AISDKError, _a14 = symbol14, _b14) {
143
336
  constructor({
144
337
  functionality,
145
338
  message = `'${functionality}' functionality not supported.`
@@ -152,7 +345,6 @@ var UnsupportedFunctionalityError = class extends AISDKError {
152
345
  return AISDKError.hasMarker(error, marker14);
153
346
  }
154
347
  };
155
- _a14 = symbol14;
156
348
 
157
349
  // src/tool-calling/build-json-system-prompt.ts
158
350
  function buildJsonToolSystemPrompt(originalSystemPrompt, tools, options) {
@@ -296,6 +488,8 @@ function convertToolResultOutput(output) {
296
488
  return { value: output.value, isError: true };
297
489
  case "content":
298
490
  return { value: output.value, isError: false };
491
+ case "execution-denied":
492
+ return { value: output.reason, isError: true };
299
493
  default: {
300
494
  const exhaustiveCheck = output;
301
495
  return { value: exhaustiveCheck, isError: false };
@@ -408,7 +602,7 @@ function convertToWebLLMMessages(prompt) {
408
602
  }
409
603
  break;
410
604
  case "tool":
411
- const toolResults = message.content.map(toToolResult);
605
+ const toolResults = message.content.filter((part) => part.type === "tool-result").map(toToolResult);
412
606
  const formattedResults = formatToolResults(toolResults);
413
607
  messages.push({
414
608
  role: "user",
@@ -424,17 +618,17 @@ function convertToWebLLMMessages(prompt) {
424
618
  var import_web_llm = require("@mlc-ai/web-llm");
425
619
 
426
620
  // src/utils/warnings.ts
427
- function createUnsupportedSettingWarning(setting, details) {
621
+ function createUnsupportedSettingWarning(feature, details) {
428
622
  return {
429
- type: "unsupported-setting",
430
- setting,
623
+ type: "unsupported",
624
+ feature,
431
625
  details
432
626
  };
433
627
  }
434
628
  function createUnsupportedToolWarning(tool, details) {
435
629
  return {
436
- type: "unsupported-tool",
437
- tool,
630
+ type: "unsupported",
631
+ feature: `tool:${tool.name}`,
438
632
  details
439
633
  };
440
634
  }
@@ -684,8 +878,21 @@ ${this.FENCE_END}`;
684
878
  };
685
879
 
686
880
  // src/web-llm-language-model.ts
881
+ function isMobile() {
882
+ if (typeof navigator === "undefined") return false;
883
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
884
+ navigator.userAgent
885
+ );
886
+ }
887
+ function checkWebGPU() {
888
+ try {
889
+ return !!globalThis?.navigator?.gpu;
890
+ } catch {
891
+ return false;
892
+ }
893
+ }
687
894
  function doesBrowserSupportWebLLM() {
688
- return globalThis?.navigator?.gpu !== void 0;
895
+ return checkWebGPU();
689
896
  }
690
897
  function extractToolName(content) {
691
898
  const jsonMatch = content.match(/\{\s*"name"\s*:\s*"([^"]+)"/);
@@ -746,7 +953,7 @@ function extractArgumentsContent(content) {
746
953
  }
747
954
  var WebLLMLanguageModel = class {
748
955
  constructor(modelId, options = {}) {
749
- this.specificationVersion = "v2";
956
+ this.specificationVersion = "v3";
750
957
  this.provider = "web-llm";
751
958
  this.isInitialized = false;
752
959
  this.supportedUrls = {
@@ -970,11 +1177,19 @@ var WebLLMLanguageModel = class {
970
1177
  }
971
1178
  return {
972
1179
  content: parts,
973
- finishReason: "tool-calls",
1180
+ finishReason: { unified: "tool-calls", raw: "tool-calls" },
974
1181
  usage: {
975
- inputTokens: response.usage?.prompt_tokens,
976
- outputTokens: response.usage?.completion_tokens,
977
- totalTokens: response.usage?.total_tokens
1182
+ inputTokens: {
1183
+ total: response.usage?.prompt_tokens,
1184
+ noCache: void 0,
1185
+ cacheRead: void 0,
1186
+ cacheWrite: void 0
1187
+ },
1188
+ outputTokens: {
1189
+ total: response.usage?.completion_tokens,
1190
+ text: void 0,
1191
+ reasoning: void 0
1192
+ }
978
1193
  },
979
1194
  request: { body: { messages: promptMessages, ...requestOptions } },
980
1195
  warnings
@@ -986,17 +1201,31 @@ var WebLLMLanguageModel = class {
986
1201
  text: textContent || rawResponse
987
1202
  }
988
1203
  ];
989
- let finishReason = "stop";
1204
+ let finishReason = {
1205
+ unified: "stop",
1206
+ raw: choice.finish_reason
1207
+ };
990
1208
  if (choice.finish_reason === "abort") {
991
- finishReason = "other";
1209
+ finishReason = { unified: "other", raw: choice.finish_reason };
992
1210
  }
993
1211
  return {
994
1212
  content,
995
1213
  finishReason,
996
1214
  usage: {
997
- inputTokens: response.usage?.prompt_tokens,
998
- outputTokens: response.usage?.completion_tokens,
999
- totalTokens: response.usage?.total_tokens
1215
+ inputTokens: {
1216
+ total: response.usage?.prompt_tokens,
1217
+ noCache: void 0,
1218
+ cacheRead: void 0,
1219
+ cacheWrite: void 0
1220
+ },
1221
+ outputTokens: {
1222
+ total: response.usage?.completion_tokens,
1223
+ text: void 0,
1224
+ reasoning: void 0
1225
+ },
1226
+ raw: {
1227
+ total: response.usage?.total_tokens
1228
+ }
1000
1229
  },
1001
1230
  request: { body: { messages: promptMessages, ...requestOptions } },
1002
1231
  warnings
@@ -1012,17 +1241,20 @@ var WebLLMLanguageModel = class {
1012
1241
  }
1013
1242
  }
1014
1243
  /**
1015
- * Check the availability of the WebLLM model
1016
- * @returns Promise resolving to "unavailable", "available", or "available-after-download"
1244
+ * Check the availability of the WebLLM model.
1245
+ * Note: On mobile devices with a worker, WebGPU detection is skipped since it
1246
+ * can't be done reliably. The actual availability will be determined at init.
1247
+ * @returns Promise resolving to "unavailable", "available", or "downloadable"
1017
1248
  */
1018
1249
  async availability() {
1019
- if (!doesBrowserSupportWebLLM()) {
1020
- return "unavailable";
1021
- }
1022
1250
  if (this.isInitialized) {
1023
1251
  return "available";
1024
1252
  }
1025
- return "downloadable";
1253
+ if (this.config.options.worker && isMobile()) {
1254
+ return "downloadable";
1255
+ }
1256
+ const supported = checkWebGPU();
1257
+ return supported ? "downloadable" : "unavailable";
1026
1258
  }
1027
1259
  /**
1028
1260
  * Creates an engine session with download progress monitoring.
@@ -1119,9 +1351,20 @@ var WebLLMLanguageModel = class {
1119
1351
  type: "finish",
1120
1352
  finishReason,
1121
1353
  usage: {
1122
- inputTokens: usage?.prompt_tokens,
1123
- outputTokens: usage?.completion_tokens,
1124
- totalTokens: usage?.total_tokens
1354
+ inputTokens: {
1355
+ total: usage?.prompt_tokens,
1356
+ noCache: void 0,
1357
+ cacheRead: void 0,
1358
+ cacheWrite: void 0
1359
+ },
1360
+ outputTokens: {
1361
+ total: usage?.completion_tokens,
1362
+ text: void 0,
1363
+ reasoning: void 0
1364
+ },
1365
+ raw: {
1366
+ total: usage?.total_tokens
1367
+ }
1125
1368
  }
1126
1369
  });
1127
1370
  controller.close();
@@ -1319,20 +1562,23 @@ var WebLLMLanguageModel = class {
1319
1562
  emitTextDelta(fenceDetector.getBuffer());
1320
1563
  fenceDetector.clearBuffer();
1321
1564
  }
1322
- let finishReason = "stop";
1565
+ let finishReason = {
1566
+ unified: "stop",
1567
+ raw: "stop"
1568
+ };
1323
1569
  if (choice.finish_reason === "abort") {
1324
- finishReason = "other";
1570
+ finishReason = { unified: "other", raw: "abort" };
1325
1571
  } else {
1326
1572
  const { toolCalls } = parseJsonFunctionCalls(accumulatedText);
1327
1573
  if (toolCalls.length > 0) {
1328
- finishReason = "tool-calls";
1574
+ finishReason = { unified: "tool-calls", raw: "tool-calls" };
1329
1575
  }
1330
1576
  }
1331
1577
  finishStream(finishReason, chunk.usage);
1332
1578
  }
1333
1579
  }
1334
1580
  if (!finished) {
1335
- finishStream("stop");
1581
+ finishStream({ unified: "stop", raw: "stop" });
1336
1582
  }
1337
1583
  } catch (error) {
1338
1584
  controller.error(error);