Documentation Index
Fetch the complete documentation index at: https://developers.autoplay.ai/llms.txt
Use this file to discover all available pages before exploring further.
Use autoplay_sdk.agent_state.v2 (SessionState) as the only supported agent-state model.
SessionState v2
Overview
autoplay_sdk.agent_state.v2 provides the supported three-state FSM and enforces a clear timeout-only exit rule from active states. Timeouts and cooldowns are session-level settings rather than per-trigger values.
States
| Wire value | Python enum | Meaning |
|---|
thinking | AgentStateV2.THINKING | Idle baseline. Proactive offers may fire — unless the cooldown period is still active. |
proactive_assistance | AgentStateV2.PROACTIVE | The assistant surfaced an unsolicited quick-reply offer. User has not yet responded. |
reactive_assistance | AgentStateV2.REACTIVE | The user opened the chatbot or sent a message; the session is in active dialogue. |
Transition rules
| From | To | How | Notes |
|---|
thinking | proactive_assistance | transition_to_proactive(trigger_id) | Returns False (no state change) if cooldown_active is still True. Flushes any expired cooldown first. |
thinking | reactive_assistance | transition_to_reactive() | Always allowed from THINKING. Clears cooldown — user initiating a chat cancels the proactive backoff period. |
proactive_assistance | thinking | tick() only — automatic | The only exit. Triggered when now − last_interaction_at > interaction_timeout_s. Always starts the cooldown on arrival. |
reactive_assistance | thinking | tick() only — automatic | The only exit. Same timeout rule as above. |
proactive_assistance | reactive_assistance | ❌ Never | Raises InvalidTransitionError. |
reactive_assistance | proactive_assistance | ❌ Never | Raises InvalidTransitionError. |
any → proactive_assistance when not THINKING | ❌ Never | Raises InvalidTransitionError. | |
any → reactive_assistance when not THINKING | ❌ Never | Raises InvalidTransitionError. | |
_timeout_to_thinking() is a private method called exclusively by tick(). Never call it directly in your code — use tick() on every incoming event or background pulse instead.
Session-level timeout settings
| Field | Default | Meaning |
|---|
interaction_timeout_s | 20 s | How long the user can go without interacting with the chatbot or visual guidance before returning to thinking. Applies in both proactive and reactive states. |
cooldown_period_s | 60 s | After returning to thinking from any active state, how long before proactive can fire again. Auto-clears when the period elapses. |
Session-owned routing fields
These fields are persisted directly on SessionState and are the canonical delivery routing source:
| Field | Default | Meaning |
|---|
conversation_linked | False | Whether this session_id is currently linked to a conversation |
conversation_id | None | Derived destination thread id used for hot-path send routing when linked |
SessionState.on_conversation_linked(link) transition rules:
ConversationEventType.NEW: always set conversation_linked=True and overwrite conversation_id
ConversationEventType.REPLY_EXISTING: set link fields only when currently unlinked; keep existing conversation_id when already linked
- invariant: during a live session, this path never flips
conversation_linked back to False
State and flag matrix
| State | Purpose | Key fields typically read/updated | Routing implication |
|---|
THINKING | Baseline idle state | thinking.cooldown_active, last_interaction_at | If conversation_linked is true, route to conversation_id; otherwise create/link |
PROACTIVE | Assistant initiated offer | proactive.user_clicked_option, last_interaction_at, visual_guidance_active | Same routing rule: state does not change session-owned conversation link |
REACTIVE | User-initiated chat | reactive.time_since_last_interaction_s, last_interaction_at, visual_guidance_active | Same routing rule: session-owned link remains the send target |
Per-state sub-dataclasses
Each state carries a typed sub-object that tracks its own metrics. These are reset when the session enters that state.
ThinkingState
| Field | Type | Meaning |
|---|
cooldown_active | bool | True while the cooldown period is blocking a new proactive offer. |
cooldown_started_at | float | Monotonic timestamp of when the cooldown began. |
active_cooldown_period_s | float | None | Tour-specific cooldown override. When set, this period is used instead of the session-level cooldown_period_s. None means use the session default. |
can_go_proactive | property | True when cooldown_active is False. |
ProactiveState
| Field | Type | Meaning |
|---|
user_clicked_option | bool | True once the user taps a quick-reply chip. |
time_since_last_interaction_s | float | Seconds since the last interaction of any kind — chat message, option click, reaction, or tour step. Updated by tick(). |
ReactiveState
| Field | Type | Meaning |
|---|
time_since_last_interaction_s | float | Seconds since last user interaction. Updated by tick(). |
Public exports
from autoplay_sdk.agent_state.v2 import (
AgentStateV2,
InvalidTransitionError,
SessionState,
ThinkingState,
ProactiveState,
ReactiveState,
)
Import InvalidTransitionError from autoplay_sdk.agent_state.v2 with the rest of the v2 symbols.
Key methods
| Method | Description |
|---|
tick() | Evaluate all timeout rules. Call on every incoming event and on a background pulse (e.g. every 5 s) to catch silent timeouts. |
can_deliver_proactive() | Returns (allowed: bool, reason: str) — the canonical FSM gate check. Call tick() first. Returns (True, "ok") when THINKING with no active cooldown, (False, "v2_state_<state>") when not in THINKING, (False, "v2_cooldown_active") when cooldown is active. Do not replicate this check inline. |
transition_to_proactive(trigger_id) | Move to PROACTIVE. Returns False if cooldown is active; raises InvalidTransitionError if not in THINKING. |
transition_to_reactive() | Move to REACTIVE. Raises InvalidTransitionError if not in THINKING. Clears cooldown. |
record_any_interaction() | Reset last_interaction_at. Call on any chatbot or visual guidance activity. |
record_user_interaction() | User sent a chat message. |
record_option_click() | User clicked a quick-reply chip. |
record_reaction() | User added an emotional reaction. |
record_tour_step() | Any tour progress (page advance, step complete). Resets the interaction_timeout_s countdown even when the user has never opened the chat — visual guidance counts as interaction. |
set_visual_guidance(active) | Toggle visual_guidance_active on the current state. Starting a tour (active=True) also resets last_interaction_at. No-op in THINKING. |
on_conversation_linked(link) | Apply canonical NEW vs REPLY_EXISTING link semantics to conversation_linked / conversation_id. |
to_dict() / from_dict(d) | Round-trip JSON snapshot for Redis or any store. from_dict raises ValueError on wrong _v. |
Persistence
# Save
await save_session(product_id, session_id, session)
# Load
session = await load_session(product_id, session_id)
session.tick() # flush any elapsed timeouts before acting
Session scope is primary: persist/load by session_id, and treat conversation_id as derived session-owned routing state.
Example
from autoplay_sdk.agent_state.v2 import (
AgentStateV2,
InvalidTransitionError,
SessionState,
)
# Start fresh
session = SessionState(interaction_timeout_s=20.0, cooldown_period_s=60.0)
# Proactive trigger fires
fired = session.transition_to_proactive("trig_001")
assert fired is True
assert session.current_state == AgentStateV2.PROACTIVE
# User clicks a chip
session.record_option_click()
# Background pulse — flush timeouts
session.tick()
# Simulate 25 s of silence → transitions back to thinking with cooldown
session.last_interaction_at -= 25.0
session.tick()
assert session.current_state == AgentStateV2.THINKING
assert session.thinking.cooldown_active is True
# During cooldown, proactive is blocked
assert session.transition_to_proactive("trig_002") is False
# After cooldown expires, proactive is allowed again
session.thinking.cooldown_started_at -= 70.0
session.tick()
assert session.thinking.can_go_proactive is True
# Persist
blob = session.to_dict()
restored = SessionState.from_dict(blob)
See also