Skip to main content
Degraded Delivery makes presentation downgrades visible — when a chart becomes text, a select becomes buttons, or a long callback is shortened, the adapter appends a short fallback note and records a machine-readable reason instead of degrading silently.

Quick Start

1

Adapt and report in one call

adapt_presentation_with_report returns the adapted presentation plus a typed report you can append to the last text block:
2

Preserve lossless callbacks

Attach a callback_store so long reply / select values round-trip losslessly — DEGRADE_CALLBACK_DATA_TOO_LONG is then not reported:
3

Aggregate by reason code

Count DEGRADE_* codes across a run to find channels or UIs that consistently degrade. Compare against the module constants, not string literals:
adapt_presentation() already downgrades unsupported controls to text, but returned no record of what it dropped — the user and the model never learned that a button vanished or a chart became text. adapt_presentation_with_report() is a drop-in replacement that returns both the adapted presentation and a typed report. The original adapt_presentation() is retained unchanged.

Which function?


DegradedDelivery

adapt_presentation_with_report() is a drop-in replacement for adapt_presentation() that also reports what degraded.
  • Import: from praisonaiagents.bots import DegradedDelivery, adapt_presentation_with_report
  • Signature: adapt_presentation_with_report(presentation, limits, *, callback_store=None) -> tuple[MessagePresentation, DegradedDelivery | None]
  • None when nothing degrades — an adapter that always appends report.fallback_text should guard on report is not None.

Reason codes

All exported from praisonaiagents.bots. Compare against the DEGRADE_* constants, not string literals.

How It Works

The report is derived from the same conversion and selection decisions as adapt_presentation, so it never disagrees with the adaptation. The report respects the adaptation order and priority:
  • select → buttons is applied first, then buttons truncation runs — so a huge select on Telegram reports select_unsupported and (if it overflows the cap) buttons_truncated.
  • A web_app / lossy-callback on a dropped button is not reported — the user never saw it.
  • Callback shortening is reported only when the adapter genuinely emitted a lossy payload — a callback that round-trips losslessly via a callback_store is not reported.
  • Plain-callback actions (already channel-safe) are never reported.
Attaching a callback store turns lossy callback shortening into lossless round-tripping — the DEGRADE_CALLBACK_DATA_TOO_LONG code stops firing.

User Interaction Flow

A user in Telegram asks a status bot for a bar chart of yesterday’s errors. Telegram cannot render charts natively. The bot delivers a compact text summary and appends "(chart rendered as text.)" — so the user knows the numbers are the whole picture. The operator dashboard buckets this event under chart_rendered_as_text.

Common Patterns

Append the fallback to the last text block — a one-liner in the adapter’s send path keeps every channel’s fallback consistent:
Attach a callback_store on channels with byte-capped callbacks — the built-in InMemoryCallbackPayloadStore (already used by TelegramBot) turns lossy shortening into lossless round-tripping. Alert on repeated buttons_truncated / options_truncated — signals a UI that consistently over-builds for the target channel.

Best Practices

Silent downgrades hide UX issues from users and operators alike — surface them.
The reason codes are stable module constants for exactly this reason.
Guard with if report is not None: before appending report.fallback_text.
It eliminates the only lossy adaptation (DEGRADE_CALLBACK_DATA_TOO_LONG) and makes long-value round-trips exact.

Interactive Bot Messages

Buttons, selects, tables, and charts across channels

Interactive Callback Store

Lossless round-tripping for long callback payloads

Failure Reply

The failure-path counterpart

Visible-Outcome Guarantee

Every turn ends in a visible outcome
Introduced in PraisonAI commit 892b9fb (fixes #3799).