Local Engine
The "engine" is the internal step loop that drives an item's execution. You don't construct it directly in Python — the @agent decorator wires it up automatically, and the explicit Item handle plays the same role for the local-execution path. This page explains the conceptual model and where the durable ledger lives.
What the engine does
The engine runs the same loop in both local and hosted modes, with budget/cost enforcement only in hosted mode:
- Take input — the element
map()/iter()yielded, the item you opened withpapayya().item(...), or the argument passed to a deployed agent. - Call your code — the engine invokes your function or your
item.step(...)callbacks. - Intercept LLM calls — an
@papayya.llmleaf items each provider call (model, tokens, timing) and inspects the response. In the hosted runtime a shim additionally auto-wraps OpenAI / Anthropic clients so their calls are captured even without the decorator. - Checkpoint each step — every step becomes a durable row in the ledger, with the wrapped call's result, timing, and (for LLM steps) token counts and stop reason.
- Inspect outcomes — the return value is checked for known degraded shapes (refusal, empty result, degenerate stop reason). A match flips the item's outcome to
degraded; the parent run'sworst_outcome_status/degraded_countroll up automatically. - Enforce budgets (hosted only) — before each LLM call the runtime reserves worst-case cost against the run's budget. If the reservation exceeds the cap, the call is held back and the run pauses with
budget_exceeded. - Detect failures — wall-clock timeouts, step timeouts, max-steps limits, and provider credit-exhaustion errors surface as typed terminal states.
- Record the result — when your code returns, the item is marked
completedand its output stored; a raised exception marks itfailed.
The local ledger
Run python agent.py locally with the free SDK — it consumes no hosted compute. For an offline run, pass an explicit store=SQLiteStore(...) to the Item handle and every checkpoint persists to that SQLite file on disk. For a prod-like local environment, use docker-compose, which mirrors production and differs only by endpoint.
When PAPAYYA_API_KEY is set, the engine selects the cloud-backed store instead — the call site is identical either way — and you view runs, items, and steps in the hosted dashboard at app.getpapayya.com (opens in a new tab).
Where the engine runs
| Path | Where the engine runs | What it sees |
|---|---|---|
Local (papayya().item(...), or an @agent function run on your laptop) | Your process, writing to an explicit SQLiteStore on disk | Each step is a checkpoint. @papayya.llm leaves item token/outcome telemetry. Cost is not tracked — the local path is durability + observability. |
Hosted (papayya deploy → worker pool) | A container in the Papayya runtime | A shim also auto-wraps OpenAI / Anthropic clients at import time, so their calls are captured, persisted, and budget-checked automatically alongside your @papayya.llm leaves. |
Why the engine isn't a Python class you import
The hosted engine is implemented in Go, runs in the worker, and talks to your agent's container over HTTP. The local engine is the durable-step machinery behind the Item handle plus the import-time shim that wraps OpenAI / Anthropic clients inside the container. Neither is an object you'd construct in your own Python code.
If you want low-level control over the step loop in your own process, open an item — item = papayya().item(agent="...") — and wrap your steps with item.step(...) / item.llm_step(...). That's the public entry point for "I want checkpointing without deploying." (The underlying class is Item, exported as PapayyaRun for backward compatibility.)
See also
- SDK Overview — the three adoption rungs side by side
- Agent — the
@agentdecorator - Types — the durable dataclasses and the checkpoint-store protocol
- Budget — how budgets are enforced in the hosted engine