A contract that asks for inference
A contract cannot receive a model's answer — but it can commission one deterministically. Including what that primitive does and does not do today.
The earlier lesson said a contract cannot *call* a model, and that is true: no node can re-execute a completion and get the same tokens back. But there is a primitive that is deterministic:
from stdlib import syscalls task_id = syscalls.ai_enqueue(b"animica/tiny-v1", b"summarise this")
This does not run a model. It commissions one, and returns a task id. Verified in the VM: the same model and prompt always produce the same task id. Every node re-executing the transaction computes the identical id and agrees.
The answer arrives later, off-chain, and reaches the chain in a separate transaction — which some other function of your contract can accept and verify.
This is the shape of every "oracle" pattern, and once you see it you will recognise it everywhere:
1. A transaction commits to a request. Deterministic, cheap, on-chain. 2. Work happens off-chain, where non-determinism is allowed. 3. A later transaction delivers the result, and the contract decides whether to believe it — by signature, by stake, by quorum, by a commitment made before the input was known.
Step 3 is where the actual design work is. ai_enqueue only gets you step 1.
A task id is not an answer. It is a receipt for a request. A contract that treats ai_enqueue as though it returned a completion has a bug that will never surface in testing, because the id it gets back is a perfectly valid 32-byte value that is simply not what it thinks it is.
syscalls also offers quantum_enqueue and blob_pin on the same principle: commit deterministically now, deliver out-of-band later.
And right now, nobody is listening. Read this before you build on it.
syscalls dispatches to a pluggable *provider*, installed by the host via syscalls_api.set_provider(...). As of 2026-08-20, the only callers of set_provider in this entire repository are unit tests. The default is _LocalNoOpProvider, whose ai_enqueue returns "note": "local-noop" and whose read_result is documented as *"results never materialize"* — it returns ready: False forever.
So a contract calling ai_enqueue today gets a well-formed, deterministic receipt for work that no one will ever perform. The commission primitive is real; the delivery side is not yet wired.
The exercise below is still worth doing — the pattern and the determinism property are exactly right, and they are what you will use when the provider lands. Just do not ship a contract that waits on a result. For inference you can actually get today, use the off-chain AICF path: submit through the aicf.* RPC namespace and deliver the result back in a transaction your contract verifies.
Your turn
Implement request(prompt): enqueue an inference job, store the task id under the requesting caller, and return it. Then task_of(addr) reads it back. Enqueueing the same prompt twice must produce the same id — prove it to yourself.
Hints
One way to do it
from stdlib import abi, events, storage, syscalls
MODEL = b"animica/tiny-v1"
def _k_task(addr: bytes) -> bytes:
return b"ai:task:" + addr
def request(prompt: bytes) -> bytes:
body = bytes(prompt)
abi.require(len(body) > 0, b"empty_prompt")
who = abi.caller()
# Deterministic: every node re-executing this computes the same task id.
task_id = syscalls.ai_enqueue(MODEL, body)
storage.set(_k_task(who), task_id)
events.emit(b"Requested", {"who": who, "task": task_id})
return task_id
def task_of(addr: bytes) -> bytes:
return storage.get(_k_task(bytes(addr)), b"")
Claim your 10 ANM
Finish this lesson and claim 10 ANM, once per address. Paid from the Animica treasury in batches — allow a few minutes.
Stuck? Ask
Answered by Animica's own free inference network. It is donated GPU capacity, so give it 20-30 seconds.