All tutorials
Track 20·State & Control

Conditional logic end to end

No new API. A shopping cart with a tiered discount shows DesignExecute, an if/elif/else in a real plan, and reading the trace to see which branch fired.

intermediate10 min
Video coming soon
Browse this tutorial's folder in tutorials-pygithub.com/OpenSymbolicAI/tutorials-py/tree/main/20-conditional-logic

No new API. This page shows Tracks 17 through 19 working together in one realistic example: a shopping cart that applies a tiered discount and reads the execution trace to see which branch fired.

The agent#

Four primitives and one decomposition. The decomposition teaches the planner the correct approach: compute each item's total, chain add() calls to build the subtotal, pick the discount tier with if/elif/else, apply it, and format the bill.

python
# cart.py
from opensymbolicai.blueprints import DesignExecute
from opensymbolicai.core import decomposition, primitive

PRICES = {
    "apple": 0.99, "banana": 0.49, "book": 12.99,
    "keyboard": 79.00, "notebook": 4.99, "pen": 1.49, "stapler": 8.99,
}

class Cart(DesignExecute):
    """A shopping cart with tiered discounts: 15% over $100, 10% over $50."""

    @primitive(read_only=True)
    def item_total(self, name: str, qty: int) -> float:
        """Price for qty units of name."""
        return round(PRICES[name] * qty, 2)

    @primitive(read_only=True)
    def add(self, a: float, b: float) -> float:
        """Add two amounts. Chain calls to sum three or more: add(add(a, b), c)."""
        return round(a + b, 2)

    @primitive(read_only=True)
    def apply_discount(self, total: float, pct: float) -> float:
        """Reduce total by pct percent (e.g. pct=15 means 15% off)."""
        return round(total * (1.0 - pct / 100.0), 2)

    @primitive(read_only=True)
    def format_bill(self, subtotal: float, discount_pct: float, final: float) -> str:
        """Format a receipt showing subtotal, discount tier, and final amount."""
        if discount_pct > 0:
            return f"Subtotal: ${subtotal:.2f}. Discount: {discount_pct:.0f}%. Total: ${final:.2f}"
        return f"Total: ${final:.2f} (no discount)"

    @decomposition(
        intent="how much for 3 notebooks, 1 stapler, and 2 pens?",
        expanded_intent=(
            "Compute each item's total, chain add() to build the subtotal "
            "(add takes exactly two arguments, so nest calls for three or more: "
            "add(add(a, b), c)), then pick the discount tier: 15% if subtotal "
            "exceeds 100, 10% if it exceeds 50, else 0. Apply the discount and "
            "format the bill."
        ),
    )
    def _example_three_items(self) -> str:
        notebooks = self.item_total("notebook", 3)
        stapler = self.item_total("stapler", 1)
        pens = self.item_total("pen", 2)
        subtotal = self.add(self.add(notebooks, stapler), pens)
        if subtotal > 100:
            discount_pct = 15
        elif subtotal > 50:
            discount_pct = 10
        else:
            discount_pct = 0
        final = self.apply_discount(subtotal, discount_pct)
        bill = self.format_bill(subtotal, discount_pct, final)
        return bill

Three tasks, one per discount tier#

python
# main.py
from cart import Cart
from opensymbolicai.llm import LLMConfig

llm = LLMConfig(provider="ollama", model="qwen2.5-coder:7b")
agent = Cart(llm=llm)

TASKS = [
    ("no discount",  "How much for 2 pens, 3 notebooks, and 1 stapler?"),
    ("10% tier",     "How much for 2 pens, 3 notebooks, 1 stapler, and 5 books?"),
    ("15% tier",     "How much for 2 pens, 3 notebooks, 1 stapler, 5 books, and 2 keyboards?"),
]

for label, task in TASKS:
    result = agent.run(task)
    print(f"=== {label} ===")
    print("plan:")
    print(result.plan)
    print("result:", result.result)
    print()
bash
uv run main.py
text
=== no discount ===
result: Total: $26.94 (no discount)

=== 10% tier ===
result: Subtotal: $91.89. Discount: 10%. Total: $82.70

=== 15% tier ===
result: Subtotal: $249.89. Discount: 15%. Total: $212.41

Reading the trace to see which branch fired#

The if/elif/else lives in the plan. The trace records only primitive calls, not the branch that was taken. To see which tier fired, look at the pct argument passed to apply_discount:

python
for step in result.trace.steps:
    status = "ok" if step.success else "FAIL"
    value = repr(step.result_value) if step.result_value is not None else ""
    print(f"  [{status}] {step.statement}")
    if value:
        print(f"         -> {value}")

Sample trace for the 10% tier task:

text
[ok] item_total(name='pen', qty=2)
         -> 2.98
  [ok] item_total(name='notebook', qty=3)
         -> 14.97
  [ok] item_total(name='stapler', qty=1)
         -> 8.99
  [ok] item_total(name='book', qty=5)
         -> 64.95
  [ok] add(...)
  ...
  [ok] apply_discount(total=91.89, pct=10)
         -> 82.7
  [ok] format_bill(subtotal=91.89, discount_pct=10, final=82.7)
         -> 'Subtotal: $91.89. Discount: 10%. Total: $82.70'

The pct=10 in the apply_discount call confirms the 10% branch ran.