@browser-ai/web-llm 1.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 ADDED
@@ -0,0 +1,1370 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name14 in all)
8
+ __defProp(target, name14, { get: all[name14], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ WebLLMLanguageModel: () => WebLLMLanguageModel,
24
+ WebWorkerMLCEngineHandler: () => import_web_llm2.WebWorkerMLCEngineHandler,
25
+ doesBrowserSupportWebLLM: () => doesBrowserSupportWebLLM,
26
+ webLLM: () => webLLM
27
+ });
28
+ module.exports = __toCommonJS(index_exports);
29
+
30
+ // ../../../node_modules/@ai-sdk/provider/dist/index.mjs
31
+ var marker = "vercel.ai.error";
32
+ var symbol = Symbol.for(marker);
33
+ var _a;
34
+ var _AISDKError = class _AISDKError2 extends Error {
35
+ /**
36
+ * Creates an AI SDK Error.
37
+ *
38
+ * @param {Object} params - The parameters for creating the error.
39
+ * @param {string} params.name - The name of the error.
40
+ * @param {string} params.message - The error message.
41
+ * @param {unknown} [params.cause] - The underlying cause of the error.
42
+ */
43
+ constructor({
44
+ name: name14,
45
+ message,
46
+ cause
47
+ }) {
48
+ super(message);
49
+ this[_a] = true;
50
+ this.name = name14;
51
+ this.cause = cause;
52
+ }
53
+ /**
54
+ * Checks if the given error is an AI SDK Error.
55
+ * @param {unknown} error - The error to check.
56
+ * @returns {boolean} True if the error is an AI SDK Error, false otherwise.
57
+ */
58
+ static isInstance(error) {
59
+ return _AISDKError2.hasMarker(error, marker);
60
+ }
61
+ static hasMarker(error, marker15) {
62
+ const markerSymbol = Symbol.for(marker15);
63
+ return error != null && typeof error === "object" && markerSymbol in error && typeof error[markerSymbol] === "boolean" && error[markerSymbol] === true;
64
+ }
65
+ };
66
+ _a = symbol;
67
+ var AISDKError = _AISDKError;
68
+ var name = "AI_APICallError";
69
+ var marker2 = `vercel.ai.error.${name}`;
70
+ var symbol2 = Symbol.for(marker2);
71
+ var _a2;
72
+ _a2 = symbol2;
73
+ var name2 = "AI_EmptyResponseBodyError";
74
+ var marker3 = `vercel.ai.error.${name2}`;
75
+ var symbol3 = Symbol.for(marker3);
76
+ var _a3;
77
+ _a3 = symbol3;
78
+ var name3 = "AI_InvalidArgumentError";
79
+ var marker4 = `vercel.ai.error.${name3}`;
80
+ var symbol4 = Symbol.for(marker4);
81
+ var _a4;
82
+ _a4 = symbol4;
83
+ var name4 = "AI_InvalidPromptError";
84
+ var marker5 = `vercel.ai.error.${name4}`;
85
+ var symbol5 = Symbol.for(marker5);
86
+ var _a5;
87
+ _a5 = symbol5;
88
+ var name5 = "AI_InvalidResponseDataError";
89
+ var marker6 = `vercel.ai.error.${name5}`;
90
+ var symbol6 = Symbol.for(marker6);
91
+ var _a6;
92
+ _a6 = symbol6;
93
+ var name6 = "AI_JSONParseError";
94
+ var marker7 = `vercel.ai.error.${name6}`;
95
+ var symbol7 = Symbol.for(marker7);
96
+ var _a7;
97
+ _a7 = symbol7;
98
+ var name7 = "AI_LoadAPIKeyError";
99
+ var marker8 = `vercel.ai.error.${name7}`;
100
+ var symbol8 = Symbol.for(marker8);
101
+ var _a8;
102
+ _a8 = symbol8;
103
+ var name8 = "AI_LoadSettingError";
104
+ var marker9 = `vercel.ai.error.${name8}`;
105
+ var symbol9 = Symbol.for(marker9);
106
+ var _a9;
107
+ var LoadSettingError = class extends AISDKError {
108
+ // used in isInstance
109
+ constructor({ message }) {
110
+ super({ name: name8, message });
111
+ this[_a9] = true;
112
+ }
113
+ static isInstance(error) {
114
+ return AISDKError.hasMarker(error, marker9);
115
+ }
116
+ };
117
+ _a9 = symbol9;
118
+ var name9 = "AI_NoContentGeneratedError";
119
+ var marker10 = `vercel.ai.error.${name9}`;
120
+ var symbol10 = Symbol.for(marker10);
121
+ var _a10;
122
+ _a10 = symbol10;
123
+ var name10 = "AI_NoSuchModelError";
124
+ var marker11 = `vercel.ai.error.${name10}`;
125
+ var symbol11 = Symbol.for(marker11);
126
+ var _a11;
127
+ _a11 = symbol11;
128
+ var name11 = "AI_TooManyEmbeddingValuesForCallError";
129
+ var marker12 = `vercel.ai.error.${name11}`;
130
+ var symbol12 = Symbol.for(marker12);
131
+ var _a12;
132
+ _a12 = symbol12;
133
+ var name12 = "AI_TypeValidationError";
134
+ var marker13 = `vercel.ai.error.${name12}`;
135
+ var symbol13 = Symbol.for(marker13);
136
+ var _a13;
137
+ _a13 = symbol13;
138
+ var name13 = "AI_UnsupportedFunctionalityError";
139
+ var marker14 = `vercel.ai.error.${name13}`;
140
+ var symbol14 = Symbol.for(marker14);
141
+ var _a14;
142
+ var UnsupportedFunctionalityError = class extends AISDKError {
143
+ constructor({
144
+ functionality,
145
+ message = `'${functionality}' functionality not supported.`
146
+ }) {
147
+ super({ name: name13, message });
148
+ this[_a14] = true;
149
+ this.functionality = functionality;
150
+ }
151
+ static isInstance(error) {
152
+ return AISDKError.hasMarker(error, marker14);
153
+ }
154
+ };
155
+ _a14 = symbol14;
156
+
157
+ // src/tool-calling/build-json-system-prompt.ts
158
+ function buildJsonToolSystemPrompt(originalSystemPrompt, tools, options) {
159
+ if (!tools || tools.length === 0) {
160
+ return originalSystemPrompt || "";
161
+ }
162
+ const parallelInstruction = "Only request one tool call at a time. Wait for tool results before asking for another tool.";
163
+ const toolSchemas = tools.map((tool) => {
164
+ const schema = getParameters(tool);
165
+ return {
166
+ name: tool.name,
167
+ description: tool.description ?? "No description provided.",
168
+ parameters: schema || { type: "object", properties: {} }
169
+ };
170
+ });
171
+ const toolsJson = JSON.stringify(toolSchemas, null, 2);
172
+ const instructionBody = `You are a helpful AI assistant with access to tools.
173
+
174
+ # Available Tools
175
+ ${toolsJson}
176
+
177
+ # Tool Calling Instructions
178
+ ${parallelInstruction}
179
+
180
+ To call a tool, output JSON in this exact format inside a \`\`\`tool_call code fence:
181
+
182
+ \`\`\`tool_call
183
+ {"name": "tool_name", "arguments": {"param1": "value1", "param2": "value2"}}
184
+ \`\`\`
185
+
186
+ Tool responses will be provided in \`\`\`tool_result fences. Each line contains JSON like:
187
+ \`\`\`tool_result
188
+ {"id": "call_123", "name": "tool_name", "result": {...}, "error": false}
189
+ \`\`\`
190
+ Use the \`result\` payload (and treat \`error\` as a boolean flag) when continuing the conversation.
191
+
192
+ Important:
193
+ - Use exact tool and parameter names from the schema above
194
+ - Arguments must be a valid JSON object matching the tool's parameters
195
+ - You can include brief reasoning before or after the tool call
196
+ - If no tool is needed, respond directly without tool_call fences`;
197
+ if (originalSystemPrompt?.trim()) {
198
+ return `${originalSystemPrompt.trim()}
199
+
200
+ ${instructionBody}`;
201
+ }
202
+ return instructionBody;
203
+ }
204
+ function getParameters(tool) {
205
+ if ("parameters" in tool) {
206
+ return tool.parameters;
207
+ }
208
+ return tool.inputSchema;
209
+ }
210
+
211
+ // src/tool-calling/parse-json-function-calls.ts
212
+ var JSON_TOOL_CALL_FENCE_REGEX = /```tool[_-]?call\s*([\s\S]*?)```/gi;
213
+ function generateToolCallId() {
214
+ return `call_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
215
+ }
216
+ function parseJsonFunctionCalls(response) {
217
+ const matches = Array.from(response.matchAll(JSON_TOOL_CALL_FENCE_REGEX));
218
+ JSON_TOOL_CALL_FENCE_REGEX.lastIndex = 0;
219
+ if (matches.length === 0) {
220
+ return { toolCalls: [], textContent: response };
221
+ }
222
+ const toolCalls = [];
223
+ let textContent = response;
224
+ for (const match of matches) {
225
+ const [fullFence, innerContent] = match;
226
+ textContent = textContent.replace(fullFence, "");
227
+ try {
228
+ const trimmed = innerContent.trim();
229
+ try {
230
+ const parsed = JSON.parse(trimmed);
231
+ const callsArray = Array.isArray(parsed) ? parsed : [parsed];
232
+ for (const call of callsArray) {
233
+ if (!call.name) continue;
234
+ toolCalls.push({
235
+ type: "tool-call",
236
+ toolCallId: call.id || generateToolCallId(),
237
+ toolName: call.name,
238
+ args: call.arguments || {}
239
+ });
240
+ }
241
+ } catch {
242
+ const lines = trimmed.split("\n").filter((line) => line.trim());
243
+ for (const line of lines) {
244
+ try {
245
+ const call = JSON.parse(line.trim());
246
+ if (!call.name) continue;
247
+ toolCalls.push({
248
+ type: "tool-call",
249
+ toolCallId: call.id || generateToolCallId(),
250
+ toolName: call.name,
251
+ args: call.arguments || {}
252
+ });
253
+ } catch {
254
+ continue;
255
+ }
256
+ }
257
+ }
258
+ } catch (error) {
259
+ console.warn("Failed to parse JSON tool call:", error);
260
+ continue;
261
+ }
262
+ }
263
+ textContent = textContent.replace(/\n{2,}/g, "\n");
264
+ return { toolCalls, textContent: textContent.trim() };
265
+ }
266
+
267
+ // src/tool-calling/format-tool-results.ts
268
+ function formatToolResults(results) {
269
+ if (results.length === 0) {
270
+ return "";
271
+ }
272
+ const lines = results.map((result) => formatSingleToolResult(result)).join("\n");
273
+ return `\`\`\`tool_result
274
+ ${lines}
275
+ \`\`\``;
276
+ }
277
+ function formatSingleToolResult(result) {
278
+ return JSON.stringify({
279
+ id: result.toolCallId,
280
+ name: result.toolName,
281
+ result: result.result,
282
+ error: result.isError ?? false
283
+ });
284
+ }
285
+
286
+ // src/convert-to-webllm-messages.tsx
287
+ function convertToolResultOutput(output) {
288
+ switch (output.type) {
289
+ case "text":
290
+ return { value: output.value, isError: false };
291
+ case "json":
292
+ return { value: output.value, isError: false };
293
+ case "error-text":
294
+ return { value: output.value, isError: true };
295
+ case "error-json":
296
+ return { value: output.value, isError: true };
297
+ case "content":
298
+ return { value: output.value, isError: false };
299
+ default: {
300
+ const exhaustiveCheck = output;
301
+ return { value: exhaustiveCheck, isError: false };
302
+ }
303
+ }
304
+ }
305
+ function toToolResult(part) {
306
+ const { value, isError } = convertToolResultOutput(part.output);
307
+ return {
308
+ toolCallId: part.toolCallId,
309
+ toolName: part.toolName,
310
+ result: value,
311
+ isError
312
+ };
313
+ }
314
+ function uint8ArrayToBase64(uint8array) {
315
+ const binary = Array.from(
316
+ uint8array,
317
+ (byte) => String.fromCharCode(byte)
318
+ ).join("");
319
+ return btoa(binary);
320
+ }
321
+ function convertDataToURL(data, mediaType) {
322
+ if (data instanceof URL) {
323
+ return data.toString();
324
+ }
325
+ if (typeof data === "string") {
326
+ return `data:${mediaType};base64,${data}`;
327
+ }
328
+ if (data instanceof Uint8Array) {
329
+ return `data:${mediaType};base64,${uint8ArrayToBase64(data)}`;
330
+ }
331
+ if (data instanceof ArrayBuffer) {
332
+ return `data:${mediaType};base64,${uint8ArrayToBase64(
333
+ new Uint8Array(data)
334
+ )}`;
335
+ }
336
+ if (typeof Buffer !== "undefined" && data instanceof Buffer) {
337
+ return `data:${mediaType};base64,${data.toString("base64")}`;
338
+ }
339
+ throw new UnsupportedFunctionalityError({
340
+ functionality: `file data type: ${typeof data}`
341
+ });
342
+ }
343
+ function convertToWebLLMMessages(prompt) {
344
+ const messages = [];
345
+ for (const message of prompt) {
346
+ switch (message.role) {
347
+ case "system":
348
+ messages.push({
349
+ role: "system",
350
+ content: message.content
351
+ });
352
+ break;
353
+ case "user":
354
+ const hasFileContent = message.content.some(
355
+ (part) => part.type === "file"
356
+ );
357
+ if (!hasFileContent) {
358
+ const userContent = [];
359
+ for (const part of message.content) {
360
+ if (part.type === "text") {
361
+ userContent.push(part.text);
362
+ }
363
+ }
364
+ messages.push({
365
+ role: "user",
366
+ content: userContent.join("\n")
367
+ });
368
+ break;
369
+ }
370
+ const content = [];
371
+ for (const part of message.content) {
372
+ if (part.type === "text") {
373
+ content.push({ type: "text", text: part.text });
374
+ } else if (part.type === "file") {
375
+ if (!part.mediaType?.startsWith("image/")) {
376
+ throw new UnsupportedFunctionalityError({
377
+ functionality: `file input with media type '${part.mediaType}'`
378
+ });
379
+ }
380
+ content.push({
381
+ type: "image_url",
382
+ image_url: {
383
+ url: convertDataToURL(part.data, part.mediaType)
384
+ }
385
+ });
386
+ }
387
+ }
388
+ messages.push({ role: "user", content });
389
+ break;
390
+ case "assistant":
391
+ let assistantContent = "";
392
+ const toolCallsInMessage = [];
393
+ for (const part of message.content) {
394
+ if (part.type === "text") {
395
+ assistantContent += part.text;
396
+ } else if (part.type === "tool-call") {
397
+ toolCallsInMessage.push({
398
+ toolCallId: part.toolCallId,
399
+ toolName: part.toolName
400
+ });
401
+ }
402
+ }
403
+ if (assistantContent) {
404
+ messages.push({
405
+ role: "assistant",
406
+ content: assistantContent
407
+ });
408
+ }
409
+ break;
410
+ case "tool":
411
+ const toolResults = message.content.map(toToolResult);
412
+ const formattedResults = formatToolResults(toolResults);
413
+ messages.push({
414
+ role: "user",
415
+ content: formattedResults
416
+ });
417
+ break;
418
+ }
419
+ }
420
+ return messages;
421
+ }
422
+
423
+ // src/web-llm-language-model.ts
424
+ var import_web_llm = require("@mlc-ai/web-llm");
425
+
426
+ // src/utils/warnings.ts
427
+ function createUnsupportedSettingWarning(setting, details) {
428
+ return {
429
+ type: "unsupported-setting",
430
+ setting,
431
+ details
432
+ };
433
+ }
434
+ function createUnsupportedToolWarning(tool, details) {
435
+ return {
436
+ type: "unsupported-tool",
437
+ tool,
438
+ details
439
+ };
440
+ }
441
+
442
+ // src/utils/tool-utils.ts
443
+ function isFunctionTool(tool) {
444
+ return tool.type === "function";
445
+ }
446
+
447
+ // src/utils/prompt-utils.ts
448
+ function extractSystemPrompt(messages) {
449
+ const systemMessages = messages.filter((msg) => msg.role === "system");
450
+ const nonSystemMessages = messages.filter((msg) => msg.role !== "system");
451
+ if (systemMessages.length === 0) {
452
+ return { systemPrompt: void 0, messages };
453
+ }
454
+ const systemPrompt = systemMessages.map((msg) => msg.content).filter((content) => typeof content === "string").join("\n\n");
455
+ return {
456
+ systemPrompt: systemPrompt || void 0,
457
+ messages: nonSystemMessages
458
+ };
459
+ }
460
+ function prependSystemPromptToMessages(messages, systemPrompt) {
461
+ if (!systemPrompt.trim()) {
462
+ return messages;
463
+ }
464
+ const systemMessageIndex = messages.findIndex((msg) => msg.role === "system");
465
+ if (systemMessageIndex !== -1) {
466
+ const newMessages = [...messages];
467
+ const existingSystemMessage = messages[systemMessageIndex];
468
+ const existingContent = typeof existingSystemMessage.content === "string" ? existingSystemMessage.content : "";
469
+ newMessages[systemMessageIndex] = {
470
+ ...existingSystemMessage,
471
+ content: systemPrompt + (existingContent ? `
472
+
473
+ ${existingContent}` : "")
474
+ };
475
+ return newMessages;
476
+ }
477
+ return [
478
+ {
479
+ role: "system",
480
+ content: systemPrompt
481
+ },
482
+ ...messages
483
+ ];
484
+ }
485
+
486
+ // src/streaming/tool-call-detector.ts
487
+ var ToolCallFenceDetector = class {
488
+ constructor() {
489
+ this.FENCE_STARTS = ["```tool_call"];
490
+ this.FENCE_END = "```";
491
+ this.buffer = "";
492
+ this.inFence = false;
493
+ this.fenceStartBuffer = "";
494
+ }
495
+ addChunk(chunk) {
496
+ this.buffer += chunk;
497
+ }
498
+ getBuffer() {
499
+ return this.buffer;
500
+ }
501
+ clearBuffer() {
502
+ this.buffer = "";
503
+ }
504
+ /**
505
+ * Detects if there's a complete fence in the buffer
506
+ *
507
+ * 1. Searches for fence start markers
508
+ * 2. If found, looks for closing fence
509
+ * 3. Computes overlap for partial fences
510
+ * 4. Returns safe text that can be emitted
511
+ *
512
+ * @returns Detection result with fence info and safe text
513
+ */
514
+ detectFence() {
515
+ const { index: startIdx, prefix: matchedPrefix } = this.findFenceStart(
516
+ this.buffer
517
+ );
518
+ if (startIdx === -1) {
519
+ const overlap = this.computeOverlapLength(this.buffer, this.FENCE_STARTS);
520
+ const safeTextLength = this.buffer.length - overlap;
521
+ const prefixText2 = safeTextLength > 0 ? this.buffer.slice(0, safeTextLength) : "";
522
+ const remaining = overlap > 0 ? this.buffer.slice(-overlap) : "";
523
+ this.buffer = remaining;
524
+ return {
525
+ fence: null,
526
+ prefixText: prefixText2,
527
+ remainingText: "",
528
+ overlapLength: overlap
529
+ };
530
+ }
531
+ const prefixText = this.buffer.slice(0, startIdx);
532
+ this.buffer = this.buffer.slice(startIdx);
533
+ const prefixLength = matchedPrefix?.length ?? 0;
534
+ const closingIdx = this.buffer.indexOf(this.FENCE_END, prefixLength);
535
+ if (closingIdx === -1) {
536
+ return {
537
+ fence: null,
538
+ prefixText,
539
+ remainingText: "",
540
+ overlapLength: 0
541
+ };
542
+ }
543
+ const endPos = closingIdx + this.FENCE_END.length;
544
+ const fence = this.buffer.slice(0, endPos);
545
+ const remainingText = this.buffer.slice(endPos);
546
+ this.buffer = "";
547
+ return {
548
+ fence,
549
+ prefixText,
550
+ remainingText,
551
+ overlapLength: 0
552
+ };
553
+ }
554
+ /**
555
+ * Finds the first occurrence of any fence start marker
556
+ *
557
+ * @param text - Text to search in
558
+ * @returns Index of first fence start and which prefix matched
559
+ * @private
560
+ */
561
+ findFenceStart(text) {
562
+ let bestIndex = -1;
563
+ let matchedPrefix = null;
564
+ for (const prefix of this.FENCE_STARTS) {
565
+ const idx = text.indexOf(prefix);
566
+ if (idx !== -1 && (bestIndex === -1 || idx < bestIndex)) {
567
+ bestIndex = idx;
568
+ matchedPrefix = prefix;
569
+ }
570
+ }
571
+ return { index: bestIndex, prefix: matchedPrefix };
572
+ }
573
+ computeOverlapLength(text, prefixes) {
574
+ let overlap = 0;
575
+ for (const prefix of prefixes) {
576
+ const maxLength = Math.min(text.length, prefix.length - 1);
577
+ for (let size = maxLength; size > 0; size -= 1) {
578
+ if (prefix.startsWith(text.slice(-size))) {
579
+ overlap = Math.max(overlap, size);
580
+ break;
581
+ }
582
+ }
583
+ }
584
+ return overlap;
585
+ }
586
+ hasContent() {
587
+ return this.buffer.length > 0;
588
+ }
589
+ getBufferSize() {
590
+ return this.buffer.length;
591
+ }
592
+ /**
593
+ * Detect and stream fence content in real-time for true incremental streaming
594
+ *
595
+ * This method is designed for streaming tool calls as they arrive:
596
+ * 1. Detects when a fence starts and transitions to "inFence" state
597
+ * 2. While inFence, emits safe content that won't conflict with fence end marker
598
+ * 3. When fence ends, returns the complete fence for parsing
599
+ *
600
+ * @returns Streaming result with current state and safe content to emit
601
+ */
602
+ detectStreamingFence() {
603
+ if (!this.inFence) {
604
+ const { index: startIdx, prefix: matchedPrefix } = this.findFenceStart(
605
+ this.buffer
606
+ );
607
+ if (startIdx === -1) {
608
+ const overlap = this.computeOverlapLength(
609
+ this.buffer,
610
+ this.FENCE_STARTS
611
+ );
612
+ const safeTextLength = this.buffer.length - overlap;
613
+ const safeContent = safeTextLength > 0 ? this.buffer.slice(0, safeTextLength) : "";
614
+ this.buffer = this.buffer.slice(safeTextLength);
615
+ return {
616
+ inFence: false,
617
+ safeContent,
618
+ completeFence: null,
619
+ textAfterFence: ""
620
+ };
621
+ }
622
+ const prefixText = this.buffer.slice(0, startIdx);
623
+ const fenceStartLength = matchedPrefix?.length ?? 0;
624
+ this.buffer = this.buffer.slice(startIdx + fenceStartLength);
625
+ if (this.buffer.startsWith("\n")) {
626
+ this.buffer = this.buffer.slice(1);
627
+ }
628
+ this.inFence = true;
629
+ this.fenceStartBuffer = "";
630
+ return {
631
+ inFence: true,
632
+ safeContent: prefixText,
633
+ completeFence: null,
634
+ textAfterFence: ""
635
+ };
636
+ }
637
+ const closingIdx = this.buffer.indexOf(this.FENCE_END);
638
+ if (closingIdx === -1) {
639
+ const overlap = this.computeOverlapLength(this.buffer, [this.FENCE_END]);
640
+ const safeContentLength = this.buffer.length - overlap;
641
+ if (safeContentLength > 0) {
642
+ const safeContent = this.buffer.slice(0, safeContentLength);
643
+ this.fenceStartBuffer += safeContent;
644
+ this.buffer = this.buffer.slice(safeContentLength);
645
+ return {
646
+ inFence: true,
647
+ safeContent,
648
+ completeFence: null,
649
+ textAfterFence: ""
650
+ };
651
+ }
652
+ return {
653
+ inFence: true,
654
+ safeContent: "",
655
+ completeFence: null,
656
+ textAfterFence: ""
657
+ };
658
+ }
659
+ const fenceContent = this.buffer.slice(0, closingIdx);
660
+ this.fenceStartBuffer += fenceContent;
661
+ const completeFence = `${this.FENCE_STARTS[0]}
662
+ ${this.fenceStartBuffer}
663
+ ${this.FENCE_END}`;
664
+ const textAfterFence = this.buffer.slice(
665
+ closingIdx + this.FENCE_END.length
666
+ );
667
+ this.inFence = false;
668
+ this.fenceStartBuffer = "";
669
+ this.buffer = textAfterFence;
670
+ return {
671
+ inFence: false,
672
+ safeContent: fenceContent,
673
+ completeFence,
674
+ textAfterFence
675
+ };
676
+ }
677
+ isInFence() {
678
+ return this.inFence;
679
+ }
680
+ resetStreamingState() {
681
+ this.inFence = false;
682
+ this.fenceStartBuffer = "";
683
+ }
684
+ };
685
+
686
+ // src/web-llm-language-model.ts
687
+ function doesBrowserSupportWebLLM() {
688
+ return globalThis?.navigator?.gpu !== void 0;
689
+ }
690
+ function extractToolName(content) {
691
+ const jsonMatch = content.match(/\{\s*"name"\s*:\s*"([^"]+)"/);
692
+ if (jsonMatch) {
693
+ return jsonMatch[1];
694
+ }
695
+ return null;
696
+ }
697
+ function extractArgumentsContent(content) {
698
+ const match = content.match(/"arguments"\s*:\s*/);
699
+ if (!match || match.index === void 0) {
700
+ return "";
701
+ }
702
+ const startIndex = match.index + match[0].length;
703
+ let result = "";
704
+ let depth = 0;
705
+ let inString = false;
706
+ let escaped = false;
707
+ let started = false;
708
+ for (let i = startIndex; i < content.length; i++) {
709
+ const char = content[i];
710
+ result += char;
711
+ if (!started) {
712
+ if (!/\s/.test(char)) {
713
+ started = true;
714
+ if (char === "{" || char === "[") {
715
+ depth = 1;
716
+ }
717
+ }
718
+ continue;
719
+ }
720
+ if (escaped) {
721
+ escaped = false;
722
+ continue;
723
+ }
724
+ if (char === "\\") {
725
+ escaped = true;
726
+ continue;
727
+ }
728
+ if (char === '"') {
729
+ inString = !inString;
730
+ continue;
731
+ }
732
+ if (!inString) {
733
+ if (char === "{" || char === "[") {
734
+ depth += 1;
735
+ } else if (char === "}" || char === "]") {
736
+ if (depth > 0) {
737
+ depth -= 1;
738
+ if (depth === 0) {
739
+ break;
740
+ }
741
+ }
742
+ }
743
+ }
744
+ }
745
+ return result;
746
+ }
747
+ var WebLLMLanguageModel = class {
748
+ constructor(modelId, options = {}) {
749
+ this.specificationVersion = "v2";
750
+ this.provider = "web-llm";
751
+ this.isInitialized = false;
752
+ this.supportedUrls = {
753
+ // WebLLM doesn't support URLs natively
754
+ };
755
+ this.modelId = modelId;
756
+ this.config = {
757
+ provider: this.provider,
758
+ modelId,
759
+ options
760
+ };
761
+ }
762
+ /**
763
+ * Check if the model is initialized and ready to use
764
+ * @returns true if the model is initialized, false otherwise
765
+ */
766
+ get isModelInitialized() {
767
+ return this.isInitialized;
768
+ }
769
+ async getEngine(options, onInitProgress) {
770
+ const availability = await this.availability();
771
+ if (availability === "unavailable") {
772
+ throw new LoadSettingError({
773
+ message: "WebLLM is not available. This library requires a browser with WebGPU support."
774
+ });
775
+ }
776
+ if (this.engine && this.isInitialized) return this.engine;
777
+ if (this.initializationPromise) {
778
+ await this.initializationPromise;
779
+ if (this.engine) return this.engine;
780
+ }
781
+ this.initializationPromise = this._initializeEngine(
782
+ options,
783
+ onInitProgress
784
+ );
785
+ await this.initializationPromise;
786
+ if (!this.engine) {
787
+ throw new LoadSettingError({
788
+ message: "Engine initialization failed"
789
+ });
790
+ }
791
+ return this.engine;
792
+ }
793
+ async _initializeEngine(options, onInitProgress) {
794
+ try {
795
+ const engineConfig = {
796
+ ...this.config.options.engineConfig,
797
+ ...options,
798
+ initProgressCallback: onInitProgress || this.config.options.initProgressCallback
799
+ };
800
+ if (this.config.options.worker) {
801
+ this.engine = await (0, import_web_llm.CreateWebWorkerMLCEngine)(
802
+ this.config.options.worker,
803
+ this.modelId,
804
+ engineConfig
805
+ );
806
+ } else {
807
+ this.engine = new import_web_llm.MLCEngine(engineConfig);
808
+ await this.engine.reload(this.modelId);
809
+ }
810
+ this.isInitialized = true;
811
+ } catch (error) {
812
+ this.engine = void 0;
813
+ this.isInitialized = false;
814
+ this.initializationPromise = void 0;
815
+ throw new LoadSettingError({
816
+ message: `Failed to initialize WebLLM engine: ${error instanceof Error ? error.message : "Unknown error"}`
817
+ });
818
+ }
819
+ }
820
+ getArgs({
821
+ prompt,
822
+ maxOutputTokens,
823
+ temperature,
824
+ topP,
825
+ topK,
826
+ frequencyPenalty,
827
+ presencePenalty,
828
+ stopSequences,
829
+ responseFormat,
830
+ seed,
831
+ tools,
832
+ toolChoice
833
+ }) {
834
+ const warnings = [];
835
+ const functionTools = (tools ?? []).filter(isFunctionTool).map((tool) => ({
836
+ name: tool.name,
837
+ description: tool.description,
838
+ parameters: tool.inputSchema
839
+ }));
840
+ const unsupportedTools = (tools ?? []).filter(
841
+ (tool) => !isFunctionTool(tool)
842
+ );
843
+ for (const tool of unsupportedTools) {
844
+ warnings.push(
845
+ createUnsupportedToolWarning(
846
+ tool,
847
+ "Only function tools are supported by WebLLM"
848
+ )
849
+ );
850
+ }
851
+ if (topK != null) {
852
+ warnings.push(
853
+ createUnsupportedSettingWarning(
854
+ "topK",
855
+ "topK is not supported by WebLLM"
856
+ )
857
+ );
858
+ }
859
+ if (stopSequences != null) {
860
+ warnings.push(
861
+ createUnsupportedSettingWarning(
862
+ "stopSequences",
863
+ "Stop sequences may not be fully implemented"
864
+ )
865
+ );
866
+ }
867
+ if (presencePenalty != null) {
868
+ warnings.push(
869
+ createUnsupportedSettingWarning(
870
+ "presencePenalty",
871
+ "Presence penalty is not fully implemented"
872
+ )
873
+ );
874
+ }
875
+ if (frequencyPenalty != null) {
876
+ warnings.push(
877
+ createUnsupportedSettingWarning(
878
+ "frequencyPenalty",
879
+ "Frequency penalty is not fully implemented"
880
+ )
881
+ );
882
+ }
883
+ if (toolChoice != null) {
884
+ warnings.push(
885
+ createUnsupportedSettingWarning(
886
+ "toolChoice",
887
+ "toolChoice is not supported by WebLLM"
888
+ )
889
+ );
890
+ }
891
+ const messages = convertToWebLLMMessages(prompt);
892
+ const requestOptions = {
893
+ messages,
894
+ temperature,
895
+ max_tokens: maxOutputTokens,
896
+ top_p: topP,
897
+ seed
898
+ };
899
+ if (responseFormat?.type === "json") {
900
+ requestOptions.response_format = { type: "json_object" };
901
+ }
902
+ return {
903
+ messages,
904
+ warnings,
905
+ requestOptions,
906
+ functionTools
907
+ };
908
+ }
909
+ /**
910
+ * Generates a complete text response using WebLLM
911
+ * @param options
912
+ * @returns Promise resolving to the generated content with finish reason, usage stats, and any warnings
913
+ * @throws {LoadSettingError} When WebLLM is not available or model needs to be downloaded
914
+ * @throws {UnsupportedFunctionalityError} When unsupported features like file input are used
915
+ */
916
+ async doGenerate(options) {
917
+ const converted = this.getArgs(options);
918
+ const { messages, warnings, requestOptions, functionTools } = converted;
919
+ const {
920
+ systemPrompt: originalSystemPrompt,
921
+ messages: messagesWithoutSystem
922
+ } = extractSystemPrompt(messages);
923
+ const systemPrompt = buildJsonToolSystemPrompt(
924
+ originalSystemPrompt,
925
+ functionTools,
926
+ {
927
+ allowParallelToolCalls: false
928
+ }
929
+ );
930
+ const promptMessages = prependSystemPromptToMessages(
931
+ messagesWithoutSystem,
932
+ systemPrompt
933
+ );
934
+ const engine = await this.getEngine();
935
+ const abortHandler = async () => {
936
+ await engine.interruptGenerate();
937
+ };
938
+ if (options.abortSignal) {
939
+ options.abortSignal.addEventListener("abort", abortHandler);
940
+ }
941
+ try {
942
+ const response = await engine.chat.completions.create({
943
+ ...requestOptions,
944
+ messages: promptMessages,
945
+ stream: false,
946
+ ...options.abortSignal && !this.config.options.worker && { signal: options.abortSignal }
947
+ });
948
+ const choice = response.choices[0];
949
+ if (!choice) {
950
+ throw new Error("No response choice returned from WebLLM");
951
+ }
952
+ const rawResponse = choice.message.content || "";
953
+ const { toolCalls, textContent } = parseJsonFunctionCalls(rawResponse);
954
+ if (toolCalls.length > 0) {
955
+ const toolCallsToEmit = toolCalls.slice(0, 1);
956
+ const parts = [];
957
+ if (textContent) {
958
+ parts.push({
959
+ type: "text",
960
+ text: textContent
961
+ });
962
+ }
963
+ for (const call of toolCallsToEmit) {
964
+ parts.push({
965
+ type: "tool-call",
966
+ toolCallId: call.toolCallId,
967
+ toolName: call.toolName,
968
+ input: JSON.stringify(call.args ?? {})
969
+ });
970
+ }
971
+ return {
972
+ content: parts,
973
+ finishReason: "tool-calls",
974
+ usage: {
975
+ inputTokens: response.usage?.prompt_tokens,
976
+ outputTokens: response.usage?.completion_tokens,
977
+ totalTokens: response.usage?.total_tokens
978
+ },
979
+ request: { body: { messages: promptMessages, ...requestOptions } },
980
+ warnings
981
+ };
982
+ }
983
+ const content = [
984
+ {
985
+ type: "text",
986
+ text: textContent || rawResponse
987
+ }
988
+ ];
989
+ let finishReason = "stop";
990
+ if (choice.finish_reason === "abort") {
991
+ finishReason = "other";
992
+ }
993
+ return {
994
+ content,
995
+ finishReason,
996
+ usage: {
997
+ inputTokens: response.usage?.prompt_tokens,
998
+ outputTokens: response.usage?.completion_tokens,
999
+ totalTokens: response.usage?.total_tokens
1000
+ },
1001
+ request: { body: { messages: promptMessages, ...requestOptions } },
1002
+ warnings
1003
+ };
1004
+ } catch (error) {
1005
+ throw new Error(
1006
+ `WebLLM generation failed: ${error instanceof Error ? error.message : "Unknown error"}`
1007
+ );
1008
+ } finally {
1009
+ if (options.abortSignal) {
1010
+ options.abortSignal.removeEventListener("abort", abortHandler);
1011
+ }
1012
+ }
1013
+ }
1014
+ /**
1015
+ * Check the availability of the WebLLM model
1016
+ * @returns Promise resolving to "unavailable", "available", or "available-after-download"
1017
+ */
1018
+ async availability() {
1019
+ if (!doesBrowserSupportWebLLM()) {
1020
+ return "unavailable";
1021
+ }
1022
+ if (this.isInitialized) {
1023
+ return "available";
1024
+ }
1025
+ return "downloadable";
1026
+ }
1027
+ /**
1028
+ * Creates an engine session with download progress monitoring.
1029
+ *
1030
+ * @example
1031
+ * ```typescript
1032
+ * const engine = await model.createSessionWithProgress(
1033
+ * (progress) => {
1034
+ * console.log(`Download progress: ${Math.round(progress.loaded * 100)}%`);
1035
+ * }
1036
+ * );
1037
+ * ```
1038
+ *
1039
+ * @param onInitProgress Optional callback receiving progress reports during model download
1040
+ * @returns Promise resolving to a configured WebLLM engine
1041
+ * @throws {LoadSettingError} When WebLLM is not available or model is unavailable
1042
+ */
1043
+ async createSessionWithProgress(onInitProgress) {
1044
+ return this.getEngine(void 0, onInitProgress);
1045
+ }
1046
+ /**
1047
+ * Generates a streaming text response using WebLLM
1048
+ * @param options
1049
+ * @returns Promise resolving to a readable stream of text chunks and request metadata
1050
+ * @throws {LoadSettingError} When WebLLM is not available or model needs to be downloaded
1051
+ * @throws {UnsupportedFunctionalityError} When unsupported features like file input are used
1052
+ */
1053
+ async doStream(options) {
1054
+ const converted = this.getArgs(options);
1055
+ const { messages, warnings, requestOptions, functionTools } = converted;
1056
+ const {
1057
+ systemPrompt: originalSystemPrompt,
1058
+ messages: messagesWithoutSystem
1059
+ } = extractSystemPrompt(messages);
1060
+ const systemPrompt = buildJsonToolSystemPrompt(
1061
+ originalSystemPrompt,
1062
+ functionTools,
1063
+ {
1064
+ allowParallelToolCalls: false
1065
+ }
1066
+ );
1067
+ const promptMessages = prependSystemPromptToMessages(
1068
+ messagesWithoutSystem,
1069
+ systemPrompt
1070
+ );
1071
+ const engine = await this.getEngine();
1072
+ const useWorker = this.config.options.worker != null;
1073
+ const abortHandler = async () => {
1074
+ await engine.interruptGenerate();
1075
+ };
1076
+ if (options.abortSignal) {
1077
+ options.abortSignal.addEventListener("abort", abortHandler);
1078
+ }
1079
+ const textId = "text-0";
1080
+ const stream = new ReadableStream({
1081
+ async start(controller) {
1082
+ controller.enqueue({
1083
+ type: "stream-start",
1084
+ warnings
1085
+ });
1086
+ let textStarted = false;
1087
+ let finished = false;
1088
+ const ensureTextStart = () => {
1089
+ if (!textStarted) {
1090
+ controller.enqueue({
1091
+ type: "text-start",
1092
+ id: textId
1093
+ });
1094
+ textStarted = true;
1095
+ }
1096
+ };
1097
+ const emitTextDelta = (delta) => {
1098
+ if (!delta) return;
1099
+ ensureTextStart();
1100
+ controller.enqueue({
1101
+ type: "text-delta",
1102
+ id: textId,
1103
+ delta
1104
+ });
1105
+ };
1106
+ const emitTextEndIfNeeded = () => {
1107
+ if (!textStarted) return;
1108
+ controller.enqueue({
1109
+ type: "text-end",
1110
+ id: textId
1111
+ });
1112
+ textStarted = false;
1113
+ };
1114
+ const finishStream = (finishReason, usage) => {
1115
+ if (finished) return;
1116
+ finished = true;
1117
+ emitTextEndIfNeeded();
1118
+ controller.enqueue({
1119
+ type: "finish",
1120
+ finishReason,
1121
+ usage: {
1122
+ inputTokens: usage?.prompt_tokens,
1123
+ outputTokens: usage?.completion_tokens,
1124
+ totalTokens: usage?.total_tokens
1125
+ }
1126
+ });
1127
+ controller.close();
1128
+ };
1129
+ try {
1130
+ const streamingRequest = {
1131
+ ...requestOptions,
1132
+ messages: promptMessages,
1133
+ stream: true,
1134
+ stream_options: { include_usage: true },
1135
+ ...options.abortSignal && !useWorker && { signal: options.abortSignal }
1136
+ };
1137
+ const response = await engine.chat.completions.create(streamingRequest);
1138
+ const fenceDetector = new ToolCallFenceDetector();
1139
+ let accumulatedText = "";
1140
+ let currentToolCallId = null;
1141
+ let toolInputStartEmitted = false;
1142
+ let accumulatedFenceContent = "";
1143
+ let streamedArgumentsLength = 0;
1144
+ let insideFence = false;
1145
+ for await (const chunk of response) {
1146
+ const choice = chunk.choices[0];
1147
+ if (!choice) continue;
1148
+ if (choice.delta.content) {
1149
+ const delta = choice.delta.content;
1150
+ accumulatedText += delta;
1151
+ fenceDetector.addChunk(delta);
1152
+ while (fenceDetector.hasContent()) {
1153
+ const wasInsideFence = insideFence;
1154
+ const result = fenceDetector.detectStreamingFence();
1155
+ insideFence = result.inFence;
1156
+ let madeProgress = false;
1157
+ if (!wasInsideFence && result.inFence) {
1158
+ if (result.safeContent) {
1159
+ emitTextDelta(result.safeContent);
1160
+ madeProgress = true;
1161
+ }
1162
+ currentToolCallId = `call_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
1163
+ toolInputStartEmitted = false;
1164
+ accumulatedFenceContent = "";
1165
+ streamedArgumentsLength = 0;
1166
+ insideFence = true;
1167
+ continue;
1168
+ }
1169
+ if (result.completeFence) {
1170
+ madeProgress = true;
1171
+ if (result.safeContent) {
1172
+ accumulatedFenceContent += result.safeContent;
1173
+ }
1174
+ if (toolInputStartEmitted && currentToolCallId) {
1175
+ const argsContent = extractArgumentsContent(
1176
+ accumulatedFenceContent
1177
+ );
1178
+ if (argsContent.length > streamedArgumentsLength) {
1179
+ const delta2 = argsContent.slice(streamedArgumentsLength);
1180
+ streamedArgumentsLength = argsContent.length;
1181
+ if (delta2.length > 0) {
1182
+ controller.enqueue({
1183
+ type: "tool-input-delta",
1184
+ id: currentToolCallId,
1185
+ delta: delta2
1186
+ });
1187
+ }
1188
+ }
1189
+ }
1190
+ const parsed = parseJsonFunctionCalls(result.completeFence);
1191
+ const parsedToolCalls = parsed.toolCalls;
1192
+ const selectedToolCalls = parsedToolCalls.slice(0, 1);
1193
+ if (selectedToolCalls.length === 0) {
1194
+ emitTextDelta(result.completeFence);
1195
+ if (result.textAfterFence) {
1196
+ emitTextDelta(result.textAfterFence);
1197
+ }
1198
+ currentToolCallId = null;
1199
+ toolInputStartEmitted = false;
1200
+ accumulatedFenceContent = "";
1201
+ streamedArgumentsLength = 0;
1202
+ insideFence = false;
1203
+ continue;
1204
+ }
1205
+ if (selectedToolCalls.length > 0 && currentToolCallId) {
1206
+ selectedToolCalls[0].toolCallId = currentToolCallId;
1207
+ }
1208
+ for (const [index, call] of selectedToolCalls.entries()) {
1209
+ const toolCallId = index === 0 && currentToolCallId ? currentToolCallId : call.toolCallId;
1210
+ const toolName = call.toolName;
1211
+ const argsJson = JSON.stringify(call.args ?? {});
1212
+ if (toolCallId === currentToolCallId) {
1213
+ if (!toolInputStartEmitted) {
1214
+ controller.enqueue({
1215
+ type: "tool-input-start",
1216
+ id: toolCallId,
1217
+ toolName
1218
+ });
1219
+ toolInputStartEmitted = true;
1220
+ }
1221
+ const argsContent = extractArgumentsContent(
1222
+ accumulatedFenceContent
1223
+ );
1224
+ if (argsContent.length > streamedArgumentsLength) {
1225
+ const delta2 = argsContent.slice(
1226
+ streamedArgumentsLength
1227
+ );
1228
+ streamedArgumentsLength = argsContent.length;
1229
+ if (delta2.length > 0) {
1230
+ controller.enqueue({
1231
+ type: "tool-input-delta",
1232
+ id: toolCallId,
1233
+ delta: delta2
1234
+ });
1235
+ }
1236
+ }
1237
+ } else {
1238
+ controller.enqueue({
1239
+ type: "tool-input-start",
1240
+ id: toolCallId,
1241
+ toolName
1242
+ });
1243
+ if (argsJson.length > 0) {
1244
+ controller.enqueue({
1245
+ type: "tool-input-delta",
1246
+ id: toolCallId,
1247
+ delta: argsJson
1248
+ });
1249
+ }
1250
+ }
1251
+ controller.enqueue({
1252
+ type: "tool-input-end",
1253
+ id: toolCallId
1254
+ });
1255
+ controller.enqueue({
1256
+ type: "tool-call",
1257
+ toolCallId,
1258
+ toolName,
1259
+ input: argsJson,
1260
+ providerExecuted: false
1261
+ });
1262
+ }
1263
+ if (result.textAfterFence) {
1264
+ emitTextDelta(result.textAfterFence);
1265
+ }
1266
+ madeProgress = true;
1267
+ currentToolCallId = null;
1268
+ toolInputStartEmitted = false;
1269
+ accumulatedFenceContent = "";
1270
+ streamedArgumentsLength = 0;
1271
+ insideFence = false;
1272
+ continue;
1273
+ }
1274
+ if (insideFence) {
1275
+ if (result.safeContent) {
1276
+ accumulatedFenceContent += result.safeContent;
1277
+ madeProgress = true;
1278
+ const toolName = extractToolName(accumulatedFenceContent);
1279
+ if (toolName && !toolInputStartEmitted && currentToolCallId) {
1280
+ controller.enqueue({
1281
+ type: "tool-input-start",
1282
+ id: currentToolCallId,
1283
+ toolName
1284
+ });
1285
+ toolInputStartEmitted = true;
1286
+ }
1287
+ if (toolInputStartEmitted && currentToolCallId) {
1288
+ const argsContent = extractArgumentsContent(
1289
+ accumulatedFenceContent
1290
+ );
1291
+ if (argsContent.length > streamedArgumentsLength) {
1292
+ const delta2 = argsContent.slice(
1293
+ streamedArgumentsLength
1294
+ );
1295
+ streamedArgumentsLength = argsContent.length;
1296
+ if (delta2.length > 0) {
1297
+ controller.enqueue({
1298
+ type: "tool-input-delta",
1299
+ id: currentToolCallId,
1300
+ delta: delta2
1301
+ });
1302
+ }
1303
+ }
1304
+ }
1305
+ }
1306
+ continue;
1307
+ }
1308
+ if (!insideFence && result.safeContent) {
1309
+ emitTextDelta(result.safeContent);
1310
+ madeProgress = true;
1311
+ }
1312
+ if (!madeProgress) {
1313
+ break;
1314
+ }
1315
+ }
1316
+ }
1317
+ if (choice.finish_reason) {
1318
+ if (fenceDetector.hasContent()) {
1319
+ emitTextDelta(fenceDetector.getBuffer());
1320
+ fenceDetector.clearBuffer();
1321
+ }
1322
+ let finishReason = "stop";
1323
+ if (choice.finish_reason === "abort") {
1324
+ finishReason = "other";
1325
+ } else {
1326
+ const { toolCalls } = parseJsonFunctionCalls(accumulatedText);
1327
+ if (toolCalls.length > 0) {
1328
+ finishReason = "tool-calls";
1329
+ }
1330
+ }
1331
+ finishStream(finishReason, chunk.usage);
1332
+ }
1333
+ }
1334
+ if (!finished) {
1335
+ finishStream("stop");
1336
+ }
1337
+ } catch (error) {
1338
+ controller.error(error);
1339
+ } finally {
1340
+ if (options.abortSignal) {
1341
+ options.abortSignal.removeEventListener("abort", abortHandler);
1342
+ }
1343
+ if (!finished) {
1344
+ controller.close();
1345
+ }
1346
+ }
1347
+ }
1348
+ });
1349
+ return {
1350
+ stream,
1351
+ request: { body: { messages: promptMessages, ...requestOptions } }
1352
+ };
1353
+ }
1354
+ };
1355
+
1356
+ // src/index.ts
1357
+ var import_web_llm2 = require("@mlc-ai/web-llm");
1358
+
1359
+ // src/web-llm-provider.ts
1360
+ function webLLM(modelId, settings) {
1361
+ return new WebLLMLanguageModel(modelId, settings);
1362
+ }
1363
+ // Annotate the CommonJS export names for ESM import in node:
1364
+ 0 && (module.exports = {
1365
+ WebLLMLanguageModel,
1366
+ WebWorkerMLCEngineHandler,
1367
+ doesBrowserSupportWebLLM,
1368
+ webLLM
1369
+ });
1370
+ //# sourceMappingURL=index.js.map