# Abstra Tasklets — Examples

Each block is a **complete tasklet stage file**. A tasklet runs once per task that arrives. See `tasklets_guide.md` for the API. Prefer `with get_trigger_task() as task:` — it locks, completes on success, and reverts to pending (auto-retry) on error.

## 1. Canonical worker — process one task idempotently

```python
from abstra.tasks import get_trigger_task
from abstra.connectors import run_connection_action
from abstra.logging import logger

# lock -> body -> complete (on success) / revert-to-pending (on exception).
with get_trigger_task() as task:
    customer = task["customer_id"]
    logger.info(f"Provisioning customer {customer}")
    run_connection_action("crm", "create_account", {"id": customer})
    # Reaching the end of the block completes the task. Raising would retry it.
```

## 2. Conditional routing — branch the workflow by task type

```python
from abstra.tasks import get_trigger_task, send_task

with get_trigger_task() as task:
    amount = task["order_total"]
    payload = {"order_id": task["order_id"], "amount": amount}

    # The emitted *type* decides which downstream stage runs (transitions route by type).
    if amount > 10_000:
        send_task("needs_approval", payload)   # -> a Form stage for a human
    else:
        send_task("auto_approved", payload)    # -> a tasklet that fulfills it
```

## 3. Fan-out — one task becomes many

```python
# Receive an order, emit one task per line item so they process in parallel.
from abstra.tasks import get_trigger_task, send_task

with get_trigger_task() as task:
    order_id = task["order_id"]
    for item in task["items"]:                 # items is a list in the payload
        send_task("ship_item", {"order_id": order_id, "sku": item["sku"], "qty": item["qty"]})
```

## 4. Idempotent external side effect — safe under retries

```python
# Failures auto-retry, so guard money-moving / create operations with a dedupe
# key before doing them.
from abstra.tasks import get_trigger_task
import abstra.tables as at
from abstra.connectors import run_connection_action

with get_trigger_task() as task:
    payment_id = task["payment_id"]

    # If we already charged this payment in a previous (failed-after-charge) run, skip.
    if at.select_one("charges", where={"payment_id": payment_id}):
        pass  # already done; just complete the task
    else:
        result = run_connection_action("stripe", "create_charge", {
            "amount": task["amount"], "customer": task["customer"],
        })
        at.insert("charges", {"payment_id": payment_id, "stripe_id": result["id"]})
```

## 5. Notify on arrival — Slack / email side effect

```python
from abstra.tasks import get_trigger_task
from abstra.connectors import run_connection_action
from abstra.messages import send_email

with get_trigger_task() as task:
    deal = task.payload                         # the whole dict

    run_connection_action("slack", "send_message", {
        "channel": "#sales",
        "text": f"New deal {deal['name']} — R$ {deal['value']}",
    })
    send_email(
        to=deal["owner_email"],
        title="Deal assigned to you",
        message=f"<p>You own <b>{deal['name']}</b> (R$ {deal['value']}).</p>",
        is_html=True,
    )
```

## 6. Document / AI processing — parse a file the payload points to

```python
# E.g. an email hook saved an invoice and emitted its path; here we extract data.
from pathlib import Path
from abstra.tasks import get_trigger_task, send_task
from abstra.ai import run_agent
import abstra.tables as at

with get_trigger_task() as task:
    pdf = Path(task["path"])

    # run_agent accepts files inline (multimodal). For a fixed extraction, prefer
    # extract_text(pdf) then prompt() on the text — more reliable for text-heavy
    # docs; for Brazilian fiscal docs prefer parse_nfe/parse_boleto/etc.
    result = run_agent(
        prompt=["Extract supplier, invoice number, total, due date as JSON.", pdf],
        max_steps=10,
    )

    at.insert("invoices", {"path": str(pdf), "extracted": result})
    send_task("invoice_parsed", {"path": str(pdf)})
```

## 7. Batch drain — process all pending tasks in one run

```python
# Less common in tasklets (more of a job pattern), but valid: pull every pending
# task at this stage and process them together. iter_tasks auto-paginates.
from abstra.tasks import iter_tasks
from abstra.logging import logger

count = 0
for task in iter_tasks(where={"region": "BR"}):
    with task:
        # ... process task.payload ...
        count += 1
logger.info(f"Processed {count} pending tasks")
```
