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:

idNameWhere Python runs
serverServer-SideExternal Flask Pro API or AXIS Worker /api/run proxy
pyne-workerpyne-worker (edge)HOOX pyne-worker Cloudflare® edge evaluator (POST /run)
pyodideClient-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

Diagram

Rendering…

Interface surface

isReady(): Promise<boolean>
run({ script, bars, config?, signal? }): Promise<RunResult>

RunResult is defined in contracts.

server engine

Config schema

KeyDefaultNotes
endpointhttp://localhost:5002Overridden by topbar / store.endpoint
modeautointerpret | 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 to RunResult with error

WebSocket contract (preferred when available)

  • URL: ws(s)://host/ws/run (derived from endpoint; requires flask-sock on 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. Health GET / reports "websocket": true when the route is mounted.
  • meta.transport is "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).

KeyDefaultNotes
endpointhttps://pyne-worker.cryptolinx.workers.devProduction workers.dev
modeinterpretinterpret | compile | auto (see /health features.modes)
preferWsfalseEdge 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

KeyDefault
indexUrl/pyodide/v0.26.2/ (self-hosted)

Boot pipeline (_ensure)

  1. Prefetch assets (prefetchPyodideAssets) — wasm, stdlib zip, micropip wheels.
  2. loadPyodide({ indexURL }).
  3. micropip.install same-origin /vendor/pynescript-0.2.0-py3-none-any.whl and antlr runtime wheel.
  4. Fetch /pyodide/pynescript_runtime.py and runPythonAsync it.

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

PathRole
frontend/src/engines/catalog.tsserverEngine, pyodideEngine, registration
frontend/src/indicators/runner.tsUI run orchestration
frontend/public/vendor/*.whlShipped wheels
frontend/public/pyodide/Self-hosted Pyodide + runtime py
frontend/worker/src/runtime.tsEdge /api/run
frontend/worker/RUNTIME.mdIn-worker Python plan

Invariants & edge cases

  1. AXIS never imports pynescript Python except through engines.
  2. Abort — pass signal from UI cancel; server engine respects it.
  3. Error as data — both engines often return status: 'error' instead of throwing so the Results panel can show messages.
  4. 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, stream mock-poll, engine pyodide, storage local
  • Requires dist/ (or Vite public) to serve /pyodide/** and /vendor/**

Tiny non-Python engine

See plugin examplesexample-tiny-pyne-engine.js implements a JS DSL with sma / ema / rsi for demos without PYNE.

Failure modes

SymptomCause
pynescript wheel returned HTMLSPA fallback; deploy vendor into dist; static server must not rewrite .whl
loadPyodide not availableMissing pyodide.js / blocked script
HTTP error from serverFlask down; wrong endpoint; CORS
Worker 503 NO_BACKENDEXTERNAL_BACKEND empty and Pyodide path disabled/failed

See also