microsoft / onnxruntime

ONNX Runtime: cross-platform, high performance ML inferencing and training accelerator
https://onnxruntime.ai
MIT License
13.5k stars 2.76k forks source link

[web] ES6 modules #10913

Open josephrocca opened 2 years ago

josephrocca commented 2 years ago

Is your feature request related to a problem? Please describe. I'm trying to get ONNX Runtime Web working in Deno. Deno maintains very close compatibility with the browser, but there's no concept of HTML/DOM since it's a server-side language, so the usual script tag import method is not possible.

System information

Describe the solution you'd like It would be great if the package had an ES6 modules build available so that it could be imported like this:

import ort from "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.10.0/dist/ort.es6.js"
josephrocca commented 2 years ago

I've just realised that you can simply write this: (Edit: wrong - read next comment)

import "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.10.0/dist/ort.js"

And it will create the window.ort global. Not ideal since it pollutes the global namespace, but at least this means it's possible to import it into Deno.

I'll leave this issue open since it still would be nice to have proper ES6 module support.

josephrocca commented 2 years ago

Oh, but now I see that a new problem arises. If we do a bare module import like this:

import "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.10.0/dist/ort.js";

then ort.js isn't able to find the wasm file because document.currentScript.src doesn't exist in modules (and ort.js is treated like a module in this case). So, to fix this, all references to document.currentScript.src should try to fall back to import.meta.url.

Here's an example of some code in ort.js trying to detect where it's running, but I think this code is actually generated by webpack? (I haven't used webpack before)

var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;

You can see other examples by searching onnxruntime-web@1.10.0/dist/ort.js for "currentScript" using Ctrl+F.

I was only able to find one reference to document.currentScript.src within the ONNX Runtime Web codebase itself.

@hanbitmyths, @fs-eire Wondering if you could provide guidance on if/how I can submit a pull request to fall back to import.meta.url when ort.js is trying to determine its own location? Specifically in regard to how this _scriptDir stuff is generated by the bundler. This would allow the library to work in Deno and with a "bare" ES6 import statement in the browser.

Note that currently it falls back to __filename which has a default hard-coded value of /index.js, which causes ort.js to try to load the wasm file from /ort-wasm-simd-threaded.wasm. So, if it's possible (?) to change the default __filename to a runtime-generated value, then a quick solution might be to set it to import.meta.url by default.

fs-eire commented 2 years ago

I will take a look at this issue.

nestarz commented 1 year ago

Any workarounds to make onnx runtime works on Deno ?

josephrocca commented 1 year ago

@fs-eire Wondering if you could just add this to the top of the published ort.js file?

if(typeof document === "undefined") {
  var document = {currentScript:{src:import.meta.url}};
}

I.e. just add a "dummy" document to fix the issue in Deno and other JS runtimes that don't have document/DOM stuff. It's not a "proper" fix, but is simple enough that it might make sense until the webpack stuff mentioned in my previous comment can be sorted out?

fs-eire commented 1 year ago

I found that Emscripten actually supports this feature. I am trying to make a change to support it for ORTWEB.

nestarz commented 1 year ago

I published a repo with a working version of onnx-runtime-web on Deno (note: it discard multithreading !).

https://github.com/nestarz/onnx_runtime

I built the wasm files with:

set_property(TARGET onnxruntime_webassembly APPEND_STRING PROPERTY LINK_FLAGS " -s EXPORT_ES6=1 -s MODULARIZE=1 -s USE_ES6_IMPORT_META=1")

fs-eire commented 1 year ago

@nestarz thank you so much for this example! it's a good learning for me.

Currently, we have 3 different scenarios for consuming the onnxruntime-web library:

  1. consume from script tag ( <script src="https://some-cdn-server/ort.min.js">):
  2. consume from module import ( import * as ort from 'onnxruntime-web' )
  3. consume from module URL (import * as ort from "https://deno.land/x/onnx_runtime/mod.ts";)

There are a few hacks we currently doing in our build process :

combining of the hacks, it makes ORT web very difficult to support ES6 module for now. We need to re-think the balance between the benefit of (single-file distribution and file size) VS. (easy-to-maintain code and webpack friendly)

fs-eire commented 1 year ago

15772: This change turns onnxruntime-common into a ESModule/commonJS "dual" package, which means it is able to comsumed as either a ESM or CJS module.

When this is merged, following-up work will start for onnxruntime-web.

josephrocca commented 1 year ago

Just realised that OR Web currently works with Deno by simply setting ort.env.wasm.wasmPaths to avoid the issue I mentioned in my earlier comment. For example, this works:

import "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.15.0/dist/ort.js";
ort.env.wasm.wasmPaths = "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.15.0/dist/";
let onnxImageSession = await ort.InferenceSession.create("https://huggingface.co/rocca/openai-clip-js/resolve/main/clip-image-vit-32-float32.onnx", { executionProviders: ["wasm"] });
fs-eire commented 9 months ago

added ESM exports for onnxruntime-web in latest main branch. ( will later paste nightly build version )

However ort.env.wasm.wasmPaths may still need to manually set.

Please try it out. If it's still not working, let me know the repro steps and I will fix

josephrocca commented 8 months ago

Awesome! Awaiting nightly script CDN URL 🫡

fs-eire commented 8 months ago

Please try 1.17.0-dev.20231014-ae211999dd

josephrocca commented 8 months ago

Hmm, for some reason on JSDelivr I get a 403 HTTP error with this message: "Package size exceeded the configured limit of 150 MB.":

let ort = await import("https://cdn.jsdelivr.net/npm/onnxruntime-web@1.17.0-dev.20231014-ae211999dd/dist/esm/ort.js");

I tried npm install onnxruntime-web@1.17.0-dev.20231014-ae211999dd and then loaded from node_modules like this:

let ort = await import("./node_modules/onnxruntime-web/dist/esm/ort.js");

Then it loads but then throws:

error: Uncaught (in promise) Error: no available backend found. ERR: [wasm] ReferenceError: __dirname is not defined
    at resolveBackend (file:///pathtofolder/node_modules/onnxruntime-web/dist/esm/ort.js:105:13)
    at async Function.create (file:///pathtofolder/node_modules/onnxruntime-web/dist/esm/ort.js:1025:26)
    at async file:///pathtofolder/main.mjs:35:24

And if I try:

import ort from "./node_modules/onnxruntime-web/dist/esm/ort.js";

Then it throws:

error: Uncaught SyntaxError: The requested module './node_modules/onnxruntime-web/dist/esm/ort.js' does not provide an export named 'default'
import ort from "./node_modules/onnxruntime-web/dist/esm/ort.js";

And using esm.sh like this:

import ort from "https://esm.sh/onnxruntime-web@1.17.0-dev.20231014-ae211999dd/dist/esm/ort.js";

also throws:

error: Uncaught SyntaxError: The requested module 'https://esm.sh/onnxruntime-web@1.17.0-dev.20231014-ae211999dd/dist/esm/ort.js' does not provide an export named 'default'
import ort from "https://esm.sh/onnxruntime-web@1.17.0-dev.20231014-ae211999dd/dist/esm/ort.js";