@pie-project/server-web 0.5.2 → 0.6.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/dist/worker.mjs CHANGED
@@ -81,20 +81,24 @@ function pie_init(log_filter) {
81
81
  var len0 = WASM_VECTOR_LEN;
82
82
  wasm2.pie_init(ptr0, len0);
83
83
  }
84
- function pie_install_language(language, component) {
84
+ function pie_install_language(language, component, precompiled) {
85
85
  const ptr0 = passStringToWasm0(language, wasm2.__wbindgen_export, wasm2.__wbindgen_export2);
86
86
  const len0 = WASM_VECTOR_LEN;
87
87
  const ptr1 = passArray8ToWasm0(component, wasm2.__wbindgen_export);
88
88
  const len1 = WASM_VECTOR_LEN;
89
- const ret = wasm2.pie_install_language(ptr0, len0, ptr1, len1);
89
+ var ptr2 = isLikeNone(precompiled) ? 0 : passArray8ToWasm0(precompiled, wasm2.__wbindgen_export);
90
+ var len2 = WASM_VECTOR_LEN;
91
+ const ret = wasm2.pie_install_language(ptr0, len0, ptr1, len1, ptr2, len2);
90
92
  return takeObject(ret);
91
93
  }
92
- function pie_install_program(component, manifest_toml) {
93
- const ptr0 = passArray8ToWasm0(component, wasm2.__wbindgen_export);
94
+ function pie_install_program(bytes, file, version) {
95
+ const ptr0 = passArray8ToWasm0(bytes, wasm2.__wbindgen_export);
94
96
  const len0 = WASM_VECTOR_LEN;
95
- const ptr1 = passStringToWasm0(manifest_toml, wasm2.__wbindgen_export, wasm2.__wbindgen_export2);
97
+ const ptr1 = passStringToWasm0(file, wasm2.__wbindgen_export, wasm2.__wbindgen_export2);
96
98
  const len1 = WASM_VECTOR_LEN;
97
- const ret = wasm2.pie_install_program(ptr0, len0, ptr1, len1);
99
+ var ptr2 = isLikeNone(version) ? 0 : passStringToWasm0(version, wasm2.__wbindgen_export, wasm2.__wbindgen_export2);
100
+ var len2 = WASM_VECTOR_LEN;
101
+ const ret = wasm2.pie_install_program(ptr0, len0, ptr1, len1, ptr2, len2);
98
102
  return takeObject(ret);
99
103
  }
100
104
  function pie_open_session() {
@@ -297,7 +301,7 @@ function __wbg_get_imports() {
297
301
  const ret = getObject(arg0).features;
298
302
  return addHeapObject(ret);
299
303
  },
300
- __wbg_fetchRange_59f4c213313c928d: function(arg0, arg1, arg2) {
304
+ __wbg_fetchRange_837bbd1b92ceed92: function(arg0, arg1, arg2) {
301
305
  __pieHost.fetchRange(arg0, arg1, arg2 >>> 0);
302
306
  },
303
307
  __wbg_finish_09ec094c10f41e7b: function(arg0) {
@@ -557,7 +561,7 @@ function __wbg_get_imports() {
557
561
  const a = state0.a;
558
562
  state0.a = 0;
559
563
  try {
560
- return __wasm_bindgen_func_elem_12287(a, state0.b, arg02, arg12);
564
+ return __wasm_bindgen_func_elem_55690(a, state0.b, arg02, arg12);
561
565
  } finally {
562
566
  state0.a = a;
563
567
  }
@@ -898,23 +902,23 @@ function __wbg_get_imports() {
898
902
  }, arguments);
899
903
  },
900
904
  __wbindgen_generic_0000000000000001: function(arg0, arg1) {
901
- const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_12285);
905
+ const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_55688);
902
906
  return addHeapObject(ret);
903
907
  },
904
908
  __wbindgen_generic_0000000000000002: function(arg0, arg1) {
905
- const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_10965);
909
+ const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_11414);
906
910
  return addHeapObject(ret);
907
911
  },
908
912
  __wbindgen_generic_0000000000000003: function(arg0, arg1) {
909
- const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_10966);
913
+ const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_11415);
910
914
  return addHeapObject(ret);
911
915
  },
912
916
  __wbindgen_generic_0000000000000004: function(arg0, arg1) {
913
- const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_10965_14);
917
+ const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_11414_15);
914
918
  return addHeapObject(ret);
915
919
  },
916
920
  __wbindgen_generic_0000000000000005: function(arg0, arg1) {
917
- const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_10965_15);
921
+ const ret = makeMutClosure(arg0, arg1, __wasm_bindgen_func_elem_11414_16);
918
922
  return addHeapObject(ret);
919
923
  },
920
924
  __wbindgen_generic_0000000000000006: function(arg0) {
@@ -946,16 +950,16 @@ function __wbg_get_imports() {
946
950
  "./platform.mjs": platform_exports
947
951
  };
948
952
  }
949
- function __wasm_bindgen_func_elem_10966(arg0, arg1, arg2) {
950
- wasm2.__wasm_bindgen_func_elem_10966(arg0, arg1, addHeapObject(arg2));
953
+ function __wasm_bindgen_func_elem_11415(arg0, arg1, arg2) {
954
+ wasm2.__wasm_bindgen_func_elem_11415(arg0, arg1, addHeapObject(arg2));
951
955
  }
952
- function __wasm_bindgen_func_elem_12287(arg0, arg1, arg2, arg3) {
953
- wasm2.__wasm_bindgen_func_elem_12287(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3));
956
+ function __wasm_bindgen_func_elem_55690(arg0, arg1, arg2, arg3) {
957
+ wasm2.__wasm_bindgen_func_elem_55690(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3));
954
958
  }
955
- function __wasm_bindgen_func_elem_12285(arg0, arg1, arg2) {
959
+ function __wasm_bindgen_func_elem_55688(arg0, arg1, arg2) {
956
960
  try {
957
961
  const retptr = wasm2.__wbindgen_add_to_stack_pointer(-16);
958
- wasm2.__wasm_bindgen_func_elem_12285(retptr, arg0, arg1, addHeapObject(arg2));
962
+ wasm2.__wasm_bindgen_func_elem_55688(retptr, arg0, arg1, addHeapObject(arg2));
959
963
  var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
960
964
  var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
961
965
  if (r1) {
@@ -965,10 +969,10 @@ function __wasm_bindgen_func_elem_12285(arg0, arg1, arg2) {
965
969
  wasm2.__wbindgen_add_to_stack_pointer(16);
966
970
  }
967
971
  }
968
- function __wasm_bindgen_func_elem_10965(arg0, arg1, arg2) {
972
+ function __wasm_bindgen_func_elem_11414(arg0, arg1, arg2) {
969
973
  try {
970
974
  const retptr = wasm2.__wbindgen_add_to_stack_pointer(-16);
971
- wasm2.__wasm_bindgen_func_elem_10965(retptr, arg0, arg1, addHeapObject(arg2));
975
+ wasm2.__wasm_bindgen_func_elem_11414(retptr, arg0, arg1, addHeapObject(arg2));
972
976
  var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
973
977
  var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
974
978
  if (r1) {
@@ -978,10 +982,10 @@ function __wasm_bindgen_func_elem_10965(arg0, arg1, arg2) {
978
982
  wasm2.__wbindgen_add_to_stack_pointer(16);
979
983
  }
980
984
  }
981
- function __wasm_bindgen_func_elem_10965_14(arg0, arg1, arg2) {
985
+ function __wasm_bindgen_func_elem_11414_15(arg0, arg1, arg2) {
982
986
  try {
983
987
  const retptr = wasm2.__wbindgen_add_to_stack_pointer(-16);
984
- wasm2.__wasm_bindgen_func_elem_10965_14(retptr, arg0, arg1, addHeapObject(arg2));
988
+ wasm2.__wasm_bindgen_func_elem_11414_15(retptr, arg0, arg1, addHeapObject(arg2));
985
989
  var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
986
990
  var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
987
991
  if (r1) {
@@ -991,10 +995,10 @@ function __wasm_bindgen_func_elem_10965_14(arg0, arg1, arg2) {
991
995
  wasm2.__wbindgen_add_to_stack_pointer(16);
992
996
  }
993
997
  }
994
- function __wasm_bindgen_func_elem_10965_15(arg0, arg1, arg2) {
998
+ function __wasm_bindgen_func_elem_11414_16(arg0, arg1, arg2) {
995
999
  try {
996
1000
  const retptr = wasm2.__wbindgen_add_to_stack_pointer(-16);
997
- wasm2.__wasm_bindgen_func_elem_10965_15(retptr, arg0, arg1, addHeapObject(arg2));
1001
+ wasm2.__wasm_bindgen_func_elem_11414_16(retptr, arg0, arg1, addHeapObject(arg2));
998
1002
  var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
999
1003
  var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
1000
1004
  if (r1) {
@@ -1477,11 +1481,11 @@ var host = {
1477
1481
  async awaitCache() {
1478
1482
  await cacheFill;
1479
1483
  },
1480
- async installLanguage(language, wasmBytes) {
1481
- return await driven(pie_install_language(language, wasmBytes));
1484
+ async installLanguage(language, wasmBytes, precompiled) {
1485
+ return await driven(pie_install_language(language, wasmBytes, precompiled));
1482
1486
  },
1483
- async install(wasmBytes, manifestToml) {
1484
- return await driven(pie_install_program(wasmBytes, manifestToml));
1487
+ async install(bytes, file, version) {
1488
+ return await driven(pie_install_program(bytes, file, version ?? void 0));
1485
1489
  },
1486
1490
  async memoryBytes() {
1487
1491
  return wasm3.memory.buffer.byteLength;
package/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { PieClient } from "@pie-project/client";
1
+ import { PieClient, programFile } from "@pie-project/client";
2
2
 
3
- export { PieClient };
3
+ export { PieClient, programFile };
4
4
 
5
5
  export interface LoadInfo {
6
6
  worker: boolean;
@@ -15,7 +15,7 @@ export interface LoadOptions {
15
15
 
16
16
  export interface BootSummary {
17
17
  model: string;
18
- sku: string;
18
+ deployment: string;
19
19
  trace: string;
20
20
  weight_bytes: number;
21
21
  kv_pages: number;
@@ -32,7 +32,7 @@ export function bootLazy(
32
32
 
33
33
  export function awaitCache(): Promise<void>;
34
34
 
35
- export function install(wasmBytes: Uint8Array | ArrayBuffer, manifestToml: string): Promise<string>;
35
+ export function install(bytes: Uint8Array | ArrayBuffer, file: string, version?: string | null): Promise<string>;
36
36
 
37
37
  export function client(): PieClient;
38
38
 
@@ -56,8 +56,15 @@ export class Server {
56
56
  readonly summary: BootSummary;
57
57
  readonly url: string;
58
58
  readonly running: boolean;
59
- installLanguage(language: "python" | "javascript" | string, source: Uint8Array | ArrayBuffer | URL | string): Promise<string>;
60
- install(source: Uint8Array | ArrayBuffer | URL | string, manifest: string): Promise<string>;
59
+ /** `precompiled` is the same component from `precompileComponent` (the
60
+ * `precompile` entry point) on this same build; it spares the first
61
+ * script inferlet compiling the component, which takes seconds in a tab. */
62
+ installLanguage(
63
+ language: "python" | "javascript" | string,
64
+ source: Uint8Array | ArrayBuffer | URL | string,
65
+ options?: { precompiled?: Uint8Array | ArrayBuffer | URL | string },
66
+ ): Promise<string>;
67
+ install(source: Uint8Array | ArrayBuffer | URL | string, file?: string | null, version?: string | null): Promise<string>;
61
68
  connect(): Promise<PieClient>;
62
69
  memoryBytes(): Promise<number>;
63
70
  awaitCache(): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pie-project/server-web",
3
- "version": "0.5.2",
3
+ "version": "0.6.1",
4
4
  "description": "The browser half of @pie-project/server: the runtime and the WebGPU engine as wasm, reached through `import { Server } from \"@pie-project/server\"` under a bundler.",
5
5
  "type": "module",
6
6
  "main": "dist/pie.mjs",
@@ -13,7 +13,8 @@
13
13
  "./languages": {
14
14
  "types": "./src/languages.d.ts",
15
15
  "default": "./src/languages.mjs"
16
- }
16
+ },
17
+ "./precompile": "./dist/precompile.mjs"
17
18
  },
18
19
  "files": [
19
20
  "dist",
@@ -22,10 +23,10 @@
22
23
  ],
23
24
  "scripts": {
24
25
  "build": "./build.sh release",
25
- "bundle": "esbuild src/pie.mjs src/worker.mjs --bundle --format=esm --outdir=dist --out-extension:.js=.mjs --external:./worker.mjs --external:fs --define:__PIE_WASM__='\"./pie_browser_bg.wasm\"' --log-level=warning --log-override:duplicate-object-key=silent && cp pkg/pie_browser_bg.wasm dist/"
26
+ "bundle": "esbuild src/pie.mjs src/worker.mjs src/precompile.mjs --bundle --format=esm --outdir=dist --out-extension:.js=.mjs --external:./worker.mjs --external:fs --external:path --external:node:fs --external:node:url --define:__PIE_WASM__='\"./pie_browser_bg.wasm\"' --log-level=warning --log-override:duplicate-object-key=silent && cp pkg/pie_browser_bg.wasm dist/"
26
27
  },
27
28
  "dependencies": {
28
- "@pie-project/client": "^0.5.0"
29
+ "@pie-project/client": "^0.6.0"
29
30
  },
30
31
  "devDependencies": {
31
32
  "esbuild": "^0.28.2"
@@ -33,6 +34,6 @@
33
34
  "repository": {
34
35
  "type": "git",
35
36
  "url": "https://github.com/pie-project/pie",
36
- "directory": "javascript/server-web"
37
+ "directory": "packages/javascript/server-web"
37
38
  }
38
39
  }
package/src/pie.mjs CHANGED
@@ -1,9 +1,9 @@
1
- import { PieClient } from "@pie-project/client";
1
+ import { PieClient, programFile } from "@pie-project/client";
2
2
 
3
3
  import { socketClass } from "./transport.mjs";
4
4
  import { attachLanguages } from "./languages.mjs";
5
5
 
6
- export { PieClient };
6
+ export { PieClient, programFile };
7
7
 
8
8
  const WASM_PATH = typeof __PIE_WASM__ === "undefined" ? "../pkg/pie_browser_bg.wasm" : __PIE_WASM__;
9
9
 
@@ -93,16 +93,22 @@ export async function connect() {
93
93
  return c;
94
94
  }
95
95
 
96
- export async function install(wasmBytes, manifestToml) {
97
- const copy = new Uint8Array(wasmBytes).slice();
98
- return await ready().call("install", [copy, manifestToml], { transfer: [copy.buffer] });
96
+ export async function install(bytes, file, version = null) {
97
+ const copy = new Uint8Array(bytes).slice();
98
+ return await ready().call("install", [copy, file, version], { transfer: [copy.buffer] });
99
99
  }
100
100
 
101
101
  /** Hand the runtime a language component (`"python"`, `"javascript"`)
102
- * from bytes; a script inferlet in that language can run once this has. */
103
- export async function installLanguage(language, wasmBytes) {
102
+ * from bytes; a script inferlet in that language can run once this has.
103
+ * `precompiled`, from `precompileComponent` on this same build, spares the
104
+ * first such inferlet compiling the component; one the runtime refuses is
105
+ * ignored and the source compiled instead. */
106
+ export async function installLanguage(language, wasmBytes, precompiled) {
104
107
  const copy = new Uint8Array(wasmBytes).slice();
105
- return await ready().call("installLanguage", [language, copy], { transfer: [copy.buffer] });
108
+ const ready_ = precompiled ? new Uint8Array(precompiled).slice() : undefined;
109
+ return await ready().call("installLanguage", [language, copy, ready_], {
110
+ transfer: ready_ ? [copy.buffer, ready_.buffer] : [copy.buffer],
111
+ });
106
112
  }
107
113
 
108
114
  export async function memoryBytes() {
@@ -117,9 +123,12 @@ export async function awaitCache() {
117
123
  return await ready().call("awaitCache", []);
118
124
  }
119
125
 
126
+ function isBytes(source) {
127
+ return source instanceof Uint8Array || source instanceof ArrayBuffer;
128
+ }
129
+
120
130
  async function bytesOf(source) {
121
- if (source instanceof Uint8Array) return source;
122
- if (source instanceof ArrayBuffer) return new Uint8Array(source);
131
+ if (isBytes(source)) return new Uint8Array(source);
123
132
  const response = await fetch(source);
124
133
  if (!response.ok) throw new Error(`pie: ${source}: ${response.status} ${response.statusText}`);
125
134
  return new Uint8Array(await response.arrayBuffer());
@@ -150,7 +159,7 @@ export class Server {
150
159
  return server;
151
160
  }
152
161
 
153
- /** What the boot found: model, sku, weight bytes, kv pages. */
162
+ /** What the boot found: model, deployment, weight bytes, kv pages. */
154
163
  get summary() {
155
164
  return this.#summary;
156
165
  }
@@ -165,16 +174,25 @@ export class Server {
165
174
  return this.#open && backend !== null;
166
175
  }
167
176
 
168
- /** A language component (`python`, `javascript`) from bytes or a URL. */
169
- async installLanguage(language, source) {
177
+ /** A language component (`python`, `javascript`) from bytes or a URL,
178
+ * and optionally the same component precompiled for this build. A
179
+ * precompiled one that fails to fetch is skipped, not fatal. */
180
+ async installLanguage(language, source, { precompiled } = {}) {
170
181
  this.#alive();
171
- return installLanguage(language, await bytesOf(source));
182
+ const [bytes, ready_] = await Promise.all([
183
+ bytesOf(source),
184
+ precompiled ? bytesOf(precompiled).catch(() => undefined) : undefined,
185
+ ]);
186
+ return installLanguage(language, bytes, ready_);
172
187
  }
173
188
 
174
- /** An inferlet from its component bytes (or URL) and manifest text, replacing an installed version. */
175
- async install(source, manifest) {
189
+ async install(source, file = null, version = null) {
176
190
  this.#alive();
177
- return install(await bytesOf(source), manifest);
191
+ if (!file) {
192
+ if (isBytes(source)) throw new Error("Server.install: bytes need `file`, the program's file name (`x.wasm`, `x.py`, `x.js`)");
193
+ file = programFile(new URL(source, location.href));
194
+ }
195
+ return install(await bytesOf(source), file, version);
178
196
  }
179
197
 
180
198
  /** A `PieClient` connected in-page. */
@@ -0,0 +1,37 @@
1
+ // Precompile a language component for the engine a tab runs inferlets on.
2
+ // A tab compiles the Python language component (37 MB) on its first Python
3
+ // inferlet, which takes seconds; a site can run this once at build time and
4
+ // serve the result beside the source, for `Server.installLanguage(language,
5
+ // source, { precompiled })`. The result fits only this exact build of
6
+ // pie_browser_bg.wasm, so run the copy that ships with the page.
7
+ //
8
+ // node precompile.mjs python.wasm python.pulley.cwasm
9
+ //
10
+ // Needs no GPU: it builds the inferlet engine, not a server.
11
+ import { readFileSync, writeFileSync } from "node:fs";
12
+ import { fileURLToPath } from "node:url";
13
+ import { initSync, pie_precompile_component } from "../pkg/pie_browser.js";
14
+
15
+ let ready = false;
16
+
17
+ /** The component's bytes, precompiled for this build's engine. */
18
+ export function precompileComponent(component) {
19
+ if (!ready) {
20
+ initSync({ module: readFileSync(new URL(__PIE_WASM__, import.meta.url)) });
21
+ ready = true;
22
+ }
23
+ return pie_precompile_component(new Uint8Array(component));
24
+ }
25
+
26
+ if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
27
+ const [source, out] = process.argv.slice(2);
28
+ if (!source || !out) {
29
+ console.error("usage: node precompile.mjs <component.wasm> <out.cwasm>");
30
+ process.exit(2);
31
+ }
32
+ const started = performance.now();
33
+ const precompiled = precompileComponent(readFileSync(source));
34
+ writeFileSync(out, precompiled);
35
+ const seconds = ((performance.now() - started) / 1000).toFixed(1);
36
+ console.log(`${out}: ${(precompiled.length / 1048576).toFixed(1)} MiB in ${seconds} s`);
37
+ }
package/src/worker.mjs CHANGED
@@ -180,11 +180,11 @@ export const host = {
180
180
  await cacheFill;
181
181
  },
182
182
 
183
- async installLanguage(language, wasmBytes) {
184
- return await driven(bindings.pie_install_language(language, wasmBytes));
183
+ async installLanguage(language, wasmBytes, precompiled) {
184
+ return await driven(bindings.pie_install_language(language, wasmBytes, precompiled));
185
185
  },
186
- async install(wasmBytes, manifestToml) {
187
- return await driven(bindings.pie_install_program(wasmBytes, manifestToml));
186
+ async install(bytes, file, version) {
187
+ return await driven(bindings.pie_install_program(bytes, file, version ?? undefined));
188
188
  },
189
189
 
190
190
  async memoryBytes() {
package/README.md DELETED
@@ -1,91 +0,0 @@
1
- # @pie-project/server-web
2
-
3
- The browser half of `@pie-project/server`: the same runtime, WebGPU engine
4
- and inferlet sandbox as `pie serve`, compiled to `wasm32-unknown-unknown`.
5
- No server-side inference: the page fetches a `.wgpu.zt` artifact, the
6
- weights land in WebGPU buffers, and inferlets run under wasmtime's Pulley
7
- interpreter inside the same module. A bundler reaches it through
8
- `@pie-project/server`'s `browser` export condition; nothing imports it by
9
- name.
10
-
11
- ## Use
12
-
13
- The package ships the wasm and a self-contained bundle (`dist/pie.mjs`,
14
- `dist/worker.mjs`, `dist/pie_browser_bg.wasm`); the client
15
- (`@pie-project/client`) is bundled in and re-exported. The worker and the
16
- wasm are fetched relative to the module, so serve them from your own origin
17
- (a bundler that understands `new URL(…, import.meta.url)` — Vite, webpack 5 —
18
- does this by itself; without one, copy `dist/` next to your page).
19
-
20
- ```js
21
- import { Server } from "@pie-project/server"; // resolves here under a bundler
22
- import "@pie-project/language-python";
23
-
24
- const server = await Server.start({ model: "/models/qwen3.5-0.8b.wgpu.zt" }); // served with Range support
25
- const program = await server.install(inferletBytes, manifestToml);
26
- const client = await server.connect(); // a PieClient, as for `pie serve`
27
- const proc = await client.launchProcess(program, { prompt: "The capital of France is", max_tokens: 16 });
28
- for (;;) {
29
- const { event, value } = await proc.recv();
30
- if (event === "return" || event === "error") break;
31
- }
32
- await server.shutdown();
33
- ```
34
-
35
- The page-level functions behind the class (`load`, `bootLazy`, `install`,
36
- `installLanguage`, `connect`, `awaitCache`, `memoryBytes`) are exported as
37
- well; `tests/browser` drives them directly.
38
-
39
-
40
- - The page must be cross-origin isolated (`Cross-Origin-Opener-Policy:
41
- same-origin`, `Cross-Origin-Embedder-Policy: require-corp`), and the
42
- browser needs WebGPU and JSPI: Chrome 137+, Firefox 153+ (Firefox on
43
- Linux has no WebGPU, so the engine cannot boot there).
44
- - `bootLazy` never holds the artifact in the tab: the loader's reads are
45
- served as 8 MiB-aligned `Range` requests, and after a boot from the
46
- network the artifact is copied into the origin's private file system
47
- (files up to about 2 GB) so the next visit reads it from there;
48
- `awaitCache()` waits for that copy. Peak wasm memory is a few times the
49
- largest tensor: 0.8B boots in 285 MiB, 9B in 4 bits in 1.5 GiB.
50
- - The engine cannot ask WebGPU how much device memory there is and assumes
51
- 8 GiB; a large model with the default pools is refused ("raise the device
52
- weight budget") — pass `device_memory_mb = 20480` (or a smaller
53
- `max_total_pages`) in the boot config. `BootConfig` in
54
- `crates/browser/src/boot.rs` lists the keys.
55
- - Rust, JavaScript and Python inferlets run. Not available in a tab: MoE
56
- host-tier expert streaming, Python inferlet snapshots, telemetry, and
57
- outbound HTTP or files from inferlets (the WASI interfaces link, a call
58
- fails).
59
- - `index.d.ts` carries the types.
60
-
61
- ## Build
62
-
63
- ```bash
64
- rustup target add wasm32-unknown-unknown
65
- cargo install wasm-bindgen-cli --version 0.2.128 --locked # must match Cargo.lock
66
- ./javascript/server-web/build.sh # release; `min` for the 23 MB shipping build, `dev` for debug
67
- ```
68
-
69
- `build.sh` builds `crates/browser`, runs wasm-bindgen into `pkg/`, and bundles
70
- `src/` with esbuild into `dist/` (`npm run bundle`). The package's own
71
- dependencies are installed on the first run. Tests, benchmarks and CI gates
72
- live under `tests/browser` (see its README).
73
-
74
- ## How it fits together
75
-
76
- | piece | what it does |
77
- |---|---|
78
- | `crates/browser` | The wasm-bindgen host: boot, install, sessions speaking the MessagePack frames `pie serve`'s WebSocket carries, `pie_tick`. |
79
- | `crates/wasmtime-web` | wasmtime in a tab: heap-backed "virtual memory", TLS slots, fibers implemented by the page's JSPI glue (`src/platform.mjs`), and the WASI host for the inferlet's imports (cli, io, clocks, random; 0.2 linked by hand, the 0.3 clocks from `crates/inferlet/wit`). The 0.2 filesystem and http interfaces a JavaScript or Python guest runtime imports regardless are stubbed per component, so they link and only a call fails. |
80
- | `crates/web-std` | The executor the page ticks, timers over `performance.now()`, crossbeam-shaped channels, and green threads on fibers. The runtime's scheduler and engine lanes run on those unchanged; `crates/runtime`'s `rt` facade names it on wasm32. |
81
- | `crates/engine-wgpu` | The same engine on both hosts, written for the browser: no GPU wait blocks, every step lands by callback, guests run from the landing. What differs by host sits in `device/host.rs`. |
82
- | `src/worker.mjs` | The host: wasm init, the tick loop, sessions, the artifact served by byte range. Runs in a Web Worker so the page stays responsive through the inferlet's in-browser compile and every forward pass; `load({ worker: false })` runs it on the page for debugging. |
83
- | `src/pie.mjs` | The page-side API: the `Server` class over `load`, `bootLazy`, `install`, `connect`. |
84
- | `src/languages.mjs` | The language registry `@pie-project/server/languages` re-exports: what a language package calls on import. |
85
- | `src/platform.mjs` | Fibers (JSPI), the clock, the wake hook the wasm imports. |
86
- | `src/transport.mjs` | The client's in-page socket over the runtime's frame API. |
87
-
88
- The native wgpu server is the engine's test host rather than a production
89
- backend: it runs the browser's code path over Vulkan, Metal or DX12 with a
90
- poll thread standing in for the page's event loop, so what the browser suite
91
- exercises is exercised natively too, and the two produce identical tokens.