Writing an adapter¶
An adapter is how Eleguá talks to a symbolic engine. Subclass Adapter, implement execute(), and Eleguá handles the rest — task dispatch, comparison, and reporting.
Adapters connect Eleguá to external symbolic engines. Each adapter translates an EleguaTask into a call to its engine and returns a ValidationToken with the result.
The Adapter interface¶
Subclass Adapter and implement two members:
adapter_id— a unique string identifying this adapterexecute()— run the task and return aValidationToken
from elegua.adapter import Adapter
from elegua.models import ValidationToken
from elegua.task import EleguaTask, TaskStatus
class MyAdapter(Adapter):
@property
def adapter_id(self) -> str:
return "my-engine"
def execute(self, task: EleguaTask) -> ValidationToken:
# Send task.action and task.payload to your engine
result = my_engine.run(task.action, task.payload)
return ValidationToken(
adapter_id=self.adapter_id,
status=TaskStatus.OK,
result=result,
)
Warning
execute() must not mutate the input EleguaTask. Return a ValidationToken with the result; the original task stays unchanged.
Run a complete comparison with your adapter¶
This example is copy-pasteable as written. It creates one task, runs it through
the built-in WolframAdapter stub and a custom adapter, and compares the two
results through the default pipeline.
from elegua.adapter import Adapter, WolframAdapter
from elegua.comparison import compare_pipeline
from elegua.models import ValidationToken
from elegua.task import EleguaTask, TaskStatus
class MyAdapter(Adapter):
@property
def adapter_id(self) -> str:
return "my-engine"
def execute(self, task: EleguaTask) -> ValidationToken:
# Replace this echo behavior with a call into your real engine.
return ValidationToken(
adapter_id=self.adapter_id,
status=TaskStatus.OK,
result=task.payload,
)
task = EleguaTask(action="Echo", payload={"expr": "x + y"})
with WolframAdapter() as oracle, MyAdapter() as iut:
oracle_token = oracle.execute(task)
iut_token = iut.execute(task)
result = compare_pipeline(oracle_token, iut_token)
print(result.layer, result.layer_name, result.status.value)
Expected output:
Because both adapters return the same payload, the comparison succeeds at layer 1. If your adapter produces a different but mathematically equivalent structure, the comparison can continue into deeper registered layers.
Return failures as tokens¶
When your engine fails, capture the failure in the returned ValidationToken
instead of raising from execute(). The snippet below is intentionally partial:
it shows only the error-handling branch.
If execution fails, return a token with an error status instead of raising:
def execute(self, task: EleguaTask) -> ValidationToken:
try:
result = my_engine.run(task.action, task.payload)
return ValidationToken(
adapter_id=self.adapter_id,
status=TaskStatus.OK,
result=result,
)
except MyEngineError as e:
return ValidationToken(
adapter_id=self.adapter_id,
status=TaskStatus.EXECUTION_ERROR,
metadata={"error": str(e)},
)
Common fail-state: no lifecycle setup¶
If your adapter uses initialize() or teardown(), run it as a context
manager. Calling execute() before setup is a common mistake for adapters that
manage external connections or processes.
adapter = MyAdapter()
token = adapter.execute(task) # safe only for adapters with no initialize() work
For adapters that open network clients, kernels, or temporary state, prefer:
For adapters backed by an HTTP oracle server, see Oracle servers.