Types
The Python types you'll interact with when using the Papayya SDK.
Public types
Importable directly from papayya:
| Type | What it is |
|---|---|
Papayya | The canonical client. Durable surface (papayya().item(...)) and hosted-resource namespaces (papayya.runs, papayya.items, ...) on one class. |
Client | Alias of Papayya (the old standalone Client folded in at 0.3.0). isinstance(x, Client) and isinstance(x, Papayya) are the same check. |
Item | The durable per-item — one thing a run processed, with an outcome, a trace, and a cost. Wraps functions as checkpointed steps. |
PapayyaRun | Deprecated alias of Item, kept so existing imports and type hints keep working. New code says Item. |
RunResult | Legacy result type of the removed Client.run_sync — a str subclass that also carries .run_id and .status. Kept importable for old annotations; nothing constructs it anymore. |
CreditExhausted | Raised when a provider reports credit/quota exhaustion. Pauses the run rather than failing it. See Observability. |
WorkloadPaused | Raised at a step boundary when a fence paused the run — a budget breach, a degraded-output streak, or a workload-level degraded rate. Carries .reason and .run_id. The completed step is checkpointed and the item is parked in the queue; resume unparks it, re-runs the steps the fence objected to, and continues from there. See Budget. |
The noun shift, made concrete. The class that wraps your steps used to be called
PapayyaRun. Under the agent → run → item → step vocabulary it is anItem: one item a run processed. Arunnow names the whole invocation (onemap()/iter()call or one cron fire) that mints many items. Several dataclass names below still say "Run" for compatibility — they describe one item's durable item. Its own surrogate id is therun_idfield (also exposed asItem.id); the invocation it belongs to isinvocation_id.
Durable dataclasses
These live in papayya.durable.types. You rarely construct them yourself — the Item handle and the checkpoint store manage them — but you'll read them back off results and stored checkpoints.
DurableRunConfig
Configuration for constructing an Item. papayya().item(agent="...", ...) builds one for you.
@dataclass
class DurableRunConfig:
agent: str # Agent name (required)
run_id: str | None = None # The item's own id; pass to resume an existing item
metadata: dict | None = None # User JSON; supports partition indexing — see [Multi-tenancy](/concepts/multi-tenancy)
store: CheckpointStore | None = None # Custom store; auto-selected (local / Cloud) via the client factory
item_id: str | None = None # CUSTOMER identity for the item (e.g. "co_42"); every step inherits it
partition_key: str | None = None # The tenant — passed at the iter()/map() boundary or on the item
parent_run_id: str | None = None # Sub-item lineage: the invocation that spawned this one
invocation_id: str | None = None # The run (invocation) row this item belongs to; minted by map()/iter()Budget and cost are hosted-only. A local
Itemis durability-only — deploy the agent and passbudget_usd=on the decorator (orbudget_centson the API) to get cost enforcement.
TaskEntry
A single completed step inside an item. Returned in the tasks list of DurableRunResult and RunCheckpoint. (Named TaskEntry from before task → step; item.step(...) and its item.task(...) alias both write these.)
@dataclass
class TaskEntry:
label: str # The step label (passed to item.step("label", fn))
result: any # The function's return value (cached on replay)
duration_ms: int # Wall-clock duration of the step call
completed_at: str # ISO-8601 timestamp
item_id: str | None = None # Record identifier for the lineage view
input_snapshot: any = None # Auto-captured from step args when item_id is in scope
output_snapshot: any = None # Step return value, captured when item_id is in scope
kind: str | None = None # "llm" for LLM steps; None otherwise
llm_prompt_tokens: int | None = None
llm_completion_tokens: int | None = None
llm_total_tokens: int | None = None
llm_model: str | None = None
llm_stop_reason: str | None = None
llm_provider_shape: str | None = None
partition_key: str | None = None # The tenant, denormalized off the item
metadata: dict | None = None
outcome_status: str = "ok" # "ok" | "degraded" | "failed" — see Outcome verdict below
outcome_reason: str | None = None # reason token when degraded (e.g. "empty_none")RunCheckpoint
The full snapshot of one item's durable item, persisted by the checkpoint store. You typically don't construct this — the Item manages it.
@dataclass
class RunCheckpoint:
run_id: str # The item's own surrogate id
agent: str
tasks: list[TaskEntry] # This item's steps, in execution order
status: str # "running" | "completed" | "failed"
created_at: str = ""
updated_at: str = ""
item_id: str | None = None # Customer item identifier
input_snapshot: any = None # The payload that produced this item — replay source
agent_version: str | None = None
metadata: dict | None = None
partition_key: str | None = None # The tenant
parent_run_id: str | None = None # Sub-item lineage
worst_outcome_status: str = "ok" # Worst outcome across this item's steps
degraded_count: int = 0 # How many steps are not 'ok'
invocation_id: str | None = None # The run (invocation) this item belongs toDurableRunResult
Returned by item.complete() and item.fail().
@dataclass
class DurableRunResult:
run_id: str # The item's id
agent: str
status: str # "completed" | "failed"
tasks: list[TaskEntry] # The item's steps
total_duration_ms: intCheckpointStore
A protocol you can implement to plug in a custom persistence backend. Papayya ships MemoryStore (the default for a bare Item), FileStore, SQLiteStore (pass store=SQLiteStore(...) to persist an offline run to disk), and a cloud-backed store selected automatically when an API key is present.
from typing import Protocol, runtime_checkable
@runtime_checkable
class CheckpointStore(Protocol):
def load(self, run_id: str) -> RunCheckpoint | None: ...
def save_task(self, run_id: str, entry: TaskEntry) -> None: ...
def set_status(self, run_id: str, status: str, output: any = None) -> None: ...
def create(self, checkpoint: RunCheckpoint) -> None: ...Outcome verdict
Every step's return value is inspected, and the item carries the result as an outcome — distinct from its execution status. An outcome is:
ok— nothing wrong detected.degraded— the call returned but didn't actually work, with a reason token naming why:empty_none,empty_string,refusal, and similar. Set by the structural inspectors, or explicitly viapapayya.mark_degraded("reason").failed— the step raised.
Outcomes roll up: a run reports worst_outcome_status (ok | degraded | failed, by severity) and a degraded_count across its items. This is the ran-vs-worked verdict — an item can return a clean HTTP 200 and still be degraded.
Status values
An item's execution status (on RunCheckpoint / DurableRunResult) is one of:
| Value | Meaning |
|---|---|
"running" | The item is in progress |
"completed" | Finished successfully via item.complete() |
"failed" | Finished via item.fail() or an unhandled exception |
A run (the invocation over N items) carries its own lifecycle, broader than a single item's:
| Value | Meaning |
|---|---|
"materializing" | The run is being expanded into items |
"queued" | Waiting in the dispatcher queue |
"running" | Items are executing |
"paused" | Paused — usually a budget cap or a provider credit error; in-flight state preserved for operator-driven resume |
"completed" | Every item finished |
"partial" | Finished, but some items failed |
"failed" | The run failed |
"cancelled" | Cancelled by an operator |
"budget_exceeded" | Hit the per-run budget cap |
Runs paused mid-stream for an operator decision surface through the quarantine lane (client.items.quarantine / release / discard) and the papayya triage CLI. See the API Reference for the full set of error_code values returned with failed runs.