SDK Reference
Types

Types

The Python types you'll interact with when using the Papayya SDK.

Public types

Importable directly from papayya:

TypeWhat it is
PapayyaThe canonical client. Durable surface (papayya().item(...)) and hosted-resource namespaces (papayya.runs, papayya.items, ...) on one class.
ClientAlias of Papayya (the old standalone Client folded in at 0.3.0). isinstance(x, Client) and isinstance(x, Papayya) are the same check.
ItemThe durable per-item — one thing a run processed, with an outcome, a trace, and a cost. Wraps functions as checkpointed steps.
PapayyaRunDeprecated alias of Item, kept so existing imports and type hints keep working. New code says Item.
RunResultLegacy 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.
CreditExhaustedRaised when a provider reports credit/quota exhaustion. Pauses the run rather than failing it. See Observability.
WorkloadPausedRaised 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 an Item: one item a run processed. A run now names the whole invocation (one map()/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 the run_id field (also exposed as Item.id); the invocation it belongs to is invocation_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 Item is durability-only — deploy the agent and pass budget_usd= on the decorator (or budget_cents on 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 to

DurableRunResult

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: int

CheckpointStore

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 via papayya.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:

ValueMeaning
"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:

ValueMeaning
"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.