Engines
Calculation engines: server (Flask/Worker) and client Pyodide — run contracts, assets, and readiness.
This page
Engines
Abstract
An engine evaluates a script against bars and returns plots, series, events, and optional drawings. AXIS ships three built-ins in src/engines/catalog.ts:
| id | Name | Where Python runs |
|---|---|---|
server | Server-Side | External Flask Pro API or AXIS Worker /api/run proxy |
pyne-worker | pyne-worker (edge) | HOOX pyne-worker Cloudflare® edge evaluator (POST /run) |
pyodide | Client-Side (Pyodide) | Browser WebAssembly via self-hosted Pyodide |
AXIS Worker in-process Pyodide (PYODIDE_IN_WORKER) remains a separate, feature-gated edge path — see Worker runtime.
Conceptual model
Rendering…
Interface surface
isReady(): Promise<boolean>
run({ script, bars, config?, signal? }): Promise<RunResult>
RunResult is defined in contracts.
server engine
Config schema
| Key | Default | Notes |
|---|---|---|
endpoint | http://localhost:5002 | Overridden by topbar / store.endpoint |
mode | auto | interpret | compile | auto (try compile, fall back to interpret) |
HTTP contract
POST ${endpoint}/run?mode=…- Body:
{ script, data: bars } - Success JSON:
plots,series?,events?,drawings?,meta?,mode,script_id,run_id - Error: non-OK status or
status: 'error'→ mapped toRunResultwitherror
WebSocket contract (preferred when available)
- URL:
ws(s)://host/ws/run(derived fromendpoint; requiresflask-sockon the Pro API) - Client →
{ type: "run", id, script, data, mode? } - Server →
{ type: "result", id, status: "success", plots, series, … }or{ type: "error", id, message } - AXIS server engine tries WS first (
preferWs, default true), then REST. HealthGET /reports"websocket": truewhen the route is mounted. meta.transportis"ws"or"rest"so the Connection HUD can show the path used.
Readiness: GET ${endpoint}/ within 8s.
Timeouts: adaptive min(180s, max(60s, 30s + bars×80ms)).
Note on Worker path: the PWA’s server engine posts to /run on whatever endpoint you set. The AXIS data-plane Worker exposes POST /api/run. When pointing the PWA at that Worker, either put a reverse-proxy path rewrite in front, or set the endpoint so the engine path matches your deployment. Worker handleRun validates { script, data, mode? } and today prefers proxying to EXTERNAL_BACKEND (Flask). In-worker Pyodide only if PYODIDE_IN_WORKER=enabled and the wheel pipeline works.
pyne-worker engine
Dedicated HOOX pyne-worker edge host (not worker-axis).
| Key | Default | Notes |
|---|---|---|
endpoint | https://pyne-worker.cryptolinx.workers.dev | Production workers.dev |
mode | interpret | interpret | compile | auto (see /health features.modes) |
preferWs | false | Edge deploys are usually REST-only |
apiKey | '' | Sent as X-API-Key + Bearer when the Worker secret API_KEY is set |
Contract: same evaluate body as Flask (POST /run). Health: GET /health → { status: "ok", worker: "pyne-worker", … }.
UI: Topbar engine list, Settings preset pyne-worker edge, Workers Manager → Use as calculation backend / Use pyne-worker engine.
Browser CORS: the pyne-worker origin must allow the AXIS page Origin (or use a same-origin reverse proxy). Auth failures return HTTP 401 without a key.
pyodide engine
Config
| Key | Default |
|---|---|
indexUrl | /pyodide/v0.26.2/ (self-hosted) |
Boot pipeline (_ensure)
- Prefetch assets (
prefetchPyodideAssets) — wasm, stdlib zip, micropip wheels. loadPyodide({ indexURL }).micropip.installsame-origin/vendor/pynescript-0.2.0-py3-none-any.whland antlr runtime wheel.- Fetch
/pyodide/pynescript_runtime.pyandrunPythonAsyncit.
Refreshing the pyne wheel (after pyne compiler/runtime changes):
# from axis repo — builds sibling ../pyne (or PYNE_ROOT; dir sometimes still named pynescript) and vendors the wheel
./scripts/sync-pyne-wheel.sh
bun run build
Keep the hard-coded wheel filename in src/engines/catalog.ts / index.js in sync if the package version changes. The browser bridge is interpret-first; mode=compile/auto uses the wheel’s pynescript.compiler when NumPy loads (object-mode works without Numba; pure-numeric compile still needs server/Numba).
5. run calls run_script(script, bars) in Python and JSON.parses the result.
Guards: assertZipAsset rejects HTML SPA fallbacks (classic deploy footgun when public/vendor is missing from dist/).
Capabilities: { offline: true, needsNetwork: false } after assets are cached same-origin.
Helpers: preloadPyodide(), LOCAL_PYODIDE_VERSION = '0.26.2'.
Internals
| Path | Role |
|---|---|
frontend/src/engines/catalog.ts | serverEngine, pyodideEngine, registration |
frontend/src/indicators/runner.ts | UI run orchestration |
frontend/public/vendor/*.whl | Shipped wheels |
frontend/public/pyodide/ | Self-hosted Pyodide + runtime py |
frontend/worker/src/runtime.ts | Edge /api/run |
frontend/worker/RUNTIME.md | In-worker Python plan |
Invariants & edge cases
- AXIS never imports pynescript Python except through engines.
- Abort — pass
signalfrom UI cancel; server engine respects it. - Error as data — both engines often return
status: 'error'instead of throwing so the Results panel can show messages. - Pyodide size — ~14MB self-hosted index; first ready can take seconds; preload on idle.
Worked examples
Desk research (Flask)
- Active engine:
server - Endpoint:
http://127.0.0.1:5002 - Backend:
make run(Flask Pro API)
Offline lab
- Source
mock-walk, streammock-poll, enginepyodide, storagelocal - Requires
dist/(or Vite public) to serve/pyodide/**and/vendor/**
Tiny non-Python engine
See plugin examples — example-tiny-pyne-engine.js implements a JS DSL with sma / ema / rsi for demos without PYNE.
Failure modes
| Symptom | Cause |
|---|---|
pynescript wheel returned HTML | SPA fallback; deploy vendor into dist; static server must not rewrite .whl |
loadPyodide not available | Missing pyodide.js / blocked script |
| HTTP error from server | Flask down; wrong endpoint; CORS |
Worker 503 NO_BACKEND | EXTERNAL_BACKEND empty and Pyodide path disabled/failed |
See also
- Evaluation map — Flask vs Pyodide vs Worker vs (not) PyneTS
- Worker runtime
- Build and serve
- PYNE runtime
- PyneTS — TS library; AXIS does not import it
- AXIS and HOOX