ESC

Type to search...

Taking Orders

CLI interface for Sandy — orders from the command line.

Sandy needs to take orders without writing Python code. Let’s build a CLI.

The evo CLI

EVOID ships with a CLI called evo. Use it to run services and manage your project:

# Create a new project
evo init sandy-shop
cd sandy-shop

# Create a service
evo service new orders

# Run the service
evo service run orders

Building the Orders Service

Edit services/orders/main.py:

from evoid import Intent, Level
from evoid.core import Context
from evoid.core.extend import add_intent

# Menu data
MENU = [
    {"id": 1, "name": "BLT", "price": 8.99},
    {"id": 2, "name": "Club", "price": 9.99},
    {"id": 3, "name": "Veggie", "price": 7.99},
]

# Intents
ORDER_SANDWICH = Intent(
    name="order_sandwich",
    level=Level.STANDARD,
    metadata={"shop": "Sandy's Sandwiches"},
)

VIEW_MENU = Intent(
    name="view_menu",
    level=Level.EPHEMERAL,
)

# Processors
async def handle_order(ctx: Context) -> dict:
    sandwich = ctx.intent.metadata.get("sandwich", "BLT")
    qty = ctx.intent.metadata.get("qty", 1)

    # Find in menu
    item = next((m for m in MENU if m["name"] == sandwich), None)
    if not item:
        return {"error": f"'{sandwich}' not on menu"}

    total = item["price"] * qty
    ctx.state["order_total"] = total

    return {
        "status": "confirmed",
        "sandwich": sandwich,
        "quantity": qty,
        "price_each": item["price"],
        "total": total,
    }

async def handle_menu(ctx: Context) -> dict:
    return {"menu": MENU}

# Register
add_intent(ORDER_SANDWICH, handle_order)
add_intent(VIEW_MENU, handle_menu)

Running as a Service

evo service run orders

The service starts and listens for Intents. Other services or adapters can send Intents to it.

Direct Execution

For testing, execute Intents directly in Python:

import asyncio
from evoid import execute

async def test():
    # View menu
    result = await execute(VIEW_MENU)
    print(result.value)
    # {'menu': [{'id': 1, 'name': 'BLT', 'price': 8.99}, ...]}

    # Place an order
    result = await execute(ORDER_SANDWICH, sandwich="BLT", qty=2)
    print(result.value)
    # {'status': 'confirmed', 'sandwich': 'BLT', 'quantity': 2, 'price_each': 8.99, 'total': 17.98}

    # Check timing
    print(f"Completed in {result.duration:.4f}s")
    print(f"Processors: {result.processors}")

asyncio.run(test())

Pipeline with Timeout

Add a timeout to prevent slow orders:

ORDER_SANDWICH = Intent(
    name="order_sandwich",
    level=Level.STANDARD,
    timeout=5.0,  # 5 second max
)

If the processor takes longer, the pipeline returns a failed Result with TimeoutError.

Adding a Processor Chain

Sandy wants inventory checking before order confirmation:

from evoid.core import Context

async def check_inventory(ctx: Context) -> dict:
    """Verify sandwich is available."""
    sandwich = ctx.intent.metadata.get("sandwich", "BLT")
    # Simplified — in real app, check database
    available = sandwich in ["BLT", "Club", "Veggie"]
    ctx.state["available"] = available
    if not available:
        return {"error": f"'{sandwich}' unavailable"}
    return {"checked": True}

async def handle_order(ctx: Context) -> dict:
    if not ctx.state.get("available", False):
        return {"error": "Item unavailable"}
    # ... process order
    return {"status": "confirmed"}

Wire them into a pipeline:

from evoid.core.extend import add_intent_with_pipeline

add_intent_with_pipeline(
    ORDER_SANDWICH,
    processors=[check_inventory, handle_order],
)

Now the pipeline runs: check_inventoryorder_sandwich.

What You Learned

ConceptWhat It Is
evo CLIProject management, service creation, running
Service modelGroup related Intents into a runnable service
execute()Run Intents directly with keyword arguments
TimeoutLimit pipeline execution time
add_intent_with_pipelineCustom processor chain for an Intent

Next: Kitchen Pipeline

Let’s build the full kitchen workflow — Kitchen Pipeline.