Cookwala / SDK and scenarios / 028
The refusals an agent sees when nobody is home
An agent planning dinner for an empty house gets needs_human_present at the first step whose envelope says unattended is false. The plan up to that step is returned, so the agent can say what could have run.
Read first: /docs/CORE/ /docs/QUICKSTART/
Steps
Shakshuka starts with cutting: refused at n1 with an empty plan.
dryRunMolokhia boils and simmers the chicken first: five steps are planned, then heating a pan stops it.
dryRunKoshari on the fryer robot: six boiling, simmering and draining steps are fine unattended; heating the pan for the vermicelli is not.
dryRunThe same molokhia with a person in the house: twelve steps, all by the device.
dryRun
Outcome
Your agent can explain a refusal to the household in one sentence and offer the alternative: start when someone is home. The rule is in Core section 6, item 6.
Command line
cookwala dryrun examples/shakshuka.cookwala.json --device examples/capabilities/demo-hob-robot.json
cookwala dryrun examples/molokhia.cookwala.json --device examples/capabilities/demo-hob-robot.json
cookwala dryrun examples/koshari.cookwala.json --device examples/capabilities/demo-fryer-robot.json
cookwala dryrun examples/molokhia.cookwala.json --device examples/capabilities/demo-hob-robot.json --human-presentRecorded output
Status: ok
[1] dryRun: {"state": "refused", "refusal": {"reason": "needs_human_present", "node": "n1", "detail": "cw.op.cut may not run unattended"}, "plan": []}
[2] dryRun: {"state": "refused", "refusal": {"reason": "needs_human_present", "node": "n6", "detail": "cw.op.heat may not run unattended"}, "plan": [{"node": "n1", "op": "cw.op.boil", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.liquid_temp"}, {"node": "n2", "op": "cw.op.simmer", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.liquid_temp"}, {"node": "n3", "op": "cw.op.drain", "by": "device", "verifiedBy": "time", "rung": "time"}, {"node": "n4", "op": "cw.op.simmer", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.liquid_temp"}, {"node": "n5", "op": "cw.op.crush", "by": "d…
[3] dryRun: {"state": "refused", "refusal": {"reason": "needs_human_present", "node": "n7", "detail": "cw.op.heat may not run unattended"}, "plan": [{"node": "n1", "op": "cw.op.boil", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.liquid_temp"}, {"node": "n2", "op": "cw.op.simmer", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.liquid_temp"}, {"node": "n3", "op": "cw.op.drain", "by": "device", "verifiedBy": "time", "rung": "time"}, {"node": "n4", "op": "cw.op.boil", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.liquid_temp"}, {"node": "n5", "op": "cw.op.boil", "by": "devi…
[4] dryRun: {"state": "accepted", "plan": [{"node": "n1", "op": "cw.op.boil", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.liquid_temp"}, {"node": "n2", "op": "cw.op.simmer", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.liquid_temp"}, {"node": "n3", "op": "cw.op.drain", "by": "device", "verifiedBy": "time", "rung": "time"}, {"node": "n4", "op": "cw.op.simmer", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.liquid_temp"}, {"node": "n5", "op": "cw.op.crush", "by": "device", "verifiedBy": "time", "rung": "time"}, {"node": "n6", "op": "cw.op.heat", "by": "device", "verifie…Code
Run: python scenarios/out/028-refusals-when-nobody-is-home/python.py
# Scenario 028: The refusals an agent sees when nobody is home
# An agent planning dinner for an empty house gets needs_human_present at the first step whose envelope says unattended is false. The plan up to that step is returned, so the agent can say what could have run.
# 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: Shakshuka starts with cutting: refused at n1 with an empty plan.
shakshuka = c.dry_run(recipe_id="shakshuka", device_id="demo-hob-robot", human_present=False)
assert pick(shakshuka, "state") == "refused", pick(shakshuka, "state")
print("ok", "state", "=", "refused")
assert pick(shakshuka, "refusal.reason") == "needs_human_present", pick(shakshuka, "refusal.reason")
print("ok", "refusal.reason", "=", "needs_human_present")
assert pick(shakshuka, "refusal.node") == "n1", pick(shakshuka, "refusal.node")
print("ok", "refusal.node", "=", "n1")
assert pick(shakshuka, "plan.length") == 0, pick(shakshuka, "plan.length")
print("ok", "plan.length", "=", 0)
# Step 2: Molokhia boils and simmers the chicken first: five steps are planned, then heating a pan stops it.
molokhia = c.dry_run(recipe_id="molokhia", device_id="demo-hob-robot", human_present=False)
assert pick(molokhia, "state") == "refused", pick(molokhia, "state")
print("ok", "state", "=", "refused")
assert pick(molokhia, "refusal.reason") == "needs_human_present", pick(molokhia, "refusal.reason")
print("ok", "refusal.reason", "=", "needs_human_present")
assert pick(molokhia, "refusal.node") == "n6", pick(molokhia, "refusal.node")
print("ok", "refusal.node", "=", "n6")
assert pick(molokhia, "plan.length") == 5, pick(molokhia, "plan.length")
print("ok", "plan.length", "=", 5)
assert pick(molokhia, "plan[0].rung") == "cw.sense.liquid_temp", pick(molokhia, "plan[0].rung")
print("ok", "plan[0].rung", "=", "cw.sense.liquid_temp")
# Step 3: Koshari on the fryer robot: six boiling, simmering and draining steps are fine unattended; heating the pan for the vermicelli is not.
koshari = c.dry_run(recipe_id="koshari", device_id="demo-fryer-robot", human_present=False)
assert pick(koshari, "state") == "refused", pick(koshari, "state")
print("ok", "state", "=", "refused")
assert pick(koshari, "refusal.reason") == "needs_human_present", pick(koshari, "refusal.reason")
print("ok", "refusal.reason", "=", "needs_human_present")
assert pick(koshari, "refusal.node") == "n7", pick(koshari, "refusal.node")
print("ok", "refusal.node", "=", "n7")
assert pick(koshari, "plan.length") == 6, pick(koshari, "plan.length")
print("ok", "plan.length", "=", 6)
# Step 4: The same molokhia with a person in the house: twelve steps, all by the device.
molokhiaWithPerson = c.dry_run(recipe_id="molokhia", device_id="demo-hob-robot", human_present=True)
assert pick(molokhiaWithPerson, "state") == "accepted", pick(molokhiaWithPerson, "state")
print("ok", "state", "=", "accepted")
assert pick(molokhiaWithPerson, "plan.length") == 12, pick(molokhiaWithPerson, "plan.length")
print("ok", "plan.length", "=", 12)
print("scenario complete")