Cookwala / SDK and scenarios / 035

Ask the device what it can do, then dry-run against its own answer

An agent should not assume a device from a catalog. Read the capability document from the Core API and pass it to the dry run as the device, so the plan is made against what the device itself declared.

Audience: agent builder · Goal: Robots for people · Level: intermediate

Read first: /docs/CORE/ /docs/QUICKSTART/

Steps

  1. The hub's device: the example arm with cut, crack and transfer. capabilities

  2. Shakshuka against the device's own document, a person present: the arm cuts, the person cooks. dryRun

  3. Nobody present: refused before motion. dryRun

  4. Koshari against the same document: the arm cuts the onions; the person does the other nineteen steps, including the frying. dryRun

Outcome

Your agent plans against the live device, never a cached idea of it, and can say which steps it will ask the household to do. Scenario 018 shows what happens when the agent then sends a request.

Command line

curl -s http://localhost:7878/v1/capabilities > device.json
cookwala dryrun examples/shakshuka.cookwala.json --device device.json --human-present
cookwala dryrun examples/koshari.cookwala.json --device device.json --human-present

Recorded output

Status: ok

[1] capabilities: {"cookwala": "0.1.0", "actor": {"kind": "robot", "id": "robot:arm-1", "vendor": "Example Robotics", "model": "CounterArm 2"}, "roles": ["executor"], "location": {"zone": "counter-left", "mobile": false}, "capabilities": {"ops": [{"op": "cw.op.cut", "params": {"shape": ["dice", "slice", "mince", "julienne"]}, "ingredientClasses": ["cw.ing.class.vegetable_firm", "cw.ing.class.herb"], "reliability": 0.97}, {"op": "cw.op.crack", "reliability": 0.95}, {"op": "cw.op.transfer", "maxMassG": 3000}], "sensors": [{"sensor": "cw.sense.vision", "visionCues": ["cw.sense.translucent", "cw.sense.egg_whites_se…
[2] dryRun: {"state": "accepted", "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": "human", "verifiedBy": "human"}, {"node": "n6", "op": "cw.op.saute", "by": "human", "verifiedBy": "human"}, {"node": "n7", "op": "cw.op.simmer", "b…
[3] dryRun: {"state": "refused", "refusal": {"reason": "needs_human_present", "node": "n1", "detail": "cw.op.cut may not run unattended"}, "plan": []}
[4] dryRun: {"state": "accepted", "plan": [{"node": "n1", "op": "cw.op.boil", "by": "human", "verifiedBy": "human"}, {"node": "n2", "op": "cw.op.simmer", "by": "human", "verifiedBy": "human"}, {"node": "n3", "op": "cw.op.drain", "by": "human", "verifiedBy": "human"}, {"node": "n4", "op": "cw.op.boil", "by": "human", "verifiedBy": "human"}, {"node": "n5", "op": "cw.op.boil", "by": "human", "verifiedBy": "human"}, {"node": "n6", "op": "cw.op.drain", "by": "human", "verifiedBy": "human"}, {"node": "n7", "op": "cw.op.heat", "by": "human", "verifiedBy": "human"}, {"node": "n8", "op": "cw.op.toast", "by": "huma…

Code

Run: python scenarios/out/035-plan-with-the-devices-own-capabilities/python.py

# Scenario 035: Ask the device what it can do, then dry-run against its own answer
# An agent should not assume a device from a catalog. Read the capability document from the Core API and pass it to the dry run as the device, so the plan is made against what the device itself declared.
# 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: The hub's device: the example arm with cut, crack and transfer.
caps = c.capabilities()
assert pick(caps, "actor.id") == "robot:arm-1", pick(caps, "actor.id")
print("ok", "actor.id", "=", "robot:arm-1")
assert pick(caps, "capabilities.ops.length") == 3, pick(caps, "capabilities.ops.length")
print("ok", "capabilities.ops.length", "=", 3)

# Step 2: Shakshuka against the device's own document, a person present: the arm cuts, the person cooks.
withPerson = c.dry_run(recipe_id="shakshuka", device=caps, human_present=True)
assert pick(withPerson, "state") == "accepted", pick(withPerson, "state")
print("ok", "state", "=", "accepted")
assert pick(withPerson, "plan.length") == 13, pick(withPerson, "plan.length")
print("ok", "plan.length", "=", 13)
assert pick(withPerson, "plan[0].by") == "device", pick(withPerson, "plan[0].by")
print("ok", "plan[0].by", "=", "device")
assert pick(withPerson, "plan[3].by") == "human", pick(withPerson, "plan[3].by")
print("ok", "plan[3].by", "=", "human")

# Step 3: Nobody present: refused before motion.
alone = c.dry_run(recipe_id="shakshuka", device=caps, human_present=False)
assert pick(alone, "state") == "refused", pick(alone, "state")
print("ok", "state", "=", "refused")
assert pick(alone, "refusal.reason") == "needs_human_present", pick(alone, "refusal.reason")
print("ok", "refusal.reason", "=", "needs_human_present")

# Step 4: Koshari against the same document: the arm cuts the onions; the person does the other nineteen steps, including the frying.
koshari = c.dry_run(recipe_id="koshari", device=caps, human_present=True)
assert pick(koshari, "state") == "accepted", pick(koshari, "state")
print("ok", "state", "=", "accepted")
assert pick(koshari, "plan.length") == 20, pick(koshari, "plan.length")
print("ok", "plan.length", "=", 20)
assert pick(koshari, "plan[10].by") == "device", pick(koshari, "plan[10].by")
print("ok", "plan[10].by", "=", "device")
assert pick(koshari, "plan[13].by") == "human", pick(koshari, "plan[13].by")
print("ok", "plan[13].by", "=", "human")

print("scenario complete")

scenarios/035-plan-with-the-devices-own-capabilities.json

Previous: 034 Next: 036