8 | Open console to see output
9 |
10 |
11 |
12 |
13 |
Prompt
14 |
15 |
16 |
Response
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/examples/get-started/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "get-started",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "parcel src/get_started.html --port 8888",
7 | "build": "parcel build src/get_started.html --dist-dir lib"
8 | },
9 | "devDependencies": {
10 | "buffer": "^5.7.1",
11 | "parcel": "^2.8.3",
12 | "process": "^0.11.10",
13 | "tslib": "^2.3.1",
14 | "typescript": "^4.9.5"
15 | },
16 | "dependencies": {
17 | "@mlc-ai/web-llm": "^0.2.17"
18 | }
19 | }
--------------------------------------------------------------------------------
/examples/get-started-rest/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "get-started",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "parcel src/get_started.html --port 8888",
7 | "build": "parcel build src/get_started.html --dist-dir lib"
8 | },
9 | "devDependencies": {
10 | "buffer": "^5.7.1",
11 | "parcel": "^2.8.3",
12 | "process": "^0.11.10",
13 | "tslib": "^2.3.1",
14 | "typescript": "^4.9.5"
15 | },
16 | "dependencies": {
17 | "@mlc-ai/web-llm": "^0.2.17"
18 | }
19 | }
--------------------------------------------------------------------------------
/examples/web-worker/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "get-started-web-worker",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "parcel src/get_started.html --port 8888",
7 | "build": "parcel build src/get_started.html --dist-dir lib"
8 | },
9 | "devDependencies": {
10 | "buffer": "^6.0.3",
11 | "parcel": "^2.8.3",
12 | "process": "^0.11.10",
13 | "tslib": "^2.3.1",
14 | "typescript": "^4.9.5"
15 | },
16 | "dependencies": {
17 | "@mlc-ai/web-llm": "^0.2.17"
18 | }
19 | }
--------------------------------------------------------------------------------
/examples/next-simple-chat/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
5 | './src/components/**/*.{js,ts,jsx,tsx,mdx}',
6 | './src/app/**/*.{js,ts,jsx,tsx,mdx}',
7 | ],
8 | theme: {
9 | extend: {
10 | backgroundImage: {
11 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
12 | 'gradient-conic':
13 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
14 | },
15 | },
16 | },
17 | plugins: [],
18 | }
19 |
--------------------------------------------------------------------------------
/examples/chrome-extension/README.md:
--------------------------------------------------------------------------------
1 | # WebLLM Chrome Extension
2 |
3 | 
4 |
5 | To run the extension, do the following steps under this folder
6 |
7 | ```bash
8 | npm install
9 | npm run build
10 | ```
11 |
12 | This will create a new directory at `chrome-extension/dist/`. To load the extension into Chrome, go to Extensions > Manage Extensions and select Load Unpacked. Add the `chrome-extension/dist/` directory. You can now pin the extension to your toolbar and use it to chat with your favorite model!
--------------------------------------------------------------------------------
/scripts/gh_deploy_site.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -euxo pipefail
3 |
4 | cd examples/simple-chat
5 | rm -rf lib
6 | npm run build
7 | cd ../..
8 |
9 | cp examples/simple-chat/lib/* site
10 | cd site && jekyll b && cd ..
11 |
12 | git fetch
13 | git checkout -B gh-pages origin/gh-pages
14 | rm -rf docs .gitignore
15 | mkdir -p docs
16 | cp -rf site/_site/* docs
17 | touch docs/.nojekyll
18 | echo "webllm.mlc.ai" >> docs/CNAME
19 |
20 | DATE=`date`
21 | git add docs && git commit -am "Build at ${DATE}"
22 | git push origin gh-pages
23 | git checkout main && git submodule update
24 | echo "Finish deployment at ${DATE}"
25 |
--------------------------------------------------------------------------------
/examples/next-simple-chat/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/chrome-extension/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chrome-extension",
3 | "version": "1.0.0",
4 | "description": "",
5 | "private": true,
6 | "scripts": {
7 | "build": "parcel build src/manifest.json --config @parcel/config-webextension"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "devDependencies": {
12 | "@parcel/config-webextension": "^2.9.3",
13 | "@types/chrome": "^0.0.242",
14 | "buffer": "^6.0.3",
15 | "parcel": "^2.9.3",
16 | "process": "^0.11.10",
17 | "url": "^0.11.1"
18 | },
19 | "dependencies": {
20 | "@mlc-ai/web-llm": "^0.2.17",
21 | "progressbar.js": "^1.1.0"
22 | }
23 | }
--------------------------------------------------------------------------------
/lib/conversation.d.ts.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../src/conversation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAE9C;;GAEG;AACH,qBAAa,YAAY;IAChB,QAAQ,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAM;IACnD,MAAM,EAAE,kBAAkB,CAAC;gBAKtB,MAAM,EAAE,kBAAkB;IAItC,OAAO,CAAC,sBAAsB;IAqC9B;;;;OAIG;IACH,cAAc,IAAI,KAAK,CAAC,MAAM,CAAC;IAI/B;;;;;;;OAOG;IACH,sBAAsB;IAOtB,KAAK;IAIL,UAAU;IASV,aAAa;IAIb,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAQ3C,iBAAiB,CAAC,IAAI,EAAE,MAAM;IAI9B,WAAW,CAAC,OAAO,EAAE,MAAM;CAS5B;AAED,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,YAAY,CAyI9G"}
--------------------------------------------------------------------------------
/site/_includes/head.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/examples/get-started-rest/README.md:
--------------------------------------------------------------------------------
1 | # WebLLM Get Started App for Local Servers
2 |
3 | This folder provides a minimum demo to show WebLLM API in a webapp setting.
4 | To try it out, first start a local server using the steps outlined [here](https://mlc.ai/mlc-llm/docs/deploy/rest.html). Next, you can do the following steps under this folder
5 |
6 | ```bash
7 | npm install
8 | npm start
9 | ```
10 |
11 | Note if you would like to hack WebLLM core package.
12 | You can change web-llm dependencies as `"file:../.."`, and follow the build from source
13 | instruction in the project to build webllm locally. This option is only recommended
14 | if you would like to hack WebLLM core package.
15 |
--------------------------------------------------------------------------------
/examples/next-simple-chat/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true,
17 | "paths": {
18 | "~/*": ["./src/*"]
19 | }
20 | },
21 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
22 | "exclude": ["node_modules"]
23 | }
24 |
--------------------------------------------------------------------------------
/examples/next-simple-chat/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 |
5 |
6 | webpack: (config, { isServer }) => {
7 | // Fixes npm packages that depend on `fs` module
8 | if (!isServer) {
9 | config.resolve.fallback = {
10 | ...config.resolve.fallback, // if you miss it, all the other options in fallback, specified
11 | // by next.js will be dropped. Doesn't make much sense, but how it is
12 | fs: false, // the solution
13 | module: false,
14 | perf_hooks: false,
15 | };
16 | }
17 |
18 | return config
19 | },
20 | }
21 |
22 | module.exports = nextConfig
23 |
--------------------------------------------------------------------------------
/examples/next-simple-chat/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next-simple-chat",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@mlc-ai/web-llm": "../..",
13 | "@types/node": "20.3.3",
14 | "@types/react": "18.2.14",
15 | "@types/react-dom": "18.2.6",
16 | "autoprefixer": "10.4.14",
17 | "eslint": "8.44.0",
18 | "eslint-config-next": "13.4.7",
19 | "next": "13.4.7",
20 | "postcss": "8.4.24",
21 | "react": "18.2.0",
22 | "react-dom": "18.2.0",
23 | "tailwindcss": "3.3.2",
24 | "typescript": "5.1.6"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # Awesome WebLLM
2 |
3 | This page contains a curated list of examples, tutorials, blogs about WebLLM usecases.
4 | Please send a pull request if you find things that belongs to here.
5 |
6 | ## Tutorial Examples
7 |
8 | - [get-started](get-started): minimum get started example.
9 | - [web-worker](web-worker): get started with web worker backed chat.
10 | - [simple-chat](simple-chat): a mininum and complete chat app.
11 |
12 | ## Demo Spaces
13 |
14 | - [web-llm-embed](https://huggingface.co/spaces/matthoffner/web-llm-embed): document chat prototype using react-llm with transformers.js embeddings
15 | - [DeVinci](https://x6occ-biaaa-aaaai-acqzq-cai.icp0.io/): AI chat app based on WebLLM and hosted on decentralized cloud platform
--------------------------------------------------------------------------------
/scripts/prep_deps.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # This file prepares all the necessary dependencies for the web build.
3 | set -euxo pipefail
4 |
5 | emcc --version
6 | npm --version
7 |
8 | TVM_HOME_SET="${TVM_HOME:-}"
9 |
10 | if [[ -z ${TVM_HOME_SET} ]]; then
11 | if [[ ! -d "3rdparty/tvm-unity" ]]; then
12 | echo "Do not find TVM_HOME env variable, cloning a version as source".
13 | git clone https://github.com/mlc-ai/relax 3rdparty/tvm-unity --recursive
14 | fi
15 | export TVM_HOME="${TVM_HOME:-3rdparty/tvm-unity}"
16 | fi
17 |
18 | cd ${TVM_HOME}/web && make && npm install && npm run build && cd -
19 | rm -rf tvm_home
20 | ln -s ${TVM_HOME} tvm_home
21 | npm install
22 | cd examples/simple-chat
23 | npm install
24 | cd ../..
25 |
--------------------------------------------------------------------------------
/examples/next-simple-chat/src/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import Head from "next/head";
2 | import ChatComponent from "~/utils/chat_component";
3 | import { Inter } from "next/font/google";
4 |
5 | const inter = Inter({ subsets: ["latin"] });
6 |
7 | export default function Home() {
8 | return (
9 | <>
10 |
11 | Example App
12 |
16 |
17 |
18 |
21 |
22 |
23 | >
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/examples/web-worker/README.md:
--------------------------------------------------------------------------------
1 | # WebLLM Get Started with WebWorker
2 |
3 | This folder provides a minimum demo to show WebLLM API using
4 | [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers).
5 | The main benefit of web worker is that all ML workloads runs on a separate thread as a result
6 | will less likely block the UI.
7 |
8 | To try it out, you can do the following steps under this folder
9 |
10 | ```bash
11 | npm install
12 | npm start
13 | ```
14 |
15 | Note if you would like to hack WebLLM core package.
16 | You can change web-llm dependencies as `"file:../.."`, and follow the build from source
17 | instruction in the project to build webllm locally. This option is only recommended
18 | if you would like to hack WebLLM core package.
19 |
--------------------------------------------------------------------------------
/examples/simple-chat/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simple-chat",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "cp src/gh-config.js src/app-config.js && parcel src/llm_chat.html --port 8888",
7 | "mlc-local": "cp src/mlc-local-config.js src/app-config.js && parcel src/llm_chat.html --port 8888",
8 | "build": "cp src/gh-config.js src/app-config.js && parcel build src/llm_chat.html --dist-dir lib --no-content-hash"
9 | },
10 | "devDependencies": {
11 | "buffer": "^5.7.1",
12 | "parcel": "^2.8.3",
13 | "process": "^0.11.10",
14 | "tslib": "^2.3.1",
15 | "typescript": "^4.9.5",
16 | "url": "^0.11.3"
17 | },
18 | "dependencies": {
19 | "@mlc-ai/web-llm": "^0.2.17"
20 | }
21 | }
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { nodeResolve } from '@rollup/plugin-node-resolve';
2 | import ignore from "rollup-plugin-ignore";
3 | import commonjs from '@rollup/plugin-commonjs';
4 | import typescript from 'rollup-plugin-typescript2'
5 |
6 | export default {
7 | input: 'src/index.ts',
8 | output: [
9 | {
10 | file: 'lib/index.js',
11 | exports: 'named',
12 | format: 'es',
13 | sourcemap: true,
14 | globals: {'ws': 'ws'}
15 | }
16 | ],
17 | plugins: [
18 | ignore(["fs", "path", "crypto"]),
19 | nodeResolve({ browser: true }),
20 | commonjs({
21 | ignoreDynamicRequires: true,
22 | }),
23 | typescript({
24 | rollupCommonJSResolveHack: false,
25 | clean: true
26 | })
27 | ]
28 | };
29 |
--------------------------------------------------------------------------------
/examples/simple-chat/src/llm_chat.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/site/_config.yml:
--------------------------------------------------------------------------------
1 |
2 | name: "Web LLM"
3 | short_name: "WebLLM"
4 |
5 | url: https://webllm.mlc.ai
6 |
7 | exclude: [README.md, serve_local.sh]
8 |
9 | plugins:
10 | - jekyll-remote-theme
11 |
12 | remote_theme: mlc-ai/jekyll-theme-mlc
13 |
14 |
15 | # Colorize code snippets with the rogue module if we want to deploy on GH.
16 | highlighter: rouge
17 |
18 | markdown: kramdown
19 |
20 | # The path structure for blog posts.
21 | permalink: /blog/:year/:month/:day/:title.html
22 |
23 | # Number of news stories on the front page.
24 | front_page_news: 8
25 |
26 | # Base pathname for links.
27 | base: ''
28 |
29 | # make pages for the _projects folder
30 | collections:
31 | projects:
32 | output: true
33 |
34 | course_title:
35 |
36 | # Navigation bar links.
37 | navigation:
38 | - title: Home
39 | link: /
40 | - title: GitHub
41 | link: https://github.com/mlc-ai/web-llm
42 |
--------------------------------------------------------------------------------
/utils/vram_requirements/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vram-requirements",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "cp src/gh-config.js src/app-config.js && parcel src/vram_requirements.html --port 8885",
7 | "build": "cp src/gh-config.js src/app-config.js && parcel build src/vram_requirements.html --dist-dir lib"
8 | },
9 | "devDependencies": {
10 | "buffer": "^5.7.1",
11 | "crypto-browserify": "^3.12.0",
12 | "events": "^3.3.0",
13 | "parcel": "^2.8.3",
14 | "path-browserify": "^1.0.1",
15 | "process": "^0.11.10",
16 | "stream-browserify": "^3.0.0",
17 | "tslib": "^2.3.1",
18 | "typescript": "^4.9.5",
19 | "url": "^0.11.3"
20 | },
21 | "dependencies": {
22 | "@mlc-ai/web-llm": "^0.2.17",
23 | "tvmjs": "file:./../../tvm_home/web"
24 | }
25 | }
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export {
2 | ModelRecord, AppConfig
3 | } from "./config";
4 |
5 |
6 | export {
7 | InitProgressCallback,
8 | InitProgressReport,
9 | ChatOptions,
10 | ChatInterface
11 | } from "./types";
12 |
13 | export {
14 | ChatModule,
15 | ChatRestModule, hasModelInCache
16 | } from "./chat_module";
17 |
18 | export {
19 | ChatWorkerHandler,
20 | ChatWorkerClient
21 | } from "./web_worker";
22 |
23 | // // uncomment to build a cdn bundle for javascript
24 |
25 | // declare global {
26 | // interface Window {
27 | // webLLM: any
28 | // }
29 | // }
30 |
31 | // // Add the module to the window when the script is executed (only works in vanilla js sript import)
32 |
33 | // import { ChatWorkerClient } from "./web_worker";
34 | // import { ChatModule } from "./chat_module";
35 |
36 | // function addToWindow() {
37 | // window.webLLM = {ChatModule, ChatWorkerClient}
38 | // }
39 |
40 | // addToWindow();
--------------------------------------------------------------------------------
/web/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es6": true
5 | },
6 | "extends": ["eslint:recommended"],
7 | "root": true,
8 | "parser": "@typescript-eslint/parser",
9 | "parserOptions": {
10 | "ecmaVersion": 2018,
11 | "sourceType": "module"
12 | },
13 | "overrides": [
14 | {
15 | "files": ["src/**.ts", "src/**.tsx"],
16 | "plugins": ["@typescript-eslint"],
17 | "extends": [
18 | "plugin:@typescript-eslint/eslint-recommended",
19 | "plugin:@typescript-eslint/recommended"
20 | ],
21 | "rules": {
22 | "require-jsdoc": 0,
23 | "@typescript-eslint/no-explicit-any": 0,
24 | "@typescript-eslint/no-empty-function": 0,
25 | "@typescript-eslint/ban-types": "off"
26 | }
27 | },
28 | {
29 | "files": ["tests/node/*.js", "apps/node/*.js"],
30 | "env": {
31 | "node": true
32 | }
33 | }
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/examples/chrome-extension/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 3,
3 | "name": "MLCBot",
4 | "version": "0.1.0",
5 | "description": "Chat with your browser",
6 | "icons": {
7 | "16": "icons/icon-16.png",
8 | "32": "icons/icon-32.png",
9 | "64": "icons/icon-64.png",
10 | "128": "icons/icon-128.png"
11 | },
12 | "content_security_policy": {
13 | "extension_pages": "style-src-elem 'self' https://cdnjs.cloudflare.com; font-src 'self' https://cdnjs.cloudflare.com; script-src 'self' 'wasm-unsafe-eval'; default-src 'self' data:; connect-src 'self' data: http://localhost:8000 https://huggingface.co https://cdn-lfs.huggingface.co https://cdn-lfs-us-1.huggingface.co https://raw.githubusercontent.com"
14 | },
15 | "action": {
16 | "default_title": "MLCBot",
17 | "default_popup": "popup.html"
18 | },
19 | "background": {
20 | "service_worker": "background.ts",
21 | "type": "module"
22 | },
23 | "permissions": [
24 | "storage",
25 | "tabs",
26 | "webNavigation"
27 | ]
28 | }
--------------------------------------------------------------------------------
/examples/get-started-rest/src/get_started.ts:
--------------------------------------------------------------------------------
1 | import * as webllm from "@mlc-ai/web-llm";
2 |
3 | function setLabel(id: string, text: string) {
4 | const label = document.getElementById(id);
5 | if (label == null) {
6 | throw Error("Cannot find label " + id);
7 | }
8 | label.innerText = text;
9 | }
10 | const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
11 | async function main() {
12 | const chat = new webllm.ChatRestModule();
13 | chat.resetChat();
14 |
15 | chat.setInitProgressCallback((report: webllm.InitProgressReport) => {
16 | setLabel("init-label", report.text);
17 | });
18 |
19 | const generateProgressCallback = (_step: number, message: string) => {
20 | setLabel("generate-label", message);
21 | };
22 |
23 | const prompt0 = "Write a song about Pittsburgh.";
24 | setLabel("prompt-label", prompt0);
25 | const reply0 = await chat.generate(prompt0, generateProgressCallback);
26 | console.log(reply0);
27 |
28 | console.log(await chat.runtimeStatsText());
29 | }
30 |
31 | main();
32 |
--------------------------------------------------------------------------------
/web/jest.config.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 |
20 | /* eslint-disable no-undef */
21 | module.exports = {
22 | testEnvironment: "node",
23 |
24 | testMatch: [
25 | "**/tests/node/*.js"
26 | ],
27 | };
28 |
--------------------------------------------------------------------------------
/notes.txt:
--------------------------------------------------------------------------------
1 | /web npm run bundle
2 |
3 | npm run build
4 |
5 | /examples/next-simple-chat npm run dev
6 |
7 | When publishing:
8 |
9 | first build and publish (without the cdn code)
10 |
11 | then uncomment the cdn window code in src/index.ts
12 | build
13 | recomment
14 | commit and push
15 | finished
16 |
17 |
18 | Strategy to sync the fork:
19 |
20 | # Add a new remote upstream repository (optional)
21 | git remote add upstream https://github.com/mlc-ai/web-llm.git
22 |
23 | # Sync your fork
24 | git fetch upstream
25 | git checkout main
26 | git merge upstream/main
27 |
28 |
29 | For tvm_home:
30 |
31 | Open up the project in a new vscode session.
32 | Git pull and merge changes
33 | delete the web folder
34 | create new web folder
35 |
36 |
37 | Other Info:
38 |
39 | require("perf_hooks") has been commented out because it causes the following error when this component is used in apps like React:
40 | Module not found: Error: Can't resolve 'perf_hooks' in '/Users/ovidijusparsiunas/Desktop/ai-chat/example-servers/ui/node_modules/deep-chat-web-llm/lib'
41 |
--------------------------------------------------------------------------------
/web/apps/browser/rpc_plugin.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/chrome-extension/src/manifest_v2.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "name": "MLCBot",
4 | "version": "0.1.0",
5 | "description": "Chat with your browser",
6 | "icons": {
7 | "16": "icons/icon-16.png",
8 | "32": "icons/icon-32.png",
9 | "64": "icons/icon-64.png",
10 | "128": "icons/icon-128.png"
11 | },
12 | "content_security_policy": "style-src-elem 'self' https://cdnjs.cloudflare.com; font-src 'self' https://cdnjs.cloudflare.com; script-src 'self' 'unsafe-eval' 'wasm-unsafe-eval'; default-src 'self' data:; connect-src 'self' data: http://localhost:8000 https://huggingface.co https://cdn-lfs.huggingface.co https://raw.githubusercontent.com",
13 | "browser_action": {
14 | "default_popup": "popup.html"
15 | },
16 | "content_scripts": [
17 | {
18 | "matches": [""],
19 | "js": ["content.js"]
20 | }
21 | ],
22 | "background": {
23 | "scripts": ["background.ts"],
24 | "persistent": false
25 | },
26 | "permissions": [
27 | "storage",
28 | "tabs",
29 | "webNavigation",
30 | "activeTab"
31 | ]
32 | }
--------------------------------------------------------------------------------
/lib/config.d.ts.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC1C,aAAa,EAAE,MAAM,CAAC;IAEtB,oBAAoB,CAAC,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAE/C,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,iBAAiB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACnC;AAED;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAa/B,CAAA"}
--------------------------------------------------------------------------------
/examples/chrome-extension/src/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Chatbot
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/lib/types.d.ts.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAEhD;;;GAGG;AACH,MAAM,WAAW,WAAY,SAAQ,OAAO,CAAC,UAAU,CAAC;CAAI;AAE5D;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IAEjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAExE;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;AAEtF;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;;;;;OAQG;IACH,uBAAuB,EAAE,CAAC,oBAAoB,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAE9E;;;;;;;;OAQG;IACH,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE,IAAI,EAAE,GAAG,QAAQ,KAAK,OAAO,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC;IAEnI;;;;;;;OAOG;IACH,QAAQ,EAAE,CACR,KAAK,EAAE,MAAM,EACb,gBAAgB,CAAC,EAAE,wBAAwB,EAC3C,cAAc,CAAC,EAAE,MAAM,KACpB,OAAO,CAAC,MAAM,CAAC,CAAC;IAErB;;;OAGG;IACH,gBAAgB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAExC;;OAEG;IACH,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAE9B;;OAEG;IACH,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5B;;OAEG;IACH,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/B;;;OAGG;IACH,8BAA8B,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAElD;;;OAGG;IACH,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACjC"}
--------------------------------------------------------------------------------
/examples/chrome-extension/src/background.ts:
--------------------------------------------------------------------------------
1 | import {ChatRestModule} from "@mlc-ai/web-llm";
2 |
3 | // TODO: Surface this as an option to the user
4 | const useWebGPU = true;
5 |
6 | var cm: ChatRestModule;
7 | if (!useWebGPU) {
8 | cm = new ChatRestModule();
9 | }
10 |
11 | // Set reponse callback for chat module
12 | const generateProgressCallback = (_step: number, message: string) => {
13 | // send the answer back to the content script
14 | chrome.runtime.sendMessage({ answer: message });
15 | };
16 |
17 | var context = "";
18 | chrome.runtime.onMessage.addListener(async function (request) {
19 | // check if the request contains a message that the user sent a new message
20 | if (request.input) {
21 | var inp = request.input;
22 | if (context.length > 0) {
23 | inp = "Use only the following context when answering the question at the end. Don't use any other knowledge.\n"+ context + "\n\nQuestion: " + request.input + "\n\nHelpful Answer: ";
24 | }
25 | console.log("Input:", inp);
26 | const response = await cm.generate(inp, generateProgressCallback);
27 | }
28 | if (request.context) {
29 | context = request.context;
30 | console.log("Got context:", context);
31 | }
32 | });
33 |
--------------------------------------------------------------------------------
/examples/simple-chat/src/mlc-local-config.js:
--------------------------------------------------------------------------------
1 | // config used when serving from local mlc-llm/dist
2 | // use web-llm/script/serve_mlc_llm_dist.sh to start the artifact server
3 | export default {
4 | "model_list": [
5 | {
6 | "model_url": "http://localhost:8000/RedPajama-INCITE-Chat-3B-v1-q4f32_1/params/",
7 | "local_id": "RedPajama-INCITE-Chat-3B-v1-q4f32_1",
8 | "model_lib_url": "http://localhost:8000/RedPajama-INCITE-Chat-3B-v1-q4f32_1/RedPajama-INCITE-Chat-3B-v1-q4f32_1-webgpu.wasm",
9 | },
10 | {
11 | "model_url": "http://localhost:8000/Llama-2-7b-chat-hf-q4f32_1/params/",
12 | "local_id": "Llama-2-7b-chat-hf-q4f32_1",
13 | "model_lib_url": "http://localhost:8000/Llama-2-7b-chat-hf-q4f32_1/Llama-2-7b-chat-hf-q4f32_1-webgpu.wasm",
14 | },
15 | // fp16 options are enabled through chrome canary flags
16 | // chrome --enable-dawn-features=enable_unsafe_apis
17 | {
18 | "model_url": "http://localhost:8000/RedPajama-INCITE-Chat-3B-v1-q4f16_1/params/",
19 | "local_id": "RedPajama-INCITE-Chat-3B-v1-q4f16_1",
20 | "model_lib_url": "http://localhost:8000/RedPajama-INCITE-Chat-3B-v1-q4f16_1/RedPajama-INCITE-Chat-3B-v1-q4f16_1-webgpu.wasm",
21 | "required_features": ["shader-f16"]
22 | }
23 | ],
24 | "use_web_worker": true
25 | }
26 |
--------------------------------------------------------------------------------
/lib/conversation.d.ts:
--------------------------------------------------------------------------------
1 | import { ConvTemplateConfig } from "./config";
2 | /**
3 | * Helper to keep track of history conversations.
4 | */
5 | export declare class Conversation {
6 | messages: Array<[string, string | undefined]>;
7 | config: ConvTemplateConfig;
8 | constructor(config: ConvTemplateConfig);
9 | private getPromptArrayInternal;
10 | /**
11 | * Get prompt arrays with the first one as system.
12 | *
13 | * @returns The prompt array.
14 | */
15 | getPromptArray(): Array;
16 | /**
17 | * Get the last round of prompt has not been fed as input.
18 | *
19 | * @note This function needs to be used with the assumption that
20 | * the caller call appendMessage then appendReplyHeader.
21 | *
22 | * @returns The prompt array.
23 | */
24 | getPrompArrayLastRound(): string[];
25 | reset(): void;
26 | getStopStr(): string;
27 | getStopTokens(): number[];
28 | appendMessage(role: string, message: string): void;
29 | appendReplyHeader(role: string): void;
30 | finishReply(message: string): void;
31 | }
32 | export declare function getConversation(conv_template: string, conv_config?: Partial): Conversation;
33 | //# sourceMappingURL=conversation.d.ts.map
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "deep-chat-web-llm",
3 | "version": "0.0.27",
4 | "description": "Hardware accelerated language model chats on browsers",
5 | "main": "lib/index.js",
6 | "types": "lib/index.d.ts",
7 | "type": "module",
8 | "scripts": {
9 | "build": "rollup -c && ./cleanup-index-js.sh",
10 | "lint": "npx eslint ."
11 | },
12 | "files": [
13 | "lib"
14 | ],
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/OvidijusParsiunas/web-llm"
18 | },
19 | "keywords": [
20 | "llm",
21 | "large language model",
22 | "machine learning"
23 | ],
24 | "license": "Apache-2.0",
25 | "homepage": "https://github.com/OvidijusParsiunas/web-llm",
26 | "devDependencies": {
27 | "@mlc-ai/web-tokenizers": "^0.1.2",
28 | "@rollup/plugin-commonjs": "^20.0.0",
29 | "@rollup/plugin-node-resolve": "^13.0.4",
30 | "@typescript-eslint/eslint-plugin": "^5.59.6",
31 | "@typescript-eslint/parser": "^5.59.6",
32 | "@webgpu/types": "^0.1.24",
33 | "buffer": "^5.7.1",
34 | "eslint": "^8.41.0",
35 | "process": "^0.11.10",
36 | "rollup": "^2.56.2",
37 | "rollup-plugin-ignore": "^1.0.10",
38 | "rollup-plugin-typescript2": "^0.34.1",
39 | "tslib": "^2.3.1",
40 | "tvmjs": "file:./web",
41 | "typescript": "^4.9.5"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/examples/web-worker/src/main.ts:
--------------------------------------------------------------------------------
1 | import * as webllm from "@mlc-ai/web-llm";
2 |
3 | function setLabel(id: string, text: string) {
4 | const label = document.getElementById(id);
5 | if (label == null) {
6 | throw Error("Cannot find label " + id);
7 | }
8 | label.innerText = text;
9 | }
10 |
11 | async function main() {
12 | // Use a chat worker client instead of ChatModule here
13 | const chat = new webllm.ChatWorkerClient(new Worker(
14 | new URL('./worker.ts', import.meta.url),
15 | { type: 'module' }
16 | ));
17 |
18 | chat.setInitProgressCallback((report: webllm.InitProgressReport) => {
19 | setLabel("init-label", report.text);
20 | });
21 |
22 | await chat.reload("Llama-2-7b-chat-hf-q4f32_1");
23 |
24 | const generateProgressCallback = (_step: number, message: string) => {
25 | setLabel("generate-label", message);
26 | };
27 |
28 | const prompt0 = "What is the capital of Canada?";
29 | setLabel("prompt-label", prompt0);
30 | const reply0 = await chat.generate(prompt0, generateProgressCallback);
31 | console.log(reply0);
32 |
33 | const prompt1 = "Can you write a poem about it?";
34 | setLabel("prompt-label", prompt1);
35 | const reply1 = await chat.generate(prompt1, generateProgressCallback);
36 | console.log(reply1);
37 |
38 | console.log(await chat.runtimeStatsText());
39 | }
40 |
41 | main();
42 |
--------------------------------------------------------------------------------
/examples/next-simple-chat/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/apps/node/wasi_rpc_server.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 |
20 | /**
21 | * Example code to start the RPC server on nodejs using WASI
22 | */
23 | const { WASI } = require("wasi");
24 | const tvmjs = require("../../dist");
25 |
26 | // Get import returns a fresh library in each call.
27 | const getImports = () => {
28 | return new WASI({
29 | args: process.argv,
30 | env: process.env
31 | });
32 | };
33 |
34 | const proxyUrl = "ws://localhost:8888/ws";
35 |
36 | new tvmjs.RPCServer(proxyUrl, "wasm", getImports, console.log);
37 |
--------------------------------------------------------------------------------
/web/src/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 |
20 | export {
21 | Scalar, DLDevice, DLDataType,
22 | PackedFunc, Module, NDArray,
23 | TVMArray, TVMObject, VirtualMachine,
24 | InitProgressCallback, InitProgressReport,
25 | ArtifactCache, Instance, instantiate, hasNDArrayInCache
26 | } from "./runtime";
27 | export { Disposable, LibraryProvider } from "./types";
28 | export { RPCServer } from "./rpc_server";
29 | export { wasmPath } from "./support";
30 | export { detectGPUDevice, GPUDeviceDetectOutput } from "./webgpu";
31 | export { assert } from "./support";
32 | export { createPolyfillWASI } from "./compact";
33 |
--------------------------------------------------------------------------------
/web/emcc/preload.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 | /* eslint-disable no-unused-vars */
20 | /**
21 | * JS config used by --pre-js in emcc.
22 | * Wrap module as a LibraryProvider.
23 | */
24 |
25 | var __wasmLib = {};
26 |
27 | function __wasmLibInstantiateWasm(imports, successCallback) {
28 | __wasmLib.imports = imports;
29 | __wasmLib.successCallback = successCallback;
30 | }
31 |
32 | function __wasmLibStart(wasmInstance) {
33 | __wasmLib.successCallback(wasmInstance);
34 | }
35 |
36 | __wasmLib.start = __wasmLibStart;
37 |
38 | var Module = {
39 | "instantiateWasm": __wasmLibInstantiateWasm,
40 | "wasmLibraryProvider": __wasmLib
41 | };
42 |
--------------------------------------------------------------------------------
/web/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 | import commonjs from '@rollup/plugin-commonjs';
20 | import ignore from "rollup-plugin-ignore";
21 | import resolve from '@rollup/plugin-node-resolve';
22 | import typescript from 'rollup-plugin-typescript2';
23 |
24 | export default {
25 | input: 'src/index.ts',
26 | output: {
27 | file: 'lib/index.js',
28 | format: 'umd',
29 | name: 'tvmjs',
30 | exports: 'named',
31 | globals: {'ws': 'ws'}
32 | },
33 | plugins: [
34 | ignore(["fs", "path", "crypto"]),
35 | resolve({ browser: true }),
36 | commonjs(),
37 | typescript({
38 | rollupCommonJSResolveHack: false,
39 | clean: true
40 | })
41 | ],
42 | external: ['ws']
43 | };
44 |
--------------------------------------------------------------------------------
/examples/chrome-extension/src/example.html:
--------------------------------------------------------------------------------
1 | In the year 2154, humanity had colonized several planets in the distant reaches of the galaxy. The planet of Xylophia-IV was one of the most remote and inhospitable, with temperatures often dropping to -200 degrees Celsius. Despite these harsh conditions, a team of scientists had established a research station on the planet to study the unique geological formations and exotic flora and fauna.
2 |
3 | One day, while conducting a routine survey of the planet's surface, the team discovered an strange object buried deep in the ice. As they examined it closer, they realized it was a small, metallic capsule with a glowing blue symbol etched onto its surface.
4 |
5 | The team's leader, a brilliant scientist named Dr. Maria Rodriguez, was immediately intrigued by the capsule's mysterious origins. She ordered her team to bring it back to the research station for further analysis.
6 |
7 | After weeks of studying the capsule, the team finally cracked the code to the symbol etched onto its surface. It was a message from an alien race, warning Earth of an impending attack from an unknown threat.
8 |
9 | The team was shocked and dismayed by the news, but they knew they had to act quickly to warn the rest of humanity. They transmitted the message to the nearest space station, which relayed it to Earth's government.
10 |
11 | As the threat of attack loomed near, the team remained on high alert, ready to face whatever dangers lay ahead. They had uncovered a secrets of the universe, and now they were determined to protect their planet and its inhabitants at all costs.
--------------------------------------------------------------------------------
/lib/chat_module.d.ts.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"chat_module.d.ts","sourceRoot":"","sources":["../src/chat_module.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,SAAS,EAAqB,MAAM,UAAU,CAAC;AAGpE,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,WAAW,EACX,wBAAwB,EACzB,MAAM,SAAS,CAAC;AAEjB;;GAEG;AACH,qBAAa,UAAW,YAAW,aAAa;IAC9C,OAAO,CAAC,MAAM,CAAqC;IACnD,OAAO,CAAC,QAAQ,CAAC,CAAkB;IACnC,OAAO,CAAC,oBAAoB,CAAC,CAAuB;IACpD,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,iBAAiB,CAAS;IAElC,uBAAuB,CAAC,oBAAoB,EAAE,oBAAoB;IAI5D,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE,IAAI,EAAE,GAAG,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAuIlH,QAAQ,CACZ,KAAK,EAAE,MAAM,EACb,gBAAgB,CAAC,EAAE,wBAAwB,EAC3C,cAAc,SAAI,GACjB,OAAO,CAAC,MAAM,CAAC;IAmBZ,iBAAiB;IAIjB,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;IAInC,SAAS;IAIT,MAAM;IAKN,8BAA8B,IAAI,OAAO,CAAC,MAAM,CAAC;IA2BjD,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAYrC;;OAEG;IACH,OAAO,IAAI,OAAO;IAIlB;;;;OAIG;IACH,UAAU,IAAI,MAAM;IAIpB;;;OAGG;IACG,OAAO,CAAC,KAAK,EAAE,MAAM;IAI3B;;OAEG;IACG,MAAM;IAIZ,OAAO,CAAC,WAAW;YAOL,kBAAkB;CAuBjC;AAED;;GAEG;AACH,qBAAa,cAAe,YAAW,aAAa;IAClD,OAAO,CAAC,MAAM,CAAqC;IACnD,OAAO,CAAC,oBAAoB,CAAC,CAAuB;IAEpD,uBAAuB,CAAC,oBAAoB,EAAE,oBAAoB;IAI5D,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAIvF,8BAA8B,IAAI,OAAO,CAAC,MAAM,CAAC;IAIjD,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAI/B,MAAM;IAIN,iBAAiB;IAIjB,QAAQ,CACZ,KAAK,EAAE,MAAM,EACb,gBAAgB,CAAC,EAAE,wBAAwB,EAC3C,cAAc,SAAI,GACjB,OAAO,CAAC,MAAM,CAAC;IA0DZ,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;IAWnC,SAAS;CAKhB;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAc9F"}
--------------------------------------------------------------------------------
/lib/web_worker.d.ts.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"web_worker.d.ts","sourceRoot":"","sources":["../src/web_worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EACL,aAAa,EACb,WAAW,EACX,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,SAAS,CAAC;AAajB,UAAU,YAAY;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,SAAS,CAAC,EAAE,SAAS,CAAA;CACtB;AAED,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,8BAA8B;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,KAAK,cAAc,GACjB,8BAA8B,GAC9B,YAAY,GACZ,cAAc,GACd,kBAAkB,GAClB,MAAM,GACN,IAAI,GACJ,MAAM,CAAC;AAYT;;;;;;;;;;GAUG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,IAAI,CAAgB;gBAEhB,IAAI,EAAE,aAAa;IAYzB,UAAU,CAAC,CAAC,SAAS,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC;IAoB/E,SAAS,CAAC,KAAK,EAAE,YAAY;CAyE9B;AAED,UAAU,UAAU;IAClB,SAAS,EAAE,GAAG,CAAC;IACf,WAAW,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,CAAC;CACrC;AAED;;;;;;;;;GASG;AACH,qBAAa,gBAAiB,YAAW,aAAa;IAC7C,MAAM,EAAE,UAAU,CAAC;IAC1B,OAAO,CAAC,oBAAoB,CAAC,CAAuB;IACpD,OAAO,CAAC,wBAAwB,CAA+C;IAC/E,OAAO,CAAC,cAAc,CAAmD;gBAE7D,MAAM,EAAE,GAAG;IAOvB,uBAAuB,CAAC,oBAAoB,EAAE,oBAAoB;IAIlE,OAAO,CAAC,UAAU;IAwBZ,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAa1F,8BAA8B,IAAI,OAAO,CAAC,MAAM,CAAC;IASjD,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAS/B,QAAQ,CACZ,KAAK,EAAE,MAAM,EACb,gBAAgB,CAAC,EAAE,wBAAwB,EAC3C,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,MAAM,CAAC;IAeZ,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;IASzC,iBAAiB,IAAI,IAAI;IASnB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IASvB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAShC,SAAS,CAAC,KAAK,EAAE,GAAG;CAwCrB"}
--------------------------------------------------------------------------------
/web/apps/node/wasi_example.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 | /**
20 | * Example code to start the runtime.
21 | */
22 | const { WASI } = require('wasi');
23 | const path = require("path");
24 | const fs = require("fs");
25 | const tvmjs = require("../../dist");
26 |
27 | const wasmPath = tvmjs.wasmPath();
28 | const wasmSource = fs.readFileSync(path.join(wasmPath, "tvmjs_runtime.wasm"));
29 |
30 | const wasi = new WASI({ args: process.argv, env: process.env });
31 | // Here we pass the javascript module generated by emscripten as the
32 | // LibraryProvider to provide WASI related libraries.
33 | const tvm = new tvmjs.Instance(new WebAssembly.Module(wasmSource), wasi);
34 |
35 | // List all the global functions from the runtime.
36 | console.log("Runtime using WASI\n", tvm.listGlobalFuncNames());
37 |
--------------------------------------------------------------------------------
/web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tvmjs",
3 | "displayName": "TVM Wasm JS runtime",
4 | "license": "Apache-2.0",
5 | "version": "0.16.0-dev0",
6 | "files": [
7 | "lib"
8 | ],
9 | "main": "lib/index.js",
10 | "types": "lib/index.d.ts",
11 | "scripts": {
12 | "prepwasm": "make && python3 tests/python/prepare_test_libs.py",
13 | "build": "rollup -c",
14 | "lint": "eslint -c .eslintrc.json .",
15 | "typedoc": "typedoc src/index.ts --plugin typedoc-plugin-missing-exports",
16 | "test": "node --experimental-wasm-eh node_modules/.bin/jest",
17 | "bundle": "npm run build && cp lib/index.js dist/index.js && cp lib/index.js dist/tvmjs.bundle.js",
18 | "example": "npm run bundle && node apps/node/example.js",
19 | "example:wasi": "npm run bundle && node --experimental-wasi-unstable-preview1 --experimental-wasm-bigint apps/node/wasi_example.js",
20 | "rpc": "npm run bundle && node --experimental-wasi-unstable-preview1 --experimental-wasm-bigint apps/node/wasi_rpc_server.js"
21 | },
22 | "devDependencies": {
23 | "@rollup/plugin-commonjs": "^20.0.0",
24 | "@rollup/plugin-node-resolve": "^13.0.4",
25 | "@types/node": "^20.4.5",
26 | "@typescript-eslint/eslint-plugin": "^5.59.6",
27 | "@typescript-eslint/parser": "^5.59.6",
28 | "@webgpu/types": "^0.1.40",
29 | "eslint": "^8.41.0",
30 | "jest": "^26.0.1",
31 | "rollup": "^2.56.2",
32 | "rollup-plugin-ignore": "^1.0.10",
33 | "rollup-plugin-typescript2": "^0.34.1",
34 | "typedoc": "^0.24.7",
35 | "typedoc-plugin-missing-exports": "2.0.0",
36 | "typescript": "^4.9.5",
37 | "ws": "^7.2.5"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/llm_chat.d.ts.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"llm_chat.d.ts","sourceRoot":"","sources":["../src/llm_chat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAItC,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,SAAS,CAAY;IAG7B,OAAO,CAAC,GAAG,CAAiB;IAC5B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,EAAE,CAAuB;IACjC,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,QAAQ,CAAmB;IACnC,OAAO,CAAC,cAAc,CAAmB;IAGzC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,WAAW,CAAC,CAA4B;IAChD,OAAO,CAAC,mBAAmB,CAAK;IAIhC,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,eAAe,CAAM;IAC7B,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,gBAAgB,CAAM;IAC9B,OAAO,CAAC,oBAAoB,CAAQ;IACpC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAgB;IAGlC,OAAO,CAAC,aAAa,CAAM;IAC3B,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,YAAY,CAAe;IAEnC,OAAO,CAAC,aAAa,CAAkB;IAEvC,OAAO,CAAC,wBAAwB,CAAa;IAI7C,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,kBAAkB,CAAK;IAG/B,OAAO,CAAC,MAAM,CAAe;gBAEjB,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU;IA2GzE,OAAO;IAYP;;OAEG;IACH,UAAU;IAIV;;OAEG;IACH,iBAAiB;IAOjB;;OAEG;IACH,SAAS;IAST;;OAEG;IACH,OAAO,IAAI,OAAO;IAIlB;;OAEG;IACH,gBAAgB,IAAI,MAAM;IAOpB,wBAAwB;IAI9B;;OAEG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB7C;;OAEG;IACG,uBAAuB,CAAC,oBAAoB,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC,sBAAsB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAehH,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,aAAa;IAiDf,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IA0CjC;;OAEG;IACH,WAAW;IAQX;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IA4BxB,OAAO,CAAC,OAAO;IAgDf,OAAO,CAAC,iBAAiB;YAcX,qBAAqB;IA4BnC,OAAO,CAAC,cAAc;IA6EhB,QAAQ;CAwCf"}
--------------------------------------------------------------------------------
/web/apps/node/example.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 | /**
20 | * Example code to start the runtime.
21 | */
22 | const path = require("path");
23 | const fs = require("fs");
24 | const tvmjs = require("../../lib");
25 |
26 | const wasmPath = tvmjs.wasmPath();
27 | const wasmSource = fs.readFileSync(path.join(wasmPath, "tvmjs_runtime.wasm"));
28 | // Here we pass the javascript module generated by emscripten as the
29 | // LibraryProvider to provide WASI related libraries.
30 | // the async version of the API.
31 | tvmjs.instantiate(wasmSource, tvmjs.createPolyfillWASI())
32 | .then((tvm) => {
33 | tvm.beginScope();
34 | const log_info = tvm.getGlobalFunc("testing.log_info_str");
35 | log_info("hello world");
36 | // List all the global functions from the runtime.
37 | console.log("Runtime functions using EmccWASI\n", tvm.listGlobalFuncNames());
38 | tvm.endScope();
39 | });
40 |
--------------------------------------------------------------------------------
/web/emcc/decorate_as_wasi.py:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 | """Decorate emcc generated js to a WASI compatible API."""
18 |
19 | import sys
20 |
21 | template_head = """
22 | function EmccWASI() {
23 | """
24 |
25 | template_tail = """
26 | this.Module = Module;
27 | this.start = Module.wasmLibraryProvider.start;
28 | this.imports = Module.wasmLibraryProvider.imports;
29 | this.wasiImport = this.imports["wasi_snapshot_preview1"];
30 | }
31 | """
32 |
33 | template_es_tail = """
34 | export default EmccWASI;
35 | """
36 |
37 | template_cjs_tail = """
38 | if (typeof module !== "undefined" && module.exports) {
39 | module.exports = EmccWASI;
40 | }
41 | """
42 |
43 |
44 | def generate_tail(mode):
45 | if mode == "es":
46 | return template_tail + template_es_tail
47 | return template_tail + template_cjs_tail
48 |
49 |
50 | if __name__ == "__main__":
51 | if len(sys.argv) != 4:
52 | print("Usage ")
53 |
54 | result = template_head + open(sys.argv[1]).read() + generate_tail(sys.argv[3])
55 | with open(sys.argv[2], "w") as fo:
56 | fo.write(result)
57 |
--------------------------------------------------------------------------------
/site/_includes/llm_chat.html:
--------------------------------------------------------------------------------
1 |
4 |
5 |
18 |
25 |
26 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/lib/config.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Conversation template config
3 | */
4 | export interface ConvTemplateConfig {
5 | system: string;
6 | roles: Array;
7 | seps: Array;
8 | separator_style: string;
9 | offset: number;
10 | stop_str: string;
11 | add_bos: boolean;
12 | stop_tokens: Array;
13 | }
14 | /**
15 | * Config of one chat model
16 | */
17 | export interface ChatConfig {
18 | tokenizer_files: Array;
19 | conv_config?: Partial;
20 | conv_template: string;
21 | conversation_history?: Array<[string, string]>;
22 | mean_gen_len: number;
23 | shift_fill_factor: number;
24 | repetition_penalty: number;
25 | top_p: number;
26 | temperature: number;
27 | }
28 | /**
29 | * Information for a model.
30 | * @param model_url: the huggingface link to download the model weights.
31 | * @param local_id: what we call the model.
32 | * @param model_lib_url: link to the model library (wasm file) the model uses.
33 | * @param vram_required_MB: amount of vram in MB required to run the model (can use
34 | * `utils/vram_requirements` to calculate).
35 | * @param low_resource_required: whether the model can run on limited devices (e.g. Android phone).
36 | * @param required_features: feature needed to run this model (e.g. shader-f16).
37 | */
38 | export interface ModelRecord {
39 | model_url: string;
40 | local_id: string;
41 | model_lib_url: string;
42 | vram_required_MB?: number;
43 | low_resource_required?: boolean;
44 | required_features?: Array;
45 | }
46 | /**
47 | * Extra configuration that can be
48 | * passed to the load.
49 | *
50 | * @param model_list: models to be used.
51 | */
52 | export interface AppConfig {
53 | model_list: Array;
54 | use_cache?: boolean;
55 | }
56 | /**
57 | * Default models and model library mapping to be used if unspecified.
58 | */
59 | export declare const prebuiltAppConfig: AppConfig;
60 | //# sourceMappingURL=config.d.ts.map
--------------------------------------------------------------------------------
/examples/simple-chat/README.md:
--------------------------------------------------------------------------------
1 | # SimpleChat
2 |
3 | This folder provides a complete implementation of a simple
4 | chat app based on WebLLM. To try it out, you can do the following steps
5 | under this folder
6 |
7 | ```bash
8 | npm install
9 | npm start
10 | ```
11 |
12 | Note if you would like to hack WebLLM core package.
13 | You can change web-llm dependencies as `"file:../.."`, and follow the build from source
14 | instruction in the project to build webllm locally. This option is only recommended
15 | if you would like to hack WebLLM core package.
16 |
17 | If you are using the Local Server option, first start the local server using the steps outlined [here](https://mlc.ai/mlc-llm/docs/deploy/rest.html).
18 |
19 | Due to the differences in command-line tools between Unix/Linux and Windows systems, special adaptation is necessary for Windows. Unix/Linux systems natively support commands like `cp` for file operations, which are not directly available in Windows. To ensure cross-platform compatibility, we use a Node.js script for file copying in Windows.
20 |
21 | ### Steps for Windows Users
22 |
23 | 1. **Create a Node.js Script File**:
24 | - In the `examples\simple-chat` directory, create a file named `copy-config.js`.
25 | - Add the following code to handle file copying:
26 | ```javascript
27 | const fs = require('fs');
28 | // Copy file
29 | fs.copyFileSync('src/gh-config.js', 'src/app-config.js');
30 | ```
31 |
32 | 2. **Modify `package.json`**:
33 | - In the `scripts` section of your `package.json`, replace Unix-style `cp` commands with our new Node.js script. For example:
34 | ```json
35 | "scripts": {
36 | "start": "node copy-config.js && parcel src/llm_chat.html --port 8888",
37 | "mlc-local": "node copy-config.js && parcel src/llm_chat.html --port 8888",
38 | "build": "node copy-config.js && parcel build src/llm_chat.html --dist-dir lib --no-content-hash"
39 | },
40 | ```
41 |
42 | 3. **Run the Application**:
43 | - Save your changes and run `npm start` in CMD or PowerShell to start the application.
44 |
--------------------------------------------------------------------------------
/web/src/support.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 |
20 | /**
21 | * Convert string to Uint8array.
22 | * @param str The string.
23 | * @returns The corresponding Uint8Array.
24 | */
25 | export function StringToUint8Array(str: string): Uint8Array {
26 | const arr = new Uint8Array(str.length + 1);
27 | for (let i = 0; i < str.length; ++i) {
28 | arr[i] = str.charCodeAt(i);
29 | }
30 | arr[str.length] = 0;
31 | return arr;
32 | }
33 |
34 | /**
35 | * Convert Uint8array to string.
36 | * @param array The array.
37 | * @returns The corresponding string.
38 | */
39 | export function Uint8ArrayToString(arr: Uint8Array): string {
40 | const ret = [];
41 | for (const ch of arr) {
42 | ret.push(String.fromCharCode(ch));
43 | }
44 | return ret.join("");
45 | }
46 |
47 | /**
48 | * Internal assert helper
49 | * @param condition The condition to fail.
50 | * @param msg The message.
51 | */
52 | export function assert(condition: boolean, msg?: string): asserts condition {
53 | if (!condition) {
54 | throw new Error("AssertError:" + (msg || ""));
55 | }
56 | }
57 |
58 | /**
59 | * Get the path to the wasm library in nodejs.
60 | * @return The wasm path.
61 | */
62 | export function wasmPath(): string {
63 | return __dirname + "/wasm";
64 | }
65 |
--------------------------------------------------------------------------------
/web/src/compact.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 | /** NodeJS and Web compact layer */
20 | import { LibraryProvider } from "./types";
21 | import EmccWASI from "./tvmjs_runtime_wasi";
22 |
23 | /**
24 | * Get performance measurement.
25 | */
26 | // export function getPerformance(): Performance {
27 | // if (typeof performance === "undefined") {
28 | // // eslint-disable-next-line @typescript-eslint/no-var-requires
29 | // const performanceNode = require("perf_hooks");
30 | // return performanceNode.performance as Performance;
31 | // } else {
32 | // return performance as Performance;
33 | // }
34 | // }
35 |
36 | /**
37 | * Create a new websocket for a given URL
38 | * @param url The url.
39 | */
40 | export function createWebSocket(url: string): WebSocket {
41 | if (typeof WebSocket === "undefined") {
42 | // eslint-disable-next-line @typescript-eslint/no-var-requires
43 | const WebSocket = require("ws");
44 | return new WebSocket(url);
45 | } else {
46 | return new (WebSocket as any)(url);
47 | }
48 | }
49 |
50 | /**
51 | * Create a WASI based on current environment.
52 | *
53 | * @return A wasi that can run on broswer or local.
54 | */
55 | export function createPolyfillWASI(): LibraryProvider {
56 | return new EmccWASI();
57 | }
58 |
--------------------------------------------------------------------------------
/web/tests/node/test_object.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 | /* eslint-disable no-undef */
20 | const path = require("path");
21 | const fs = require("fs");
22 | const assert = require("assert");
23 | const tvmjs = require("../../dist/tvmjs.bundle")
24 |
25 | const wasmPath = tvmjs.wasmPath();
26 | const wasmSource = fs.readFileSync(path.join(wasmPath, "tvmjs_runtime.wasm"));
27 |
28 | let tvm = new tvmjs.Instance(
29 | new WebAssembly.Module(wasmSource),
30 | tvmjs.createPolyfillWASI());
31 |
32 | test("object", () => {
33 | tvm.withNewScope(() => {
34 | let data = [1, 2, 3, 4, 5, 6];
35 | let a = tvm.empty([2, 3], "float32").copyFrom(data);
36 |
37 | let t = tvm.makeTVMArray([]);
38 | let b = tvm.makeTVMArray([a, t]);
39 | // assert b instanceof tvmjs.TVMArray
40 | assert(b instanceof tvmjs.TVMArray);
41 | assert(b.size() == 2);
42 |
43 | let t1 = b.get(1);
44 | assert(t1.getHandle() == t.getHandle());
45 |
46 | let s0 = tvm.makeString("hello world");
47 | assert(s0.toString() == "hello world");
48 | s0.dispose();
49 |
50 | let ret_string = tvm.getGlobalFunc("testing.ret_string");
51 | let s1 = ret_string("hello");
52 | assert(s1.toString() == "hello");
53 | ret_string.dispose();
54 | s1.dispose();
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/web/tests/node/test_ndarray.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 | /* eslint-disable no-undef */
20 | const path = require("path");
21 | const fs = require("fs");
22 | const assert = require("assert");
23 | const tvmjs = require("../../dist/tvmjs.bundle")
24 |
25 | const wasmPath = tvmjs.wasmPath();
26 | const wasmSource = fs.readFileSync(path.join(wasmPath, "tvmjs_runtime.wasm"));
27 |
28 | let tvm = new tvmjs.Instance(
29 | new WebAssembly.Module(wasmSource),
30 | tvmjs.createPolyfillWASI()
31 | );
32 |
33 | // Basic fields.
34 | assert(tvm.listGlobalFuncNames() !== undefined);
35 |
36 | // Test ndarray
37 | function testArrayCopy(dtype, arrayType) {
38 | let data = [1, 2, 3, 4, 5, 6];
39 | let a = tvm.empty([2, 3], dtype).copyFrom(data);
40 |
41 | assert(a.device.toString() == "cpu(0)");
42 | assert(a.shape[0] == 2 && a.shape[1] == 3);
43 |
44 | let ret = a.toArray();
45 | assert(ret instanceof arrayType);
46 | assert(ret.toString() == arrayType.from(data).toString());
47 | }
48 |
49 | test("array copy", () => {
50 | tvm.withNewScope(() => {
51 | testArrayCopy("float32", Float32Array);
52 | testArrayCopy("int", Int32Array);
53 | testArrayCopy("int8", Int8Array);
54 | testArrayCopy("uint8", Uint8Array);
55 | testArrayCopy("float64", Float64Array);
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/web/src/types.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 | /** Common type definitions. */
20 |
21 | /**
22 | * Library interface provider that can provide
23 | * syslibs(e.g. libs provided by WASI and beyond) for the Wasm runtime.
24 | *
25 | * It can be viewed as a generalization of imports used in WebAssembly instance creation.
26 | *
27 | * The {@link LibraryProvider.start} callback will be called
28 | * to allow the library provider to initialize related resources during startup time.
29 | *
30 | * We can use Emscripten generated js Module as a { wasmLibraryProvider: LibraryProvider }.
31 | */
32 | export interface LibraryProvider {
33 | /** The imports that can be passed to WebAssembly instance creation. */
34 | imports: Record;
35 | /**
36 | * Callback function to notify the provider the created instance.
37 | * @param inst The created instance.
38 | */
39 | start: (inst: WebAssembly.Instance) => void;
40 | }
41 |
42 | /**
43 | * Disposable classes that contains resources (WasmMemory, GPU buffer)
44 | * which needs to be explicitly disposed.
45 | */
46 | export interface Disposable {
47 | /**
48 | * Dispose the internal resource
49 | * This function can be called multiple times,
50 | * only the first call will take effect.
51 | */
52 | dispose: () => void;
53 | }
54 |
--------------------------------------------------------------------------------
/web/Makefile:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 |
18 | TVM_ROOT=$(shell cd ..; pwd)
19 |
20 | INCLUDE_FLAGS = -I$(TVM_ROOT) -I$(TVM_ROOT)/include\
21 | -I$(TVM_ROOT)/3rdparty/dlpack/include -I$(TVM_ROOT)/3rdparty/dmlc-core/include\
22 | -I$(TVM_ROOT)/3rdparty/compiler-rt -I$(TVM_ROOT)/3rdparty/picojson
23 |
24 | .PHONY: clean all rmtypedep preparetest
25 |
26 | all: dist/wasm/tvmjs_runtime.wasm dist/wasm/tvmjs_runtime.wasi.js src/tvmjs_runtime_wasi.js
27 |
28 | EMCC = emcc
29 |
30 | EMCC_CFLAGS = $(INCLUDE_FLAGS) -O3 -std=c++17 -Wno-ignored-attributes -fwasm-exceptions
31 |
32 | EMCC_LDFLAGS = --no-entry -s WASM_BIGINT=1 -s ALLOW_MEMORY_GROWTH=1 -s STANDALONE_WASM=1\
33 | -s ERROR_ON_UNDEFINED_SYMBOLS=0 --pre-js emcc/preload.js
34 |
35 | dist/wasm/%.bc: emcc/%.cc
36 | @mkdir -p $(@D)
37 | $(EMCC) $(EMCC_CFLAGS) -c -MM -MT dist/wasm/$*.bc $< >dist/wasm/$*.d
38 | $(EMCC) $(EMCC_CFLAGS) -emit-llvm -c -o dist/wasm/$*.bc $<
39 |
40 |
41 | dist/wasm/tvmjs_runtime.wasm: dist/wasm/wasm_runtime.bc dist/wasm/tvmjs_support.bc dist/wasm/webgpu_runtime.bc
42 | @mkdir -p $(@D)
43 | $(EMCC) $(EMCC_CFLAGS) -o dist/wasm/tvmjs_runtime.js $+ $(EMCC_LDFLAGS)
44 |
45 | dist/wasm/tvmjs_runtime.wasi.js: dist/wasm/tvmjs_runtime.wasm emcc/decorate_as_wasi.py
46 | python3 emcc/decorate_as_wasi.py dist/wasm/tvmjs_runtime.js $@ cjs
47 |
48 | src/tvmjs_runtime_wasi.js: dist/wasm/tvmjs_runtime.wasm emcc/decorate_as_wasi.py
49 | python3 emcc/decorate_as_wasi.py dist/wasm/tvmjs_runtime.js $@ es
50 |
51 | clean:
52 | @rm -rf dist/wasm lib src/tvmjs_runtime_wasi.js
53 |
54 | -include dist/wasm/*.d
55 |
--------------------------------------------------------------------------------
/examples/get-started/src/get_started.ts:
--------------------------------------------------------------------------------
1 | import * as webllm from "@mlc-ai/web-llm";
2 |
3 | function setLabel(id: string, text: string) {
4 | const label = document.getElementById(id);
5 | if (label == null) {
6 | throw Error("Cannot find label " + id);
7 | }
8 | label.innerText = text;
9 | }
10 |
11 | async function main() {
12 | const chat = new webllm.ChatModule();
13 |
14 | chat.setInitProgressCallback((report: webllm.InitProgressReport) => {
15 | setLabel("init-label", report.text);
16 | });
17 |
18 | // Option 1: Specify appConfig to decide what models to include
19 | const myAppConfig: AppConfig = {
20 | model_list: [
21 | {
22 | "model_url": "https://huggingface.co/mlc-ai/Llama-2-7b-chat-hf-q4f32_1-MLC/resolve/main/",
23 | "local_id": "Llama-2-7b-chat-hf-q4f32_1",
24 | "model_lib_url": "https://raw.githubusercontent.com/mlc-ai/binary-mlc-llm-libs/main/Llama-2-7b-chat-hf/Llama-2-7b-chat-hf-q4f32_1-ctx4k_cs1k-webgpu.wasm",
25 | },
26 | {
27 | "model_url": "https://huggingface.co/mlc-ai/Mistral-7B-Instruct-v0.2-q4f16_1-MLC/resolve/main/",
28 | "local_id": "Mistral-7B-Instruct-v0.2-q4f16_1",
29 | "model_lib_url": "https://raw.githubusercontent.com/mlc-ai/binary-mlc-llm-libs/main/Mistral-7B-Instruct-v0.2/Mistral-7B-Instruct-v0.2-q4f16_1-sw4k_cs1k-webgpu.wasm",
30 | "required_features": ["shader-f16"],
31 | },
32 | // Add your own models here...
33 | ]
34 | }
35 | const selectedModel = "Llama-2-7b-chat-hf-q4f32_1"
36 | // const selectedModel = "Mistral-7B-Instruct-v0.1-q4f16_1"
37 | await chat.reload(selectedModel, undefined, myAppConfig);
38 |
39 | // Option 2: If we do not specify appConfig, we use `prebuiltAppConfig` defined in `config.ts`
40 | // await chat.reload("Llama-2-7b-chat-hf-q4f32_1");
41 |
42 | const generateProgressCallback = (_step: number, message: string) => {
43 | setLabel("generate-label", message);
44 | };
45 |
46 | const prompt0 = "What is the capital of Canada?";
47 | setLabel("prompt-label", prompt0);
48 | const reply0 = await chat.generate(prompt0, generateProgressCallback);
49 | console.log(reply0);
50 |
51 | const prompt1 = "Can you write a poem about it?";
52 | setLabel("prompt-label", prompt1);
53 | const reply1 = await chat.generate(prompt1, generateProgressCallback);
54 | console.log(reply1);
55 |
56 | console.log(await chat.runtimeStatsText());
57 | }
58 |
59 | main();
60 |
--------------------------------------------------------------------------------
/web/tests/node/test_relax_vm.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 | /* eslint-disable no-undef */
20 | // Load Emscripten Module, need to change path to root/lib
21 | const path = require("path");
22 | const fs = require("fs");
23 | const assert = require("assert");
24 | const tvmjs = require("../../dist");
25 |
26 | const wasmPath = tvmjs.wasmPath();
27 | const wasmSource = fs.readFileSync(path.join(wasmPath, "test_relax.wasm"));
28 |
29 | const tvm = new tvmjs.Instance(
30 | new WebAssembly.Module(wasmSource),
31 | tvmjs.createPolyfillWASI()
32 | );
33 |
34 | function randomArray(length, max) {
35 | return Array.apply(null, Array(length)).map(function () {
36 | return Math.random() * max;
37 | });
38 | }
39 |
40 | test("add one", () => {
41 | tvm.beginScope();
42 | // Load system library
43 | const vm = tvm.createVirtualMachine(tvm.cpu());
44 | // grab pre-loaded function
45 | const fadd = vm.getFunction("main");
46 |
47 | assert(tvm.isPackedFunc(fadd));
48 | const n = 124;
49 | const A = tvm.empty(n).copyFrom(randomArray(n, 1));
50 | const B = tvm.empty(n).copyFrom(randomArray(n, 1));
51 |
52 | // call the function.
53 | const C = fadd(A, B);
54 | const AA = A.toArray(); // retrieve values in js array
55 | const BB = B.toArray(); // retrieve values in js array
56 | const CC = C.toArray(); // retrieve values in js array
57 | // verify
58 | for (var i = 0; i < BB.length; ++i) {
59 | assert(Math.abs(CC[i] - (AA[i] + BB[i])) < 1e-5);
60 | }
61 | tvm.endScope();
62 | // assert auto release scope behavior
63 | assert(vm.mod.getHandle(false) == 0);
64 | assert(fadd._tvmPackedCell.getHandle(false) == 0);
65 | });
66 |
--------------------------------------------------------------------------------
/lib/web_worker.d.ts:
--------------------------------------------------------------------------------
1 | import { AppConfig } from "./config";
2 | import { ChatInterface, ChatOptions, GenerateProgressCallback, InitProgressCallback, InitProgressReport } from "./types";
3 | interface ReloadParams {
4 | localIdOrUrl: string;
5 | chatOpts?: ChatOptions;
6 | appConfig?: AppConfig;
7 | }
8 | interface GenerateParams {
9 | input: string;
10 | streamInterval?: number;
11 | }
12 | interface GenerateProgressCallbackParams {
13 | step: number;
14 | currentMessage: string;
15 | }
16 | type MessageContent = GenerateProgressCallbackParams | ReloadParams | GenerateParams | InitProgressReport | string | null | number;
17 | /**
18 | * Worker handler that can be used in a WebWorker
19 | *
20 | * @example
21 | *
22 | * // setup a chat worker handler that routes
23 | * // requests to the chat
24 | * const chat = new ChatModule();
25 | * cont handler = new ChatWorkerHandler(chat);
26 | * onmessage = handler.onmessage;
27 | */
28 | export declare class ChatWorkerHandler {
29 | private chat;
30 | constructor(chat: ChatInterface);
31 | handleTask(uuid: string, task: () => Promise): Promise;
32 | onmessage(event: MessageEvent): void;
33 | }
34 | interface ChatWorker {
35 | onmessage: any;
36 | postMessage: (message: any) => void;
37 | }
38 | /**
39 | * A client of chat worker that exposes the chat interface
40 | *
41 | * @example
42 | *
43 | * const chat = new webllm.ChatWorkerClient(new Worker(
44 | * new URL('./worker.ts', import.meta.url),
45 | * {type: 'module'}
46 | * ));
47 | */
48 | export declare class ChatWorkerClient implements ChatInterface {
49 | worker: ChatWorker;
50 | private initProgressCallback?;
51 | private generateCallbackRegistry;
52 | private pendingPromise;
53 | constructor(worker: any);
54 | setInitProgressCallback(initProgressCallback: InitProgressCallback): void;
55 | private getPromise;
56 | reload(localIdOrUrl: string, chatOpts?: ChatOptions, appConfig?: AppConfig): Promise;
57 | getMaxStorageBufferBindingSize(): Promise;
58 | getGPUVendor(): Promise;
59 | generate(input: string, progressCallback?: GenerateProgressCallback, streamInterval?: number): Promise;
60 | runtimeStatsText(): Promise;
61 | interruptGenerate(): void;
62 | unload(): Promise;
63 | resetChat(): Promise;
64 | onmessage(event: any): void;
65 | }
66 | export {};
67 | //# sourceMappingURL=web_worker.d.ts.map
--------------------------------------------------------------------------------
/web/tests/node/test_module_load.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 | /* eslint-disable no-undef */
20 | // Load Emscripten Module, need to change path to root/lib
21 | const path = require("path");
22 | const fs = require("fs");
23 | const assert = require("assert");
24 | const tvmjs = require("../../dist");
25 |
26 | const wasmPath = tvmjs.wasmPath();
27 | const wasmSource = fs.readFileSync(path.join(wasmPath, "test_addone.wasm"));
28 |
29 | const tvm = new tvmjs.Instance(
30 | new WebAssembly.Module(wasmSource),
31 | tvmjs.createPolyfillWASI()
32 | );
33 |
34 |
35 | function randomArray(length, max) {
36 | return Array.apply(null, Array(length)).map(function () {
37 | return Math.random() * max;
38 | });
39 | }
40 |
41 | test("add one", () => {
42 | tvm.beginScope();
43 | // Load system library
44 | const sysLib = tvm.systemLib();
45 | // grab pre-loaded function
46 | const faddOne = sysLib.getFunction("add_one");
47 | tvm.detachFromCurrentScope(faddOne);
48 |
49 | assert(tvm.isPackedFunc(faddOne));
50 | const n = 124;
51 | const A = tvm.empty(n).copyFrom(randomArray(n, 1));
52 | const B = tvm.empty(n);
53 | // call the function.
54 | faddOne(A, B);
55 | const AA = A.toArray(); // retrieve values in js array
56 | const BB = B.toArray(); // retrieve values in js array
57 | // verify
58 | for (var i = 0; i < BB.length; ++i) {
59 | assert(Math.abs(BB[i] - (AA[i] + 1)) < 1e-5);
60 | }
61 | tvm.endScope();
62 |
63 | // assert auto release scope behavior
64 | assert(sysLib.getHandle(false) == 0);
65 | // fadd is not released because it is detached
66 | assert(faddOne._tvmPackedCell.handle != 0);
67 | faddOne.dispose();
68 | assert(A.getHandle(false) == 0);
69 | assert(B.getHandle(false) == 0);
70 | });
71 |
--------------------------------------------------------------------------------
/lib/chat_module.d.ts:
--------------------------------------------------------------------------------
1 | import { AppConfig } from "./config";
2 | import { InitProgressCallback, ChatInterface, ChatOptions, GenerateProgressCallback } from "./types";
3 | /**
4 | * This is the main interface to the chat module.
5 | */
6 | export declare class ChatModule implements ChatInterface {
7 | private logger;
8 | private pipeline?;
9 | private initProgressCallback?;
10 | private interruptSignal;
11 | private deviceLostIsError;
12 | setInitProgressCallback(initProgressCallback: InitProgressCallback): void;
13 | reload(localId: string, chatOpts?: ChatOptions, appConfig?: AppConfig, files?: File[] | FileList): Promise;
14 | generate(input: string, progressCallback?: GenerateProgressCallback, streamInterval?: number): Promise;
15 | interruptGenerate(): Promise;
16 | runtimeStatsText(): Promise;
17 | resetChat(): Promise;
18 | unload(): Promise;
19 | getMaxStorageBufferBindingSize(): Promise;
20 | getGPUVendor(): Promise;
21 | /**
22 | * @returns Whether the generation stopped.
23 | */
24 | stopped(): boolean;
25 | /**
26 | * Get the current generated response.
27 | *
28 | * @returns The current output message.
29 | */
30 | getMessage(): string;
31 | /**
32 | * Run a prefill step with a given input.
33 | * @param input The input prompt.
34 | */
35 | prefill(input: string): Promise;
36 | /**
37 | * Run a decode step to decode the next token.
38 | */
39 | decode(): Promise;
40 | private getPipeline;
41 | private asyncLoadTokenizer;
42 | }
43 | /**
44 | * This is the interface to the chat module that connects to the REST API.
45 | */
46 | export declare class ChatRestModule implements ChatInterface {
47 | private logger;
48 | private initProgressCallback?;
49 | setInitProgressCallback(initProgressCallback: InitProgressCallback): void;
50 | reload(localId: string, chatOpts?: ChatOptions, appConfig?: AppConfig): Promise;
51 | getMaxStorageBufferBindingSize(): Promise;
52 | getGPUVendor(): Promise;
53 | unload(): Promise;
54 | interruptGenerate(): Promise;
55 | generate(input: string, progressCallback?: GenerateProgressCallback, streamInterval?: number): Promise;
56 | runtimeStatsText(): Promise;
57 | resetChat(): Promise;
58 | }
59 | export declare function hasModelInCache(localId: string, appConfig?: AppConfig): Promise;
60 | //# sourceMappingURL=chat_module.d.ts.map
--------------------------------------------------------------------------------
/web/tests/python/prepare_test_libs.py:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 | # Prepare test library for standalone wasm runtime test.
18 |
19 | import tvm
20 | from tvm import te
21 | from tvm.contrib import tvmjs
22 | from tvm.relay.backend import Runtime
23 | from tvm import relax
24 | from tvm.script import relax as R
25 | import os
26 |
27 |
28 | def prepare_relax_lib(base_path):
29 | pipeline = relax.get_pipeline()
30 |
31 | @tvm.script.ir_module
32 | class Mod:
33 | @R.function
34 | def main(x: R.Tensor(["n"], "float32"), y: R.Tensor(["n"], "float32")):
35 | lv0 = R.add(x, y)
36 | return lv0
37 |
38 | target = tvm.target.Target("llvm -mtriple=wasm32-unknown-unknown-wasm")
39 |
40 | mod = pipeline(Mod)
41 | ex = relax.build(mod, target)
42 | wasm_path = os.path.join(base_path, "test_relax.wasm")
43 | ex.export_library(wasm_path, fcompile=tvmjs.create_tvmjs_wasm)
44 |
45 |
46 | def prepare_tir_lib(base_path):
47 | runtime = Runtime("cpp", {"system-lib": True})
48 | target = "llvm -mtriple=wasm32-unknown-unknown-wasm"
49 | if not tvm.runtime.enabled(target):
50 | raise RuntimeError("Target %s is not enbaled" % target)
51 | n = te.var("n")
52 | A = te.placeholder((n,), name="A")
53 | B = te.compute(A.shape, lambda *i: A(*i) + 1.0, name="B")
54 | s = te.create_schedule(B.op)
55 | fadd = tvm.build(s, [A, B], target, runtime=runtime, name="add_one")
56 |
57 | wasm_path = os.path.join(base_path, "test_addone.wasm")
58 | fadd.export_library(wasm_path, fcompile=tvmjs.create_tvmjs_wasm)
59 |
60 |
61 | if __name__ == "__main__":
62 | curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__)))
63 | base_path = os.path.join(curr_path, "../../dist/wasm")
64 | prepare_tir_lib(base_path)
65 | prepare_relax_lib(base_path)
66 |
--------------------------------------------------------------------------------
/src/config.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Conversation template config
3 | */
4 | export interface ConvTemplateConfig {
5 | system: string;
6 | roles: Array;
7 | seps: Array;
8 | separator_style: string;
9 | offset: number;
10 | stop_str: string;
11 | add_bos: boolean;
12 | stop_tokens: Array;
13 | }
14 |
15 | /**
16 | * Config of one chat model
17 | */
18 | export interface ChatConfig {
19 | tokenizer_files: Array;
20 | conv_config?: Partial;
21 | conv_template: string;
22 | // [userMessage, aiMessage][]
23 | conversation_history?: Array<[string, string]>;
24 | // additional metadata
25 | mean_gen_len: number;
26 | shift_fill_factor: number;
27 | repetition_penalty: number;
28 | top_p: number;
29 | temperature: number;
30 | }
31 |
32 | /**
33 | * Information for a model.
34 | * @param model_url: the huggingface link to download the model weights.
35 | * @param local_id: what we call the model.
36 | * @param model_lib_url: link to the model library (wasm file) the model uses.
37 | * @param vram_required_MB: amount of vram in MB required to run the model (can use
38 | * `utils/vram_requirements` to calculate).
39 | * @param low_resource_required: whether the model can run on limited devices (e.g. Android phone).
40 | * @param required_features: feature needed to run this model (e.g. shader-f16).
41 | */
42 | export interface ModelRecord {
43 | model_url: string;
44 | local_id: string;
45 | model_lib_url: string;
46 | vram_required_MB?: number;
47 | low_resource_required?: boolean;
48 | required_features?: Array;
49 | }
50 |
51 | /**
52 | * Extra configuration that can be
53 | * passed to the load.
54 | *
55 | * @param model_list: models to be used.
56 | */
57 | export interface AppConfig {
58 | model_list: Array;
59 | use_cache?: boolean;
60 | }
61 |
62 | /**
63 | * Default models and model library mapping to be used if unspecified.
64 | */
65 | export const prebuiltAppConfig: AppConfig = {
66 | model_list: [
67 | {
68 | "model_url": "https://huggingface.co/mlc-ai/RedPajama-INCITE-Chat-3B-v1-q4f32_1-MLC/resolve/main/",
69 | "model_lib_url": "https://raw.githubusercontent.com/mlc-ai/binary-mlc-llm-libs/main/RedPajama-INCITE-Chat-3B-v1/RedPajama-INCITE-Chat-3B-v1-q4f32_1-ctx2k-webgpu.wasm",
70 | "local_id": "RedPajama-INCITE-Chat-3B-v1-q4f32_1",
71 | },
72 | {
73 | "model_url": "https://huggingface.co/mlc-ai/Llama-2-7b-chat-hf-q4f32_1-MLC/resolve/main/",
74 | "model_lib_url": "https://raw.githubusercontent.com/mlc-ai/binary-mlc-llm-libs/main/Llama-2-7b-chat-hf/Llama-2-7b-chat-hf-q4f32_1-ctx4k_cs1k-webgpu.wasm",
75 | "local_id": "Llama-2-7b-chat-hf-q4f32_1"
76 | }
77 | ]
78 | }
79 |
--------------------------------------------------------------------------------
/lib/llm_chat.d.ts:
--------------------------------------------------------------------------------
1 | import * as tvmjs from "tvmjs";
2 | import { Tokenizer } from "@mlc-ai/web-tokenizers";
3 | import { ChatConfig } from "./config";
4 | export declare class LLMChatPipeline {
5 | private config;
6 | private tokenizer;
7 | private tvm;
8 | private device;
9 | private vm;
10 | private prefill;
11 | private decoding;
12 | private fclearKVCaches;
13 | private params;
14 | private kvCache;
15 | private logitsOnCPU?;
16 | private filledKVCacheLength;
17 | private bosTokenId;
18 | private maxWindowLength;
19 | private slidingWindowSize;
20 | private attentionSinkSize;
21 | private prefillChunkSize;
22 | private resetStatsPerPrefill;
23 | private stopStr;
24 | private stopTokens;
25 | private outputMessage;
26 | private outputIds;
27 | private stopTriggered;
28 | private appearedTokens;
29 | private conversation;
30 | private sinkTriggered;
31 | private slidingWindowCacheOffset;
32 | private decodingTotalTime;
33 | private decodingTotalTokens;
34 | private prefillTotalTime;
35 | private prefillTotalTokens;
36 | private logger;
37 | constructor(tvm: tvmjs.Instance, tokenizer: Tokenizer, config: ChatConfig);
38 | dispose(): void;
39 | /**
40 | * Get the current message.
41 | */
42 | getMessage(): string;
43 | /**
44 | * Reset the runtime statistics
45 | */
46 | resetRuntimeStats(): void;
47 | /**
48 | * Reset the chat history
49 | */
50 | resetChat(): void;
51 | /**
52 | * @returns Whether stop is triggered.
53 | */
54 | stopped(): boolean;
55 | /**
56 | * @returns Runtime stats information.
57 | */
58 | runtimeStatsText(): string;
59 | asyncLoadWebGPUPipelines(): Promise;
60 | /**
61 | * Generate the first token given input prompt
62 | */
63 | prefillStep(inp: string): Promise;
64 | /**
65 | * Add and process existing messages for future input context
66 | */
67 | prefillStepConversation(conversation_history: Required['conversation_history']): Promise;
68 | private prefillCleanup;
69 | private processTokens;
70 | decodeStep(): Promise;
71 | /**
72 | * Manually trigger stop if it is not stopped.
73 | */
74 | triggerStop(): void;
75 | /**
76 | * Add a generated token and check for stop.
77 | *
78 | * @param nextToken The next token.
79 | */
80 | private processNextToken;
81 | private forward;
82 | private updateLogitsOnCPU;
83 | private sampleTokenFromLogits;
84 | private getInputTokens;
85 | evaluate(): Promise;
86 | }
87 | //# sourceMappingURL=llm_chat.d.ts.map
--------------------------------------------------------------------------------
/web/tests/python/relax_rpc_test.py:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 | """Test relax vm through rpc."""
18 |
19 | import tvm
20 | import numpy as np
21 | from tvm import rpc, relax
22 | from tvm.contrib import utils, tvmjs
23 | from tvm.script import relax as R
24 |
25 | proxy_host = "127.0.0.1"
26 | proxy_port = 9090
27 |
28 |
29 | def get_model():
30 | pipeline = relax.get_pipeline()
31 |
32 | @tvm.script.ir_module
33 | class Mod:
34 | @R.function
35 | def main(x: R.Tensor([1024], "float32"), y: R.Tensor([1024], "float32")):
36 | lv0 = R.add(x, y)
37 | return lv0
38 |
39 | mod = pipeline(Mod)
40 | sch = tvm.tir.Schedule(mod)
41 | # manually transform loop
42 | sch.work_on("add")
43 | (i,) = sch.get_loops(block=sch.get_block("T_add"))
44 | i0, i1 = sch.split(i, [None, 128])
45 | sch.bind(i0, "blockIdx.x")
46 | sch.bind(i1, "threadIdx.x")
47 | return sch.mod
48 |
49 |
50 | def test_rpc():
51 | if not tvm.runtime.enabled("rpc"):
52 | return
53 | n = 1024
54 | dtype = "float32"
55 | temp = utils.tempdir()
56 | wasm_path = temp.relpath("relax.wasm")
57 | target = tvm.target.Target("webgpu", host="llvm -mtriple=wasm32-unknown-unknown-wasm")
58 |
59 | mod = get_model()
60 | ex = relax.build(mod, target)
61 | ex.export_library(wasm_path, fcompile=tvmjs.create_tvmjs_wasm)
62 | wasm_binary = open(wasm_path, "rb").read()
63 |
64 | remote = rpc.connect(
65 | proxy_host,
66 | proxy_port,
67 | key="wasm",
68 | session_constructor_args=["rpc.WasmSession", wasm_binary],
69 | )
70 |
71 | def check(remote):
72 | dev = remote.webgpu(0)
73 | # invoke the function
74 | vm = relax.VirtualMachine(remote.system_lib(), device=dev)
75 | adata = np.random.uniform(size=n).astype(dtype)
76 | bdata = np.random.uniform(size=n).astype(dtype)
77 | a = tvm.nd.array(adata, dev)
78 | b = tvm.nd.array(bdata, dev)
79 | vm.set_input("main", a, b)
80 | vm.invoke_stateful("main")
81 | c = vm.get_outputs("main")
82 | np.testing.assert_equal(c.numpy(), a.numpy() + b.numpy())
83 |
84 | check(remote)
85 |
86 |
87 | test_rpc()
88 |
--------------------------------------------------------------------------------
/web/tests/python/webgpu_rpc_test.py:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 | """Simple testcode to test Javascript RPC
18 |
19 | To use it, start a rpc proxy with "python -m tvm.exec.rpc_proxy".
20 | Connect javascript end to the websocket port and connect to the RPC.
21 | """
22 |
23 | import tvm
24 | from tvm import te
25 | from tvm import rpc
26 | from tvm.contrib import utils, tvmjs
27 | from tvm.relay.backend import Runtime
28 | import numpy as np
29 |
30 | proxy_host = "127.0.0.1"
31 | proxy_port = 9090
32 |
33 |
34 | def test_rpc():
35 | if not tvm.runtime.enabled("rpc"):
36 | return
37 | # generate the wasm library
38 | target = tvm.target.Target("webgpu", host="llvm -mtriple=wasm32-unknown-unknown-wasm")
39 | runtime = Runtime("cpp", {"system-lib": True})
40 |
41 | n = te.var("n")
42 | A = te.placeholder((n,), name="A")
43 | B = te.compute(A.shape, lambda *i: te.log(te.abs(A(*i) + 1)), name="B")
44 | mod = tvm.IRModule.from_expr(te.create_prim_func([A, B]))
45 | sch = tvm.tir.Schedule(mod)
46 | (i,) = sch.get_loops(block=sch.get_block("B"))
47 | i0, i1 = sch.split(i, [None, 32])
48 | sch.bind(i0, "blockIdx.x")
49 | sch.bind(i1, "threadIdx.x")
50 |
51 | fadd = tvm.build(sch.mod, target=target, runtime=runtime)
52 | temp = utils.tempdir()
53 |
54 | wasm_path = temp.relpath("addone_gpu.wasm")
55 | fadd.export_library(wasm_path, fcompile=tvmjs.create_tvmjs_wasm)
56 |
57 | wasm_binary = open(wasm_path, "rb").read()
58 | remote = rpc.connect(
59 | proxy_host,
60 | proxy_port,
61 | key="wasm",
62 | session_constructor_args=["rpc.WasmSession", wasm_binary],
63 | )
64 |
65 | def check(remote, size):
66 | # basic function checks.
67 | dev = remote.webgpu(0)
68 | adata = np.random.uniform(size=size).astype(A.dtype)
69 | a = tvm.nd.array(adata, dev)
70 | b = tvm.nd.array(np.zeros(size, dtype=A.dtype), dev)
71 |
72 | np.testing.assert_equal(a.numpy(), adata)
73 | f1 = remote.system_lib()
74 | addone = f1.get_function("main")
75 | addone(a, b)
76 | np.testing.assert_allclose(b.numpy(), np.log(np.abs(a.numpy()) + 1), atol=1e-5, rtol=1e-5)
77 | print("Test pass..")
78 |
79 | check(remote, 71821 * 32)
80 |
81 |
82 | test_rpc()
83 |
--------------------------------------------------------------------------------
/examples/next-simple-chat/src/utils/chat_component.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { ChatModule } from "@mlc-ai/web-llm";
3 | import ChatUI from "~/utils/chat_ui";
4 |
5 | const ChatComponent = () => {
6 | const [messages, setMessages] = useState<{ kind: string; text: string }[]>(
7 | []
8 | );
9 | const [prompt, setPrompt] = useState("");
10 | const [runtimeStats, setRuntimeStats] = useState("");
11 | const [chat_ui] = useState(new ChatUI(new ChatModule()));
12 | const updateMessage = (kind: string, text: string, append: boolean) => {
13 | if (kind == "init") {
14 | text = "[System Initalize] " + text;
15 | }
16 | const msgCopy = [...messages];
17 | if (msgCopy.length == 0 || append) {
18 | setMessages([...msgCopy, { kind, text }]);
19 | } else {
20 | msgCopy[msgCopy.length - 1] = { kind, text };
21 | setMessages([...msgCopy]);
22 | }
23 | };
24 | return (
25 |