Cette page a été traduite par une machine et n'a pas encore été révisée par une personne. L'anglais est la référence ; les corrections sont les bienvenues sur GitHub. GitHub

Cookwala / SDK et scénarios / 042

Resume a waiting run with If-Match set to the current seq

When the device waits for a person, the person confirms by resuming. The resume carries If-Match with the seq read from the status, so two people cannot both answer the same question.

Audience: kitchen operator · Objectif: Des robots pour les gens · Niveau: beginner

Lire d'abord: /docs/CORE/ /docs/HUB/

Étapes

  1. Start a shakshuka run with a person present. startExecution

  2. The arm cuts; the run then waits for a person to heat the pan.

  3. Read the status and keep its seq. getExecution

  4. Resume with the seq just read. The hub accepts and the run continues: the person is heating the pan. resumeExecution

  5. One simulated minute of heating passes.

  6. The next step, sautéing the onion, is again one a person performs on this device, so the run waits once more with a higher seq. getExecution

Résultat

You have resumed a run with the right precondition. Next: scenario 043 shows what happens with a stale seq.

Ligne de commande

cookwala hub --recipes examples --speed 200
jq '. + {"x-hub-human-present": true}' examples/core/execute-request.json | curl -s -X POST http://localhost:7878/v1/executions -H 'Content-Type: application/json' -H 'Idempotency-Key: ex-2026-10-04-001-a' -d @-
curl -si http://localhost:7878/v1/executions/ex-2026-10-04-001
curl -s -X POST http://localhost:7878/v1/executions/ex-2026-10-04-001/resume -H 'If-Match: 6' -H 'Idempotency-Key: resume-0001'

Sortie enregistrée

Statut: ok

[1] startExecution: {"core": "0.2.0", "kind": "ExecutionStatus", "id": "ex-042-resume", "seq": 0, "state": "accepted", "request": "ex-042-resume", "updatedAt": "2026-10-07T00:54:17Z", "x-hub-plan": [{"node": "n1", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n2", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n3", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n4", "op": "cw.op.heat", "by": "human", "verifiedBy": "human"}, {"node": "n5", "op": "cw.op.saute", "by":…
[3] getExecution: {"core": "0.2.0", "kind": "ExecutionStatus", "id": "ex-042-resume", "seq": 6, "state": "needs_human", "request": "ex-042-resume", "updatedAt": "2026-10-07T00:54:19Z", "x-hub-plan": [{"node": "n1", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n2", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n3", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n4", "op": "cw.op.heat", "by": "human", "verifiedBy": "human"}, {"node": "n5", "op": "cw.op.saute", "b…
[4] resumeExecution: {"core": "0.2.0", "kind": "ExecutionStatus", "id": "ex-042-resume", "seq": 7, "state": "running", "request": "ex-042-resume", "updatedAt": "2026-10-07T00:54:21Z", "x-hub-plan": [{"node": "n1", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n2", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n3", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n4", "op": "cw.op.heat", "by": "human", "verifiedBy": "human"}, {"node": "n5", "op": "cw.op.saute", "by": …
[6] getExecution: {"core": "0.2.0", "kind": "ExecutionStatus", "id": "ex-042-resume", "seq": 8, "state": "needs_human", "request": "ex-042-resume", "updatedAt": "2026-10-07T00:54:21Z", "x-hub-plan": [{"node": "n1", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n2", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n3", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n4", "op": "cw.op.heat", "by": "human", "verifiedBy": "human"}, {"node": "n5", "op": "cw.op.saute", "b…

Code

Exécuter: python scenarios/out/042-resume-with-the-current-seq/python.py

# Scenario 042: Resume a waiting run with If-Match set to the current seq
# When the device waits for a person, the person confirms by resuming. The resume carries If-Match with the seq read from the status, so two people cannot both answer the same question.
# Run a hub first: python hub/cookwala_hub.py --recipes examples
import json, os
from cookwala.client import CookwalaClient, CookwalaProblem

def load(path):
    with open(path, encoding="utf-8") as f:
        return json.load(f)

def pick(obj, path):
    for part in path.replace("]", "").replace("[", ".").split("."):
        obj = obj[int(part)] if part.isdigit() else (len(obj) if part == "length" else obj.get(part))
    return obj

c = CookwalaClient(os.environ.get("COOKWALA_HUB", "http://localhost:7878"))


# Step 1: Start a shakshuka run with a person present.
try:
    started = c.start_execution({"core": "0.2.0", "kind": "ExecuteRequest", "id": "ex-042-resume", "recipe": "cw:cookwala.ai:example-shakshuka", "recipeHash": "sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841", "servings": 4, "requestedBy": "agent:planner-1", "idempotencyKey": "ex-042-resume-a"}, "ex-042-resume-key", True)
except CookwalaProblem as p:
    started = p.body  # a refusal is a result, not a crash
    print("refused:", p.refusal)
assert pick(started, "state") == "accepted", pick(started, "state")
print("ok", "state", "=", "accepted")

# Step 2: The arm cuts; the run then waits for a person to heat the pan.
import time; time.sleep(4)

# Step 3: Read the status and keep its seq.
status = c.get_execution("ex-042-resume")
assert pick(status, "state") == "needs_human", pick(status, "state")
print("ok", "state", "=", "needs_human")
assert pick(status, "seq") == 6, pick(status, "seq")
print("ok", "seq", "=", 6)

# Step 4: Resume with the seq just read. The hub accepts and the run continues: the person is heating the pan.
try:
    resumed = c.resume_execution("ex-042-resume", pick(status, "seq"))
except CookwalaProblem as p:
    resumed = p.body  # a refusal is a result, not a crash
    print("refused:", p.refusal)
assert pick(resumed, "state") == "running", pick(resumed, "state")
print("ok", "state", "=", "running")
assert pick(resumed, "seq") == 7, pick(resumed, "seq")
print("ok", "seq", "=", 7)

# Step 5: One simulated minute of heating passes.
import time; time.sleep(2)

# Step 6: The next step, sautéing the onion, is again one a person performs on this device, so the run waits once more with a higher seq.
next = c.get_execution("ex-042-resume")
assert pick(next, "state") == "needs_human", pick(next, "state")
print("ok", "state", "=", "needs_human")
assert pick(next, "seq") == 8, pick(next, "seq")
print("ok", "seq", "=", 8)
assert pick(next, "humanNeeded.why") == "a person performs cw.op.saute", pick(next, "humanNeeded.why")
print("ok", "humanNeeded.why", "=", "a person performs cw.op.saute")

print("scenario complete")

scenarios/042-resume-with-the-current-seq.json

Précédent: 041 Suivant: 043