Spaces:
Running
Running
File size: 4,569 Bytes
73f2932 17411d4 73f2932 02baa9b 73f2932 17411d4 73f2932 17411d4 73f2932 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | const PYODIDE_URL = "https://cdn.jsdelivr.net/pyodide/v0.26.1/full/pyodide.mjs";
import managerCode from "../../../backend/src/manager.py?raw";
import logicCode from "../../../backend/src/logic.py?raw";
import type { CsvParseResult, CsvSettings, DataPoints, DecisionBoundaryResult, DecisionBoundarySettings, ModelSettings } from "./types";
export class PyodideBackend {
private initialized: boolean = false;
private initPromise: Promise<void> | null = null;
private chain: Promise<void> = Promise.resolve();
private pyodide: any = null;
private manager: any = null;
private errorHandler: ((message: string) => void) | null = null;
setErrorHandler(handler: (message: string) => void): void {
this.errorHandler = handler;
}
async init(): Promise<void> {
if (this.initialized) {
return;
}
if (this.initPromise) {
return this.initPromise;
}
this.initPromise = (async () => {
const { loadPyodide } = await import(/* @vite-ignore */ PYODIDE_URL);
this.pyodide = await loadPyodide({
indexURL: "https://cdn.jsdelivr.net/pyodide/v0.26.1/full/"
});
await this.pyodide.loadPackage(["numpy", "scikit-learn", "pandas"]);
this.pyodide.FS.writeFile("logic.py", logicCode);
this.pyodide.FS.writeFile("manager.py", managerCode);
this.pyodide.runPython(`from manager import Manager; manager = Manager();`);
this.manager = this.pyodide.globals.get("manager");
if (!this.manager) {
throw new Error("Failed to initialize pyodide manager");
}
console.log("Pyodide initialized");
this.initialized = true;
})();
return this.initPromise;
}
async setDataset2d(dataset: DataPoints): Promise<void> {
console.log("setting datset 2d");
await this.init();
const inputs = dataset.xPoints.map((x, i) => [x, dataset.yPoints[i]]);
const outputs = dataset.labels;
await this.handleCall<void>("handle_set_dataset", { inputs, outputs });
}
async setModelConfig(settings: ModelSettings): Promise<void> {
await this.handleCall<void>(
"handle_set_model_config", { type: settings.type, arguments: settings.arguments }
);
}
async buildModel(): Promise<void> {
await this.handleCall<void>("handle_build_model");
}
async getDecisionBoundary(settings: DecisionBoundarySettings): Promise<DecisionBoundaryResult> {
return await this.handleCall<DecisionBoundaryResult>("handle_get_decision_boundary", settings);
}
async setDatasetCsv(buffer: ArrayBuffer, settings: CsvSettings): Promise<CsvParseResult> {
return await this.handleCall<CsvParseResult>(
"handle_set_dataset_csv", { buffer: buffer, settings }
);
}
async setCsvSettings(settings: CsvSettings): Promise<CsvParseResult> {
return await this.handleCall<CsvParseResult>("handle_set_csv_settings", settings);
}
async getDatasetCsv(): Promise<string> {
return await this.handleCall<string>("handle_get_dataset_csv");
}
private enqueue<T>(fn: () => Promise<T>): Promise<T> {
const next = this.chain.then(() => fn(), () => fn());
this.chain = next.then(() => undefined, () => undefined);
return next;
}
private async handleCall<T>(methodName: string, args?: unknown): Promise<T> {
await this.init();
return this.enqueue(async () => {
let pyArgs: any = null;
let pyResult: any = null;
try {
if (args !== undefined) {
pyArgs = this.pyodide.toPy(args);
}
const fn = this.manager[methodName];
if (!fn) {
throw new Error(`Manager has no method named ${methodName}`);
}
pyResult = args === undefined ? fn.call(this.manager) : fn.call(this.manager, pyArgs);
const result = pyResult && typeof pyResult.toJs === "function"
? pyResult.toJs({ dict_converter: Object.fromEntries })
: pyResult;
if (result?.status === "USER_ERROR") {
const message = result.message || "An error occurred in the backend";
throw new Error(message);
}
return result as T;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
this.errorHandler?.(message);
throw error instanceof Error ? error : new Error(message);
} finally {
if (pyArgs && typeof pyArgs.destroy === "function") {
pyArgs.destroy();
}
if (pyResult && typeof pyResult.destroy === "function") {
pyResult.destroy();
}
}
});
}
};
|