Turn Completion Notes

Tell chat users why an agent turn stopped early instead of a silent truncation

Turn a coarse last_stop_reason into a concise, user-safe note so a chat user learns why a turn stopped early β€” instead of getting a silently truncated or empty reply.

Diagram

Turn Completion Notes

πŸ€– Agent turn

⚠️ last_stop_reason=max_steps

πŸ“ TurnCompletion.note

βœ… Reply + note

Covered reasons mirror Agent.last_stop_reason: completed | max_steps | cancelled | error, plus the three provider-block reasons content_filtered | refused | length_truncated. A deep-research agent hits its step limit; instead of a truncated answer, the reply ends with ⏳ I stopped after reaching the step limit β€” reply "continue" to carry on. As of PR #5764, error is reliably set on a terminal LLM failure (server 500, revoked key, exhausted retries) in both the sync and async paths, so the gateway can surface the default ⚠️ This turn ended early due to an error β€” please try again. note instead of a silent empty reply.

python
from praisonaiagents import Agent
from praisonai.bots import BotSessionManager

agent = Agent(
    name="Deep Researcher",
    instructions="Do multi-step web research and summarise findings.",
    max_steps=20,  # long budget β€” truncation is possible
)

# Opt in on the session manager: tell the user *why* if a turn stops early.
session = BotSessionManager(
    platform="telegram",
    surface_completion_reason=True,
)
# When the agent hits its step budget, the reply ends with the completion note.
reply = await session.chat(agent, user_id="123", prompt="Research X in depth")

Quick Start

  1. Enable the notes on the gateway

    Construct the session manager with surface_completion_reason=True. Clean completed turns still surface nothing.

    python
    from praisonai.bots import BotSessionManager
    
    session = BotSessionManager(
        platform="telegram",
        surface_completion_reason=True,  # append the note when a turn stops early
    )
  2. Customise a note per turn

    Override the default note for a turn by returning an AgentReply with a TurnCompletion that carries detail.

    python
    from praisonaiagents.bots import AgentReply, TurnCompletion
    
    reply = AgentReply(
        text="Here's what I found so far.",
        completion=TurnCompletion(
            reason="max_steps",
            detail="Long research task β€” reply 'continue' to keep going.",
        ),
    )

How It Works

After each turn the session manager reads the agent’s last_stop_reason, wraps it in a TurnCompletion, and β€” when enabled β€” appends the note to the reply.

Diagram
Userappend_completion_noteextract_completionBotSessionManagerAgentUserappend_completion_noteextract_completionBotSessionManagerAgentturn ends (last_stop_reason=max_steps)extract_completion(result / agent)TurnCompletion(reason=”max_steps”)append_completion_note(text, completion, enabled=True)text + “⏳ I stopped after…”reply + note

extract_completion recognises an AgentReply with a completion, a serialised dict, or any object exposing completion / last_stop_reason (including a bare Agent). append_completion_note returns the text unchanged when disabled, when there is no completion, or when the turn completed cleanly.


Reasons and Default Notes

TurnCompletion.note() maps each stop reason to a user-safe message. A clean completion is intentionally silent.

reasonNote surfaced to the user
completed(nothing β€” clean completion is silent)
max_steps⏳ I stopped after reaching the step limit β€” reply "continue" to carry on.
cancelledπŸ›‘ This turn was interrupted before it finished β€” send it again to retry.
error⚠️ This turn ended early due to an error β€” please try again.
content_filtered⏳ This turn ended early β€” please try again. (generic fallback β€” see note)
refused⏳ This turn ended early β€” please try again. (generic fallback β€” see note)
length_truncated⏳ This turn ended early β€” please try again. (generic fallback β€” see note). Fires only after output_continuation‘s cap is exhausted β€” see Output Continuation.
(unknown/new)⏳ This turn ended early β€” please try again.

Configuration Options

SettingTypeDefaultDescription
surface_completion_reasonboolFalseOn BotSessionManager. Opt in to append the completion note to gateway replies. Clean completed turns still surface nothing.

TurnCompletion and append_completion_note take these fields:

Field / ArgumentTypeDefaultDescription
TurnCompletion.reasonstr"completed"Stop reason (`completed \max_steps \cancelled \error`; unknown tolerated).
TurnCompletion.detailstr""Optional user-safe override for the default note.
append_completion_note(..., enabled=)boolFalseOff by default β€” returns the text untouched unless enabled.

Common Patterns

Long research agents with a high max_steps β€” enable the note so "continue" becomes a discoverable UX:

python
agent = Agent(name="Researcher", instructions="Deep multi-step research.", max_steps=20)
session = BotSessionManager(platform="telegram", surface_completion_reason=True)
# Hits the budget β†’ reply ends with the "reply continue" note.

Cancellation UX β€” the user hits /stop and sees the cancelled note instead of no reply:

python
# reason="cancelled" β†’ "πŸ›‘ This turn was interrupted before it finished β€” send it again to retry."

Error transparency β€” the user sees the error note and can retry without opening a ticket:

python
# reason="error" β†’ "⚠️ This turn ended early due to an error β€” please try again."

Best Practices

Leave off for internal bots

Enable it for public-facing gateways. Internal bots often prefer silence, so keep the default False there.

Use detail to stay on-brand

TurnCompletion.detail overrides the default note per turn β€” use it to keep messages localised and on-brand.

Happy paths stay quiet

Notes never surface on a clean completion, so enabling this adds no noise to successful turns.

Pair with max_steps

Combine with the step-limit docs so users understand the “reply continue” flow when a turn hits its budget.


Related