Skip to content

Oracle servers

Read this page when your compute kernel runs remotely — it explains the oracle protocol, how to run a Wolfram server, and how to connect via OracleAdapter.

What it does — Run a Wolfram, Julia, or other compute kernel behind an HTTP server. Connect via OracleAdapter and let the pipeline handle lifecycle, context isolation, and error recovery.
Use this when — You're deploying a remote symbolic engine for Eleguá to use as an adapter backend.
Prerequisites — Writing an adapter for the adapter pattern.
Outcome — Know the oracle protocol, how to run a Wolfram server, how to build a custom server, and how to connect it via OracleAdapter.

An oracle server wraps a compute kernel (Wolfram, Julia, Sage) in an HTTP server that implements the oracle protocol.

For new integrations, use OracleAdapter — point it at a compatible server, provide any domain-specific expression mapping, and the adapter handles lifecycle and token mapping. See Architecture for how oracle servers fit into the three-tier execution model.

The oracle protocol

Your server must implement these HTTP endpoints:

Endpoint Method Purpose
/health GET Return {"status": "ok"} within 5 seconds
/evaluate POST Evaluate expression without init script
/evaluate-with-init POST Evaluate with init script loaded, context isolation
/cleanup POST Clear user-defined state between test files
/restart POST Hard-restart the kernel (emergency recovery)
/check-state GET Return {"clean": true/false, "leaked": [...]}

Request format (/evaluate-with-init)

{
  "expr": "1 + 1",
  "timeout": 60,
  "context_id": "optional-unique-id"
}

Response format

{
  "status": "ok",
  "result": "2",
  "timing_ms": 5
}

Error response:

{"status": "error", "error": "description of what went wrong"}

Timeout response:

{"status": "timeout", "error": "Evaluation timed out after 60s"}

The /evaluate endpoint uses the same format as /evaluate-with-init but without context_id or init script loading.

Wolfram-specific implementation

The elegua[wolfram] optional extra ships a ready-made Wolfram kernel oracle server:

pip install elegua[wolfram]
python -m elegua.wolfram serve --port 8765

Or with Docker:

just oracle-up    # starts on localhost:8765
just oracle-down  # stops the container

Configuring the Wolfram server for your domain

The Wolfram oracle accepts environment variables for domain-specific setup:

  • ELEGUA_WOLFRAM_INIT — path to a .wl script loaded on first /evaluate-with-init call
  • ELEGUA_WOLFRAM_CLEANUP — Wolfram expression executed on /cleanup (defaults to clearing Global context)

Example for sxAct (tensor calculus):

ELEGUA_WOLFRAM_INIT=/opt/xAct/init.wl \
ELEGUA_WOLFRAM_CLEANUP='Manifolds={}; Tensors={}; "cleanup-ok"' \
python -m elegua.wolfram serve

Build your own oracle server

To support a different compute engine, implement the 6 endpoints listed in the protocol table. The protocol is the stable contract; the Wolfram server is just one implementation of it. For protocol-level testing, EchoOracle provides a minimal implementation:

from elegua.testing import EchoOracle

# Reference implementation that echoes expressions back (no real kernel).
# Useful for testing your client code before the real server exists.
with EchoOracle(port=8765) as oracle:
    print(f"Echo oracle running on {oracle.url}")

Connecting your server to the pipeline

Once your server is running, prefer OracleAdapter over building a new transport adapter from scratch. It already handles health checks, cleanup, context IDs, timeout mapping, and ValidationToken creation. Use it as a context manager so initialize() and teardown() are called automatically:

from elegua.wolfram.adapter import OracleAdapter

with OracleAdapter(
    base_url="http://localhost:8765",
    adapter_id="my-engine",
    expr_builder=lambda action, payload: payload["expression"],
) as adapter:
    token = adapter.execute(task)

If your server returns a different response shape, pass a result_mapper that converts the response into a ValidationToken:

from elegua.models import ValidationToken
from elegua.task import TaskStatus
from elegua.wolfram.adapter import OracleAdapter

def result_mapper(action: str, payload: dict, data: dict) -> ValidationToken:
    return ValidationToken(
        adapter_id="my-engine",
        status=TaskStatus.OK if data["status"] == "ok" else TaskStatus.EXECUTION_ERROR,
        result={"repr": data.get("result", "")},
        metadata={"execution_time_ms": data.get("timing_ms", 0)},
    )

with OracleAdapter(
    base_url="http://localhost:8765",
    adapter_id="my-engine",
    expr_builder=lambda action, payload: payload["expression"],
    result_mapper=result_mapper,
) as adapter:
    token = adapter.execute(task)

Note

WolframOracleAdapter is deprecated. Use OracleAdapter for new work, even for Wolfram-backed servers.

When to use OracleClient directly

Drop below OracleAdapter only if you need transport behavior that the adapter cannot express. The OracleClient method names still reflect the Wolfram-first history of the module, so it is better treated as a lower-level implementation detail than as the main extension point for new integrations.

Whole game: EchoOracle end-to-end

This self-contained example starts an EchoOracle server, connects via OracleAdapter as a context manager, executes two tasks, and compares the results through the pipeline — all without requiring a real kernel.

from elegua.testing import EchoOracle
from elegua.wolfram.adapter import OracleAdapter
from elegua.comparison import compare_pipeline
from elegua.task import EleguaTask

# 1. Start an echo server on a free port
with EchoOracle(port=0) as oracle:
    # 2. Connect an OracleAdapter (context manager calls initialize/teardown)
    with OracleAdapter(
        base_url=oracle.url,
        adapter_id="echo",
        expr_builder=lambda action, payload: payload["expr"],
    ) as adapter:

        # 3. Create two tasks with different expressions
        task_a = EleguaTask(action="Evaluate", payload={"expr": "1 + 1"})
        task_b = EleguaTask(action="Evaluate", payload={"expr": "2 + 2"})

        # 4. Execute both tasks through the adapter
        token_a = adapter.execute(task_a)
        token_b = adapter.execute(task_b)

        # 5. Compare results — both succeed but differ at L1
        result = compare_pipeline(token_a, token_b)
        print(f"{token_a.result} vs {token_b.result}: {result.status.value}")
        # => {'repr': '1 + 1', ...} vs {'repr': '2 + 2', ...}: math_mismatch

        # 6. Run the same expression through a second adapter
        with OracleAdapter(
            base_url=oracle.url,
            adapter_id="echo-2",
            expr_builder=lambda action, payload: payload["expr"],
        ) as adapter2:
            token_1 = adapter.execute(task_a)
            token_2 = adapter2.execute(task_a)

            result = compare_pipeline(token_1, token_2)
            print(f"Same input, two adapters: {result.status.value}")
            # => Same input, two adapters: ok

This pattern works with any oracle server — replace EchoOracle with a real Wolfram, Julia, or Sage server and the same OracleAdapter + compare_pipeline code handles the rest.

Error recovery

If an evaluation times out or the kernel crashes, the server should automatically restart the kernel and return an error or timeout status. The Wolfram implementation does this: on timeout, it kills the kernel, starts a fresh one, and returns {"status": "timeout"}. The /restart endpoint provides manual recovery if automatic restart fails.

Context isolation

When context_id is provided in /evaluate-with-init, the server should evaluate the expression in an isolated scope so that symbols defined by one test do not leak into another. The Wolfram implementation achieves this with Block + ToExpression wrapping. Your server can use whatever isolation mechanism your kernel supports.

Snapshot record and replay

Record oracle responses for offline CI using the snapshot infrastructure:

ELEGUA_RECORD=1 just test-integration   # records to tests/snapshots/
just test-integration                    # replays from snapshots (no server needed)

See RecordingAdapter and ReplayAdapter in elegua.snapshot for details.