aione-web 0.0.0-stage → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AIOne.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ export interface AIOneOptions {
2
+ workerUrl?: string | URL;
3
+ runtimeUrl?: string;
4
+ wasmUrl?: string;
5
+ threads?: boolean;
6
+ name?: string;
7
+ }
8
+
9
+ export default class AIOne {
10
+ static create(options?: AIOneOptions): Promise<AIOne>;
11
+
12
+ loadModel(file: File): Promise<void>;
13
+ generate(prompt: string, maxTokens?: number): Promise<string>;
14
+ unloadModel(): Promise<void>;
15
+ close(): void;
16
+ }
17
+
18
+ export interface Message {
19
+ role: "user" | "assistant";
20
+ content: string;
21
+ }
package/AIOne.js ADDED
@@ -0,0 +1,119 @@
1
+ export default class AIOne {
2
+ static async create(options = {}) {
3
+ const workerUrl = new URL(
4
+ options.workerUrl ?? "/aio1-worker.js",
5
+ globalThis.location?.href ?? import.meta.url
6
+ );
7
+
8
+ if (options.threads) {
9
+ workerUrl.searchParams.set("threads", "1");
10
+ }
11
+
12
+ if (options.runtimeUrl) {
13
+ workerUrl.searchParams.set("runtime", options.runtimeUrl);
14
+ }
15
+
16
+ if (options.wasmUrl) {
17
+ workerUrl.searchParams.set("wasm", options.wasmUrl);
18
+ }
19
+
20
+ if (typeof SharedWorker === "undefined") {
21
+ throw new Error("SharedWorker is not supported by this browser");
22
+ }
23
+
24
+ const worker = new SharedWorker(workerUrl, {
25
+ name: options.name ?? "AIOne",
26
+ type: "classic"
27
+ });
28
+
29
+ worker.onerror = event => {
30
+ console.error("AIOne SharedWorker error:", event);
31
+ };
32
+
33
+ const client = new AIOne(worker.port);
34
+ await client.ready();
35
+
36
+ return client;
37
+ }
38
+
39
+ constructor(port) {
40
+ this.port = port;
41
+ this.nextId = 0;
42
+ this.pending = new Map();
43
+
44
+ this.readyPromise = new Promise((resolve, reject) => {
45
+ this.resolveReady = resolve;
46
+ this.rejectReady = reject;
47
+ });
48
+
49
+ this.port.onmessage = ({ data }) => {
50
+ if (data.type === "ready") {
51
+ this.resolveReady();
52
+ return;
53
+ }
54
+
55
+ const pending = this.pending.get(data.id);
56
+ if (!pending) return;
57
+
58
+ this.pending.delete(data.id);
59
+
60
+ if (data.error) {
61
+ pending.reject(new Error(data.error));
62
+ } else {
63
+ pending.resolve(data.result);
64
+ }
65
+ };
66
+
67
+ this.port.onmessageerror = () => {
68
+ this.rejectReady(new Error("AIOne worker message error"));
69
+
70
+ for (const pending of this.pending.values()) {
71
+ pending.reject(new Error("AIOne worker message error"));
72
+ }
73
+
74
+ this.pending.clear();
75
+ };
76
+
77
+ this.port.start();
78
+ }
79
+
80
+ ready() {
81
+ return this.readyPromise;
82
+ }
83
+
84
+ call(method, args = {}) {
85
+ const id = ++this.nextId;
86
+
87
+ return new Promise((resolve, reject) => {
88
+ this.pending.set(id, { resolve, reject });
89
+
90
+ this.port.postMessage({
91
+ id,
92
+ method,
93
+ args
94
+ });
95
+ });
96
+ }
97
+
98
+ loadModel(file) {
99
+ return this.call("loadModel", { file });
100
+ }
101
+
102
+ generate(prompt, maxTokens = 512) {
103
+ return this.call("generate", { prompt, maxTokens });
104
+ }
105
+
106
+ unloadModel() {
107
+ return this.call("unloadModel");
108
+ }
109
+
110
+ close() {
111
+ this.port.close();
112
+
113
+ for (const pending of this.pending.values()) {
114
+ pending.reject(new Error("AIOne connection closed"));
115
+ }
116
+
117
+ this.pending.clear();
118
+ }
119
+ }
package/README.md CHANGED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
package/RPCClient.js ADDED
@@ -0,0 +1,55 @@
1
+ export default class RPCClient {
2
+ constructor(options = {}) {
3
+ this.workerUrl = options.workerUrl ??
4
+ new URL("./shared-worker.js", import.meta.url);
5
+
6
+ this.pending = new Map();
7
+ this.nextId = 0;
8
+ }
9
+
10
+ connect() {
11
+ return new Promise((resolve, reject) => {
12
+ this.worker = new SharedWorker(this.workerUrl, {
13
+ type: "module",
14
+ name: "AIOne"
15
+ });
16
+
17
+ this.port = this.worker.port;
18
+
19
+ this.port.onmessage = ({ data }) => {
20
+ const pending = this.pending.get(data.id);
21
+ if (!pending) return;
22
+
23
+ this.pending.delete(data.id);
24
+
25
+ if (data.error) {
26
+ pending.reject(new Error(data.error));
27
+ } else {
28
+ pending.resolve(data.result);
29
+ }
30
+ };
31
+
32
+ this.port.onmessageerror = () => {
33
+ reject(new Error("AIOne worker message error"));
34
+ };
35
+
36
+ this.port.start();
37
+
38
+ this.call("hello").then(resolve, reject);
39
+ });
40
+ }
41
+
42
+ call(method, args = {}) {
43
+ const id = ++this.nextId;
44
+
45
+ return new Promise((resolve, reject) => {
46
+ this.pending.set(id, { resolve, reject });
47
+
48
+ this.port.postMessage({
49
+ id,
50
+ method,
51
+ args
52
+ });
53
+ });
54
+ }
55
+ }
package/package.json CHANGED
@@ -1,6 +1,67 @@
1
1
  {
2
2
  "name": "aione-web",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.1",
4
+ "description": "Browser AI inference powered by llama.cpp and WebGPU",
5
+ "keywords": [
6
+ "ai",
7
+ "llm",
8
+ "llama.cpp",
9
+ "webgpu",
10
+ "wasm",
11
+ "inference",
12
+ "browser",
13
+ "sharedworker"
14
+ ],
15
+ "homepage": "https://github.com/Iemand005/AIOneWeb#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/Iemand005/AIOneWeb/issues"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/Iemand005/AIOneWeb.git"
22
+ },
23
+ "license": "ISC",
24
+ "author": "",
25
+ "type": "module",
26
+ "main": "./AIOne.js",
27
+ "module": "./AIOne.js",
28
+ "types": "./AIOne.d.ts",
29
+ "exports": {
30
+ ".": {
31
+ "types": "./AIOne.d.ts",
32
+ "import": "./AIOne.js",
33
+ "default": "./AIOne.js"
34
+ },
35
+ "./rpc": {
36
+ "import": "./RPCClient.js",
37
+ "default": "./RPCClient.js"
38
+ },
39
+ "./runtime": "./worker-runtime.js",
40
+ "./workerfs-bridge": "./workerfs-bridge.js",
41
+ "./single": "./build/Release/AIOneWeb.js",
42
+ "./single/wasm": "./build/Release/AIOneWeb.wasm",
43
+ "./mt": "./build/Release-mt/AIOneWebMT.js",
44
+ "./mt/wasm": "./build/Release-mt/AIOneWebMT.wasm",
45
+ "./AIOneWeb.js": "./build/Release/AIOneWeb.js",
46
+ "./AIOneWeb.wasm": "./build/Release/AIOneWeb.wasm",
47
+ "./AIOneWebMT.js": "./build/Release-mt/AIOneWebMT.js",
48
+ "./AIOneWebMT.wasm": "./build/Release-mt/AIOneWebMT.wasm",
49
+ "./build/*": "./build/*",
50
+ "./package.json": "./package.json"
51
+ },
52
+ "files": [
53
+ "AIOne.js",
54
+ "AIOne.d.ts",
55
+ "RPCClient.js",
56
+ "worker-runtime.js",
57
+ "workerfs-bridge.js",
58
+ "build/Release/AIOneWeb.js",
59
+ "build/Release/AIOneWeb.wasm",
60
+ "build/Release-mt/AIOneWebMT.js",
61
+ "build/Release-mt/AIOneWebMT.wasm",
62
+ "README.md"
63
+ ],
64
+ "publishConfig": {
65
+ "access": "public"
66
+ }
67
+ }
@@ -0,0 +1,124 @@
1
+ let module;
2
+ let session;
3
+ let initializing;
4
+ let mountedFile;
5
+
6
+ let operationQueue = Promise.resolve();
7
+
8
+ const WORKER_URL = new URL(self.location.href);
9
+ const SEARCH_PARAMS = WORKER_URL.searchParams;
10
+ const USE_THREADS = SEARCH_PARAMS.get("threads") === "1";
11
+
12
+ const DEFAULT_RUNTIME_DIR = new URL(
13
+ USE_THREADS ? "./build/Release-mt/" : "./build/Release/",
14
+ WORKER_URL
15
+ );
16
+
17
+ const RUNTIME_URL = SEARCH_PARAMS.get("runtime")
18
+ ? new URL(SEARCH_PARAMS.get("runtime"), WORKER_URL).href
19
+ : new URL(
20
+ USE_THREADS ? "AIOneWebMT.js" : "AIOneWeb.js",
21
+ DEFAULT_RUNTIME_DIR
22
+ ).href;
23
+
24
+ const WASM_URL = SEARCH_PARAMS.get("wasm");
25
+ const HAS_EXTERNAL_RUNTIME = typeof Module !== "undefined";
26
+
27
+ if (!HAS_EXTERNAL_RUNTIME) {
28
+ importScripts(RUNTIME_URL);
29
+ }
30
+
31
+ function locateFile(path) {
32
+ if (WASM_URL) {
33
+ return new URL(WASM_URL, WORKER_URL).href;
34
+ }
35
+
36
+ return new URL(path, RUNTIME_URL).href;
37
+ }
38
+
39
+ async function initialize(file) {
40
+ if (session) {
41
+ throw new Error("A model is already loaded");
42
+ }
43
+
44
+ if (module && mountedFile !== file.name) {
45
+ throw new Error("This runtime is already initialized with another model file");
46
+ }
47
+
48
+ if (!initializing && !module) {
49
+ initializing = (async () => {
50
+ const moduleOptions = { workerFile: file };
51
+
52
+ if (WASM_URL || !HAS_EXTERNAL_RUNTIME) {
53
+ moduleOptions.locateFile = locateFile;
54
+ }
55
+
56
+ module = await Module(moduleOptions);
57
+
58
+ mountedFile = file.name;
59
+ session = new module.ModelSession();
60
+ })();
61
+
62
+ try {
63
+ await initializing;
64
+ } catch (error) {
65
+ initializing = undefined;
66
+ module = undefined;
67
+ throw error;
68
+ }
69
+ } else if (initializing) {
70
+ await initializing;
71
+ }
72
+
73
+ session.loadModel("/models/" + file.name);
74
+ }
75
+
76
+ async function invoke(method, args) {
77
+ switch (method) {
78
+ case "loadModel":
79
+ return initialize(args.file);
80
+
81
+ case "generate":
82
+ if (!session?.isLoaded()) {
83
+ throw new Error("No model is loaded");
84
+ }
85
+
86
+ return session.generate(args.prompt, args.maxTokens ?? 512);
87
+
88
+ case "unloadModel":
89
+ if (session?.isLoaded()) {
90
+ session.unloadModel();
91
+ }
92
+ return;
93
+
94
+ default:
95
+ throw new Error(`Unknown method: ${method}`);
96
+ }
97
+ }
98
+
99
+ self.onconnect = event => {
100
+ const port = event.ports[0];
101
+
102
+ port.onmessage = ({ data }) => {
103
+ const execute = operationQueue.then(() =>
104
+ invoke(data.method, data.args ?? {})
105
+ );
106
+
107
+ operationQueue = execute.catch(() => {});
108
+
109
+ execute.then(
110
+ result => port.postMessage({
111
+ id: data.id,
112
+ result
113
+ }),
114
+
115
+ error => port.postMessage({
116
+ id: data.id,
117
+ error: error?.message ?? String(error)
118
+ })
119
+ );
120
+ };
121
+
122
+ port.start();
123
+ port.postMessage({ type: "ready" });
124
+ };
@@ -0,0 +1,10 @@
1
+
2
+ Module['preRun'] = Module['preRun'] || [];
3
+
4
+ Module['preRun'].push(function() {
5
+ FS.mkdir('/models');
6
+
7
+ FS.mount(WORKERFS, {
8
+ files: [Module['workerFile']]
9
+ }, '/models');
10
+ });