@pie-project/server-web 0.5.2
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/README.md +91 -0
- package/dist/pie.mjs +3357 -0
- package/dist/pie_browser_bg.wasm +0 -0
- package/dist/worker.mjs +1531 -0
- package/index.d.ts +65 -0
- package/package.json +38 -0
- package/src/languages.d.ts +5 -0
- package/src/languages.mjs +27 -0
- package/src/pie.mjs +211 -0
- package/src/platform.mjs +60 -0
- package/src/transport.mjs +74 -0
- package/src/worker.mjs +234 -0
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
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.
|