├── .gitmodules ├── 3rdparty └── .gitkeep ├── web ├── .eslintignore ├── .gitignore ├── typedoc.json ├── src │ ├── tvmjs_runtime_wasi.d.ts │ ├── index.ts │ ├── support.ts │ ├── compact.ts │ ├── types.ts │ ├── environment.ts │ └── ctypes.ts ├── tsconfig.json ├── .eslintrc.json ├── jest.config.js ├── apps │ ├── browser │ │ ├── rpc_plugin.html │ │ └── rpc_server.html │ └── node │ │ ├── wasi_rpc_server.js │ │ ├── wasi_example.js │ │ └── example.js ├── emcc │ ├── preload.js │ ├── decorate_as_wasi.py │ └── wasm_runtime.cc ├── rollup.config.mjs ├── package.json ├── tests │ ├── node │ │ ├── test_object.js │ │ ├── test_ndarray.js │ │ ├── test_relax_vm.js │ │ ├── test_module_load.js │ │ └── test_packed_func.js │ └── python │ │ ├── prepare_test_libs.py │ │ ├── relax_rpc_test.py │ │ ├── webgpu_rpc_test.py │ │ └── websock_rpc_test.py ├── Makefile └── README.md ├── examples ├── .gitignore ├── simple-chat │ ├── .gitignore │ ├── src │ │ ├── img │ │ │ ├── plane.png │ │ │ └── reset.png │ │ ├── worker.ts │ │ ├── llm_chat.html │ │ ├── mlc-local-config.js │ │ └── llm_chat.css │ ├── package.json │ └── README.md ├── next-simple-chat │ ├── .eslintrc.json │ ├── postcss.config.js │ ├── src │ │ ├── pages │ │ │ ├── _app.tsx │ │ │ ├── _document.tsx │ │ │ ├── api │ │ │ │ └── hello.ts │ │ │ └── index.tsx │ │ ├── utils │ │ │ ├── chat_component.tsx │ │ │ └── chat_ui.ts │ │ └── styles │ │ │ └── globals.css │ ├── README.md │ ├── .gitignore │ ├── tailwind.config.js │ ├── public │ │ ├── vercel.svg │ │ └── next.svg │ ├── tsconfig.json │ ├── next.config.js │ └── package.json ├── chrome-extension │ ├── src │ │ ├── icons │ │ │ ├── icon-128.png │ │ │ ├── icon-16.png │ │ │ ├── icon-32.png │ │ │ └── icon-64.png │ │ ├── content.js │ │ ├── manifest.json │ │ ├── manifest_v2.json │ │ ├── popup.html │ │ ├── background.ts │ │ ├── example.html │ │ ├── popup.css │ │ └── popup.ts │ ├── README.md │ └── package.json ├── web-worker │ ├── src │ │ ├── worker.ts │ │ ├── get_started.html │ │ └── main.ts │ ├── package.json │ └── README.md ├── get-started │ ├── README.md │ ├── src │ │ ├── get_started.html │ │ └── get_started.ts │ └── package.json ├── get-started-rest │ ├── src │ │ ├── get_started.html │ │ └── get_started.ts │ ├── package.json │ └── README.md └── README.md ├── .eslintignore ├── site ├── img │ ├── fig │ │ ├── demo.gif │ │ └── pitts.png │ └── logo │ │ ├── uw.jpg │ │ ├── sjtu.png │ │ ├── cmuscs.png │ │ ├── octoml.png │ │ └── catalyst.svg ├── .gitignore ├── _includes │ ├── head.html │ └── llm_chat.html ├── _config.yml └── index.md ├── utils └── vram_requirements │ ├── README.md │ ├── src │ ├── vram_requirements.html │ └── vram_requirements.ts │ └── package.json ├── scripts ├── local_deploy_site.sh ├── serve_mlc_llm_dist.sh ├── gh_deploy_site.sh └── prep_deps.sh ├── .eslintrc.cjs ├── lib ├── index.d.ts.map ├── index.d.ts ├── conversation.d.ts.map ├── config.d.ts.map ├── types.d.ts.map ├── conversation.d.ts ├── chat_module.d.ts.map ├── web_worker.d.ts.map ├── llm_chat.d.ts.map ├── config.d.ts ├── web_worker.d.ts ├── chat_module.d.ts ├── llm_chat.d.ts └── types.d.ts ├── tsconfig.json ├── cleanup-index-js.sh ├── rollup.config.js ├── src ├── index.ts ├── config.ts └── types.ts ├── notes.txt ├── package.json └── .gitignore /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /3rdparty/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | debug 3 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | -------------------------------------------------------------------------------- /examples/simple-chat/.gitignore: -------------------------------------------------------------------------------- 1 | src/app-config.js 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | debug 3 | lib 4 | build 5 | node_modules 6 | .eslintrc.cjs 7 | -------------------------------------------------------------------------------- /examples/next-simple-chat/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /site/img/fig/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OvidijusParsiunas/web-llm/HEAD/site/img/fig/demo.gif -------------------------------------------------------------------------------- /site/img/logo/uw.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OvidijusParsiunas/web-llm/HEAD/site/img/logo/uw.jpg -------------------------------------------------------------------------------- /site/img/fig/pitts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OvidijusParsiunas/web-llm/HEAD/site/img/fig/pitts.png -------------------------------------------------------------------------------- /site/img/logo/sjtu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OvidijusParsiunas/web-llm/HEAD/site/img/logo/sjtu.png -------------------------------------------------------------------------------- /site/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | llm-chat-config.json 3 | _includes/stable_diffusion.html 4 | _site 5 | llm_chat.* 6 | -------------------------------------------------------------------------------- /site/img/logo/cmuscs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OvidijusParsiunas/web-llm/HEAD/site/img/logo/cmuscs.png -------------------------------------------------------------------------------- /site/img/logo/octoml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OvidijusParsiunas/web-llm/HEAD/site/img/logo/octoml.png -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | *~ 3 | out 4 | node_modules 5 | build 6 | debug 7 | .ndarray_cache 8 | src/tvmjs_runtime_wasi.js 9 | -------------------------------------------------------------------------------- /examples/simple-chat/src/img/plane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OvidijusParsiunas/web-llm/HEAD/examples/simple-chat/src/img/plane.png -------------------------------------------------------------------------------- /examples/simple-chat/src/img/reset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OvidijusParsiunas/web-llm/HEAD/examples/simple-chat/src/img/reset.png -------------------------------------------------------------------------------- /examples/next-simple-chat/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/chrome-extension/src/icons/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OvidijusParsiunas/web-llm/HEAD/examples/chrome-extension/src/icons/icon-128.png -------------------------------------------------------------------------------- /examples/chrome-extension/src/icons/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OvidijusParsiunas/web-llm/HEAD/examples/chrome-extension/src/icons/icon-16.png -------------------------------------------------------------------------------- /examples/chrome-extension/src/icons/icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OvidijusParsiunas/web-llm/HEAD/examples/chrome-extension/src/icons/icon-32.png -------------------------------------------------------------------------------- /examples/chrome-extension/src/icons/icon-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OvidijusParsiunas/web-llm/HEAD/examples/chrome-extension/src/icons/icon-64.png -------------------------------------------------------------------------------- /utils/vram_requirements/README.md: -------------------------------------------------------------------------------- 1 | ### vRAM Requirements 2 | 3 | To check vRAM requirement for a model, add models to check in `gh-config.json`. 4 | 5 | Then run `npm install` followed by `npm start`. -------------------------------------------------------------------------------- /web/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "out": "dist/docs", 3 | "readme": "none", 4 | "excludeProtected": true, 5 | "excludePrivate": true, 6 | "includes": ["src"], 7 | "exclude": ["**/node_modules/**"] 8 | } 9 | -------------------------------------------------------------------------------- /examples/next-simple-chat/src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import '~/styles/globals.css' 2 | import type { AppProps } from 'next/app' 3 | 4 | export default function App({ Component, pageProps }: AppProps) { 5 | return 6 | } 7 | -------------------------------------------------------------------------------- /scripts/local_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 | 11 | cd site && jekyll serve --host localhost --port 8888 12 | -------------------------------------------------------------------------------- /web/src/tvmjs_runtime_wasi.d.ts: -------------------------------------------------------------------------------- 1 | import { LibraryProvider } from "./types"; 2 | 3 | export declare class EmccWASI implements LibraryProvider { 4 | imports: Record; 5 | start: (inst: WebAssembly.Instance) => void; 6 | } 7 | 8 | export default EmccWASI; 9 | -------------------------------------------------------------------------------- /examples/chrome-extension/src/content.js: -------------------------------------------------------------------------------- 1 | // Only the content script is able to access the DOM 2 | chrome.runtime.onConnect.addListener(function(port) { 3 | port.onMessage.addListener(function(msg) { 4 | port.postMessage({contents: document.body.innerHTML}); 5 | }); 6 | }); -------------------------------------------------------------------------------- /examples/web-worker/src/worker.ts: -------------------------------------------------------------------------------- 1 | import { ChatWorkerHandler, ChatModule } from "@mlc-ai/web-llm"; 2 | 3 | // Hookup a chat module to a worker handler 4 | const chat = new ChatModule(); 5 | const handler = new ChatWorkerHandler(chat); 6 | self.onmessage = (msg: MessageEvent) => { 7 | handler.onmessage(msg); 8 | }; 9 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], 3 | parser: '@typescript-eslint/parser', 4 | plugins: ['@typescript-eslint'], 5 | root: true, 6 | rules: { 7 | "@typescript-eslint/no-explicit-any": "off" 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /examples/simple-chat/src/worker.ts: -------------------------------------------------------------------------------- 1 | // Serve the chat workload through web worker 2 | import { ChatWorkerHandler, ChatModule } from "@mlc-ai/web-llm"; 3 | 4 | const chat = new ChatModule(); 5 | const handler = new ChatWorkerHandler(chat); 6 | self.onmessage = (msg: MessageEvent) => { 7 | handler.onmessage(msg); 8 | }; 9 | -------------------------------------------------------------------------------- /web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "outDir": "lib", 5 | "types": ["node", "@webgpu/types"], 6 | "rootDir": "src", 7 | "declaration": true, 8 | "sourceMap": true, 9 | "skipLibCheck": true, 10 | }, 11 | "include": ["src"], 12 | "exclude": ["**/node_modules/**"] 13 | } 14 | -------------------------------------------------------------------------------- /examples/next-simple-chat/src/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from 'next/document' 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /utils/vram_requirements/src/vram_requirements.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 |

vRAM Requirement Report

9 | Open console to see logs 10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/next-simple-chat/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project using web-llm. 2 | 3 | ## Getting Started 4 | 5 | First, install web-llm from source. 6 | 7 | Then, run the development server: 8 | 9 | ```bash 10 | npm run dev 11 | # or 12 | yarn dev 13 | # or 14 | pnpm dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | -------------------------------------------------------------------------------- /examples/next-simple-chat/src/pages/api/hello.ts: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | import type { NextApiRequest, NextApiResponse } from 'next' 3 | 4 | type Data = { 5 | name: string 6 | } 7 | 8 | export default function handler( 9 | req: NextApiRequest, 10 | res: NextApiResponse 11 | ) { 12 | res.status(200).json({ name: 'John Doe' }) 13 | } 14 | -------------------------------------------------------------------------------- /lib/index.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EAAE,SAAS,EACvB,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,WAAW,EACX,aAAa,EACd,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,UAAU,EACV,cAAc,EAAE,eAAe,EAChC,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,cAAc,CAAC;AAItB,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,MAAM,EAAE,GAAG,CAAA;KACZ;CACF"} -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | export { ModelRecord, AppConfig } from "./config"; 2 | export { InitProgressCallback, InitProgressReport, ChatOptions, ChatInterface } from "./types"; 3 | export { ChatModule, ChatRestModule, hasModelInCache } from "./chat_module"; 4 | export { ChatWorkerHandler, ChatWorkerClient } from "./web_worker"; 5 | declare global { 6 | interface Window { 7 | webLLM: any; 8 | } 9 | } 10 | //# sourceMappingURL=index.d.ts.map -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "declaration": true, 5 | "outDir": "lib", 6 | "declarationMap": true, 7 | "sourceMap": true, 8 | "strict": true, 9 | "moduleResolution": "Node", 10 | "esModuleInterop": true 11 | }, 12 | "typeRoots": [ "./node_modules/@webgpu/types", "./node_modules/@types"], 13 | "include": ["src"], 14 | "exclude": ["node_modules", "build", "dist", "rollup.config.cjs"] 15 | } 16 | -------------------------------------------------------------------------------- /scripts/serve_mlc_llm_dist.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This file prepares all the necessary dependencies for the web build. 3 | set -euxo pipefail 4 | 5 | npm --version 6 | 7 | MLC_LLM_HOME_SET="${MLC_LLM_HOME:-}" 8 | 9 | if [[ -z ${MLC_LLM_HOME_SET} ]]; then 10 | echo "Do not find MLC_LLM_HOME env variable, need to set this to work". 11 | fi 12 | cd ${MLC_LLM_HOME}/dist 13 | echo "Serving ${MLC_LLM_HOME}/dist for local debugging purposes" 14 | npx http-server -p 8000 --cors 15 | cd - 16 | -------------------------------------------------------------------------------- /cleanup-index-js.sh: -------------------------------------------------------------------------------- 1 | # Remove instances of string "const{createRequire:createRequire}=await import('module');" 2 | # This is required to allow background workers packaged with Parcel for the chrome extension 3 | # to run the `ChatModule`. 4 | sed -e s/"const{createRequire:createRequire}=await import('module');"//g -i .backup lib/index.js 5 | sed -e s/"const{createRequire:createRequire}=await import('module');"//g -i .backup lib/index.js.map 6 | 7 | # Cleanup backup files 8 | rm lib/index.js.backup 9 | rm lib/index.js.map.backup -------------------------------------------------------------------------------- /examples/get-started/README.md: -------------------------------------------------------------------------------- 1 | # WebLLM Get Started App 2 | 3 | This folder provides a minimum demo to show WebLLM API in a webapp setting. 4 | To try it out, 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/web-worker/src/get_started.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 |

WebLLM Test Page

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/src/get_started.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 |

WebLLM Test Page

8 | Open console to see output 9 |
10 |
11 | 12 | 13 |

Prompt

14 | 15 | 16 |

Response

17 | 18 |
19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /examples/next-simple-chat/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /examples/get-started-rest/src/get_started.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 |

WebLLM Test Page

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 | ![Chrome Extension](https://github.com/mlc-ai/mlc-llm/assets/11940172/0d94cc73-eff1-4128-a6e4-70dc879f04e0) 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 |
27 | 31 |
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 |
26 | 36 | 37 |
38 |
39 | {messages.map((value, index) => ( 40 |
41 |
42 |
${value.text}
43 |
44 |
45 | ))} 46 |
47 | 48 |
49 | { 55 | if (event.key === "Enter") { 56 | chat_ui 57 | .onGenerate(prompt, updateMessage, setRuntimeStats) 58 | .catch((error) => console.log(error)); 59 | } 60 | }} 61 | value={prompt} 62 | onChange={(event) => setPrompt(event.target.value)} 63 | /> 64 | 74 |
75 |
76 | 77 |
78 | 86 | 87 |
88 |
89 | ); 90 | }; 91 | 92 | export default ChatComponent; 93 | -------------------------------------------------------------------------------- /examples/next-simple-chat/src/styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --foreground-rgb: 0, 0, 0; 7 | --background-start-rgb: 214, 219, 220; 8 | --background-end-rgb: 255, 255, 255; 9 | } 10 | 11 | @media (prefers-color-scheme: dark) { 12 | :root { 13 | --foreground-rgb: 255, 255, 255; 14 | --background-start-rgb: 0, 0, 0; 15 | --background-end-rgb: 0, 0, 0; 16 | } 17 | } 18 | 19 | body { 20 | color: rgb(var(--foreground-rgb)); 21 | background: linear-gradient( 22 | to bottom, 23 | transparent, 24 | rgb(var(--background-end-rgb)) 25 | ) 26 | rgb(var(--background-start-rgb)); 27 | } 28 | 29 | a { 30 | color: inherit; 31 | text-decoration: none; 32 | } 33 | 34 | * { 35 | box-sizing: border-box; 36 | } 37 | 38 | chatui-chat { 39 | height: 100; 40 | } 41 | 42 | .chatui { 43 | display: flex; 44 | flex-flow: column wrap; 45 | justify-content: space-between; 46 | width: 100%; 47 | max-width: 867px; 48 | margin: 25px 10px; 49 | height: 600px; 50 | border: 2px solid #ddd; 51 | border-radius: 5px; 52 | box-shadow: 0 15px 15px -5px rgba(0, 0, 0, 0.2); 53 | } 54 | 55 | s .chatui-header { 56 | display: flex; 57 | justify-content: space-between; 58 | padding: 10px; 59 | border-bottom: 2px solid #ddd; 60 | background: #eee; 61 | color: #666; 62 | } 63 | 64 | .chatui-chat { 65 | flex: 1; 66 | overflow-y: auto; 67 | padding: 10px; 68 | } 69 | 70 | .chatui-chat::-webkit-scrollbar { 71 | width: 6px; 72 | } 73 | 74 | .chatui-chat::-webkit-scrollbar-track { 75 | background: #ddd; 76 | } 77 | 78 | .chatui-chat::-webkit-scrollbar-thumb { 79 | background: #bdbdbd; 80 | } 81 | 82 | .msg { 83 | display: flex; 84 | align-items: flex-end; 85 | margin-bottom: 10px; 86 | } 87 | 88 | .msg:last-of-type { 89 | margin: 0; 90 | } 91 | 92 | .msg-bubble { 93 | max-width: 450px; 94 | padding: 15px; 95 | border-radius: 15px; 96 | background: #ececec; 97 | } 98 | 99 | .left-msg .msg-bubble { 100 | border-bottom-left-radius: 0; 101 | } 102 | 103 | .error-msg .msg-bubble { 104 | border-bottom-left-radius: 0; 105 | color: #f15959; 106 | } 107 | 108 | .init-msg .msg-bubble { 109 | border-bottom-left-radius: 0; 110 | } 111 | 112 | .right-msg { 113 | flex-direction: row-reverse; 114 | } 115 | 116 | .right-msg .msg-bubble { 117 | background: #579ffb; 118 | color: #fff; 119 | border-bottom-right-radius: 0; 120 | } 121 | 122 | .chatui-inputarea { 123 | display: flex; 124 | padding: 10px; 125 | border-top: 2px solid #ddd; 126 | background: #eee; 127 | } 128 | 129 | .chatui-inputarea * { 130 | padding: 10px; 131 | border: none; 132 | border-radius: 3px; 133 | font-size: 1em; 134 | } 135 | 136 | .chatui-input { 137 | flex: 1; 138 | background: #ddd; 139 | } 140 | 141 | .chatui-btn { 142 | margin-left: 10px; 143 | background: #579ffb; 144 | color: #fff; 145 | font-weight: bold; 146 | cursor: pointer; 147 | padding: 10px; 148 | } 149 | 150 | .chatui-btn:hover { 151 | background: #577bfb; 152 | } 153 | 154 | .chatui-chat { 155 | background-color: #fcfcfe; 156 | } 157 | -------------------------------------------------------------------------------- /lib/types.d.ts: -------------------------------------------------------------------------------- 1 | import { AppConfig, ChatConfig } from "./config"; 2 | /** 3 | * Custom options that can be used to 4 | * override known config values. 5 | */ 6 | export interface ChatOptions extends Partial { 7 | } 8 | /** 9 | * Report during intialization. 10 | */ 11 | export interface InitProgressReport { 12 | progress: number; 13 | text: string; 14 | } 15 | /** 16 | * Callbacks used to report initialization process. 17 | */ 18 | export type InitProgressCallback = (report: InitProgressReport) => void; 19 | /** 20 | * Callbacks used to report initialization process. 21 | */ 22 | export type GenerateProgressCallback = (step: number, currentMessage: string) => void; 23 | /** 24 | * Common interface of chat module that UI can interact with 25 | */ 26 | export interface ChatInterface { 27 | /** 28 | * Set an initialization progress callback function 29 | * which reports the progress of model loading. 30 | * 31 | * This function can be useful to implement an UI that 32 | * update as we loading the model. 33 | * 34 | * @param initProgressCallback The callback function 35 | */ 36 | setInitProgressCallback: (initProgressCallback: InitProgressCallback) => void; 37 | /** 38 | * Reload the chat with a new model. 39 | * 40 | * @param localIdOrUrl local_id of the model or model artifact url. 41 | * @param chatOpts Extra options to overide chat behavior. 42 | * @param appConfig Override the app config in this load. 43 | * @returns A promise when reload finishes. 44 | * @note This is an async function. 45 | */ 46 | reload: (localIdOrUrl: string, chatOpts?: ChatOptions, appConfig?: AppConfig, files?: File[] | FileList) => Promise; 47 | /** 48 | * Generate a response for a given input. 49 | * 50 | * @param input The input prompt. 51 | * @param progressCallback Callback that is being called to stream intermediate results. 52 | * @param streamInterval callback interval to call progresscallback 53 | * @returns The final result. 54 | */ 55 | generate: (input: string, progressCallback?: GenerateProgressCallback, streamInterval?: number) => Promise; 56 | /** 57 | * @returns A text summarizing the runtime stats. 58 | * @note This is an async function 59 | */ 60 | runtimeStatsText: () => Promise; 61 | /** 62 | * Interrupt the generate process if it is already running. 63 | */ 64 | interruptGenerate: () => void; 65 | /** 66 | * Explicitly unload the current model and release the related resources. 67 | */ 68 | unload: () => Promise; 69 | /** 70 | * Reset the current chat session by clear all memories. 71 | */ 72 | resetChat: () => Promise; 73 | /** 74 | * Returns the device's maxStorageBufferBindingSize, can be used to guess whether the device 75 | * has limited resources like an Android phone. 76 | */ 77 | getMaxStorageBufferBindingSize(): Promise; 78 | /** 79 | * Returns the device's gpu vendor (e.g. arm, qualcomm, apple) if available. Otherwise return 80 | * an empty string. 81 | */ 82 | getGPUVendor(): Promise; 83 | } 84 | //# sourceMappingURL=types.d.ts.map -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { AppConfig, ChatConfig } from "./config" 2 | 3 | /** 4 | * Custom options that can be used to 5 | * override known config values. 6 | */ 7 | export interface ChatOptions extends Partial { } 8 | 9 | /** 10 | * Report during intialization. 11 | */ 12 | export interface InitProgressReport { 13 | progress: number; 14 | // timeElapsed: number; 15 | text: string; 16 | } 17 | 18 | /** 19 | * Callbacks used to report initialization process. 20 | */ 21 | export type InitProgressCallback = (report: InitProgressReport) => void; 22 | 23 | /** 24 | * Callbacks used to report initialization process. 25 | */ 26 | export type GenerateProgressCallback = (step: number, currentMessage: string) => void; 27 | 28 | /** 29 | * Common interface of chat module that UI can interact with 30 | */ 31 | export interface ChatInterface { 32 | /** 33 | * Set an initialization progress callback function 34 | * which reports the progress of model loading. 35 | * 36 | * This function can be useful to implement an UI that 37 | * update as we loading the model. 38 | * 39 | * @param initProgressCallback The callback function 40 | */ 41 | setInitProgressCallback: (initProgressCallback: InitProgressCallback) => void; 42 | 43 | /** 44 | * Reload the chat with a new model. 45 | * 46 | * @param localIdOrUrl local_id of the model or model artifact url. 47 | * @param chatOpts Extra options to overide chat behavior. 48 | * @param appConfig Override the app config in this load. 49 | * @returns A promise when reload finishes. 50 | * @note This is an async function. 51 | */ 52 | reload: (localIdOrUrl: string, chatOpts?: ChatOptions, appConfig?: AppConfig, files?: File[] | FileList) => Promise; 53 | 54 | /** 55 | * Generate a response for a given input. 56 | * 57 | * @param input The input prompt. 58 | * @param progressCallback Callback that is being called to stream intermediate results. 59 | * @param streamInterval callback interval to call progresscallback 60 | * @returns The final result. 61 | */ 62 | generate: ( 63 | input: string, 64 | progressCallback?: GenerateProgressCallback, 65 | streamInterval?: number, 66 | ) => Promise; 67 | 68 | /** 69 | * @returns A text summarizing the runtime stats. 70 | * @note This is an async function 71 | */ 72 | runtimeStatsText: () => Promise; 73 | 74 | /** 75 | * Interrupt the generate process if it is already running. 76 | */ 77 | interruptGenerate: () => void; 78 | 79 | /** 80 | * Explicitly unload the current model and release the related resources. 81 | */ 82 | unload: () => Promise; 83 | 84 | /** 85 | * Reset the current chat session by clear all memories. 86 | */ 87 | resetChat: () => Promise; 88 | 89 | /** 90 | * Returns the device's maxStorageBufferBindingSize, can be used to guess whether the device 91 | * has limited resources like an Android phone. 92 | */ 93 | getMaxStorageBufferBindingSize(): Promise; 94 | 95 | /** 96 | * Returns the device's gpu vendor (e.g. arm, qualcomm, apple) if available. Otherwise return 97 | * an empty string. 98 | */ 99 | getGPUVendor(): Promise; 100 | } 101 | 102 | -------------------------------------------------------------------------------- /web/tests/python/websock_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 | runtime = Runtime("cpp", {"system-lib": True}) 39 | target = "llvm -mtriple=wasm32-unknown-unknown-wasm" 40 | if not tvm.runtime.enabled(target): 41 | raise RuntimeError("Target %s is not enbaled" % target) 42 | n = te.var("n") 43 | A = te.placeholder((n,), name="A") 44 | B = te.compute(A.shape, lambda *i: A(*i) + 1.0, name="B") 45 | s = te.create_schedule(B.op) 46 | 47 | fadd = tvm.build(s, [A, B], target, runtime=runtime, name="addone") 48 | temp = utils.tempdir() 49 | 50 | wasm_path = temp.relpath("addone.wasm") 51 | fadd.export_library(wasm_path, fcompile=emcc.create_tvmjs_wasm) 52 | 53 | wasm_binary = open(wasm_path, "rb").read() 54 | 55 | remote = rpc.connect( 56 | proxy_host, 57 | proxy_port, 58 | key="wasm", 59 | session_constructor_args=["rpc.WasmSession", wasm_binary], 60 | ) 61 | 62 | def check(remote): 63 | # basic function checks. 64 | faddone = remote.get_function("testing.asyncAddOne") 65 | fecho = remote.get_function("testing.echo") 66 | assert faddone(100) == 101 67 | assert fecho(1, 2, 3) == 1 68 | assert fecho(1, 2, 3) == 1 69 | assert fecho(100, 2, 3) == 100 70 | assert fecho("xyz") == "xyz" 71 | assert bytes(fecho(bytearray(b"123"))) == b"123" 72 | # run the generated library. 73 | f1 = remote.system_lib() 74 | dev = remote.cpu(0) 75 | a = tvm.nd.array(np.random.uniform(size=1024).astype(A.dtype), dev) 76 | b = tvm.nd.array(np.zeros(1024, dtype=A.dtype), dev) 77 | # invoke the function 78 | addone = f1.get_function("addone") 79 | addone(a, b) 80 | 81 | # time evaluator 82 | time_f = f1.time_evaluator("addone", dev, number=100, repeat=10) 83 | time_f(a, b) 84 | cost = time_f(a, b).mean 85 | print("%g secs/op" % cost) 86 | np.testing.assert_equal(b.numpy(), a.numpy() + 1) 87 | 88 | check(remote) 89 | 90 | 91 | test_rpc() 92 | -------------------------------------------------------------------------------- /site/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Home 4 | notitle: true 5 | --- 6 | 11 | 12 | # Web LLM 13 | 14 | **Llama 2 7B/13B are now available in Web LLM!!** Try it out in our [chat demo](#chat-demo). 15 | 16 | **Llama 2 70B is also supported.** If you have a Apple Silicon Mac with 64GB or more memory, you can follow the [instructions](#instructions) below to download and launch Chrome Canary and try out the 70B model in Web LLM. 17 | 18 | **Mistral 7B models and WizardMath** are all supported! 19 | 20 | This project brings large-language model and LLM-based chatbot to web browsers. **Everything runs inside the browser with no server support and accelerated with WebGPU.** This opens up a lot of fun opportunities to build AI assistants for everyone and enable privacy while enjoying GPU acceleration. Please check out our [GitHub repo](https://github.com/mlc-ai/web-llm) to see how we did it. 21 | You can use WebLLM as a base [npm package](https://www.npmjs.com/package/@mlc-ai/web-llm) and build your own web application on top of it by following the [documentation](https://mlc.ai/mlc-llm/docs/deploy/javascript.html). 22 | 23 | 24 | 25 | 26 | We have been seeing amazing progress in generative AI and LLM recently. Thanks to the open-source efforts like LLaMA, Alpaca, Vicuna and Dolly, we start to see an exciting future of building our own open source language models and personal AI assistant. 27 | 28 | These models are usually big and compute-heavy. To build a chat service, we will need a large cluster to run an inference server, while clients send requests to servers and retrieve the inference output. We also usually have to run on a specific type of GPUs where popular deep-learning frameworks are readily available. 29 | 30 | This project is our step to bring more diversity to the ecosystem. Specifically, can we simply bake LLMs directly into the client side and directly run them inside a browser? If that can be realized, we could offer support for client personal AI models with the benefit of cost reduction, enhancement for personalization and privacy protection. The client side is getting pretty powerful. 31 | 32 | Won’t it be even more amazing if we can simply open up a browser and directly bring AI natively to your browser tab? There is some level of readiness in the ecosystem. This project provides an affirmative answer to the question. 33 | 34 | 35 | ## Instructions 36 | 37 | WebGPU just shipped in Chrome 113. 38 | 39 | Select the model you want to try out. Enter your inputs, click “Send” – we are ready to go! 40 | The chat bot will first fetch model parameters into local cache. The download may take a few minutes, only for the first run. 41 | The subsequent refreshes and runs will be faster. We have tested it on Windows and Mac, you will need a GPU with about 6GB memory to run Llama-7B, Vicuna-7B, and about 3GB memory to run RedPajama-3B. 42 | 43 | ## Chat Demo 44 | 45 | The chat demo is based on [Llama 2](https://ai.meta.com/llama/), [Mistral-7B](https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.2) and its variants, and [RedPajama-INCITE-Chat-3B-v1](https://huggingface.co/togethercomputer/RedPajama-INCITE-Chat-3B-v1) model. More model supports are on the way. 46 | 47 |
48 | 49 | 50 | 51 | ## Links 52 | 53 | - [Web LLM GitHub](https://github.com/mlc-ai/web-llm) 54 | - You might also be interested in [Web Stable Diffusion](https://websd.mlc.ai/). 55 | 56 | ## Disclaimer 57 | 58 | This demo site is for research purposes only, subject to the model License of LLaMA, Vicuna and RedPajama. Please contact us if you find any potential violation. 59 | -------------------------------------------------------------------------------- /web/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | # TVM WebAssembly Runtime 19 | 20 | This folder contains TVM WebAssembly Runtime. 21 | 22 | ## Installation 23 | 24 | The LLVM main branch support webassembly as a target, we can directly 25 | build TVM with LLVM mainline to generate wasm modules. 26 | Note that, however, we still need emscripten to compile the runtime and provide system library support. 27 | 28 | Note that so far we requires everything to be in the source and setup PYTHONPATH(instead of use setup.py install). 29 | 30 | ### Setup Emscripten 31 | 32 | We use emscripten to compile our runtime wasm library as well as a WASI variant that we can deploy 33 | to the browser environment. 34 | 35 | Follow [Emscripten](https://emscripten.org/) to download emsdk and install emcc on your local environment. 36 | 37 | ### Build TVM Wasm Runtime 38 | 39 | After the emcc is setup correctly. We can build tvm's wasm runtime by typing `make` in the web folder. 40 | 41 | ```bash 42 | make 43 | ``` 44 | 45 | This command will create the follow files: 46 | - `dist/wasm/libtvm_runtime.bc` bitcode library `tvm.contrib.emcc` will link into. 47 | - `dist/wasm/tvmjs_runtime.wasm` a standalone wasm runtime for testing purposes. 48 | - `dist/wasm/tvmjs_runtime.wasi.js` a WASI compatible library generated by emscripten that can be fed into runtime. 49 | 50 | 51 | ### Build TVM Wasm JS Frontend 52 | 53 | Type the following command in the web folder. 54 | 55 | ```bash 56 | npm run bundle 57 | ``` 58 | 59 | This command will create the tvmjs library that we can use to interface with the wasm runtime. 60 | 61 | 62 | ## Use TVM to Generate Wasm Library and Run it 63 | 64 | Check code snippet in 65 | 66 | - [tests/python/prepare_test_libs.py](https://github.com/apache/tvm/tree/main/web/tests/python/prepare_test_libs.py) 67 | shows how to create a wasm library that links with tvm runtime. 68 | - Note that all wasm libraries have to created using the `--system-lib` option 69 | - emcc.create_wasm will automatically link the runtime library `dist/wasm/libtvm_runtime.bc` 70 | - [tests/web/test_module_load.js](https://github.com/apache/tvm/tree/main/web/tests/node/test_module_load.js) demonstrate 71 | how to run the generated library through tvmjs API. 72 | 73 | 74 | ## Run Wasm Remotely through WebSocket RPC. 75 | 76 | We can now use js side to start an RPC server and connect to it from python side, 77 | making the testing flow easier. 78 | 79 | The following is an example to reproduce this. 80 | - run `python -m tvm.exec.rpc_proxy --example-rpc=1` to start proxy. 81 | - Start the WebSocket RPC 82 | - Browswer version: open https://localhost:8888, click connect to proxy 83 | - NodeJS version: `npm run rpc` 84 | - run `python tests/python/websock_rpc_test.py` to run the rpc test. 85 | 86 | 87 | ## WebGPU Experiments 88 | 89 | Web gpu is still experimental, so apis can change. 90 | Right now we use the SPIRV to generate shaders that can be accepted by Chrome and Firefox. 91 | 92 | - Obtain a browser that support webgpu. 93 | - So far only Chrome Canary on MacOS works 94 | - Firefox should be close pending the support of Fence. 95 | - Download vulkan SDK (1.1 or higher) that supports SPIRV 1.3 96 | - Start the WebSocket RPC 97 | - run `python tests/node/webgpu_rpc_test.py` 98 | -------------------------------------------------------------------------------- /utils/vram_requirements/src/vram_requirements.ts: -------------------------------------------------------------------------------- 1 | import ModelRecord from "@mlc-ai/web-llm"; 2 | import appConfig from "./app-config"; // Modify this to inspect vram requirement for models of choice 3 | import * as tvmjs from "tvmjs"; 4 | 5 | function setLabel(id: string, text: string) { 6 | const label = document.getElementById(id); 7 | if (label == null) { 8 | throw Error("Cannot find label " + id); 9 | } 10 | label.innerText = text; 11 | } 12 | 13 | interface AppConfig { 14 | model_list: Array; 15 | } 16 | 17 | let dtypeBytesMap = new Map([ 18 | ["uint32", 4], 19 | ["uint16", 2], 20 | ["float32", 4], 21 | ["float16", 4] 22 | ]); 23 | 24 | async function main() { 25 | let config: AppConfig = appConfig; 26 | let report: string = ""; 27 | for (let i = 0; i < config.model_list.length; ++i) { 28 | // 1. Read each model record 29 | const modelRecord: ModelRecord = config.model_list[i]; 30 | const local_id = modelRecord.local_id; 31 | // 2. Load the wasm 32 | const wasmUrl = modelRecord.model_lib_url; 33 | const wasmSource = await (await fetch(wasmUrl)).arrayBuffer(); 34 | report += `${local_id}: \n`; 35 | // 3. Initialize tvmjs instance and virtual machine using the wasm 36 | const tvm = await tvmjs.instantiate( 37 | new Uint8Array(wasmSource), 38 | tvmjs.createPolyfillWASI(), 39 | console.log 40 | ); 41 | const gpuDetectOutput = await tvmjs.detectGPUDevice(); 42 | if (gpuDetectOutput == undefined) { 43 | throw Error("Cannot find WebGPU in the environment"); 44 | } 45 | tvm.initWebGPU(gpuDetectOutput.device); 46 | tvm.beginScope(); 47 | const vm = tvm.detachFromCurrentScope( 48 | tvm.createVirtualMachine(tvm.webgpu()) 49 | ); 50 | // 4. Get metadata from the vm 51 | let fgetMetadata: any; 52 | try { 53 | fgetMetadata = vm.getFunction("_metadata"); 54 | } catch (err) { 55 | console.error("The wasm needs to have function `_metadata` to inspect vram requirement.", err); 56 | } 57 | const ret_value = fgetMetadata(); 58 | const metadataStr = tvm.detachFromCurrentScope(ret_value).toString(); 59 | const metadata = JSON.parse(metadataStr); 60 | // 5. Parse the vram requirement 61 | // 5.1. Get bytes for loading params 62 | let paramBytes = 0; 63 | metadata.params.forEach((param: any) => { 64 | if (Math.min(...param.shape) > 0) { 65 | // Possible to have shape -1 signifying a dynamic shape -- we disregard them 66 | const dtypeBytes = dtypeBytesMap.get(param.dtype); 67 | if (dtypeBytes === undefined) { 68 | throw Error("Cannot find size of " + param.dtype + ", add it to `dtypeBytesMap`.") 69 | } 70 | const numParams = param.shape.reduce((a: number, b: number) => a * b); 71 | paramBytes += numParams * dtypeBytes; 72 | } else { 73 | console.log(`${local_id}'s ${param.name} has dynamic shape; excluded from vRAM calculation.`) 74 | } 75 | }); 76 | // 5.2. Get maximum bytes needed for temporary buffer across all functions 77 | let maxTempFuncBytes: number = 0; 78 | Object.entries(metadata.memory_usage).forEach(([funcName, funcBytes]) => { 79 | if (typeof funcBytes !== "number") { 80 | throw Error("`memory_usage` expects entry `funcName: funcBytes`.") 81 | } 82 | maxTempFuncBytes = Math.max(maxTempFuncBytes, funcBytes); 83 | }) 84 | // 5.3. Get kv cache bytes 85 | const kv_cache_bytes: number = metadata.kv_cache_bytes; 86 | // 5.4. Get total vRAM needed 87 | const totalBytes = paramBytes + maxTempFuncBytes + kv_cache_bytes; 88 | // 6. Report vRAM Requirement 89 | report += ( 90 | `totalBytes: ${(totalBytes / 1024 / 1024).toFixed(2)} MB\n` + 91 | `paramBytes: ${(paramBytes / 1024 / 1024).toFixed(2)} MB\n` + 92 | `maxTempFuncBytes: ${(maxTempFuncBytes / 1024 / 1024).toFixed(2)} MB\n` + 93 | `kv_cache_bytes: ${(kv_cache_bytes / 1024 / 1024).toFixed(2)} MB\n\n` 94 | ); 95 | // 7. Dispose everything 96 | tvm.endScope(); 97 | vm.dispose(); 98 | tvm.dispose(); 99 | } 100 | setLabel("report-label", report); 101 | } 102 | 103 | main(); 104 | -------------------------------------------------------------------------------- /examples/simple-chat/src/llm_chat.css: -------------------------------------------------------------------------------- 1 | .chatui { 2 | display: flex; 3 | position: relative; 4 | flex-flow: column wrap; 5 | justify-content: space-between; 6 | width: 100%; 7 | max-width: 867px; 8 | margin: 25px 10px; 9 | height: 600px; 10 | border: 2px solid #ddd; 11 | border-radius: 5px; 12 | background-color: #1F2027; 13 | } 14 | 15 | .chatui-select-wrapper { 16 | display: flex; 17 | justify-content: center; 18 | background-color: #1F2027; 19 | padding: 10px 0; 20 | } 21 | 22 | #chatui-select { 23 | width: 350px; 24 | background-color: #1F2027; 25 | color: white; 26 | border: none; 27 | } 28 | 29 | #chatui-select:focus { 30 | outline: none; 31 | } 32 | 33 | #chatui-select::-webkit-scrollbar { 34 | display: none; 35 | } 36 | 37 | #chatui-select option { 38 | background-color: #1F2027; 39 | color: white; 40 | } 41 | 42 | #chatui-select option:hover { 43 | background-color: #474747; 44 | color: white; 45 | } 46 | 47 | s .chatui-header { 48 | display: flex; 49 | justify-content: space-between; 50 | padding: 10px; 51 | border-bottom: 2px solid #ddd; 52 | background: #eee; 53 | color: #666; 54 | } 55 | 56 | /* Used to remove tiny white lines in android devices; not sure if there is a better way */ 57 | *, 58 | *::before, 59 | *::after { 60 | box-sizing: content-box; 61 | } 62 | 63 | .chatui-chat { 64 | flex: 1; 65 | overflow-y: auto; 66 | padding: 10px; 67 | background-color: #1F2027; 68 | } 69 | 70 | .chatui-chat::-webkit-scrollbar { 71 | width: 6px; 72 | } 73 | 74 | .chatui-chat::-webkit-scrollbar-track { 75 | background: #1F2027; 76 | } 77 | 78 | .chatui-chat::-webkit-scrollbar-thumb { 79 | background: #888; 80 | } 81 | 82 | .chatui-chat::-webkit-scrollbar-thumb:hover { 83 | background: #555; 84 | } 85 | 86 | .msg { 87 | display: flex; 88 | align-items: flex-end; 89 | margin-bottom: 10px; 90 | } 91 | 92 | .msg:last-of-type { 93 | margin: 0; 94 | } 95 | 96 | .msg-bubble { 97 | background-color: #f0f0f0; 98 | border-radius: 8px; 99 | padding: 16px; 100 | margin: 5px auto; 101 | width: calc(100% - 20px); 102 | box-sizing: border-box; 103 | color: black; 104 | border: none; 105 | font-size: medium; 106 | margin-left: auto; 107 | margin-right: auto; 108 | } 109 | 110 | .left-msg .msg-bubble { 111 | background-color: #343541; 112 | color: #ececec; 113 | } 114 | 115 | .error-msg .msg-bubble { 116 | background-color: #343541; 117 | color: #f15959; 118 | } 119 | 120 | .init-msg .msg-bubble { 121 | background-color: #343541; 122 | color: #ececec; 123 | } 124 | 125 | .right-msg .msg-bubble { 126 | background-color: #444654; 127 | color: #ececec; 128 | } 129 | 130 | .chatui-inputarea { 131 | display: flex; 132 | padding: 10px; 133 | border-top: 2px solid transparent; 134 | background-color: #1F2027; 135 | } 136 | 137 | .chatui-inputarea * { 138 | padding: 10px; 139 | border: none; 140 | border-radius: 3px; 141 | font-size: 1em; 142 | color: white; 143 | background: rgba(0, 0, 0, 0.3); 144 | } 145 | 146 | .chatui-input { 147 | flex: 1; 148 | background-color: #40414F; 149 | color: white; 150 | } 151 | 152 | .chatui-reset-btn { 153 | margin-left: 10px; 154 | background-color: #40414F; 155 | color: #fff; 156 | font-weight: bold; 157 | cursor: pointer; 158 | background-image: url('img/reset.png'); 159 | background-repeat: no-repeat; 160 | background-position: center; 161 | width: 40px; 162 | background-repeat: no-repeat; 163 | background-position: center; 164 | background-size: 20px 20px; 165 | } 166 | 167 | .chatui-reset-btn:hover { 168 | background-color: #03a33e; 169 | } 170 | 171 | .chatui-send-btn { 172 | margin-left: 10px; 173 | background-color: #40414F; 174 | color: #fff; 175 | font-weight: bold; 176 | cursor: pointer; 177 | background-image: url('img/plane.png'); 178 | background-repeat: no-repeat; 179 | background-position: center; 180 | width: 40px; 181 | background-repeat: no-repeat; 182 | background-position: center; 183 | background-size: 20px 20px; 184 | } 185 | 186 | .chatui-send-btn:hover { 187 | background-color: #03a33e; 188 | } -------------------------------------------------------------------------------- /examples/chrome-extension/src/popup.css: -------------------------------------------------------------------------------- 1 | *, 2 | *::before, 3 | *::after { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | } 8 | 9 | html { 10 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, 11 | sans-serif; 12 | color: #222; 13 | } 14 | 15 | body { 16 | margin: 0; 17 | padding: 0.5rem; 18 | background-color: #778da9; 19 | width: 320px; 20 | font-size: small; 21 | } 22 | 23 | p { 24 | margin: 0; 25 | } 26 | 27 | /* LOADING BAR */ 28 | #loadingContainer { 29 | margin-bottom: 15px; 30 | width: 300px; 31 | height: 8px; 32 | } 33 | 34 | /* INPUT AREA */ 35 | #query-input { 36 | border: 1px solid #ccc; 37 | border-radius: 4px; 38 | } 39 | 40 | .input-container { 41 | display: flex; 42 | flex-direction: row; 43 | align-items: center; 44 | } 45 | 46 | .input-container input { 47 | width: 100%; 48 | outline: none; 49 | padding: 0.5rem; 50 | margin-right: 0.5rem; 51 | } 52 | 53 | /* SUBMIT BUTTON */ 54 | .btn { 55 | background-color: #1b263b; 56 | color: white; 57 | font-size: small; 58 | cursor: pointer; 59 | border-radius: 4px; 60 | border: none; 61 | padding: 0.5rem; 62 | } 63 | 64 | .btn:hover { 65 | background-color: #d0d0d0; 66 | } 67 | 68 | .btn:disabled { 69 | background-color: #a7a7a7; 70 | color: rgb(255, 255, 255); 71 | cursor: default; 72 | } 73 | 74 | .btn img { 75 | width: 1rem; 76 | height: 1rem; 77 | } 78 | 79 | /* LOADING */ 80 | 81 | .stage { 82 | display: flex; 83 | justify-content: center; 84 | align-items: center; 85 | position: relative; 86 | margin: 0 -5%; 87 | overflow: hidden; 88 | } 89 | 90 | #loading-indicator { 91 | display: none; 92 | color: white; 93 | margin-top: 0.5rem; 94 | } 95 | 96 | .dot-flashing { 97 | position: relative; 98 | width: 10px; 99 | height: 10px; 100 | border-radius: 5px; 101 | background-color: #1b263b; 102 | color: #1b263b; 103 | animation: dot-flashing 0.4s infinite linear alternate; 104 | animation-delay: 0.2s; 105 | } 106 | 107 | .dot-flashing::before, 108 | .dot-flashing::after { 109 | content: ""; 110 | display: inline-block; 111 | position: absolute; 112 | top: 0; 113 | } 114 | 115 | .dot-flashing::before { 116 | left: -15px; 117 | width: 10px; 118 | height: 10px; 119 | border-radius: 5px; 120 | background-color: #1b263b; 121 | color: #1b263b; 122 | animation: dot-flashing 0.4s infinite alternate; 123 | animation-delay: 0s; 124 | } 125 | 126 | .dot-flashing::after { 127 | left: 15px; 128 | width: 10px; 129 | height: 10px; 130 | border-radius: 5px; 131 | background-color: #1b263b; 132 | color: #1b263b; 133 | animation: dot-flashing 0.4s infinite alternate; 134 | animation-delay: 0.4s; 135 | } 136 | 137 | @keyframes dot-flashing { 138 | 0% { 139 | background-color: #1b263b; 140 | } 141 | 142 | 50%, 143 | 100% { 144 | background-color: #415a77; 145 | } 146 | } 147 | 148 | /* ANSWERS */ 149 | #queriesAnswersContainer { 150 | display: block; 151 | color: white; 152 | margin-top: 0.5rem; 153 | } 154 | 155 | #answer { 156 | color: #333333; 157 | } 158 | 159 | #answerWrapper { 160 | display: none; 161 | background-color: #ffd166; 162 | border-radius: 8px; 163 | padding: 0.5rem; 164 | margin-top: 0.5rem; 165 | } 166 | 167 | .queriesAnswers { 168 | border-radius: 8px; 169 | background-color: #ffd166;; 170 | padding: 0.5rem; 171 | color: #333333; 172 | } 173 | 174 | #lastQuery { 175 | color: rgb(188, 188, 188); 176 | } 177 | 178 | #lastAnswer { 179 | color: white; 180 | margin-top: 0.5rem; 181 | } 182 | 183 | #lastRequest { 184 | padding: 0.5rem; 185 | margin-top: 0.5rem; 186 | background-color: #333333; 187 | border-radius: 4px; 188 | } 189 | 190 | /* ANSWER OPTIONS */ 191 | .timeStamp { 192 | color: #9a8c98; 193 | } 194 | 195 | .copyRow { 196 | display: flex; 197 | flex-direction: row; 198 | align-items: end; 199 | justify-content: space-between; 200 | color: #a7a7a7; 201 | margin-top: 0.5rem; 202 | } 203 | 204 | .copyText { 205 | display: none; 206 | color: #a7a7a7; 207 | margin-right: 0.5rem; 208 | } 209 | 210 | .copyButton { 211 | color: #415a77; 212 | background-color: transparent; 213 | border: none; 214 | cursor: pointer; 215 | padding: 0; 216 | margin-left: 0.5rem; 217 | } 218 | 219 | .copyButton:hover { 220 | color: #5e80a7; 221 | background-color: transparent; 222 | } 223 | 224 | .removeButton { 225 | color: #415a77; 226 | background-color: transparent; 227 | border: none; 228 | cursor: pointer; 229 | padding: 0; 230 | } 231 | 232 | .removeButton:hover { 233 | color: #5e80a7; 234 | background-color: transparent; 235 | } 236 | -------------------------------------------------------------------------------- /examples/next-simple-chat/src/utils/chat_ui.ts: -------------------------------------------------------------------------------- 1 | import { type ChatInterface } from "@mlc-ai/web-llm"; 2 | 3 | export default class ChatUI { 4 | private chat: ChatInterface; 5 | private chatLoaded = false; 6 | private requestInProgress = false; 7 | // We use a request chain to ensure that 8 | // all requests send to chat are sequentialized 9 | private chatRequestChain: Promise = Promise.resolve(); 10 | 11 | constructor(chat: ChatInterface) { 12 | this.chat = chat; 13 | } 14 | /** 15 | * Push a task to the execution queue. 16 | * 17 | * @param task The task to be executed; 18 | */ 19 | private pushTask(task: () => Promise) { 20 | const lastEvent = this.chatRequestChain; 21 | this.chatRequestChain = lastEvent.then(task); 22 | } 23 | // Event handlers 24 | // all event handler pushes the tasks to a queue 25 | // that get executed sequentially 26 | // the tasks previous tasks, which causes them to early stop 27 | // can be interrupted by chat.interruptGenerate 28 | async onGenerate(prompt: string, messageUpdate: (kind: string, text: string, append: boolean) => void, setRuntimeStats: (runtimeStats: string) => void) { 29 | if (this.requestInProgress) { 30 | return; 31 | } 32 | this.pushTask(async () => { 33 | await this.asyncGenerate(prompt, messageUpdate, setRuntimeStats); 34 | }); 35 | return this.chatRequestChain 36 | } 37 | 38 | async onReset(clearMessages: () => void) { 39 | if (this.requestInProgress) { 40 | // interrupt previous generation if any 41 | this.chat.interruptGenerate(); 42 | } 43 | // try reset after previous requests finishes 44 | this.pushTask(async () => { 45 | await this.chat.resetChat(); 46 | clearMessages(); 47 | }); 48 | return this.chatRequestChain 49 | } 50 | 51 | async asyncInitChat(messageUpdate: (kind: string, text: string, append: boolean) => void) { 52 | if (this.chatLoaded) return; 53 | this.requestInProgress = true; 54 | messageUpdate("init", "", true); 55 | const initProgressCallback = (report: { text: string }) => { 56 | messageUpdate("init", report.text, false); 57 | } 58 | this.chat.setInitProgressCallback(initProgressCallback); 59 | 60 | try { 61 | await this.chat.reload("Llama-2-7b-chat-hf-q4f32_1", undefined, { 62 | "model_list": [ 63 | { 64 | "model_url": "https://huggingface.co/mlc-ai/Llama-2-7b-chat-hf-q4f32_1-MLC/resolve/main/", 65 | "local_id": "Llama-2-7b-chat-hf-q4f32_1", 66 | "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", 67 | }, 68 | ] 69 | }); 70 | } catch (err: unknown) { 71 | messageUpdate("error", "Init error, " + (err?.toString() ?? ""), true); 72 | console.log(err); 73 | await this.unloadChat(); 74 | this.requestInProgress = false; 75 | return; 76 | } 77 | this.requestInProgress = false; 78 | this.chatLoaded = true; 79 | } 80 | 81 | private async unloadChat() { 82 | await this.chat.unload(); 83 | this.chatLoaded = false; 84 | } 85 | 86 | /** 87 | * Run generate 88 | */ 89 | private async asyncGenerate(prompt: string, messageUpdate: (kind: string, text: string, append: boolean) => void, setRuntimeStats: (runtimeStats: string) => void) { 90 | await this.asyncInitChat(messageUpdate); 91 | this.requestInProgress = true; 92 | // const prompt = this.uiChatInput.value; 93 | if (prompt == "") { 94 | this.requestInProgress = false; 95 | return; 96 | } 97 | 98 | messageUpdate("right", prompt, true); 99 | // this.uiChatInput.value = ""; 100 | // this.uiChatInput.setAttribute("placeholder", "Generating..."); 101 | 102 | messageUpdate("left", "", true); 103 | const callbackUpdateResponse = (step: number, msg: string) => { 104 | messageUpdate("left", msg, false); 105 | }; 106 | 107 | try { 108 | const output = await this.chat.generate(prompt, callbackUpdateResponse); 109 | messageUpdate("left", output, false); 110 | this.chat.runtimeStatsText().then(stats => setRuntimeStats(stats)).catch(error => console.log(error)); 111 | } catch (err: unknown) { 112 | messageUpdate("error", "Generate error, " + (err?.toString() ?? ""), true); 113 | console.log(err); 114 | await this.unloadChat(); 115 | } 116 | this.requestInProgress = false; 117 | } 118 | } -------------------------------------------------------------------------------- /web/src/environment.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 | * Runtime environment that provide js libaries calls. 21 | */ 22 | import { Pointer } from "./ctypes"; 23 | import { LibraryProvider } from "./types"; 24 | import { assert } from "./support"; 25 | import * as ctypes from "./ctypes"; 26 | 27 | /** 28 | * Detect library provider from the importObject. 29 | * 30 | * @param importObject The import object. 31 | */ 32 | function detectLibraryProvider( 33 | importObject: Record 34 | ): LibraryProvider | undefined { 35 | if ( 36 | importObject["wasmLibraryProvider"] && 37 | importObject["wasmLibraryProvider"]["start"] && 38 | importObject["wasmLibraryProvider"]["imports"] !== undefined 39 | ) { 40 | const item = importObject as { wasmLibraryProvider: LibraryProvider }; 41 | // create provider so that we capture imports in the provider. 42 | return { 43 | imports: item.wasmLibraryProvider.imports, 44 | start: (inst: WebAssembly.Instance): void => { 45 | item.wasmLibraryProvider.start(inst); 46 | }, 47 | }; 48 | } else if (importObject["imports"] && importObject["start"] !== undefined) { 49 | return importObject as LibraryProvider; 50 | } else if (importObject["wasiImport"] && importObject["start"] !== undefined) { 51 | // WASI 52 | return { 53 | imports: { 54 | "wasi_snapshot_preview1": importObject["wasiImport"], 55 | }, 56 | start: (inst: WebAssembly.Instance): void => { 57 | importObject["start"](inst); 58 | } 59 | }; 60 | } else { 61 | return undefined; 62 | } 63 | } 64 | 65 | /** 66 | * Environment to impelement most of the JS library functions. 67 | */ 68 | export class Environment implements LibraryProvider { 69 | logger: (msg: string) => void; 70 | imports: Record; 71 | /** 72 | * Maintains a table of FTVMWasmPackedCFunc that the C part 73 | * can call via TVMWasmPackedCFunc. 74 | * 75 | * We maintain a separate table so that we can have un-limited amount 76 | * of functions that do not maps to the address space. 77 | */ 78 | packedCFuncTable: Array = [ 79 | undefined, 80 | ]; 81 | /** 82 | * Free table index that can be recycled. 83 | */ 84 | packedCFuncTableFreeId: Array = []; 85 | 86 | private libProvider?: LibraryProvider; 87 | 88 | constructor( 89 | importObject: Record = {}, 90 | logger: (msg: string) => void = console.log 91 | ) { 92 | this.logger = logger; 93 | this.libProvider = detectLibraryProvider(importObject); 94 | // get imports from the provider 95 | if (this.libProvider !== undefined) { 96 | this.imports = this.libProvider.imports; 97 | } else { 98 | this.imports = importObject; 99 | } 100 | // update with more functions 101 | this.imports.env = this.environment(this.imports.env); 102 | } 103 | 104 | /** Mark the start of the instance. */ 105 | start(inst: WebAssembly.Instance): void { 106 | if (this.libProvider !== undefined) { 107 | this.libProvider.start(inst); 108 | } 109 | } 110 | 111 | private environment(initEnv: Record): Record { 112 | // default env can be overriden by libraries. 113 | const defaultEnv = { 114 | "__cxa_thread_atexit": (): void => {}, 115 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 116 | "emscripten_notify_memory_growth": (index: number): void => {} 117 | }; 118 | const wasmPackedCFunc: ctypes.FTVMWasmPackedCFunc = ( 119 | args: Pointer, 120 | typeCodes: Pointer, 121 | nargs: number, 122 | ret: Pointer, 123 | resourceHandle: Pointer 124 | ): number => { 125 | const cfunc = this.packedCFuncTable[resourceHandle]; 126 | assert(cfunc !== undefined); 127 | return cfunc(args, typeCodes, nargs, ret, resourceHandle); 128 | }; 129 | 130 | const wasmPackedCFuncFinalizer: ctypes.FTVMWasmPackedCFuncFinalizer = ( 131 | resourceHandle: Pointer 132 | ): void => { 133 | this.packedCFuncTable[resourceHandle] = undefined; 134 | this.packedCFuncTableFreeId.push(resourceHandle); 135 | }; 136 | 137 | const newEnv = { 138 | TVMWasmPackedCFunc: wasmPackedCFunc, 139 | TVMWasmPackedCFuncFinalizer: wasmPackedCFuncFinalizer, 140 | "__console_log": (msg: string): void => { 141 | this.logger(msg); 142 | } 143 | }; 144 | return Object.assign(defaultEnv, initEnv, newEnv); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /web/apps/browser/rpc_server.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | TVM RPC Test Page 22 | 23 | 24 | 25 | 26 | 27 | 28 | 89 | 90 | 95 | 96 |

TVM WebSocket RPC Server

97 | To use this page 98 |
    99 |
  • Run "make" and "npm run bundle" to create the libraries.
  • 100 |
  • 101 | run "python -m tvm.exec.rpc_proxy --example-rpc=1" to start proxy. 102 |
  • 103 |
  • Click Connect to proxy.
  • 104 |
  • run "python tests/python/websock_rpc_test.py" to run the rpc client.
  • 105 |
106 | 107 |

Options

108 | Proxy URL
114 | RPC Server Key
120 | NDArrayCache - 121 | 124 | CacheDevice - 125 | 129 |
130 | 131 | 132 |
133 |
134 | 135 |
136 |
137 |
138 | 139 | 140 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | params/ 3 | *.bak 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | *.S 10 | # C extensions 11 | *.so 12 | 13 | 14 | *.ll 15 | .npm 16 | # Distribution / packaging 17 | .Python 18 | env/ 19 | build/ 20 | build-*/ 21 | develop-eggs/ 22 | dist/ 23 | downloads/ 24 | eggs/ 25 | .eggs/ 26 | web/lib/ 27 | lib64/ 28 | parts/ 29 | sdist/ 30 | var/ 31 | wheels/ 32 | pip-wheel-metadata/ 33 | share/python-wheels/ 34 | *.egg-info/ 35 | .installed.cfg 36 | *.egg 37 | MANIFEST 38 | 39 | .conda/ 40 | # PyInstaller 41 | # Usually these files are written by a python script from a template 42 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 43 | *.manifest 44 | *.spec 45 | 46 | # Generated by python/gen_requirements.py 47 | python/requirements/*.txt 48 | 49 | # Installer logs 50 | pip-log.txt 51 | pip-delete-this-directory.txt 52 | 53 | # Unit test / coverage reports 54 | htmlcov/ 55 | .tox/ 56 | .nox/ 57 | .coverage 58 | .coverage.* 59 | .cache 60 | nosetests.xml 61 | coverage.xml 62 | *.cover 63 | *.py,cover 64 | .hypothesis/ 65 | .pytest_cache/ 66 | 67 | # Translations 68 | *.mo 69 | *.pot 70 | 71 | # Django stuff: 72 | *.log 73 | local_settings.py 74 | db.sqlite3 75 | db.sqlite3-journal 76 | 77 | # Flask stuff: 78 | instance/ 79 | .webassets-cache 80 | 81 | # Scrapy stuff: 82 | .scrapy 83 | 84 | # Sphinx documentation 85 | docs/_build/ 86 | docs/_staging/ 87 | 88 | # PyBuilder 89 | target/ 90 | /target/ 91 | 92 | # Jupyter Notebook 93 | .ipynb_checkpoints 94 | 95 | # IPython 96 | profile_default/ 97 | ipython_config.py 98 | 99 | # pyenv 100 | .python-version 101 | 102 | # pipenv 103 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 104 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 105 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 106 | # install all needed dependencies. 107 | #Pipfile.lock 108 | 109 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 110 | __pypackages__/ 111 | 112 | # Celery stuff 113 | celerybeat-schedule 114 | celerybeat.pid 115 | 116 | # SageMath parsed files 117 | *.sage.py 118 | 119 | # Environments 120 | .env 121 | .venv 122 | env/ 123 | venv/ 124 | ENV/ 125 | env.bak/ 126 | venv.bak/ 127 | 128 | # Spyder project settings 129 | .spyderproject 130 | .spyproject 131 | 132 | # Rope project settings 133 | .ropeproject 134 | *~ 135 | *.pyc 136 | *~ 137 | config.mk 138 | config.cmake 139 | Win32 140 | *.dir 141 | perf 142 | *.wasm 143 | .emscripten 144 | 145 | ## IOS 146 | DerivedData/ 147 | 148 | ## Java 149 | *.class 150 | jvm/*/target/ 151 | jvm/*/*/target/ 152 | jvm/native/*/generated 153 | jvm/native/src/main/native/org_apache_tvm_native_c_api.h 154 | *.worksheet 155 | *.idea 156 | *.iml 157 | *.classpath 158 | *.project 159 | *.settings 160 | */node_modules/ 161 | 162 | ## Various settings 163 | *.pbxuser 164 | !default.pbxuser 165 | *.mode1v3 166 | !default.mode1v3 167 | *.mode2v3 168 | !default.mode2v3 169 | *.perspectivev3 170 | !default.perspectivev3 171 | xcuserdata/ 172 | .pkl_memoize_* 173 | 174 | .emscripten* 175 | .m2 176 | 177 | # Compiled Dynamic libraries 178 | *.so 179 | *.dylib 180 | *.dll 181 | 182 | # Compiled Object files 183 | *.slo 184 | *.lo 185 | *.o 186 | *.obj 187 | 188 | # Precompiled Headers 189 | *.gch 190 | *.pch 191 | 192 | # Compiled Static libraries 193 | *.lai 194 | *.la 195 | *.a 196 | *.lib 197 | 198 | # Executables 199 | *.exe 200 | *.out 201 | *.app 202 | 203 | ## Other 204 | *.moved-aside 205 | *.xccheckout 206 | *.xcscmblueprint 207 | .DS_Store 208 | tags 209 | cscope* 210 | *.lock 211 | 212 | # vim temporary files 213 | *.swp 214 | *.swo 215 | 216 | # TVM generated code 217 | perf 218 | .bash_history 219 | # *.json 220 | *.params 221 | *.ro 222 | *.onnx 223 | *.h5 224 | synset.txt 225 | cat.jpg 226 | cat.png 227 | docs.tgz 228 | cat.png 229 | *.mlmodel 230 | tvm_u.* 231 | tvm_t.* 232 | # Mac OS X 233 | .DS_Store 234 | 235 | # Jetbrain 236 | .idea 237 | .ipython 238 | .jupyter 239 | .nv 240 | .pylint.d 241 | .python_history 242 | .pytest_cache 243 | .local 244 | cmake-build-debug 245 | 246 | # Visual Studio 247 | .vs 248 | 249 | # Visual Studio Code 250 | .vscode 251 | 252 | # tmp file 253 | .nfs* 254 | 255 | # keys 256 | *.pem 257 | *.p12 258 | *.pfx 259 | *.cer 260 | *.crt 261 | *.der 262 | 263 | # patch sentinel 264 | patched.txt 265 | 266 | # Python type checking 267 | .mypy_cache/ 268 | .pyre/ 269 | 270 | # pipenv files 271 | Pipfile 272 | Pipfile.lock 273 | 274 | # conda package artifacts 275 | conda/Dockerfile.cuda* 276 | conda/pkg 277 | .node_repl_history 278 | # nix files 279 | .envrc 280 | *.nix 281 | 282 | # Docker files 283 | .sudo_as_admin_successful 284 | 285 | # Downloaded models/datasets 286 | .tvm_test_data 287 | .dgl 288 | .caffe2 289 | 290 | # Local docs build 291 | _docs/ 292 | jvm/target 293 | .config/configstore/ 294 | .ci-py-scripts/ 295 | 296 | # Generated Hexagon files 297 | src/runtime/hexagon/rpc/hexagon_rpc.h 298 | src/runtime/hexagon/rpc/hexagon_rpc_skel.c 299 | src/runtime/hexagon/rpc/hexagon_rpc_stub.c 300 | 301 | # Local tvm-site checkout 302 | tvm-site/ 303 | 304 | # Generated docs files 305 | gallery/how_to/work_with_microtvm/micro_tvmc.py 306 | 307 | # Test sample data files 308 | !tests/python/ci/sample_prs/*.json 309 | 310 | # Used in CI to communicate between Python and Jenkins 311 | .docker-image-names/ 312 | 313 | # Printed TIR code on disk 314 | *.tir 315 | 316 | # GDB history file 317 | .gdb_history 318 | 319 | 3rdparty 320 | dist 321 | tvm_home 322 | node_modules 323 | .parcel-cache 324 | -------------------------------------------------------------------------------- /web/tests/node/test_packed_func.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 | 34 | test("GetGlobal", () => { 35 | tvm.beginScope(); 36 | let flist = tvm.listGlobalFuncNames(); 37 | let faddOne = tvm.getGlobalFunc("testing.add_one"); 38 | let fecho = tvm.getGlobalFunc("testing.echo"); 39 | 40 | assert(faddOne(tvm.scalar(1, "int")) == 2); 41 | assert(faddOne(tvm.scalar(-1, "int")) == 0); 42 | 43 | // check function argument with different types. 44 | assert(fecho(1123) == 1123); 45 | assert(fecho("xyz") == "xyz"); 46 | 47 | let bytes = new Uint8Array([1, 2, 3]); 48 | let rbytes = fecho(bytes); 49 | assert(rbytes.length == bytes.length); 50 | 51 | for (let i = 0; i < bytes.length; ++i) { 52 | assert(rbytes[i] == bytes[i]); 53 | } 54 | 55 | assert(fecho(undefined) == undefined); 56 | 57 | tvm.beginScope(); 58 | 59 | let arr = tvm.empty([2, 2]).copyFrom([1, 2, 3, 4]); 60 | let arr2 = fecho(arr); 61 | assert(arr.getHandle() == arr2.getHandle()); 62 | assert(arr2.toArray().toString() == arr.toArray().toString()); 63 | 64 | tvm.moveToParentScope(arr2); 65 | tvm.endScope(); 66 | // test move to parent scope and tracking 67 | assert(arr.getHandle(false) == 0); 68 | assert(arr2.handle != 0); 69 | 70 | let mod = tvm.systemLib(); 71 | let ret = fecho(mod); 72 | assert(ret.getHandle() == mod.getHandle()); 73 | assert(flist.length != 0); 74 | tvm.endScope(); 75 | 76 | // assert auto release scope behavior 77 | assert(mod.getHandle(false) == 0); 78 | assert(ret.getHandle(false) == 0); 79 | assert(arr2.getHandle(false) == 0); 80 | assert(fecho._tvmPackedCell.getHandle(false) == 0); 81 | assert(faddOne._tvmPackedCell.getHandle(false) == 0); 82 | }); 83 | 84 | test("ReturnFunc", () => { 85 | tvm.beginScope(); 86 | function addy(y) { 87 | function add(x, z) { 88 | return x + y + z; 89 | } 90 | return add; 91 | } 92 | 93 | let fecho = tvm.getGlobalFunc("testing.echo"); 94 | let myf = tvm.toPackedFunc(addy); 95 | assert(tvm.isPackedFunc(myf)); 96 | let myf2 = tvm.toPackedFunc(myf); 97 | assert(myf2._tvmPackedCell.handle === myf._tvmPackedCell.handle); 98 | let f = myf(10); 99 | 100 | assert(tvm.isPackedFunc(f)); 101 | assert(f(11, 0) == 21); 102 | assert(f("x", 1) == "x101"); 103 | assert(f("x", "yz") == "x10yz"); 104 | 105 | fecho.dispose(); 106 | myf.dispose(); 107 | myf2.dispose(); 108 | // test multiple dispose. 109 | f.dispose(); 110 | f.dispose(); 111 | tvm.endScope(); 112 | }); 113 | 114 | test("RegisterGlobal", () => { 115 | tvm.beginScope(); 116 | tvm.registerFunc("xyz", function (x, y) { 117 | return x + y; 118 | }); 119 | 120 | let f = tvm.getGlobalFunc("xyz"); 121 | assert(f(1, 2) == 3); 122 | f.dispose(); 123 | 124 | let syslib = tvm.systemLib(); 125 | syslib.dispose(); 126 | tvm.endScope(); 127 | }); 128 | 129 | test("ExceptionPassing", () => { 130 | tvm.beginScope(); 131 | tvm.registerFunc("throw_error", function (msg) { 132 | throw Error(msg); 133 | }); 134 | let f = tvm.getGlobalFunc("throw_error"); 135 | try { 136 | f("error-xyz"); 137 | throw Error("error not caught"); 138 | } catch (error) { 139 | assert(error.message.indexOf("error-xyz") != -1); 140 | } 141 | tvm.endScope(); 142 | }); 143 | 144 | test("NDArrayCbArg", () => { 145 | tvm.beginScope(); 146 | let use_count = tvm.getGlobalFunc("testing.object_use_count"); 147 | let record = []; 148 | 149 | let fcheck = tvm.toPackedFunc(function (x, retain) { 150 | assert(use_count(x) == 2); 151 | assert(x.handle != 0); 152 | record.push(x); 153 | if (retain) { 154 | tvm.detachFromCurrentScope(x); 155 | } 156 | }); 157 | 158 | let x = tvm.empty([2], "float32").copyFrom([1, 2]); 159 | assert(use_count(x) == 1); 160 | 161 | fcheck(x, 0); 162 | // auto-released when it is out of scope. 163 | assert(record[0].getHandle(false) == 0); 164 | 165 | assert(use_count(x) == 1); 166 | 167 | fcheck(x, 1); 168 | assert(use_count(x) == 2); 169 | assert(record[1].handle != 0); 170 | tvm.attachToCurrentScope(record[1]); 171 | tvm.endScope(); 172 | assert(record[1].getHandle(false) == 0); 173 | }); 174 | 175 | test("Logging", () => { 176 | tvm.beginScope(); 177 | const log_info = tvm.getGlobalFunc("testing.log_info_str"); 178 | log_info("helow world") 179 | log_info.dispose(); 180 | tvm.endScope(); 181 | }); 182 | -------------------------------------------------------------------------------- /web/emcc/wasm_runtime.cc: -------------------------------------------------------------------------------- 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 | * \file wasm_runtime.cc 22 | * \brief TVM wasm runtime library pack. 23 | */ 24 | 25 | // configurations for tvm logging 26 | #define TVM_LOG_STACK_TRACE 0 27 | #define TVM_LOG_DEBUG 0 28 | #define TVM_LOG_CUSTOMIZE 1 29 | 30 | #define DMLC_USE_LOGGING_LIBRARY 31 | 32 | #include 33 | #include 34 | 35 | #include "src/runtime/c_runtime_api.cc" 36 | #include "src/runtime/container.cc" 37 | #include "src/runtime/contrib/sort/sort.cc" 38 | #include "src/runtime/cpu_device_api.cc" 39 | #include "src/runtime/file_utils.cc" 40 | #include "src/runtime/library_module.cc" 41 | #include "src/runtime/logging.cc" 42 | #include "src/runtime/module.cc" 43 | #include "src/runtime/ndarray.cc" 44 | #include "src/runtime/object.cc" 45 | #include "src/runtime/profiling.cc" 46 | #include "src/runtime/registry.cc" 47 | #include "src/runtime/rpc/rpc_channel.cc" 48 | #include "src/runtime/rpc/rpc_endpoint.cc" 49 | #include "src/runtime/rpc/rpc_event_impl.cc" 50 | #include "src/runtime/rpc/rpc_local_session.cc" 51 | #include "src/runtime/rpc/rpc_module.cc" 52 | #include "src/runtime/rpc/rpc_session.cc" 53 | #include "src/runtime/system_library.cc" 54 | #include "src/runtime/workspace_pool.cc" 55 | // relax setup 56 | #include "src/runtime/memory/memory_manager.cc" 57 | #include "src/runtime/nvtx.cc" 58 | #include "src/runtime/relax_vm/builtin.cc" 59 | #include "src/runtime/relax_vm/bytecode.cc" 60 | #include "src/runtime/relax_vm/executable.cc" 61 | #include "src/runtime/relax_vm/lm_support.cc" 62 | #include "src/runtime/relax_vm/ndarray_cache_support.cc" 63 | #include "src/runtime/relax_vm/vm.cc" 64 | 65 | // --- Implementations of backend and wasm runtime API. --- 66 | 67 | int TVMBackendParallelLaunch(FTVMParallelLambda flambda, void* cdata, int num_task) { 68 | TVMParallelGroupEnv env; 69 | env.num_task = 1; 70 | flambda(0, &env, cdata); 71 | return 0; 72 | } 73 | 74 | int TVMBackendParallelBarrier(int task_id, TVMParallelGroupEnv* penv) { return 0; } 75 | 76 | // --- Environment PackedFuncs for testing --- 77 | namespace tvm { 78 | namespace runtime { 79 | namespace detail { 80 | // Override logging mechanism 81 | [[noreturn]] void LogFatalImpl(const std::string& file, int lineno, const std::string& message) { 82 | std::cerr << "[FATAL] " << file << ":" << lineno << ": " << message << std::endl; 83 | abort(); 84 | } 85 | 86 | void LogMessageImpl(const std::string& file, int lineno, int level, const std::string& message) { 87 | static const char* level_strings_[] = { 88 | "[DEBUG] ", 89 | "[INFO] ", 90 | "[WARNING] ", 91 | "[ERROR] ", 92 | }; 93 | std::cout << level_strings_[level] << file << ":" << lineno << ": " << message << std::endl; 94 | } 95 | 96 | } // namespace detail 97 | 98 | TVM_REGISTER_GLOBAL("testing.echo").set_body([](TVMArgs args, TVMRetValue* ret) { 99 | *ret = args[0]; 100 | }); 101 | 102 | TVM_REGISTER_GLOBAL("testing.ret_string").set_body([](TVMArgs args, TVMRetValue* ret) { 103 | *ret = args[0].operator String(); 104 | }); 105 | 106 | TVM_REGISTER_GLOBAL("testing.log_info_str").set_body([](TVMArgs args, TVMRetValue* ret) { 107 | LOG(INFO) << args[0].operator String(); 108 | }); 109 | 110 | TVM_REGISTER_GLOBAL("testing.log_fatal_str").set_body([](TVMArgs args, TVMRetValue* ret) { 111 | LOG(FATAL) << args[0].operator String(); 112 | }); 113 | 114 | TVM_REGISTER_GLOBAL("testing.add_one").set_body_typed([](int x) { return x + 1; }); 115 | 116 | TVM_REGISTER_GLOBAL("testing.wrap_callback").set_body([](TVMArgs args, TVMRetValue* ret) { 117 | PackedFunc pf = args[0]; 118 | *ret = runtime::TypedPackedFunc([pf]() { pf(); }); 119 | }); 120 | 121 | // internal function used for debug and testing purposes 122 | TVM_REGISTER_GLOBAL("testing.object_use_count").set_body([](TVMArgs args, TVMRetValue* ret) { 123 | runtime::ObjectRef obj = args[0]; 124 | // substract the current one because we always copy 125 | // and get another value. 126 | *ret = (obj.use_count() - 1); 127 | }); 128 | 129 | void ArrayDecodeStorage(NDArray cpu_arr, std::string bytes, std::string format, std::string dtype) { 130 | if (format == "f32-to-bf16" && dtype == "float32") { 131 | std::vector buffer(bytes.length() / 2); 132 | std::memcpy(buffer.data(), bytes.data(), buffer.size() * 2); 133 | // decode bf16 to f32 134 | const uint16_t* bf16 = reinterpret_cast(buffer.data()); 135 | uint32_t* data = static_cast(cpu_arr->data); 136 | ICHECK(cpu_arr.IsContiguous()); 137 | size_t size = 1; 138 | for (int i = 0; i < cpu_arr->ndim; ++i) { 139 | size *= cpu_arr->shape[i]; 140 | } 141 | ICHECK_EQ(size, bytes.length() / 2); 142 | for (size_t i = 0; i < size; ++i) { 143 | data[i] = static_cast(bf16[i]) << 16; 144 | } 145 | } else { 146 | cpu_arr.CopyFromBytes(bytes.data(), bytes.length()); 147 | } 148 | } 149 | 150 | TVM_REGISTER_GLOBAL("tvmjs.array.decode_storage").set_body_typed(ArrayDecodeStorage); 151 | } // namespace runtime 152 | } // namespace tvm 153 | -------------------------------------------------------------------------------- /examples/chrome-extension/src/popup.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This code is partially adapted from the openai-chatgpt-chrome-extension repo: 4 | // https://github.com/jessedi0n/openai-chatgpt-chrome-extension 5 | 6 | import './popup.css'; 7 | 8 | import { ChatModule, AppConfig, InitProgressReport } from "@mlc-ai/web-llm"; 9 | import { ProgressBar, Line } from "progressbar.js"; 10 | 11 | // TODO: Surface this as an experimental option to the user 12 | const useWebGPU = true; 13 | 14 | const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); 15 | 16 | const queryInput = document.getElementById("query-input")!; 17 | const submitButton = document.getElementById("submit-button")!; 18 | 19 | var cm: ChatModule; 20 | var context = ""; 21 | var isLoadingParams = false; 22 | 23 | const generateProgressCallback = (_step: number, message: string) => { 24 | updateAnswer(message); 25 | }; 26 | 27 | if (useWebGPU) { 28 | 29 | fetchPageContents(); 30 | 31 | (submitButton).disabled = true; 32 | 33 | cm = new ChatModule(); 34 | var progressBar: ProgressBar = new Line('#loadingContainer', { 35 | strokeWidth: 4, 36 | easing: 'easeInOut', 37 | duration: 1400, 38 | color: '#ffd166', 39 | trailColor: '#eee', 40 | trailWidth: 1, 41 | svgStyle: { width: '100%', height: '100%' } 42 | }); 43 | 44 | const appConfig: AppConfig = { 45 | model_list: [ 46 | { 47 | "model_url": "https://huggingface.co/mlc-ai/Mistral-7B-Instruct-v0.2-q4f16_1-MLC/resolve/main/", 48 | "local_id": "Mistral-7B-Instruct-v0.2-q4f16_1", 49 | "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", 50 | "required_features": ["shader-f16"], 51 | } 52 | ] 53 | } 54 | 55 | cm.setInitProgressCallback((report: InitProgressReport) => { 56 | console.log(report.text, report.progress); 57 | progressBar.animate(report.progress, { 58 | duration: 50 59 | }); 60 | if (report.progress == 1.0) { 61 | enableInputs(); 62 | } 63 | }); 64 | 65 | await cm.reload("Mistral-7B-Instruct-v0.1-q4f32_1", undefined, appConfig); 66 | 67 | isLoadingParams = true; 68 | } else { 69 | const loadingBarContainer = document.getElementById("loadingContainer")!; 70 | loadingBarContainer.remove(); 71 | queryInput.focus(); 72 | } 73 | 74 | function enableInputs() { 75 | if (isLoadingParams) { 76 | sleep(500); 77 | (submitButton).disabled = false; 78 | const loadingBarContainer = document.getElementById("loadingContainer")!; 79 | loadingBarContainer.remove(); 80 | queryInput.focus(); 81 | isLoadingParams = false; 82 | } 83 | } 84 | 85 | // Disable submit button if input field is empty 86 | queryInput.addEventListener("keyup", () => { 87 | if ((queryInput).value === "") { 88 | (submitButton).disabled = true; 89 | } else { 90 | (submitButton).disabled = false; 91 | } 92 | }); 93 | 94 | // If user presses enter, click submit button 95 | queryInput.addEventListener("keyup", (event) => { 96 | if (event.code === "Enter") { 97 | event.preventDefault(); 98 | submitButton.click(); 99 | } 100 | }); 101 | 102 | // Listen for clicks on submit button 103 | async function handleClick() { 104 | // Get the message from the input field 105 | const message = (queryInput).value; 106 | console.log("message", message); 107 | if (!useWebGPU) { 108 | // Send the query to the background script 109 | chrome.runtime.sendMessage({ input: message }); 110 | } 111 | // Clear the answer 112 | document.getElementById("answer")!.innerHTML = ""; 113 | // Hide the answer 114 | document.getElementById("answerWrapper")!.style.display = "none"; 115 | // Show the loading indicator 116 | document.getElementById("loading-indicator")!.style.display = "block"; 117 | 118 | if (useWebGPU) { 119 | // Generate response 120 | var inp = message; 121 | if (context.length > 0) { 122 | inp = "Use only the following context when answering the question at the end. Don't use any other knowledge.\n" + context + "\n\nQuestion: " + message + "\n\nHelpful Answer: "; 123 | } 124 | console.log("Input:", inp); 125 | const response = await cm.generate(inp, generateProgressCallback); 126 | console.log("response", response); 127 | } 128 | } 129 | submitButton.addEventListener("click", handleClick); 130 | 131 | // Listen for messages from the background script 132 | chrome.runtime.onMessage.addListener(({ answer, error }) => { 133 | if (answer) { 134 | updateAnswer(answer); 135 | } 136 | }); 137 | 138 | function updateAnswer(answer: string) { 139 | // Show answer 140 | document.getElementById("answerWrapper")!.style.display = "block"; 141 | const answerWithBreaks = answer.replace(/\n/g, '
'); 142 | document.getElementById("answer")!.innerHTML = answerWithBreaks; 143 | // Add event listener to copy button 144 | document.getElementById("copyAnswer")!.addEventListener("click", () => { 145 | // Get the answer text 146 | const answerText = answer; 147 | // Copy the answer text to the clipboard 148 | navigator.clipboard.writeText(answerText) 149 | .then(() => console.log("Answer text copied to clipboard")) 150 | .catch((err) => console.error("Could not copy text: ", err)); 151 | }); 152 | const options: Intl.DateTimeFormatOptions = { month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }; 153 | const time = new Date().toLocaleString('en-US', options); 154 | // Update timestamp 155 | document.getElementById("timestamp")!.innerText = time; 156 | // Hide loading indicator 157 | document.getElementById("loading-indicator")!.style.display = "none"; 158 | } 159 | 160 | function fetchPageContents() { 161 | chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) { 162 | var port = chrome.tabs.connect(tabs[0].id, { name: "channelName" }); 163 | port.postMessage({}); 164 | port.onMessage.addListener(function (msg) { 165 | console.log("Page contents:", msg.contents); 166 | if (useWebGPU) { 167 | context = msg.contents 168 | } else { 169 | chrome.runtime.sendMessage({ context: msg.contents }); 170 | } 171 | }); 172 | }); 173 | } 174 | 175 | // Grab the page contents when the popup is opened 176 | window.onload = function () { 177 | if (!useWebGPU) { 178 | fetchPageContents(); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /web/src/ctypes.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 | * Types for C API. 22 | */ 23 | 24 | /** A pointer to points to the raw address space. */ 25 | export type Pointer = number; 26 | 27 | /** A pointer offset, need to add a base address to get a valid ptr. */ 28 | export type PtrOffset = number; 29 | 30 | // -- TVM runtime C API -- 31 | /** 32 | * const char *TVMGetLastError(); 33 | */ 34 | export type FTVMGetLastError = () => Pointer; 35 | 36 | /** 37 | * void TVMAPISetLastError(const char* msg); 38 | */ 39 | export type FTVMAPISetLastError = (msg: Pointer) => void; 40 | 41 | /** 42 | * int TVMModGetFunction(TVMModuleHandle mod, 43 | * const char* func_name, 44 | * int query_imports, 45 | * TVMFunctionHandle *out); 46 | */ 47 | export type FTVMModGetFunction = ( 48 | mod: Pointer, funcName: Pointer, queryImports: number, out: Pointer) => number; 49 | /** 50 | * int TVMModImport(TVMModuleHandle mod, 51 | * TVMModuleHandle dep); 52 | */ 53 | export type FTVMModImport = (mod: Pointer, dep: Pointer) => number; 54 | 55 | /** 56 | * int TVMModFree(TVMModuleHandle mod); 57 | */ 58 | export type FTVMModFree = (mod: Pointer) => number; 59 | 60 | /** 61 | * int TVMFuncFree(TVMFunctionHandle func); 62 | */ 63 | export type FTVMFuncFree = (func: Pointer) => number; 64 | 65 | /** 66 | * int TVMFuncCall(TVMFunctionHandle func, 67 | * TVMValue* arg_values, 68 | * int* type_codes, 69 | * int num_args, 70 | * TVMValue* ret_val, 71 | * int* ret_type_code); 72 | */ 73 | export type FTVMFuncCall = ( 74 | func: Pointer, argValues: Pointer, typeCode: Pointer, 75 | nargs: number, retValue: Pointer, retCode: Pointer) => number; 76 | 77 | /** 78 | * int TVMCFuncSetReturn(TVMRetValueHandle ret, 79 | * TVMValue* value, 80 | * int* type_code, 81 | * int num_ret); 82 | */ 83 | export type FTVMCFuncSetReturn = ( 84 | ret: Pointer, value: Pointer, typeCode: Pointer, numRet: number) => number; 85 | 86 | /** 87 | * int TVMCbArgToReturn(TVMValue* value, int* code); 88 | */ 89 | export type FTVMCbArgToReturn = (value: Pointer, code: Pointer) => number; 90 | 91 | /** 92 | * int TVMFuncListGlobalNames(int* outSize, const char*** outArray); 93 | */ 94 | export type FTVMFuncListGlobalNames = (outSize: Pointer, outArray: Pointer) => number; 95 | 96 | /** 97 | * int TVMFuncRegisterGlobal( 98 | * const char* name, TVMFunctionHandle f, int override); 99 | */ 100 | export type FTVMFuncRegisterGlobal = ( 101 | name: Pointer, f: Pointer, override: number) => number; 102 | 103 | /** 104 | *int TVMFuncGetGlobal(const char* name, TVMFunctionHandle* out); 105 | */ 106 | export type FTVMFuncGetGlobal = (name: Pointer, out: Pointer) => number; 107 | 108 | /** 109 | * int TVMArrayAlloc(const tvm_index_t* shape, 110 | * int ndim, 111 | * int dtype_code, 112 | * int dtype_bits, 113 | * int dtype_lanes, 114 | * int device_type, 115 | * int device_id, 116 | * TVMArrayHandle* out); 117 | */ 118 | export type FTVMArrayAlloc = ( 119 | shape: Pointer, ndim: number, 120 | dtypeCode: number, dtypeBits: number, 121 | dtypeLanes: number, deviceType: number, deviceId: number, 122 | out: Pointer) => number; 123 | 124 | /** 125 | * int TVMArrayFree(TVMArrayHandle handle); 126 | */ 127 | export type FTVMArrayFree = (handle: Pointer) => number; 128 | 129 | /** 130 | * int TVMArrayCopyFromBytes(TVMArrayHandle handle, 131 | * void* data, 132 | * size_t nbytes); 133 | */ 134 | export type FTVMArrayCopyFromBytes = ( 135 | handle: Pointer, data: Pointer, nbytes: number) => number; 136 | 137 | /** 138 | * int TVMArrayCopyToBytes(TVMArrayHandle handle, 139 | * void* data, 140 | * size_t nbytes); 141 | */ 142 | export type FTVMArrayCopyToBytes = ( 143 | handle: Pointer, data: Pointer, nbytes: number) => number; 144 | 145 | /** 146 | * int TVMArrayCopyFromTo(TVMArrayHandle from, 147 | * TVMArrayHandle to, 148 | * TVMStreamHandle stream); 149 | */ 150 | export type FTVMArrayCopyFromTo = ( 151 | from: Pointer, to: Pointer, stream: Pointer) => number; 152 | 153 | /** 154 | * int TVMSynchronize(int device_type, int device_id, TVMStreamHandle stream); 155 | */ 156 | export type FTVMSynchronize = ( 157 | deviceType: number, deviceId: number, stream: Pointer) => number; 158 | 159 | /** 160 | * typedef int (*TVMBackendPackedCFunc)(TVMValue* args, 161 | * int* type_codes, 162 | * int num_args, 163 | * TVMValue* out_ret_value, 164 | * int* out_ret_tcode); 165 | */ 166 | export type FTVMBackendPackedCFunc = ( 167 | argValues: Pointer, argCodes: Pointer, nargs: number, 168 | outValue: Pointer, outCode: Pointer) => number; 169 | 170 | 171 | /** 172 | * int TVMObjectFree(TVMObjectHandle obj); 173 | */ 174 | export type FTVMObjectFree = (obj: Pointer) => number; 175 | 176 | /** 177 | * int TVMObjectGetTypeIndex(TVMObjectHandle obj, unsigned* out_tindex); 178 | */ 179 | export type FTVMObjectGetTypeIndex = (obj: Pointer, out_tindex: Pointer) => number; 180 | 181 | /** 182 | * int TVMObjectTypeIndex2Key(unsigned tindex, char** out_type_key); 183 | */ 184 | export type FTVMObjectTypeIndex2Key = (type_index: number, out_type_key: Pointer) => number; 185 | 186 | /** 187 | * int TVMObjectTypeKey2Index(const char* type_key, unsigned* out_tindex); 188 | */ 189 | export type FTVMObjectTypeKey2Index = (type_key: Pointer, out_tindex: Pointer) => number; 190 | 191 | // -- TVM Wasm Auxiliary C API -- 192 | 193 | /** void* TVMWasmAllocSpace(int size); */ 194 | export type FTVMWasmAllocSpace = (size: number) => Pointer; 195 | 196 | /** void TVMWasmFreeSpace(void* data); */ 197 | export type FTVMWasmFreeSpace = (ptr: Pointer) => void; 198 | 199 | /** 200 | * int TVMWasmPackedCFunc(TVMValue* args, 201 | * int* type_codes, 202 | * int num_args, 203 | * TVMRetValueHandle ret, 204 | * void* resource_handle); 205 | */ 206 | export type FTVMWasmPackedCFunc = ( 207 | args: Pointer, typeCodes: Pointer, nargs: number, 208 | ret: Pointer, resourceHandle: Pointer) => number; 209 | 210 | /** 211 | * int TVMWasmFuncCreateFromCFunc(void* resource_handle, 212 | * TVMFunctionHandle *out); 213 | */ 214 | export type FTVMWasmFuncCreateFromCFunc = ( 215 | resource: Pointer, out: Pointer) => number; 216 | 217 | /** 218 | * void TVMWasmPackedCFuncFinalizer(void* resource_handle); 219 | */ 220 | export type FTVMWasmPackedCFuncFinalizer = (resourceHandle: Pointer) => void; 221 | 222 | /** 223 | * Size of common data types. 224 | */ 225 | export const enum SizeOf { 226 | U8 = 1, 227 | U16 = 2, 228 | I32 = 4, 229 | I64 = 8, 230 | F32 = 4, 231 | F64 = 8, 232 | TVMValue = 8, 233 | DLDataType = I32, 234 | DLDevice = I32 + I32, 235 | } 236 | 237 | /** 238 | * Argument Type code in TVM FFI. 239 | */ 240 | export const enum ArgTypeCode { 241 | Int = 0, 242 | UInt = 1, 243 | Float = 2, 244 | TVMOpaqueHandle = 3, 245 | Null = 4, 246 | TVMDataType = 5, 247 | DLDevice = 6, 248 | TVMDLTensorHandle = 7, 249 | TVMObjectHandle = 8, 250 | TVMModuleHandle = 9, 251 | TVMPackedFuncHandle = 10, 252 | TVMStr = 11, 253 | TVMBytes = 12, 254 | TVMNDArrayHandle = 13, 255 | TVMObjectRValueRefArg = 14 256 | } 257 | -------------------------------------------------------------------------------- /site/img/logo/catalyst.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 51 | 53 | 62 | 63 | 68 | 70 | 77 | 78 | 83 | 85 | 86 | 87 | 118 | 119 | --------------------------------------------------------------------------------