/ concept-collection / stan-web-ide
Sign in
concept-collection / stan-web-ide
stan-web-ide / src / stan / compile.ts
75 lines · 2.2 KBBlameHistoryRaw
1// Client for the compile server (stan-wasm-wasi — stan-playground's
2// compilation protocol, producing pure-WASI modules): POST the .stan source,
3// get a model id, and reference the compiled module at
4// /download/{model_id}/main.wasm. The server caches compilations by source
5// hash; on top of that we keep a small in-session cache so re-running an
6// unchanged model skips the round trip (validated with a HEAD request,
7// since server redeploys invalidate ids).
9export interface CompileResult {
10 mainWasmUrl?: string;
11 error?: string;
14const cache = new Map<string, string>();
16export async function compileStanProgram(
17 serverUrl: string,
18 stanProgram: string,
19 onStatus: (message: string) => void,
20): Promise<CompileResult> {
21 const cacheKey = `${serverUrl}\0${stanProgram}`;
23 const cached = cache.get(cacheKey);
24 if (cached && await urlExists(cached)) {
25 onStatus('compiled (cached)');
26 return { mainWasmUrl: cached };
27 }
29 try {
30 onStatus('compiling...');
31 const response = await fetch(`${serverUrl}/compile`, {
32 method: 'POST',
33 headers: {
34 'Content-Type': 'text/plain',
35 // the compile server passcode (fixed, same as stan-playground)
36 'Authorization': 'Bearer 1234',
37 },
38 body: stanProgram,
39 });
40 if (!response.ok) {
41 return { error: `compilation failed: ${await messageOrStatus(response)}` };
42 }
43 const { model_id } = await response.json();
44 const mainWasmUrl = `${serverUrl}/download/${model_id}/main.wasm`;
46 onStatus('checking download of main.wasm');
47 if (!await urlExists(mainWasmUrl)) {
48 return { error: `compiled, but main.wasm is not downloadable from ${mainWasmUrl}` };
49 }
51 cache.set(cacheKey, mainWasmUrl);
52 onStatus('compiled');
53 return { mainWasmUrl };
54 } catch (error) {
55 return { error: `compilation request failed: ${error} (is the compile server at ${serverUrl} running? if it just woke from idle, try again)` };
56 }
59async function urlExists(url: string): Promise<boolean> {
60 try {
61 const response = await fetch(url, { method: 'HEAD' });
62 return response.ok;
63 } catch {
64 return false;
65 }
68async function messageOrStatus(response: Response): Promise<string> {
69 try {
70 const body = await response.json();
71 return body?.message ?? response.statusText;
72 } catch {
73 return response.statusText;
74 }
moveopenescclose